资讯专栏INFORMATION COLUMN

JavaScript——闭包理解

AprilJ / 2214人阅读

摘要:闭包占用大量内存通常,函数的作用域及其所有的变量都会在函数执行结束后被销毁。也就是说,可以通过闭包创建私有作用域将某些变量作为局部变量,避免使用全局变量而占用过多的内存。

JavaScript——闭包理解 1、闭包是什么,如何使用?

闭包指的是函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,也就是说闭包有权访问另一个函数作用域中的变量的函数。
下面是一个简单闭包的函数

function mackFn() {
    var name = "Husky"
    function sayName () {
        console.log("name:" + name)
    }
    return sayName
}
var myFn = mackFn()
myFn()

mackFn()创建了一个局部变量name和一个名为sayName()的函数。sayName()是定义在mackFn()里的内部函数,sayName()可以访问到外部函数的变量,所以sayName()可以使用父函数mackFn()函数中声明的变量name。myFn是执行了mackFn时创建的sayName函数实例的引用,且mackFn()函数形成闭包,所以sayName实例仍可访问其词法作用域的变量,即可以访问name。

2、闭包的作用有哪些?
闭包其实用处很大,通过上面的例子,可以了解到闭包允许将数据与其所操作的某些数据关联起来,因此但我们使用只有一个方法的对象的地方,就可以使用闭包。闭包可以用来访问私有变量和模拟私有方法

通过闭包可以访问私有变量的共有方法

闭包技术可以用来共享私有变量,下面的例子创建了一个addPrivateProperty()函数来实现私有属性存取器方法。这个函数给对象o增加了属性存取器方法,方法名称为"get" + name"set" + name。如果提供了一个判定函数, setter方法就会用它来检测参数的合法性,然后再存储它, 如果判定函数返回false,setter方法抛出异常。对于两个存取器方法来说value这个变量是私有的,没有办法绕过存取器方法来设置或修改这个值。

function addPrivateProperty(o, name, predicate){
    var value   // 私有变量
    
    // 私有函数
    o["get" + name] = function() {
        return value
    }
    o["set" + name] = function(v) {
        if (predicate && !predicate(v)){
            throw Error("set" + name + ": invalid value" + v)
        } else {
            value = v
        }
    }
}

var o = {}   //设置一个空对象
addPrivateProperty(o, "Name",function(x){
    return typeof x == "string"
})
o.setName("Frank")  //设置属性值
console.log(o.getName())   // => "Frank"

上述的例子中,在同一个作用域链中定义了两个闭包,这两个闭包共同享用同样的私有变量或变量。

3、闭包存在的问题

闭包中的循环陷阱

通过循环创建多个闭包会产生一定的缺陷,即闭包只能取得包含函数中任何变量的最后一个值, 下面的第一个例子中,定时器函数返回的是10, 因为每个函数的作用域都保存着createFn()函数的活动对象。所以它们引用的都是同一个变量n。当createFn()函数中,执行完循环之后变量n的值是10,此时每个定时器函数都应用这保存变量n的同一个变量对象,所以在每个函数内部n的值都是10

function createFn() {
  for (var n = 0; n < 5; n++) {
    setTimeout(function() {
      return console.log(n);
    }, 100 * n);
  }
}
createFn()   // => 5 5 5 5 5

可以通过定义了一个匿名函数并将立即执行该匿名函数,在调用每个匿名函数的时候,传入了变量num,由于函数参数是按值传递的,所以就会将变量k的当前值赋值给匿名函数的参数num,而在这个匿名函数的内部,又会创建并返回了一个访问k的闭包,因此定时器函数中都有自己k变量的一个副本,便可以返回不同的数值。

function createFn() {
  for (var k = 0; k < 10; k++) {
    (function(num) {
      setTimeout(function() {
        return console.log(num);
      }, 100 * k);
    })(k);
  }
}
createFn();  // => 1 2 3 4 5

闭包占用大量内存

