由于某些原因,文章已经删除,打算迁移到别处,目前正在寻找更合适的平台。
请大家关注我的新公众号ar_indus,随后我会在公众号里推送新的博客地址。
后续计划的《react进阶系列》文章也会在新公众号中推送。
公众号二维码

由于某些原因,文章已经删除,打算迁移到别处,目前正在寻找更合适的平台。
请大家关注我的新公众号ar_indus,随后我会在公众号里推送新的博客地址。
后续计划的《react进阶系列》文章也会在新公众号中推送。
公众号二维码
本文标题:前端基础进阶(四):详细图解作用域链与闭包
本文链接:https://www.haomeiwen.com/subject/cehaittx.html
网友评论
当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。
这条叙述中 这个执行上下文(代号A) 并不包括全局上下文 是吗?
function time(index) {
setTimeout(function () {
console.log(index)
},index*1000)
}
time(i)
}
闭包就是有权访问另一个函数作用域中变量的函数.
分析这句话:
1.闭包是定义在A函数中的B函数(体现在A函数内上下文将B函数赋值到A函数外部的变量中).
2.闭包能访问A函数中的c变量(实际A函数外部不能直接访问c变量).
3.即使A函数执行完了, 被闭包引用的c变量也得不到释放(A函数外部存在变量对B函数的引用,调用该引用即可访问c变量).
上面这一段没有太理解,大神能不能再通俗的指点一下,谢谢
(function(){
var a = 3;
(function(){
var b = 4;
function _closure(){
console.log(b);
console.log(a);
console.log(c);
}
_CLOSURE=_closure;
})()
})()
var _CLOSURE;
_CLOSURE();
这样相当于形成了两个闭包吗,chrome的scope里面确实有两个closure.抱歉,好像评论不能上传截图。
就是这里的call stack和执行上下文(execution context)的区别,比如是每个函数的execution context包含了对应的call stack呢? 还是对应的call stack包含了对应的执行上下文?
PS:前几篇都看了,写的真好!
function foo() {
var a = 2;
function innnerFoo() {
console.log(c); // 在这里,试图访问函数bar中的c变量,会抛出错误
console.log(a);
}
fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn
}
function bar() {
var c = 100;
fn(); // 此处的保留的innerFoo的引用
}
foo();
bar();
建议作者在这个例子后面补充 词法作用域相关知识,这样方便理解
。
function test() {
var b = a + 10;
function innerTest() {
var c = 10;
return b + c;
}
return innerTest();
}
test();
在上面的例子中,全局,函数test,函数innerTest的执行上下文先后创建。我们设定他们的变量对象分别为VO(global),VO(test), VO(innerTest)。
上面的说法是否有误呀?
因为test中的函数innerTest在test的可执行代码中,并没有被调用执行,因此执行test时,innerTest不会创建新的上下文?
function timer() {
var num = i;
function innerTimer(){
console.log(num);
}
return innerTimer;
}
var result = timer();
setTimeout(result , i*1000 );
}
var a = 20;
function test() {
var b = a + 10;
function innerTest1() {
var c = 10;
return b + c;
}
function innerTest2(){
......//
}
return innerTest();
}
test();
如果test()里面有多个子函数,就不是起点指向终点了吧,test包含多个子函数,innerTest1与innerTest2是同一作用域吧,理解起来还是包含的味道
function test() {
var b = a + 10;
function innerTest() {
var c = 10;
return b + c;
}
return innerTest();
}
test();
这个函数里面 innerTest()并没有执行啊 也会生成执行上下文么
// immediately register returned function a..
setTimeout((function (){
var b = i;
console.log('b: ' + b);
return function a() {
console.log(b);
}
})(), i*1000 );
}
波哥,来了。。自己再理一下,其实timeout函数把要执行的函数注册好了,但是打印函数执行时,this已经指向window,window.i 已经是最大值。。所以可以这么理解,要借助Closure保存不同时期的i 值,那就用一个私有变量 b=i 放到一个函数作用域中,返回打印函数,函数保留了对私有变量的访问。其实是保留了好几个匿名函数作用域,不知道我理解的对吗
let o = {
test:1
};
let val = o.test;
let a =Object.defineProperty(o, 'test', {
get(){
return val
},
set(newVal){
val = newVal;
return newVal
}
});
window.o = o;
}()
console.log(o)
console.log(o)
这里的 IFEE 算不算闭包? IFEE运行完后,属性在用set,get,的时候 还是可以访问到val,我觉的算。
var a=15;
function bar(){
console.log(a);
}
function foo(){
var a=10;
bar();
}
foo(); //15;
这是否说明了作用域链是在代码编译是确定的?
var a = 20;
function innerTest() {
var c = 10;
return b + c;
}
function test(){
var b = a + 10;
innerTest();
}
test(); //ReferenceError: b is not defined
顺便问下作者哪里能找到ES的最新信息、包括编译器的一些知识。
简单说就是ES的官方网址是哪里。多谢
这个应该是跟js是静态作用域有关吧!只跟程序的定义的位置有关
最开始学JS的时候从网上的一些文章中了解到js代码的整个执行过程分为预编译阶段和执行阶段,大概讲的是预编译阶段会创建所有var的变量并赋值为undefined,创建所有的函数引用;执行阶段会进行变量的赋值,然后执行代码。
看完您的文章后我有两个疑问:1以前所说的js预编译阶段和执行阶段指的其实是全局环境的执行上下文的创建阶段和执行阶段;2按照您第四篇文章中对js代码执行过程的区分,我以前所看到的这种“预编译阶段和执行阶段”的观点指的其实都是您文中所描述的执行阶段,他们对应的是全局环境的执行上下文的创建和代码执行阶段;
哪一个才是正确的观点呢
作用域链: 一系列的vo(在代码执行阶段的 第一个生命周期 创建阶段)
function fn() {
console.log('this is test.')
}
var timer = setTimeout(fn, 1000);
console.log(timer);
这里你也说了 “执行上面的代码,变量timer的值,会立即输出出来,表示setTimeout这个函数本身已经执行完毕了。”也就是说setTimeout函数的执行,是在console.log(timer)之前。但是你在另一篇文章中(详解那道setTimeout与循环闭包的经典面试题),又说 “而这个队列执行的时间,需要等待到函数调用栈清空之后才开始执行。即所有可执行代码执行完毕之后,才会开始执行由setTimeout定义的操作”,按照这样说的话,应该是setTimeout的执行在console.log(timer)之后才对啊。
我不知道是自己哪里理解有误,还请波波老师指教!
console.log('this is test.')
}
var timer = setTimeout(fn, 1000);
console.log(timer);// 这里为什么会是1?
setTimeout((function timer() {
console.log(i);
})(i), i*1000 );
}
我想问我这么做有用到闭包吗?
@波同学,麻烦你帮我理清一下思路,谢谢了。
这个解释不准确。简而言之闭包就是运行时能访问另外一个函数内的变量的函数。例如:function A(){ var a="aa"; return function B(){alert(a)}}; funcA = A()。A和B都不算是闭包,A和B组合成构造了闭包环境,真正的闭包是funcA。即A函数包变量a封闭起来了,外面无法访问,但是提供了一个包B函数可以访问。A和B一闭一包。上面那句话可以解释为: 闭包就是运行时(funcA())能访问另外一个函数(A)内的变量(a)的函数(funcA)。
在我的理解A才是闭包吧。
var num2 = !!num2 ? num2 : b;
这两句话怎么读,没看懂
function test() {
var b = a + 10;
function innerTest() {
var c = 10;
return b + c;
}
return innerTest();
}
test();
不是return innerTest()了吗,为什么还会创建innerTest()的上下文
rturn innerTest():innerTest是执行了的。
return innerTest:innerTest是未执行的,返回的只是innerTest的引用地址
function compare(value1, value2){
if(value1 < value2){return -1;}
else if(value1 > value2) {return 1;}
else{return 0;}
}
var res = compare(5, 10);
以这个为例子吧:
此时VO={
arguments:[value1, value2]
//没有变量和函数
}
到执行上下文的执行阶段VO变成AO
AO = {
arguments: {value1: 5, value:10}
}
是这样吗?主要是想知道是不是
VO时,arguments只有属性名,属性名就是参数名;
然后执行阶段AO,arguments就有了属性名和属性值,因为函数值传进来了?
还是VO,AO都是arguments:{value:5, value: 10}??
这是在函数的内部实现中,setTimeout通过特殊的方式,保留了fn的引用,让setTimeout的变量对象,并没有在其执行完毕后被垃圾收集器回收。
能不能再解释一下这一部分?
fn=innerFoo,不是只是复制了innerFoo函数的地址给全局变量fn?难道复制是复制整个变量对象?
setTimeout这部分描述,应该原因是相似的吧?
谢谢
所以说闭包到底是哪个? @波同学
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i)
}
使用自执行来解决setTimeout带来的问题
var i=0;
function b(){ return (++i); }
return b;
}
var c=a()();
alert(c);//输出多少呢
多次调用a()()呢
@波同学
var foo = 'foo';
(function () {
function bar() {
var a = 1;
console.log(a);
}
function baz() {
var b = 1;
console.log(b);
}
bar();
baz();
})();
console.log(foo);
上面代码的baz的编译阶段是发生在baz()执行之前,bar()执行之后,还是刚开始就bar,baz方法一起编译了,因为网上又有人说js是解释型语言,是边编译边执行的,没太搞懂这句话,有点混乱。希望解惑下
```javascript
for (var i=1; i<=5; i++) {
setTimeout( function timer(i) {
console.log(i);
}, i*1000 ,i );
}
```
function test() {
if (true) {
function fun() {
console.log("in fun!");
};
fun(); // in fun!
}
fun(); // 报错:fun is not defined
}
test();
请问这个怎么用作用域链解释呢?
return function(i) {
setTimeout(function() {
console.log(i);
}, i*1000)
}
};
for (var i = 1; i <= 5; i++) {
loop()(i);
}
這麼做也行 就是有點複雜
(function(i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000)
})(i);
}
可否理解成
AO1 = {
arguemnts: {
i: 1
}
}
setTimeout(function timer() {
console.log(AO1.arguments.i);
}, AO1.arguments.i * 1000)
AO2 = {
arguemnts: {
i: 2
}
}
setTimeout(function timer() {
console.log(AO2.arguments.i);
}, AO2.arguments.i * 1000)
...
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
这个我还是不能理解,有没有大神能够细致的讲解一下。跪求
function foo() {
var a = 2;
var b = 3;//新增的
innnerFoo(); // 将 innnerFoo的引用,赋值给全局变量中的fn
function innnerFoo() {
console.log(a);
}
}
foo();
对你的demo略作调整,当我断点到innnerFoo内部的console时,在cosole中访问b为啥访问不了?
根据你上面的解释 “这个行为,导致了foo的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。” 理论上应该可以访问的呀。求解释。
function changeColor(){
var anotherColor = 'blue';
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColors();
这段代码来看,全局环境上下文里的->changeColor上下文里的->swapColors里面调用了全局上下文的color属性,这样隔了一层上下文环境,可不可以说swapColors就是相对于全局环境的一个闭包?
for(var i = 1; i <= 5; i++) {
(function (){
var index = i;
setTimeout(function timer() {
console.log(index);
}, i * 1000);
})();
}
哎呀我去,今天认真来看 不小心就过了12点了
console.log('this is test.')
}
var timer = setTimeout(fn, 3000);
console.log(timer);
web-9fe947c….js:10 13
undefined
web-9fe947c….js:10 this is test.
不理解那个输出的那个13
for(var i = 1; i <=5; i++){
setTimeout(function timer(){
console.log(i);
}, i * 1000);
}
对于上述代码的理解:
i在1000ms内早已变成了6,所以会console.log出5次6,但是此时有一个疑问,不考虑let,setTimeout是怎么记录下当前的i值呢? 为什么不会到5s的时候瞬间 console.log 5次6呢?
for(var i = 1; i <=5; i++){
(function (i){
setTimeout(function timer(){
console.log(i);
}, i * 1000);
})(i);
}
还有第二个问题:
上述代码使用IIFE,可以理解,保持i的值,但是闭包的定义严格来说应该不是IIFE而是timer函数定义在setTimeout作用域外而在该作用域内执行了? (代码未体现timer定义在作用域外。)
“当一个函数可以记住并访问其被创建时所在的作用域(全局作用域除外),并在定义该函数的作用域之外执行时,该函数就可以称之为一个闭包。”
“
function test() {
function bar (str) {
console.log(str);
}
function foo (fn, string) {
fn(string);
}
foo(bar, 'this is closure');
}
test();
”
“先直截了当的抛出闭包的定义:当一个函数可以记住并访问所在的作用域(全局作用域除外),并在定义该函数的作用域之外执行时,该函数就可以称之为一个闭包。
简单来说,假设函数A在函数B的内部进行定义了,并在函数B的作用域之外执行(不管是上层作用域,下层作用域,还有其他作用域),那么A就是一个闭包。记住这个定义,你在其他地方很难看到了。”
这个定义出处是什么地方?
从https://www.w3schools.com/js/js_function_closures.asp 底部黄色部分
“A closure is a function having access to the parent scope, even after the parent function has closed.”
后半部分决定了这个例子是否闭包。
也可以看看这个
http://stackoverflow.com/questions/1801957/what-exactly-does-closure-refer-to-in-javascript
http://javascriptissexy.com/understand-javascript-closures-with-ease/
这里也有相关的关键点
1、函数内部访问了父级作用域上的东东
2、函数可以在父级执行完毕后依旧存在,仍然可以执行
但对于上面1这点,我曾经试过在内部函数挂eval,然后在外部传入javascript字符串,然后获取外部作用域的东东,实测是可行的。
所以对于闭包这个定义,我还是没找到一个完整的。
希望继续和你探讨。
function timer(i) {
setTimeout( function() {console.log(i);}, i*1000 );
}
timer(i);
}
波哥这个如何?
console.log(i);
}
for (var i = 1; i <= 5; i++) {
(function(i){
setTimeout(timer(i), i * 1000);
})(i)
}
我这么一折腾,全都一下子出来了,和五楼兄弟还是区别有点大啊
“JavaScript中只有全局作用域与函数作用域”,ES6中添加了块级作用域,关键词let。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let
结合老师那个 test() 的例子,我觉得写成这样大家更好理解一点,不知道对不对
for (var i=1; i<=5; i++) {
function res(i){
function timer(){
console.log(i);
}
setTimeout( timer, i*1000 );
}
res(i)
}
请老师指正~
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
setTimeout 的可记住的并且可访问的作用于是window,所以不是闭包,回调函数执行结果是全局的i=6;
for (var i=1; i<=5; i++) {
(function (i) {setTimeout( function timer() {
console.log(i);
}, i*1000 )})(i);
}
setTimeout 的可记住的并且可访问的作用于是IIFE,符合定义前半段,回调函数执行环境是window,符合了在定义该函数作用域以外执行,所符合闭包,这时回调函数打印出来的i是IIFE的arguments里面的i
不知道这样理解有没有问题
timer不是作为setTimeout的第一个参数吗?
难不成发生了变量提升??
```
var i;
for (i = 1; i <= 5; i++) {
}
```
那么有两种解决方法:
- 使用`let`定义变量`i`,这样每一次的循环中都会创建一个新的作用域。
- 在回调函数的外面加一层执行上下文,加了以后作用域链就是`[VO(self), VO(IIFE), VO(global)]`。由于`VO(IIFE)`在`VO(global)`前面,所以回调函数运行时访问的是`VO(IIFE)`中的变量`i`,而不再是`VO(global)`的变量`i`。
```
for (var i=1; i<=5; i++) {
setTimeout((function (i) {
console.log(i);
})(i), i*1000 );
}
```
如有理解错误,还望指出。
你的setTimeout的第一个参数,被你立即执行了,这样就没有构成一个闭包。与我文中闭包的定义不符。
反正我也是在入门阶段,都是多看几遍;
不过波波老师的文章已经完胜很多人了,哈哈
console.log(i);
}
for (var i=1; i<=5; i++) {
setTimeout( timer(i), i*1000 );
}
这样写貌似有些问题,不会一个一个的出现,而且最后还抛出一个23,不懂为什么
你要想办法通过闭包保存i值,可参考5楼写法
for (let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
console.log(i);
}
for (var i=1; i<=5; i++) {
setTimeout( timer(i), i*1000 );
}
这样写貌似有些问题,不会一个一个的出现,而且最后还抛出一个23,不懂为什么