作用域和作用域链
当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用argument和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,...直至作为作用域链终点的全局执行环境。
闭包
当在函数内部定义了函数时,就创建了闭包。
闭包的作用域链包含着它自己的作用域,函数的作用域和全局的作用域。
在函数内定义的闭包,因为保存着函数的作用域,在使用后不会被回收,因此造成了内存泄露。
规避内存泄露的方法是使用后将闭包引用到的函数内的变量至为空。注意:不能只将它引用到的变量至为空,因为它引用的是整个函数的活动对象。
说到活动对象,下面看一个关于闭包经常会靠的问题
<ul>
<li>编号1,点击我请弹出1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++) {
list[i].addEventListener('click', function(){
alert(i + 1)
}, true)
}...
这个的结果是每个输出的都是6。因为每个函数内,都保存着全局的活动对象,所以它们引用的都是同一个变量i。
我们可以通过创建另一个匿名函数强制让闭包的行为符合预期
var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++) {
list[i].addEventListener('click', function(i){
return function(){
alert(i + 1)
}
}(i), true)
}...
闭包中this的问题
因为闭包经常设计到创建匿名函数,所以需要非常注意你创建的闭包的this指向的问题。关于this指向只有一个原则,只要它是个匿名函数,那它一个this指向window。致于怎样判断它是不是一个匿名函数,就需要多加练习了。
另外要提到的一点是,在《javascript高级程序设计》中,提到了可以用
var _this = this
的办法来保存this的指向。这是一个很好的方法,但是在有了es6的箭头函数后,关于this指向带来的问题几乎都可以用它来规避了。
用闭包创建块级作用域
在es5中是没有块级作用域的概念的,只有函数作用域。创建环境作用域的目的是避免多人协同工作是变量名相互重复。
(function(){})()
这样就创建了一个块级作用域。为什么要用括号括起来?因为解析器会把以function开头的解析为函数声明。
在es6中,我们可以使用let来创建块级作用域。
网友评论