通常,函数的作用域及其所有的变量都会在函数执行结束后被销毁。但是,当函数一旦返回了闭包,这个函数的作用域将会一直在内存中保存到闭包不存在为止。
但是可以通过模仿块级作用域来实现。通过创建并立即调用一个函数,函数内部的所有变量都会被立即销毁(除某些变量赋值给了包含作用域中的变量),这样既可以执行其中的代码,又不会在内存中留在对该函数的引用。

function Fn(count) {
    (function() {
        for(var i = 0; i < count; i++) {
            console.log(i)
        }
    })()
    console.log("i:" + i)  // 抛出错误
}
Fn(5)

通过创建函数Fn(),在for循环外部插入一个块级作用域(私有作用域),在匿名函数中定义的任何变量,都会在执行结束的时候被销毁。因此,变量i只能坐在循环中使用,使用后就被销毁。而在使用作用域中能够访问count变量,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量。

4、性能优化之内存管理

通过创建私有作用域,在全局作用域中被函数外部使用,从而限制向全局作用域中添加过多的变量和函数。通过此方法不仅可以使用自己变量,而且不用担心搞乱全局作用域。也就是说,可以通过闭包创建私有作用域将某些变量作为局部变量,避免使用全局变量而占用过多的内存。

总结

闭包有着其优点,可以利用其优点实现很多功能,如闭包可以用来访问私有变量和模拟私有方法(访问私有属性的模式有很多,以后总结)等等,但是因为创建闭包必须维护额外的作用域,所以过渡使用闭包会占用大量的内存。

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

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

相关文章

  • 理解 JavaScript 闭包

    摘要:如何在初学就理解闭包你需要接着读下去。这样定义闭包是函数和声明该函数的词法环境的组合。小结闭包在中随处可见。闭包是中的精华部分,理解它需要具备一定的作用域执行栈的知识。 这是本系列的第 4 篇文章。 作为 JS 初学者,第一次接触闭包的概念是因为写出了类似下面的代码: for (var i = 0; i < helpText.length; i++) { var item = he...

    宠来也 评论0 收藏0
  • 理解Javascript闭包

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

    dayday_up 评论0 收藏0
  • Javascript闭包入门(译文)

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

    Fourierr 评论0 收藏0
  • 【译】理解JavaScript闭包

    摘要:当面试中让我解释一下闭包时我懵逼了。这个解释开始可能有点晦涩,让我们抽丝剥茧摘下闭包的真面目。此文不详述作用域有专门的主题阐述,不过作用域是理解闭包原理的基础。这才是闭包的真正便利之处。闭包使用不当就会很坑。 原文链接 为什么深度学习JavaScript? JavaScript如今是最流行的编程语言之一。它运行在浏览器、服务器、移动设备、桌面应用,也可能包括冰箱。无需我举其他再多不相干...

    岳光 评论0 收藏0
  • 通过示例学习JavaScript闭包

    摘要:译者按在上一篇博客,我们通过实现一个计数器,了解了如何使用闭包,这篇博客将提供一些代码示例,帮助大家理解闭包。然而,如果通过代码示例去理解闭包,则简单很多。不过,将闭包简单地看做局部变量,理解起来会更加简单。 - 译者按: 在上一篇博客,我们通过实现一个计数器,了解了如何使用闭包(Closure),这篇博客将提供一些代码示例,帮助大家理解闭包。 原文: JavaScript Clos...

    xingpingz 评论0 收藏0
  • JavaScript中的闭包

    摘要:闭包引起的内存泄漏总结从理论的角度将由于作用域链的特性中所有函数都是闭包但是从应用的角度来说只有当函数以返回值返回或者当函数以参数形式使用或者当函数中自由变量在函数外被引用时才能成为明确意义上的闭包。 文章同步到github js的闭包概念几乎是任何面试官都会问的问题,最近把闭包这块的概念梳理了一下,记录成以下文章。 什么是闭包 我先列出一些官方及经典书籍等书中给出的概念,这些概念虽然...

    HmyBmny 评论0 收藏0

发表评论

0条评论

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