js 闭包

author: 陈家宾(617822642@qq.com)
date: 2018-1-29

什么是闭包(closure)

在我们最通俗的理解里,闭包就是函数里反回函数,不过自从看了小绿书《JavaScript 权威指南》后,发现这个解释其实是错的

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”。

从技术的角度讲,所有的 JavaScript 函数都是闭包:它们都是对象,它们都关联到作用域链。

——《JavaScript 权威指南》第 6 版,P182-P183

所以闭包本质上讲的是函数所包起来作用域(scope),所以闭包是作用域的深层次范畴。

那又为什么有“闭包就是函数里返回函数”的说法呢?

当调用函数时闭包所指向的作用域链和定义函数时的作用域链不是同一个作用域链时,事情就变得非常微妙。

——《JavaScript 权威指南》第 6 版,P183

所以应该说是闭包的特殊特性让他成为人们所熟知的 “闭包” 。

JavaScript 函数的执行用到了作用域链,这个作用域链是函数定义的时候创建的

也就是说定义函数的时候会创建一个作用域链,函数执行的时候会查询这个作用域链。在闭包情况下,也就是当函数 A 定义在函数 B 里时,函数 A 的作用域链就包含当前 B 包起来的作用域 b。

所以常说的 “闭包” 指的是函数 A 作用域链里这个特殊的作用域 b。(所以上下文讲的带双引号的 “闭包” 都是指待这种特殊场景下(函数里的函数)的闭包)

怎么实现 “闭包”

// demo1
function b() {
  var c = 1;
  return function a() {
    console.log(c);
  }
}
console.log(c); // error
b()(); // 1

demo1 就是一个简单的 “闭包” 的例子

闭包能干嘛

实现私有变量

demo1 也是一个私有变量的例子,在全局里不能访问变量 c ,用 b 函数实现的闭包可以访问局部变量 c

用法1:实现对多个元素循环添加事件(let 能简单解决这个问题,这里不做讨论)

  <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
  </ul>
  <script>
      var $lis = document.getElementsByTagName('li');
      for (var i=0; i<$lis.length; i++) {
          (function (i) {
              $lis[i].onclick = function () {
                  console.log(i);
              };
          })(i);
      }
  </script>

用法2:函数工厂

  function makeAdder(x) {
    return function(y) {
      return x + y;
    };
  }

  var add5 = makeAdder(5);
  var add10 = makeAdder(10);

  console.log(add5(2));  // 7
  console.log(add10(2)); // 12

闭包的副作用

  1. this 转移

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

    this 对象是在运行时基于函数的执行环境绑定的()

    所以最保险的做法是将外部作用域中的 this 对象保存在一个装饰能够访问的变量里

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

    由于闭包作用域中保存着的元素无法被自动销毁,会导致内存泄露。因此,在有可能的情况下,还应记得手动销毁这个元素

    function assignHandler() {
     var element = document.getElementById('someElement');
     var id = element.id;
     element.onclick = function () {
       alert(id);
     };
     element = null;
    }

参考资料

  1. 《JavaScript 权威指南》第 6 版,David Flanagan 著,淘宝前端团队 译
  2. 《JavaScript 高级程序设计》第 3 版,Nicholas C.Zaks 著,李松峰 曹力 译
  3. MDN Web 文档,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closuresszengtal, Dec 8, 2017, 6:25:49 AM
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值