在《javaScript高级程序设计》里对闭包的解释是这样的:闭包是指有权访问另一个函数作用域中的变量的函数。
由此我们可知,闭包是一个函数,在这个函数里可以获取到其他变量。
函数在调用时发生了什么
1.函数一旦被调用,它会创建一个执行环境和一个作用域链,大家都知道作用域链是用来确定变量是否可以被访问到。
2.函数会先用其参数和其他变量来初始化该函数的活动对象。
3.外部函数的活动对象则处于第二位,以此类推知道包括全局的变量对象为止。
4.当函数需要访问一个变量时,它会沿着作用域链查找。
一般来说,除了全局的变量对象,局部对象只有在函数执行期间保存,执行完函数之后就会销毁局部的活动对象。但闭包不是
匿名函数一般作为闭包被返回, 一旦被返回,该匿名函数的作用域链还引用着的外部函数的变量对象就不会被销毁,除非销毁该匿名函数(设置为null)
闭包与变量
function createFunctions(){
var result = new Array();
for(var i = 0; i < 10; i++) {
result[i] = function(){
return i;
}
}
return result;
}
在这段代码中,本来的意思是想要返回一个数组,这个数组是一个函数数组,按理说每个函数应该返回自己的索引。
结果如图(运行result0)
运行结果为10???简直不敢相信。
明明就是在i = 0的时候传入的啊。原来闭包的作用域链包含i,所以在被返回后没有销毁这个变量对象,但我们记得在createFunction结束时i的值为10,所以所有的匿名函数被返回时引用的都是同一个i,即10
function createFunctions(){
var result = new Array();
for(var i = 0; i < 10; i++) {
result[i] = function(num){
return function() {
return num;
}
}(i);
}
return result;
}
var result = createFunctions();
alert(result[0]());
结果如下
主要的思想是为了消除i的引用,又因为函数的参数都是按值传递的,因此可以立即执行一个函数,令num为i的一个副本,立即执行则是为了让每个数组内的元素的num都不一样。num也是被返回的匿名函数的活动对象,但是每一次result[?]都有一个num副本,因此不会发生指向同一个变量的事情
使用es6中的let解决问题
for(let i = 0; i < 10; i++) {
result[i] = function(){
return i;
};
}
闭包与this
全局函数中的this为window对象,匿名函数的执行环境一般是全局的,所以其this指向window对象。
内部函数会有两个特殊的对象:arguments和this。为什么是特殊的呢?因为函数在访问这两个对象时只会搜索到其变量对象为止,而不会去往外搜索了。
所以,当一个闭包返回this.属性时一般该属性都是window对象的属性。
window.name = "amy";
var obj = {
name: "sam",
age: 18,
getName: function() {
return function() {
return this.name;
}
}
};
alert(obj.getName()());
结果
知道了原因,我们只需要将that指向obj对象,再将this改为that即可。
window.name = "amy";
var obj = {
name: "sam",
age: 18,
getName: function() {
that = this;
return function() {
return that.name;
}
}
};
alert(obj.getName()());
结果
补充:闭包的具体应用(转载)
闭包返回监听函数
现在我们要创建几行字,大家都知道字体的单位为em
时字体的大小是根据其最近的父级字体大小设置的。
我们现在要做的就是,根据点击不同按钮修改body的字体大小,从而达到修改所有字体大小的效果。
html
<body>
<p>p tag</p>
<h1>h1 tag</h1>
<h2>h2 tag</h2>
<button id="12">12px</button>
<button id="14">14px</button>
<button id="16">16px</button>
</body>
css
body {
font-size: 12px;
}
p {
font-size: 1em;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
js
function setFontSize(fontsize) {
return function() {
document.body.style.fontSize = fontsize + 'px';
}
}
const button12 = document.getElementById('12');
const button14 = document.getElementById('14');
const button16 = document.getElementById('16');
button12.onclick = setFontSize(12);
button14.onclick = setFontSize(14);
button16.onclick = setFontSize(16);
点击12按钮
点击14按钮
点击16按钮
看出来了吗?其实setFontSize就是一个闭包,通过其传入参数,并返回操作该参数的函数。将返回函数赋值到点击事件上就好了。
闭包模拟私有方法
可以通过闭包一个对象的私有方法,方法是在外面定义私有变量或私有函数,在返回一个对象,该对象包含所有的公有方法。
var Counter = function() {
var counts = 0;
return {
increase: function() {
counts++;
},
decrease: function() {
counts--;
},
getCounts: function() {
return counts;
}
}
}
var counter = Counter();
console.log('a');
console.log(counter.getCounts()); // 0
counter.increase();
counter.increase();
console.log(counter.getCounts()); // 1