来看一些关于闭包的定义:
1.闭包是指有权访问另一个函数作用域中变量的函数 --《JS高级程序设计第三版》 p178
2.函数对象可以通过作用域链相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性称为 ‘闭包’ 。 --《JS权威指南》 p183
3.内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments)。 --《JS语言精粹》p36
来个定义总结
1.可以访问外部函数作用域中变量的函数
2.被内部函数访问的外部函数的变量可以保存在外部函数作用域内而不被回收---这是核心,后面我们遇到闭包都要想到,我们要重点关注被闭包引用的这个变量。
创建一个闭包:
var sayName =function(){
var name ='jozo';
return function(){
alert(name);
}
};
var say = sayName();
say();
var say = sayName() :返回了一个匿名的内部函数保存在变量say中,并且引用了外部函数的变量name,由于垃圾回收机制,sayName函数执行完毕后,变量name并没有被销毁。
say() :执行返回的内部函数,依然能访问变量name,输出 'jozo' .
2. 闭包中的作用域链
变量在作用域中的查找方式应该都很熟悉了,其实这就是顺着作用域链往上查找的。
当函数被调用时:
1.先创建一个执行环境(execution context),及相应的作用域链;
2.将arguments和其他命名参数的值添加到函数的活动对象(activation object)
作用域链:当前函数的活动对象优先级最高,外部函数的活动对象次之,外部函数的外部函数的活动对象依次递减,直至作用域链的末端--全局作用域。优先级就是变量查找的先后顺序;
3. 闭包的实例
实例1:实现累加
// 方式1
var a =0;
var add=function(){
a++;
console.log(a)
}
add();
add();
//方式2 :闭包
var add= (function(){
var a = 0;
return function(){
a++;
console.log(a);
}})();
console.log(a);//undefined
add();
add();
实例2 :给每个li添加点击事件
var oli =document.getElementsByTagName('li');
var i;
for(i =0;i <5;i++){
oli[i].onclick =function(){
alert(i);
}
}
console.log(i);// 5
//执行匿名函数
(function(){
alert(i); //5
}());
上面是一个经典的例子,我们都知道执行结果是都弹出5,也知道可以用闭包解决这个问题,但是我刚开始始终不能明白为什么每次弹出都是5,为什么闭包可以解决这问题。后来捋一捋还是把它弄清晰了:
a. 先来分析没用闭包前的情况:for循环中,我们给每个li点击事件绑定了一个匿名函数,匿名函数中返回了变量i的值,当循环结束后,变量i的值变为5,此时我们再去点击每个li,也就是执行相应的匿名函数(看上面的代码),这是变量i已经是5了,所以每个点击弹出5. 因为这里返回的每个匿名函数都是引用了同一个变量i,如果我们新建一个变量保存循环执行时当前的i的值,然后再让匿名函数应用这个变量,最后再返回这个匿名函数,这样就可以达到我们的目的了,这就是运用闭包来实现的!
4. 闭包的运用
01.匿名自执行函数
02.实现封装/模块化代码
var person=function(){
//变量作用域为函数内部,外部无法访问
var name ="default";
return{
getName:function(){
return name;
},
setName:function(newName){
name = newName;
}
}
}();
console.log(person.name);//直接访问,结果为undefined
console.log(person.getName());//default
person.setName("jozo");
console.log(person.getName());//jozo
03. 实现面向对象中的对象