https://segmentfault.com/a/1190000012646488 https://yangbo5207.github.io/wutongluo/
说明:此处只是记录阅读前端基础进阶的理解和总结,如有需要请阅读上面的链接
一、作用域链
作用域链是由当前环境和当前环境以上的一系列变量对象组成的,它保证了有权限的变量对象的有序访问。这句话怎么理解,看下下面的例子
var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test();
VO(global),VO(test), VO(innerTest)分别表示全局上下文,函数test的执行上下文,函数(innerTest)的执行上下文的变量对象,那么函数test的作用域链包括VO(test)、VO(global),而函数innerTest的作用域链则包括以上三个变量对象。
可以用一个数组来表示作用域链,当前环境的变量对象放在作用域链的前端,末端是全局上下文的变量对象。可以把作用域链理解为以最前端为起点,最末端为终点的单方向通道
innerTestEC = { VO: {...}, // 变量对象 scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链 }
二、闭包
如果子函数B中访问了父函数A中的变量,则形成闭包。大部分书籍以函数B指代闭包,如闭包B,而谷歌浏览器以函数A指代闭包。
闭包的存在使得原本应该在函数结束时释放的变量空间保存了下来,供子函数调用的时候使用。
// demo01 function foo() { var a = 20; var b = 30; function bar() { return a + b; } return bar; } var bar = foo(); bar();
如上函数bar访问了父函数foo中的变量a,b形成闭包,并且当foo函数执行完时,变量对象a,b并没有被释放,而是保存了下来供函数bar使用,正常情况下如果没有闭包变量对象a,b是应该释放的。
闭包只产生在只有父子关系的函数中吗,其实不是的,只要函数B访问了其以上的任何一个函数的变量对象,闭包都会产生,看如下例子
function foo() { var a = 20; var b = 30; function bar() { return function dd() { return a + b; } } return bar; } var bar = foo(); var dd = bar(); console.log(dd());
三、练习题
利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5
for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log(i); }, i*1000 ); }
这个题目需要了解setTimeout的详细用法和闭包的概念,详细答案https://mp.weixin.qq.com/s/XB6OS2b162fV8YQ-TzE_nQ
for (var i=1; i<=5; i++) { setTimeout( (function(i) { return function() { console.log(i); } })(i), i*1000 ); }
这里记一下为什么会用到自执行函数,用到自执行函数主要是为了保存i的值,利用传参把每次i的值保存在每一个自执行函数的参数中,然后利用闭包访问每次保存的值。需要注意的是setTimeout里面的函数中的i和for循环的i其实是两个不同对象,它是函数的参数保存着i的值,因此也可以像下面这样写
for (var i = 1; i <= 5; i++) { setTimeout((function (a) { return function () { console.log(a); } })(i), i * 1000); }