javascript 中的闭包
理解必包三个基本事实
1. JS允许你引用在当前函数以外定义的变量。
ex:
function makeSandwich(){
var magicIngredient = “peanut butter”;
function make(filling){
return magicIngredient +’ and ‘+ filling;
}
return make(‘jelly’);
}
makeSandwich();
2. 即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。这意味着,你可以返回一个内部函数。
ex:
function mkSandwich(){
var magicIngredient = ‘peanut butter’;
function make (filling){
return magicIngredient +’ and ‘+filling;
}
return make;
}
var mk = mkSandwich();
mk(‘jelly’);
mk(‘bananas’);
mk(‘marshmallows’);
Javascript函数值在内部存储它们可能会引用的定义在其封闭作用域的变量。
那些在其所涵盖的作用域内跟踪变量的函数被称为闭包。
make函数就是一个闭包,引用两个外部变量: magicIngredient和filling。
函数可以引用在其作用域内的任何变量,包括参数和外部函数变量。
ex:
function mkSandwich(magicIngredient){
function make(filling){
return magicIngredient + ’and’ + filling;
}
return make;
}
var mk = mkSandwich(‘peanut butter’);
mk(‘jelly’);
mk(‘cheese’);
var mkspeical = mkSandwich(’turkey’);
mkspeical(’swiss’);
mkspeical(‘provolone’);
构建闭包的字面量语法-函数表达式
ex:
function mkSandwich(magicIngredient){
return function(filling){
return magicIngredient +’ and ‘+ filling;
}
}
3. 必包可以更新外部变量的值。实际上必包存储的事外部变量的引用,而不是它们值的副本。
ex:
function box(){
var val = undefined;
return {
set : function(newVal){
val = newVal;
},
get : function(){
return val;
},
type : function(){
return typeof val;
},
}
}
var boxdemo = box();
boxdemo.type();
boxdemo.set(998);
boxdemo.get();
boxdemo.type();
要搞清楚闭包,首先要知道javascript中的作用域.
什么是作用域,你可以简单的理解成当前上下文环境.
javascript 中的作用域有2种
属于全局作用域.
属于局部作用域
全局作用域:
比如我们在浏览器端编写的 javascript 代码,
1 |
var name=‘test’; |
我们直接定义了一个变量name ,那么这个变量当前作用域就是全局,对于浏览器端全局就是window
更确切的说,name 是window 对象的一个属性.
1 2 3 |
console.log(window.name); //test console.log(name); //test console.log(this.name); //test |
上面三种输出方法,结果都是一样的,因为当前作用域就是window对象,this指向当前作用域.
局部作用域:
此类作用域是针对javascript中函数的作用域.
是指在函数内部定义的变量,如下:
1 2 3 4 |
function test(){ var name=‘yijiebuyi’; alert(this.name); } |
上面 name 是一个局部变量,定义在函数内部,所以它的作用域就是此函数内部可以访问.
alert(this.name); this 指向当前作用域,因为在函数内部,所以指函数作用域.
如果函数外部 alert(name); 将会报错,返回 undefined
但是 函数内部可以访问函数外部作用域,如下:
1 2 3 4 |
var name=‘test’; function test(){ alert(name); //test } |
上面的代码没有问题,函数内部作用域可以访问全局作用域,(或者简单理解成 函数内部作用于可以访问它父级作用域)
既然函数可以访问父作用域变量,而且函数是一等公民,可以当做参数,返回值等.
那么问题来了:
1 2 3 4 5 6 |
function test(){ var name=‘yijiebuyi’; return function(){ console.log(name); } } |
1 2 |
var rtn=test(); rtn(); //yijiebuyi |
我们上面运行 rtn() ,得到 yijiebuyi,说明了2个问题:
1.函数确实是头等公民,可以当参数,可以当返回值.
2.函数可以访问它的父作用域,因为成功输出了 yijiebuyi
我们再换一个思路:
如果我不返回这个匿名函数,但是我想得到 test 函数中的 name 属性值,怎么办?
好像不行,办不到,只能返回一个函数来获取 test 函数内部变量了,其实这个过程就是 一个典型的闭包!
我们在返回匿名函数的时候并没有返回 var name=‘yijiebuyi’ 但是运行 rtn 函数却得到了 name 属性值.
所以:
闭包的作用就是:把函数内部私有变量暴露出来. (暴露作用域)
闭包如何实现:函数里面返回一个函数.
这个返回函数自动带上了父函数所在的作用域,所以可以访问其内部变量,
也正是因为闭包的存在,导致父函数执行完成后并没有被垃圾回收,它的作用域内的变量都将保存在内存中供闭包使用.