也谈JavaScript闭包

闭包对于很多JavaScript初学者来说,都是比较难以理解的一个概念。其实,闭包并不是那么难以掌握的,理解闭包,只需要学会3个的基本事实


首先我们来看第一个事实,JavaScript允许当前函数引用外部定义的变量,如下:
function makeSandwich() {
    var magicIngredient = "peanut butter";
    function make(filling) {
        return magicIngredient + " and " + filling;
    }
    return make("jelly");
}
makeSandwich(); // "peanut butter and jelly"

 

上述代码中,函数make是可以使用magicIngredient这个变量的。这是因为,JavaScript首先会在make函数内部以及他的原型上寻找magicIngredient这个变量,结果没有找到,于是,他就会沿着词法环境(或者说是作用域链)向它上一级作用域寻找,于是,就在makeSandwich里找打了。
 
接下来,我们再来看一下第二个事实,即使外部函数已经返回,当前函数依然可以使用外部函数的变量:
复制代码
function sandwichMaker() {
    var magicIngredient = "peanut butter";
    function make(filling) {
        return magicIngredient + " and " + filling;
    }
    return make;
}
var f = sandwichMaker();
f("jelly"); // "peanut butter and jelly"
f("bananas"); // "peanut butter and bananas"
f("marshmallows"); // "peanut butter and marshmallows"
复制代码
这个例子与第一个例子几乎是一样的,只是,make函数不是直接在sandwichMaker里面直接执行,而是将他返回到函数外部,然后才执行。即使函数已经返回到外部了,f函数(也即make函数,其实他们的引用是一样的)依然记住magicIngredient变量的值。
 
这是如何工作的呢?答案是,JavaScript的函数值包含的信息比它执行时所需要的信息还要多。而且,JavaScript函数内部存储了可能会用到的在外部定义的变量的引用(注意是引用,而不是值)。那些在其所涵盖的作用域内跟踪变量的函数称为闭包。这里的make函数就是闭包了,它引用了两个外部变量,magicIngredient和filling。每当make函数被调用时,都能够用到这两个变量,因为,他的内部已经存储了他们的引用( 指针)。
 
函数可以引用其作用域内的任何变量,包括参数和和外部函数变量,上面的代码可以改成如下形式,使它更为灵活通用:
复制代码
function sandwichMaker(magicIngredient) {
    function make(filling) {
        return magicIngredient + " and " + filling;
    }
    return make;
}
var hamAnd = sandwichMaker("ham");
hamAnd("cheese"); // "ham and cheese"
hamAnd("mustard"); // "ham and mustard"
var turkeyAnd = sandwichMaker("turkey");
turkeyAnd("Swiss"); // "turkey and Swiss"
turkeyAnd("Provolone"); // "turkey and Provolone"
复制代码

 

JavaScript还提供了一种通过字面量来创建闭包的方法,那就是函数表达式,如下:
function sandwichMaker(magicIngredient) {
    return function(filling) {
        return magicIngredient + " and " + filling;
    };
}

 

我们会发现,这个函数表达式是一个匿名函数,但是,这并无影响的,因为,我们并没有在里面调用执行它。
 
最后,我们来看一下第三个事实,闭包可以更新外面变量的值。
 
事实上,闭包内部存储的是外部变量的引用地址,而不是外部变量的值的副本。光说是很难理解的,下面我们来看一个例子:
复制代码
function box() {
    var val = undefined;
    return {
        set: function(newVal) { val = newVal; },
        get: function() { return val; },
        type: function() { return typeof val; }
    };
}
var b = box();
b.type(); // "undefined"
b.set(98.6);
b.get(); // 98.6
b.type(); // "number"
复制代码
这个例子,box函数可以返回一个对象,这个对象具有3个闭包,分别是set,get,type,他们都可以访问到val这个变量,其中,set是设置val的值,get是获取val的值,type是获取val的值的类型。
 
总结:
1.函数可以引用定义在其外部的变量
2.闭包比创建他们的函数具有更长的生命周期
3.闭包内部存储了外部变量的指针,既可以读取他们,也可以修改。

posted on   ChessZhang  阅读(876)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示