js---作用域链,立即执行函数,闭包

1.作用域链----函数 定义时会获得 父级的作用域链的值放在自己的 [[Scopes]]属性中, [[Scopes]]是系统自带的隐式属性,通过console.dir(函数名)   可以查看这个函数的作用域链,即 [[Scopes]]。任何函数都至少会有Global的作用域,嵌套越深,作用域链越长。当执行函数的前一刻时,会生成当前函数的独一无二的AO,添加到自己的作用域链中。
注意:任何函数只能使用自己作用域链里的东西,并且是按照作用域链中的由顶向下查看(顺序:自己的作用域--父级作用域---...---Gobal   也就是说每次添加都是在第一位添加,其他已经存在的顺次向下移),一旦找到就停止寻找。
var a={
	init:function(){
		var num=123;
		this.test();//报错---注意作用域链是在函数定义的时候获得父级作用域链的,此处test不是在init里定义的,作用域链上只有全局的作用域
	},
	test:function(){
	    console.log(num);
	}
}
a.init();

//类似于以下情况
function a(){
	var num=0;
	b();
}
function b(){
	console.log(num);
}
a();//报错---num is not defined

2.立即执行函数(针对初始化功能使用的函数,也可以在项目中起区块作用,避免团队变量函数名称重复相互影响)

(function a(){console.log(1)}());//1 再调用a()---报错
(function b(a,c){console.log(a,c)})(1,2);//1 2 再调用b()---报错
//所以函数名失去意义,上面的函数和下面的函数是作用相同的
(function (){console.log(1)}());//1
(function (){console.log(2)})();//2

//函数只要变成表达式的形式,定义的时候而不是执行的时候,就会发生失去函数名
+function c(){console.log(11)}();//报错---c is not defined

var d=(1+4,2+1);
console.log(d)  //3 逗号表示计算第一个表达式,然后再计算后一个表达式并且覆盖前一个的结果
var a=(function aa(){return 1},+function bb(){return 2});
console.log(a); //NaN 第二个表达式的结果

var a=(function aa(){return 1},function bb(){return 2});
console.log(a); //function bb(){return 2} a(); //2bb(); //报错,var a=(function bb(){}) 是表达式,使bb这个名字失去意义
var cc=1;
if(function c(){}){//除了0 undefined null "" false NaN,其他转成boolean都是true   (function c(){})---表达式  导致c相当于未定义
   console.log(typeof c+cc);//"undefined1"  //typeof undefined 只有typeof 一个未声明定义的变量不会报错,是"undefined".
}

3.闭包---由于子函数将继承父级AO到自己的作用域链中,当子集函数的生命周期超过父级函数的生命周期时会产生闭包。

通俗点来说,就是父级函数已经执行完毕,父函数执行的过程中:子函数被定义获得父级函数的AO后,被保存到外部。使子函数变成一个与父函数同级但是拥有父函数AO的函数。

闭包的作用:实现公有变量:函数累加器;  可以做缓存,存储结构;封装,属性私有化;模块化开发,防止变量污染。

function aa(){
    var num=0;
    function bb(){
        num++;
        console.log(num);
    }
    num=123;
    return bb;
}
var cc=aa();
cc();//124  说明bb和aa是共同使用一个AO,不是复制一份给子函数,是直接公用
cc();//125  函数执行的时候回产生唯一的独一无二的AO,但是闭包中使用的父级的AO是确定的,不会再产生新的,因为父级函数并没有执行,所以是125不是124。

//闭包的小栗子
function bibao(){
  var arr=[];
  for(var i=0;i<5;i++){
     //赋值的时候arr[i]中的i是当前循环的i
     arr[i]=function(){
        console.log(i);
     }
  }
return arr;//最后的arr=[function(){console.log(i)},...,function(){console.log(i)}]
}
var demo=bibao();
demo.forEach(function(item){
  item();//5..5 结果是5个5  item()是function(){console.log(i)} 此时的i是bibao执行时的独一无二的AO中的i的值,循环结束的时候就已经是5了
});

//在求索引的时候很容易出现闭包问题,例如
function indexBiBao(){
  var list=document.getElementsByTagName('li');//假设HTML中有一个ul下有四个li 要给li添加点击事件
    for(var i=0;i<4;i++){
      list[i].addEventListener('click',function(event){
       console.log(i);
    });
    }
}
indexBiBao();//最后点击每个li打印的都是4,因为点击事件被绑定在li上,肯定是在外部了,都不在script标签里了。
             //也就是点击li的时候去执行这个函数,而此时的i应该在indexBiBao执行结束的时候就变成了4

//解决方法
function indexBiBao(){
  var list=document.getElementsByTagName('li');//假设HTML中有一个ul下有四个li 要给li添加点击事件
    for(var i=0;i<4;i++){
        (function(j){ 
        //接收形参  类似于var j;
             //实参赋值  j=i  将当前的i保存在立即执行函数的AO里面,在外面调用时会直接调用父级AO的j     
       list[j].addEventListener('click',function(event){
          console.log(j);
       });      
       })(i);
   }
})
}indexBiBao();//最后点击每个li打印的就是当前的索引值

猜你喜欢

转载自blog.csdn.net/github_39132847/article/details/79638884