JavaScript 闭包理解
本文参考自https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8
结合文末的例子一起看效果更佳
前置知识
在了解闭包前要先了解js的执行上下文
当代码在ja中运行时,执行代码的环境非常重要,并将概括为以下几点:
全局作用域 -- 第一次执行代码的默认环境
函数作用域 -- 当执行流进入函数体时
(..) -- 我们当做 执行上下文 是当前代码执行的一个环境与作用域
当执行到函数声明的时候会把该声明放到当前的执行上下文中,比如在全局作用域中声明一个函数,这个函数的声明就会放到全局执行上下文
当去执行一个函数时,会创建一个新的执行上下文,我们叫做本地执行上下文
闭包
闭包就是当一个函数被创建并传递或从另一个函数返回时,它会携带一个背包,背包中是函数声明时作用域内的所有变量。
一个本地执行上下文的相关的参数的会先在闭包中寻找, 如果没有则会不断往上层执行上下文寻找, 如果一致没找到, 这个参数默认为undefined,
函数最后遇到return或者右括号, 则会将结果返回调用上下文中, 并且将该本地执行上下文销毁, 所以当一个执行上下文返回一个函数定义时, 定义它的名字最后会被销毁, 存在于调用上下文中的函数声明是匿名的
例子
1 // 前面的序号代表执行步骤 2 3 function createCounter(){ // 1. 运行到此处, 将createCounter函数定义放入全局执行上下文 4 let counter = 0; // 4.在本地执行上下文中声明新变量counter并赋为0 5 const myFunction = function(){ // 5.将myFunction函数声明放入本地执行上下文 6 counter = counter + 1; // 8. 先在闭包中看看有没有counter, 发现存在为0的counter, 将其值设置为1后再次存储在闭包中 7 return counter; 8 } 9 return myFunction; // 6.返回myFunction函数定义和它的闭包, 闭包包括创建它时在作用域内的变量, 并将该本地执行上下文删除, myFunction和counter不再存在 10 } 11 12 const increment = createCounter(); // 2.在全局执行上下文中声明increment新变量 13 // 3.然后调用creteCounter, 这时会创建一个新的本地上下文 14 15 const c1 = increment(); // 7. 声明新变量, 执行函数, 执行的是之前返回的myFunction函数定义, 不过实际上不叫myFunction 16 const c2 = increment(); // 9. 与7类似, 只有由于闭包中的counter变化了, 所以结果也会有所不同 17 const c3 = increment(); 18 19 console.log("example increment",c1,c2,c3); // 1 2 3