JS中的 变量提升¶••⊿作用域¶••⊿闭包 核心原懂得读

作者:日期:2018-03-25 19:27:41 点击:614

数据类型的操作原理

基础数据类型

1••。var a=12;
2.var b=a;
3.b=13;
4••。console.log(a); //=>12

直接在当前作用域中创立了基础数据类型的值(或者说基础类型值直接存储在当前作用域中),然后把这个值和变量关联起来(一个变量只能关联一个值,关联下一个值后和之前关联的值就没关系了),我们把关联这个操作叫做 变量赋值,基础数据类型是直接 按值操作

引用数据类型

1••。var o={name:'珠峰培训'};
2.var p=o;
3.p.name='中国最权威的前端培训机构';
4••。console.log(o.name);

引用数据类型不是直接按值操作的(它的结构复杂,要存储很多值,无法直接的创立值),在JS中遇到引用数据类型(对象或者函数),按照如下操作进行:

1¶••⊿首先开辟一个新的内存空间(浏览器为其分配一个16进制的地址)
2¶••⊿把需要存储的内容存储到内存空间中

  • 对象是把键值对依次存储到空间中
  • 函数是把函数体中的代码当做 ‘字符串’ 存储到内存中

3¶••⊿把空间的地址赋值给对应的变量,所以我们也说:引用数据类型是按照空间的引用地址操作

1.function fn(){
2. var total=null;
3. total=1+1;
4. return total;
5.}
6••。fn();
7••。fn();
8••。...

函数履行的时候也有属于自己的一系列操作
1¶••⊿函数履行首先会形成一个新的私有作用域,目标是为函数体中的代码供给一个履行的环境(而且这个环境是私有的)
2¶••⊿把创立时候存储的代码字符串copy一份到自己的私有作用域中,然后把字符串转换为JS表达式,再然后依次自上而下履行

[总结]
每一次履行函数都是形成一个新的私有作用域,然后把代码重新自上而下履行(一切都从新开端),所以多次履行函数之间是没有直接接洽的,互不干扰;

函数履行形成的私有作用域,掩护了里面的私有变量不受外界的干扰,我们把函数履行的这种掩护机制叫做 闭包(如果当前变量是私有的,那么在函数体中涌现的所有这个变量都和外界没有任何关系)

全局作用域

浏览器渲染页面(渲染页面中的JS)的时候,会供给一个供代码履行的环境 =>全局作用域(window/global)

Alt text

堆内存 & 栈内存

JS中的内存一共两种:堆内存和栈内存

堆内存

作用:用来存储引用数据类型值的内存空间叫做堆内存(对象存储的是键值对,函数存储的是代码字符串)

形成:只要遇到对象/数组/正则/函数等引用类型的值,浏览器首先第一步就是创立一个堆内存…

释放:如果当前的堆内存被变量(或者函数以及元素事件等)占用了(占用了:堆内存地址赋值给变量了),此时的堆内存是有用的,不能烧毁;我们想要手动释放堆内存,只需要让存储地址的变量等于其它值即可(最好等于null,null是空对象指针,本意就是不指向任何的堆内存);

1••。var o={name:'珠峰培训'};//<=> o=AAAFFF000
2••。o=null;//=> 此时堆内存不被占用,浏览器会在空闲的时间,把所有不被占用的堆内存进行主动回收释放(谷歌浏览器的机制,IE浏览器是靠计数器来统计当前堆内存被占用的次数:当计数器统计为零次,阐明没有人占用它,浏览器烧毁这个堆内存)

栈内存

作用:又称为作用域,目标就是供给JS代码履行的环境(供代码履行的),基础数据类型值都是直接的存储在栈内存中

全局作用域(栈内存):
形成:浏览器渲染页面,首先就会形成一个全局作用域
烧毁:关闭当前页面(F5刷新页面:先把页面关闭,然后再重新打开)

私有作用域(栈内存):
形成:函数履行会形成私有的作用域
烧毁:一般情况下,函数体中的代码履行完成,形成的栈内存会立即释放;当然也有不释放的情况,后面再详细来讲••。

变量提升(预解释)

当前作用域中,JS代码自上而下履行之前,浏览器首先会把所有带var/function要害字的,进行提前的声明(declare)/定义(defined),这种提前声明变量的机制,我们称之为变量提升

1¶••⊿变量提升只对当前作用域下的变量或者函数起作用
2¶••⊿带var的在变量提升阶段只是提前的声明(告诉当前作用域有这个变量了而已)
3¶••⊿带function要害字的,在变量提升阶段不仅仅是声明,而且还定义了(定义:其实就是赋值的操作)

1.console.log(total);//=>不会报错:变量提升阶段已经告诉全局有一个total了,只声明没有定义,默认值undefined
2.//fn();//=>可以履行:变量提升阶段就已经完成了fn的声明和赋值操作,此时的fn已经是一个函数了
3.
4.function fn(num1,num2){
5. var total=null;
6. total=num1+num2;
7••。 return total;
8••。}//->代码履行遇到此处直接跳过:变量提升阶段已经完成过一次了
9.var total=fn(10,20);//->给total赋值:变量提升阶段只是完成了它的声明,没有赋值,所以代码履行到这一步需要赋值
10.console.log(total.toFixed(2));

Alt text

函数履行会形成一个新的私有作用域,和全局作用域一样,进来的第一步不是代码履行,私有作用域和全局也有一些差别:私有作用域进来的第一步也不是变量提升

第一步:如果有形参,第一步是形参赋值
第二步:变量提升
第三步:代码自上而下履行

[私有变量]
形参在私有作用域中声明的变量(函数)都是私有的,和外界的其它变量互不干扰

作用域链

函数履行形成私有作用域,在私有作用域中涌现的变量可能是私有的,有可能不是自己私有的,私有和非私有我们的操作步骤是不一样的

[ 私有的 ]
如果是私有的,和外面的就没有任何的关系了,以后在函数体中操作的当前变量都按照私有的处理

[ 非私有的 ]
不是自己私有的,首先向其上级作用域查找,如果也不是上级作用域私有的,则持续向上查找,一直到window全局作用域为止,我们把这种查找机制称为作用域链

1.var a=10,b=20,x=30,y=40;
2.function fn(x){
3. console.log(x,y,a,b);
4. var a=b=x=y=100;
5••。 a=x+y;
6. b=x-y;
7. console.log(x,y,a,b);
8.}
9.fn(x);
10.console.log(x,y,a,b);
11.
12./*
13. var a=10,b=20;
14••。 <=>
15••。 var a=10;
16. var b=20;
17••。
18••。 var a=b=20;
19••。 <=>
20. var a=20;
21. b=20;
22.*/

Alt text

查找上级作用域

当前函数的上级作用域和它在哪履行的没有关系,只和它在哪定义的有关系:在哪个作用域下定义的,那么它的上级作用域就是谁

1.var n=10;
2.function fn(){
3. console.log(n);
4.}
5••。fn();//=>10
6••。
7.~function(){
8. var n=100;
9. fn();//=>10 和在哪履行没关系,FN是在全局下定义的,它的上级作用域是全局作用域,当前自履行函数形成的私有作用域仅是它的‘宿主环境’
10••。}();

不释放的栈内存

一般情况下,函数履行完成,形成的私有作用域都会主动释放;

但是有很多时候,函数履行完,形成的作用域(栈内存)无法释放:当前私有栈内存中的某些东西被栈内存以外的其它内容占用了

当我们手动把栈内存以外占用其内容的东西扫除掉(或者不让其在占用了),之前没有烧毁的栈内存会在浏览器空闲的时候主动烧毁释放

1.var obj={
2. name:'珠峰培训',
3. fn:(function(){
4. var i=10;
5••。 return function(){
6. i++;
7••。 console.log(i);
8. }
9. })()//=>如果传obj.name会报错:因为此时键值对还没有完整存储到堆内存中呢,obj和堆内存还没有关系呢,obj=undefined (毛病:Cannot ready property 'name' of undefined)
10••。};
11.obj.fn();
12.obj.fn();
13••。...
14••。
15.
16.//var obj={name:'xxx'};
17••。//obj••。fn=(function(){})(obj••。name); 这样是可以的,因为此时的obj已经和堆内存关联在一起了

