关于词法作用域和闭包的一点疑问

2020-05-04 11:45:36 +08:00
 Aloehuang

let a = 0;

function addone() { let a = 10; addtwo(); }

function addtwo() { let a = 20; addthree(); }

function addthree() { console.log(a); }

addone();

结果为 0,为什么不是 20 呢?不是说一层一层从调用上下文查找直到全局上下文吗?

3792 次点击
所在节点    JavaScript
38 条回复
YadongZhang
2020-05-04 14:00:40 +08:00
@zhengjian #20 所以说 addThree() 函数里的 a 和其他两个函数体的 a 没有关系
Junh
2020-05-04 14:40:17 +08:00
@ljpCN 建议重新看闭包🐶
whoami9894
2020-05-04 15:48:48 +08:00
你对比一下

let a = 0;
let addone = () => { let a = 10; addtwo(); }
let addtwo = () => console.log(a);
addone()

/*===*/

let a = 0;
let addone = () => { let a = 10; let addtwo = () => console.log(a); addtwo(); }
addone()
xiaoming1992
2020-05-04 16:13:24 +08:00
为什么要研究这样的东西?明明一个 use strict 就能解决的问题
ZehaiZhang
2020-05-04 17:08:05 +08:00
面试题吧,考察对 js 作用域的理解=
leihongtao1230
2020-05-04 17:31:20 +08:00
作用域和执行栈不是一个概念啊
iMusic
2020-05-04 18:15:17 +08:00
词法作用域是在写代码时,函数的位置确定的
autoxbc
2020-05-04 18:46:34 +08:00
@Junh #22 这里确实没有闭包,不要看到闭合结构包着一个变量就说闭包
ljpCN
2020-05-04 19:47:03 +08:00
@Junh 😂好的我回炉重造
zackwan95
2020-05-04 20:20:06 +08:00
你把代码写成这样在我们组 code review 都过不去,我最不明白国内的一点就是拿这些要么是错误要么是极差的代码当作考题。唯一的答案不应该是永远别写成这样么
xieranmaya
2020-05-05 09:51:02 +08:00
函数里对变量的访问仅仅取决于函数的定义位置,不取决于函数的调用位置(这才是词法作用域的关键)。
具体来说,你的 addthree 函数不管在哪调用(就算是被引擎调用),他都之访问到外面这个 a 。
Aloehuang
2020-05-05 11:47:49 +08:00
@whoami9894 谢谢,上午抽空继续学习了下,理解了。我其实是把变量查找的原理(词法作用域)和执行栈、执行上下文混在一起了。
你上面那段代码在 addtwo 中没有找到 a,于是从父级执行上下文寻找,但这个父级执行上下文有两种理解方式:第一种,执行上下文栈层面上的父级,也就是从哪个函数调用那么该函数就是父级执行上下文;第二种,作用域链层面上的父级(或者词法层面上的父级)。虽然有执行上下文栈,每调用一个函数就生成新的执行上下文并压入栈中,但是作用域链并不是和执行上下文栈一一对应的,作用域链由词法作用域导出,通常作用域链长度小于等于执行上下文栈的长度。
上面那段代码在 addtwo 中没有找到 a,就从父级执行上下文找,但 addone 和 addtwo 实际上在词法层面上是同级的,所以这个父级执行上下文就是全局执行上下文。自然输出 0 。
下面的父级执行上下文是 addone 函数执行时创建的执行上下文,所以输出 10 。
Aloehuang
2020-05-05 11:51:52 +08:00
@xiaoming1992 不是 use strict 的问题
Aloehuang
2020-05-05 11:53:19 +08:00
@leihongtao1230 对啊,我搞混了,现在清晰了很多。大佬一针见血说出了我迷惑的地方。
Aloehuang
2020-05-05 11:57:12 +08:00
@zackwan95 真写代码肯定不会这样写啊,只是面对和自己预期不相符的结果时,是不是应该探究一下为什么和预期不符呢。要是真写代码这样写自己都会疯掉,无时无刻不要注意 js 语言的规则。
Junh
2020-05-05 13:01:44 +08:00
@autoxbc 亲,这里建议您看下闭包的定义呢
lbw
2020-05-05 14:43:52 +08:00
词法作用域是 compiling/parsing 期间确定,而不是运行时
xiaoming1992
2020-05-06 01:25:05 +08:00
@Aloehuang 好吧,我原本以为"use strict"会管这些,原来不管,那就添加 eslint 规则,no-redeclare 。

我的意思是,不管结果怎么样,这样的代码根本就不应该出现。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/668409

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX