执行环境与做用域:前端
每一个执行环境都有与之相关联的变量对象,若是这个环境是函数,则将其活动对象(activation object)做为变量对象,环境中定义的全部变量和函数都保存在这个变量中,活动对象在最开始只包含一个arguments对象。数组
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行以后,栈将该函数的变量对象弹出,把控制权交给以前的执行环境变量对象。闭包
做用域污染:函数
只要函数内定义了一个局部变量,函数在解析时都会将这个变量“提早声明”this
var scope = "global"; function fn(){ console.log(scope);//result:undefined var scope = "local"; console.log(scope);//result:local; } fn();
在js中能够随时定义全局变量,可是全局变量会削弱程序的灵活性,增大了模块之间的耦合性。spa
在多人协做时,若是定义过多的全局变量,有可能形成全局变量的冲突,也就是全局变量的污染,有两种解决方法:code
(1)定义全局变量命名空间xml
只建立一个全局变量,并定义该变量为当前容器,把其余全局变量写到该命名空间下。对象
var myInfo={ name:'chengxi', like:'football', likeDo:function(){ alert(this.like); } }
(2)利用匿名函数将脚本包裹起来blog
(function(){ var exp={}; var name='aa'; exp.method=function(){ return name; }; window.ex=exp; })(); console.log(ex.method());//aa
做用域:
在js中做用域分为全局做用域和函数做用域。
做用域链:
当代码在一个环境中执行时,会建立一个做用域链,其用途是保证对执行环境有权访问的全部变量和函数的有序访问。
好比查找一个变量,首先在当前做用域中查找,若是没有,就会向上级做用域去查,直到查到全局做用域,这么一个查找的过程造成的链条就叫作做用域链。
做用域链的前端,始终都是当前执行的代码所在环境的变量对象,若是这个环境是函数,则将其活动对象做为变量对象,活动对象在最开始时只包含一个变量,即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
若是没使用以上方式,i是贯穿整个做用于的,并非给每一个result分配一个i,因此用当即执行函数建立一个独立的做用域便可。
咱们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将将当即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终函数要返回的值。在调用每一个匿名函数时,传入变量i的值,因为函数参数是按值传递的,因此会将变量i的值赋值给参数num,而这个匿名函数内部,又建立并返回一个访问num的闭包,这样数组中的每一个函数都有本身num变量的一个副本,所以就能够返回各自不一样的值了。