执行环境与作用域:
每个执行环境都有与之相关联的变量对象,如果这个环境是函数,则将其活动对象(activation object)作为变量对象,环境中定义的所有变量和函数都保存在这个变量中,活动对象在最开始只包含一个arguments对象。
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
作用域污染:
只要函数内定义了一个局部变量,函数在解析时都会将这个变量“提前声明”
var scope = "global"; function fn(){ console.log(scope);//result:undefined var scope = "local"; console.log(scope);//result:local; } fn();
在js中可以随时定义全局变量,但是全局变量会削弱程序的灵活性,增大了模块之间的耦合性。
在多人协作时,如果定义过多的全局变量,有可能造成全局变量的冲突,也就是全局变量的污染,有两种解决方法:
(1)定义全局变量命名空间
只创建一个全局变量,并定义该变量为当前容器,把其他全局变量写到该命名空间下。
var myInfo={ name:'chengxi', like:'football', likeDo:function(){ alert(this.like); } }
(2)利用匿名函数将脚本包裹起来
(function(){ var exp={}; var name='aa'; exp.method=function(){ return name; }; window.ex=exp; })(); console.log(ex.method());//aa
作用域链:
当代码在一个环境中执行时,会创建一个作用域链,其用途是保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的前端,始终都是当前执行的代码所在环境的变量对象,如果这个环境是函数,则将其活动对象作为变量对象,活动对象在最开始时只包含一个变量,即arguments对象。作用域链的下一变量对象来自于包含(外部)环境,全局执行环境的变量对象始终都是作用域链中的最后一个对象。
<script> function outer(){ var scope = "outer"; function inner(){ return scope; } return inner; } var fn = outer(); fn(); </script>
一般来说,当某个环境中的所有代码执行完毕后,该环境被销毁(弹出环境栈),保存在其中的所有变量和函数也随之销毁(全局执行环境变量直到应用程序退出,如网页关闭才会被销毁)
但是像上面那种有内部函数的又有所不同,当outer()函数执行结束,执行环境被销毁,但是其关联的活动对象并没有随之销毁,而是一直存在于内存中,因为该活动对象被其内部函数的作用域链所引用。
像上面这种内部函数的作用域链仍然保持着对父函数活动对象的引用,就是闭包(closure)
闭包有两个作用:
第一个就是可以读取自身函数外部的变量(沿着作用域链寻找)
第二个就是让这些外部变量始终保存在内存中
//闭包 function outer(){ var result = new Array(); for(var i = 0; i < 2; i++){ result[i] = function(num){ return function(){ return num; } }(i) } return result; } var fn = outer(); console.log(fn[0]());//result:0 console.log(fn[1]());//result:1
我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终函数要返回的值。在调用每个匿名函数时,传入变量i的值,由于函数参数是按值传递的,所以会将变量i的值赋值给参数num,而这个匿名函数内部,又创建并返回一个访问num的闭包,这样数组中的每个函数都有自己num变量的一个副本,因此就可以返回各自不同的值了。
详情见:https://blog.csdn.net/whd526/article/details/70990994
this对象:
在闭包中this的使用会导致一些问题,this在函数执行时绑定:在全局函数时,this指向window;当函数作为对象方法调用时,this指向调用的那个对象;但是匿名函数的执行环境具有全局性,因此其this对象指向window。
name='window';
var object1={
name:'my object',
getNameValue:function(){
return function(){
return this.name;
}
}
};
console.log(object1.getNameValue()());//window
每个函数在被调用时会自动获取两个特殊变量:this和argument,内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量,不过,把外部作用域中的this对象保存在闭包可以访问的变量中,就可以使闭包访问该对象了。
name='window';
var object1={
name:'my object',
getNameValue:function(){
var that=this;
return function(){
return that.name;
}
}
};
console.log(object1.getNameValue()());//my object