在谈作用域链之前先说一下与作用域链关系紧密的执行环境和作用域。
作用域:作用域指的是变量的适用范围。
作用域链:作用域链(Scope Chain)是javascript内部中一种变量、函数查找机制,它决定了变量和函数的作用范围,即作用域。当代码在一个环境中执行时,会创建变量对象的一个作用域链。其用途在于保证对执行环境有权访问的所有变量和函数的有序访问。
执行环境: 每个函数都有自己的执行环境,定义了变量或者函数有权访问的
其它数据
。每个执行环境都有一个与之关联的变量对象
,执行环境中定义的所有变量和函数都保存在这个对象中。web浏览器中的全局执行环境是window
。
每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中,函数执行完毕后,栈将该环境弹出。
变量对象:变量对象是与执行环境相关的特殊对象,用来存储执行环境的函数声明,函数形参和变量。在global全局环境中,变量对象也是全局对象自身,在函数上下文中,变量对象被表示为活动对象。
活动对象:在函数被执行时存在,此时被作为函数的变量对象使用,因为在函数执行上下文时,变量对象是不能直接访问的。活动对象包含普通参数和特殊参数。
作用域链会将函数自身的变量对象与外部的变量对象串联,使函数内部可以沿着作用域链一级一级搜索变量,内部环境可以通过作用域链访问外部环境中,但是外部环境却不能访问内部环境的任何变量和函数。
with可以延长作用域链
闭包:是指有权访问另一个函数作用域中的函数。
创建闭包的常见方式,就是在一个函数内部创建另一个函数。
在一个函数内部定义的函数会将其外部函数的活动对象添加到它自己的作用域链中,当这个内部函数从外部函数中被返回后,它的作用域链是外部函数的活动对象和全局变量对象,因此内部函数可以访问到外部函数内部的变量。重点是外部函数执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象,仍然留在内存中。
使用闭包一定要注意释放内存,因为闭包会携带包含它的函数的作用域,会比其它函数占用更多的内存。
通过将返回的函数保存在一个变量中,调用之后,可以手动解除对返回函数的引用,设置为null,就等于已经通知垃圾回收例程将其清除了。
借助闭包解决循环计数的问题
for(var i=0,arr=[];i<5;i++){
arr.push(function () {
console.log(i)
})
}
arr[0]()//5
arr中每一项执行时,都会去上级作用域寻找i,而i在for循环执行结束后就已经变成了4,所以arr中每一项执行的结果都是一样的。函数在预解释阶段,都被当成字符串存入堆内存,在真正执行时,才会被拿出来执行,数组中存储的,其实只是指向这个堆内存的指针,i并没有传进去,执行的时候i才被传进去。
for(var j=0,arr_j=[];j<5;j++){
arr_j[j]=function(num){
return function(){
return num
}
}(j);
}
console.log(arr_j[3]())//3
改造的方法中定义了一个匿名函数,并将立即执行该匿名函数的结果赋给数组。匿名函数有一个参数num,在调用每个匿名函数时
关于闭包函数中的this:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
this指向了window,关于这一点的解释书上写的是每个函数被调用时都会自动去的两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,永远不可能直接访问到外部函数中的这两个变量。
我对这段话的理解是,this是不可能从函数自身的活动对象之外去寻找的,所以this不可能指向object。而这个闭包函数是在全局环境调用的,所以this指向调用者也就是window,虽然当这个闭包的外部方法定义在一个对象上,所以从调用方式上来看可能有点不好分辨,可能会觉得是object.getNameFunc的调用,但其实不然。换个方式来看,object.getNameFunc()()
这段代码执行过程如下:
myFunc()//在全局环境中被调用,this指向window
这是闭包函数的一个特点,this一般都指向window
网友评论