Alt text

i++和++i

i++:先去拿原有的值和别人运算,运算完成后自身累加1
++i:先自身累加1,累加后,拿最新的成果和别人运算

1.var i=4;
2••。console.log(10+(i++)); //=>14
3.console.log(i); //=>5
4••。
5••。i=4;
6••。console.log(10+(++i)); //=>15
7.console.log(i); //=>5

案例练习

1.var i = 0;
2.function fn() {
3. i += 2;
4••。 return function (n) {
5. console.log(n + (++i));
6. }
7••。}
8••。var f = fn(3);
9.f(4);
10.fn(5)(6);
11••。f(7);
12••。fn(8)(9);

Alt text

this

在js中this代表当前函数履行的主体(谁履行的这个函数)

this是谁和函数在哪定义以及在哪履行的,都没有任何关系

1••。function fn(){
2. console.log(this);
3.}
4.var obj={name:'珠峰培训',fn:fn};
5.obj.fn();//->this:obj
6••。fn();//->this:window <=> window.fn();

1¶••⊿自履行函数中的this一般都是window

1.var obj={
2. name:'珠峰培训',
3. fn:(function(){
4. console.log(this);//->window(全局对象也可以叫做浏览器对象)
5. return function(){}
6. })()
7••。};

2¶••⊿给元素的某个事件绑定方法,事件触发方法被履行,此时方法中的this一般都是当前操作元素本身

1.oBox.onclick=function(){
2••。 //->当触发点击事件,履行这个匿名函数的时候,方法中的 this:oBox
3••。}

3¶••⊿方法履行看方法名前面是否有点,有点,点前面是谁,方法中的this就是谁,没有点,方法中的this就是window

1.function fn(){
2. console.log(this);
3.}
4.var obj={name:'珠峰培训',fn:fn};
5.obj.fn();//->this:obj
6.fn();//->this:window

4¶••⊿以上三条都是限定在非严格模式下,在JS严格模式下,上述部分需要换一下

不指定履行主体,this是undefined而不是之前非严格模式下默认的window

1••。~function(){
2. //this->window
3••。}();
4••。fn(); //->fn中的this:window
1••。"use strict";//=>放在JS代码第一行(必定要是当前作用域第一行),让全部JS开启严格模式
2.~function(){
3••。 //this->undefined
4••。}();
5.fn(); //->fn中的this:undefined
6.window.fn(); //->fn中的this:window

综合练习

1.var num = 10;
2••。var obj = {num: 15};
3.obj.fn = (function (num) {
4. this.num += 10;
5••。 num *= 2;
6••。 return function (n) {
7. this.num += n;
8. console.log(n + (--num));
9••。 }
10.})(obj.num);
11.var fn = obj.fn;
12.fn(10);
13.obj.fn(15);
14.console.log(window.num, obj.num);

Alt text

闭包汇总

函数履行,形成一个私有作用域,掩护里面的私有变量不受外界的干扰,这种掩护机制叫做 闭包

但是现在市面上,99%的IT开发者都认为:函数履行,形成一个不烧毁的私有作用域,除了掩护私有变量以外,还可以存储一些内容,这样的模式才是闭包

1.var utils=(function(){
2.
3. return {
4.
5. }
6.})();

闭包作用:
1¶••⊿掩护
团队协作开发,每个开发者把自己的代码存放在一个私有的作用域中,防止相互之间的冲突;把需要供别人应用的方法,通过return或者window.xxx裸露在全局下即可;

jQuery源码中也是利用掩护机制实现的

1.~function(){
2. var jQuery=function(){
3. ...
4. }
5. ...
6. window.$=window.jQuery=jQuery;
7.}();

2¶••⊿保存
选项卡闭包解决措施

单例模式(JS高阶编程技巧:惰性思想/柯理化函数思想…)

上一篇: 面向对象深入解读

下一篇: 正则详解一

幸运农场投注官网 SG飞艇投注平台 吉吉彩票手机app 秒速时时彩技巧 吉吉彩票开奖网 幸运农场开奖结果 台湾宾果官网开奖 吉吉彩票注册平台 快速飞艇官网开奖 九亿九彩票开奖网