第一次简单了解闭包

闭包

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

var scope = "global scope";

function checkScope(name) {
  var scope = "local scope";
  var fun = function () { 
    console.log(`name: ${name}`);
    console.log(`scope: ${scope}`);
    }
  return fun;
}

checkScope('cez')();  // name: cez
					// scope: local scope

在这个例子中,我们在外部函数 checkScope 中又定义了函数 fun ,并且,内部函数 fun 可以引用外部函数 checkScope 的参数(name)和局部变量(scope),当外部函数 checkScope 返回内部函数 fun 时,相关参数的变量都保存在内部函数中,这种特性称为闭包。


不同外层函数中的内部函数不共享作用域链(也就是局部变量互不干扰)。

function constfunc(i) { return function() { return i; }; }

var funs = [];
for (var i = 0; i < 10; i++) {  // 此循环在外层函数外执行
  funcs[i] = constfunc(i);      // 每循环一次都执行一次外层函数,导致每次都会创建一个新的作用域链
}

funs[0]();  // 0
funs[5]();  // 5
funs[9]();  // 9

for 循环执行完毕之后,数组 funs 存放着10个函数 function() { return i; }。因为每次调用函数 constfunc() 都会创建一个新的作用域链,所以数组中10个函数的 i 是互相独立的,都是指向自己当前作用域链中的 i。也就是说,循环变量会绑定到对应的函数,无论该循环变量后续如何更改,都不会影响到已绑定到函数的 i


一个外层函数中所有内部函数都共享作用域链(也就是共享局部变量)。

function counter() {
  var funs = [];
  for (var i = 0; i < 10; i++) {         // 此循环是在外层函数内执行
    funs[i] = function() { return i; };  // 每循环一次创建一个内部函数
  }
  return funs;
}

var arr = counter();
arr[0]();  // 10
arr[5]();  // 10
arr[9]();  // 10

首先执行 var arr= counter(); ,函数 counter() 被调用,依次执行函数体内的代码逻辑,执行到 for 循环时,将一个个函数 function() { return i; } 添加到数组中,但是这些函数并没有被调用。当 for 循环结束时, 此时 i = 10,数组 funs 存放着 10 个函数,也就是创建了10个闭包,这些闭包都是在同一个函数调用中定义的,因此它们可以共享变量 i

因为闭包特点,内部函数 function() { return i; } 引用的 i 是外部函数 counter() 的局部变量,也就是 for 循环里面的 i,并且所有内部函数都共享变量 i,所以数组中10个函数 function() { return i; } 中的 i 都是指向同一个 i。如上段分析,循环结束时 i = 10,所以,不管调用数组 arr 中的哪个函数都是返回 10。

再看一个例子:

function counter() {
  var n = 0;
  return {
    count: function() { return n++; },
    reset: function() { n = 0; }
  };
}

var c = counter(), d = counter();  // 创建两个计数器
c.count();  // 0
d.count();  // 0 => 它们互不干扰
c.reset();  // reset() 和 count() 方法共享局部变量
c.count();  // 0 => 因为上一步重置了 c
d.count();  // 1 => 而没有重置 d

counter() 函数返回一个“计数器”对象,这个对象包含两个方法,这两个方法都可以访问局部变量 n,并且是共享局部变量,当其中一个方法改变 n 的值时,就会影响到另一个方法。

每次调用 counter() 都会创建一个新的作用域链和一个新的局部变量。因此,如果调用 counter() 两次,则会得到两个计数器对象,而且彼此包含不同的作用域和局部变量,调用其中一个计数器对象的 count()reset() 不会影响到另外一个对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值