您的位置:首页 > Web前端

前端基础进阶(四):详细图解作用域链与闭包

2018-02-26 22:25 441 查看
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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: