资讯专栏INFORMATION COLUMN

Javascript 闭包

edagarli / 1960人阅读

摘要:总结闭包的作用域链如下图通过上述分析我们还可以看出,闭包有一个不同于普通函数的特性,就是它会携带包含它的函数的作用域,因此会占用更多内存。

闭包的作用域链

闭包是有权访问另一个函数作用域中的变量的函数,比如:

function createFunc(words) {
    return function() {
        return words;
    }
}
var func = createFunc("Hello World!")
func();
// "Hello World!"

上述例子中createFunc的返回值是一个函数(闭包),这个返回值在调用时仍然可以访问createFuncwords属性,这是为什么呢?还记得在之前的文章Javascript 变量、作用域和内存问题中提到的,一个函数在创建时,会生成一个内部属性[[scope]],这个属性包含函数被创建的作用域中对象的集合,也就包括了createFunc的活动对象,而如果没有闭包,createFunc的活动对象在调用结束时就可以进入GC序列,只有销毁对闭包的引用,即func = null,才会使createFunc的活动对象被GC。
总结闭包的作用域链如下图:

通过上述分析我们还可以看出,闭包有一个不同于普通函数的特性,就是它会携带包含它的函数的作用域,因此会占用更多内存。

闭包与变量

闭包的作用域链决定了闭包只能包含外部函数中任何变量的最终值,举个例子:

function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
var result = createFunctions();
result[3](); // 10

上述例子中,我们在createFunctions函数中,通过for循环,创建了多个函数,并期望每一个都能返回创建它时的索引值,但结果发现,每一个函数都只能返回i的最终值(由于ECMAScript中没有块级作用域,因此icreateFunctions中的局部变量),之所以是这样的结果,是因为每一个闭包能够访问到的i都是对局部变量i的引用,由于这种情况的存在,因此我们发现很多闭包都是这样写的:

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();
result[3](); // 3

原理就是将动态的局部变量参数化,这样每一个闭包都保存了该局部变量某个时刻的副本。

在闭包中使用this

this是基于执行环境绑定的,如果是全局函数,那么thiswindow,如果是某个对象的函数,那么this指这个对象,而匿名函数的执行环境具有全局性,因此闭包中的this一般指window,比如:

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
console.log(object.getNameFunc()()); //"The Window"

上述例子中,闭包是在全局环境中执行的,而我们知道,每个函数在执行时会基于执行环境自动获得this,因此this指向了window
上述例子中,如何使闭包可以访问object呢,可以做如下修改:

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
object.getNameFunc()()
// "My Object"
内存泄漏

在之前的文章Javascript 变量、作用域和内存问题提到过IE在版本9之前,ECMAScript对象和DOM对象的GC机制不同,循环引用会导致DOM对象永远不能被回收,学习完这一章节后才发现自己经常写的一段代码就存在这样的问题!-_-,书中也提到了这个例子:

function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        console.log(element.id);
    };
}

上面的代码就创建了一个闭包作为element的事件处理程序,这里的循环引用体现在element的属性onclick的值中存在对element的引用,即使退出assignHandlerelement这个DOM对象也不会被引用计数机制GC,那么不在闭包中显式地引用element,总可以了吧,就比如:

function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        console.log(id);
    };
}

答案是不行的,因为闭包中无论如何都是要保存一份对 assignHandler活动对象的引用的,自然包含element
之前提到过,由于闭包中保存的只是函数活动对象的引用,那么闭包中能够访问的变量就具有动态性,上个例子中,闭包由于引用了assignHandler的活动对象,就引用了element,而element引用了一个DOM对象,那么,如果element不引用DOM对象,而是其他对象,比如null,那么element就可以被标记清楚机制GC。

function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        console.log(id);
    };
    element = null;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94881.html

相关文章

  • Javascript闭包入门(译文)

    摘要:也许最好的理解是闭包总是在进入某个函数的时候被创建,而局部变量是被加入到这个闭包中。在函数内部的函数的内部声明函数是可以的可以获得不止一个层级的闭包。 前言 总括 :这篇文章使用有效的javascript代码向程序员们解释了闭包,大牛和功能型程序员请自行忽略。 译者 :文章写在2006年,可直到翻译的21小时之前作者还在完善这篇文章,在Stackoverflow的How do Java...

    Fourierr 评论0 收藏0
  • 理解Javascript闭包

    摘要:但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。可惜的是,并没有提供相关的成员和方法来访问闭包中的局部变量。 (收藏自 技术狂) 前言:还是一篇入门文章。Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将...

    dayday_up 评论0 收藏0
  • JavaScript闭包,只学这篇就够了

    摘要:当在中调用匿名函数时,它们用的都是同一个闭包,而且在这个闭包中使用了和的当前值的值为因为循环已经结束,的值为。最好将闭包当作是一个函数的入口创建的,而局部变量是被添加进这个闭包的。 闭包不是魔法 这篇文章使用一些简单的代码例子来解释JavaScript闭包的概念,即使新手也可以轻松参透闭包的含义。 其实只要理解了核心概念,闭包并不是那么的难于理解。但是,网上充斥了太多学术性的文章,对于...

    CoderBear 评论0 收藏0
  • JavaScript深入之闭包

    摘要:深入系列第八篇,介绍理论上的闭包和实践上的闭包,以及从作用域链的角度解析经典的闭包题。定义对闭包的定义为闭包是指那些能够访问自由变量的函数。 JavaScript深入系列第八篇,介绍理论上的闭包和实践上的闭包,以及从作用域链的角度解析经典的闭包题。 定义 MDN 对闭包的定义为: 闭包是指那些能够访问自由变量的函数。 那什么是自由变量呢? 自由变量是指在函数中使用的,但既不是函数参数也...

    caige 评论0 收藏0
  • 还担心面试官问闭包

    摘要:一言以蔽之,闭包,你就得掌握。当函数记住并访问所在的词法作用域,闭包就产生了。所以闭包才会得以实现。从技术上讲,这就是闭包。执行后,他的内部作用域并不会消失,函数依然保持有作用域的闭包。 网上总结闭包的文章已经烂大街了,不敢说笔者这篇文章多么多么xxx,只是个人理解总结。各位看官瞅瞅就好,大神还希望多多指正。此篇文章总结与《JavaScript忍者秘籍》 《你不知道的JavaScri...

    tinyq 评论0 收藏0
  • [学习笔记] JavaScript 闭包

    摘要:但是,必须强调,闭包是一个运行期概念。通过原型链可以实现继承,而与闭包相关的就是作用域链。常理来说,一个函数执行完毕,其执行环境的作用域链会被销毁。所以此时,的作用域链虽然销毁了,但是其活动对象仍在内存中。 学习Javascript闭包(Closure)javascript的闭包JavaScript 闭包深入理解(closure)理解 Javascript 的闭包JavaScript ...

    sunsmell 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<