资讯专栏INFORMATION COLUMN

闭包是...

Keven / 233人阅读

摘要:首先引用文档的一句话作为开头闭包是函数和声明该函数的词法环境的组合。这个环境包含了这个闭包创建时所能访问的所有局部变量。

首先引用 MDN 文档的一句话作为开头

闭包是函数和声明该函数的词法环境的组合。
闭包的概念

当一个函数被 return 的时候,这个函数内部的词法作用域中的变量是可以被外界访问到的,外层函数执行完毕时被销毁,但由于内部函数作为值返回出去,这些值得以保存下来,存储在内存中,也就是私有性。

一个基本的例子:

// 来自 MDN
function makeFunc() {
  var name = "DOG";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。执行 makeFunc 时创建的 displayName 函数实例的引用,而 displayName 实例仍可访问其词法作用域中的变量,即可以访问到 name 。由此,当 myFunc 被调用时,name 仍可被访问。

闭包的应用 私有属性

在 JavaScript 中,是没有原生支持私有属性的(据说现在已经有了提议),但是我们可以使用闭包来创建一个私有属性。

// 来自 MDN
var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter = makeCounter();

console.log(counter.privateCounter); // undefined
console.log(counter.value()); // 0
存储变量
function func() {
  var x = 100;
  return {
    function() {
      return x;
    }
  }
}
var m = func(); //运行函数,变量 x 被 m 引用

此时 m 引用了变量 x ,所以函数执行后 x 不会被释放,可以把比较重要或者计算耗费很大的值存在 x 中,只需要第一次计算赋值后,就可以通过 m 函数引用 x 的值,不必重复计算,同时也不容易被修改。

导致的问题

看一个例子:

var a = [];
for(var i = 0; i < 10; i++) {
  a[i] = () => {
    return i;
  };
}
a[6](); //10

在这个简单的函数中,变量 i 是在 for 循环中定义的,如果是在 C++ 或者 Java 中,这样定义的变量,一旦循环结束,变量也就随之销毁,i 的作用范围只在循环这个小块,就称为块级作用域。在 JavaScript中,没有这样的块级作用域。所以上一段代码其实相当于:

var a = [];
var i = 0;
for(; i < 10; i++) {
  a[i] = () => {
    return i;
  };
}
a[6](); //10

这样就很好理解了。由于匿名函数里面没有 i 这个变量,在函数执行的时候,这个 i 他要从父级函数中寻找,而父级函数中的 ifor 循环中,当找到这个 i 的时候,是 for 循环已经循环完毕,所以所得与预想不一致。

改进:

var a = [];
for(var i = 0; i < 10; i++) {
  a[i] = (() => {
    return i;
  })();
}
a[6]; // 6
总结

看到了一段很有用的话:

当一个子函数被创建时,是父函数的执行导致的,所以当子函数创建时,父函数已经处于执行阶段,所以父函数的执行上下文已经创建了。同时,因为子函数也在父函数的局部变量作用域内,所以,子函数在创建的时候,除了要引用全局上下文,也需要引用父函数的执行上下文。当一个子函数执行时,因为它同样是函数,所以它同样需要创建自己的执行上下文,当它返回的时候,同样也只解除属性中对自身执行上下文的引用,对父函数的执行上下文的引用并没有解除,这意味着,父函数的执行上下文与子函数本身共存亡了。只要子函数还存在引用,垃圾收集器就不会销毁它们所在的执行上下文。另外,因为父函数的局部变量并不在全局上下文中,所以它只能在子函数的变量解析中被访问,自然而然就相当于它们是子函数私有的了。

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

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

相关文章

  • JS 中的闭包什么?

    摘要:大名鼎鼎的闭包面试必问。闭包的作用是什么。看到闭包在哪了吗闭包到底是什么五年前,我也被这个问题困扰,于是去搜了并总结下来。关于闭包的谣言闭包会造成内存泄露错。闭包里面的变量明明就是我们需要的变量,凭什么说是内存泄露这个谣言是如何来的因为。 本文为饥人谷讲师方方原创文章,首发于 前端学习指南。 大名鼎鼎的闭包!面试必问。请用自己的话简述 什么是「闭包」。 「闭包」的作用是什么。 首先...

    Enlightenment 评论0 收藏0
  • 闭包,又见闭包。。。。?

    摘要:完美的闭包,对,闭包就这么简单。这仅仅是闭包的一部分,闭包利用函数作用域达到了访问外层变量的目的。此时一个完整的闭包实现了,的垃圾回收机制由于闭包的存在无法销毁变量。 1.闭包是指有权访问另一个函数作用域中的变量的函数。 上面这段话来自 javascript 高级程序设计 第三版 P178 。作者说闭包是一个函数,它有访问另一个函数作用域中的变量的能力。 2.函数访问它被创建时所处的...

    keelii 评论0 收藏0
  • 多层级理解闭包

    摘要:第二梯队理解有了第一梯队的认识,我们慢慢修正大脑中对闭包的认识。理解这句话就可以很好的与闭包这两个字关联起来理解闭包这个概念了。总结第二梯队理解闭包是一个有特定功能的函数。第四梯队理解闭包通过访问外部变量,一个闭包可以维持这些变量。 闭包 闭包的概念困惑了我很久,记得当时我面试的时候最后一面有一个问题就是问题关于闭包的问题,然而到现在已经完全不记得当时的题目是啥了,但仍然能够回忆起当时...

    nemo 评论0 收藏0
  • 面试官问我:什么JavaScript闭包,我该如何回答

    摘要:到底什么是闭包这个问题在面试是时候经常都会被问,很多小白一听就懵逼了,不知道如何回答好。上面这么说闭包是一种特殊的对象。闭包的注意事项通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。从而使用闭包模块化代码,减少全局变量的污染。 闭包,有人说它是一种设计理念,有人说所有的函数都是闭包。到底什么是闭包?这个问题在面试是时候经常都会被问,很多小白一听就懵逼了,不知道如何回答好。这个...

    BenCHou 评论0 收藏0
  • 理解Javascript的闭包

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

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

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

    Fourierr 评论0 收藏0

发表评论

0条评论

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