Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS 变量提升的存在是为了满足什么目的呢? #197

Open
xxleyi opened this issue Feb 5, 2020 · 10 comments
Open

JS 变量提升的存在是为了满足什么目的呢? #197

xxleyi opened this issue Feb 5, 2020 · 10 comments

Comments

@xxleyi
Copy link
Owner

xxleyi commented Feb 5, 2020

JS 中的「变量提升」可谓众所周知。

但它到底意味什么?尤其是在 ES6 之后,var let const 三者与变量提升的关系又是为何?

归根结底一句话:变量提升的存在是为了满足什么目的呢?

@xxleyi xxleyi added the WHY label Feb 5, 2020
@xxleyi xxleyi added this to To do in JS OR FP via automation Feb 5, 2020
@xxleyi xxleyi moved this from To do to In progress in JS OR FP Feb 5, 2020
@xxleyi
Copy link
Owner Author

xxleyi commented Feb 5, 2020

我自己的背景是对 Python 更熟,熟到对 Python 中一个知名的错误有过一点点小研究。这个致命错误是 UnboundLocalError: local variable 'xxx' referenced before assignment

{console.log(a); let a;}

这样一段 JS 代码报的错误是 Uncaught ReferenceError: Cannot access 'a' before initialization.

作为对比:

{console.log(a);}

的报错是 ReferenceError: a is not defined.

单看这两个对比,结论很可能是 JS 中 let 不存在变量提升。

别急,再来看一个对比:

let a = 66;
{console.log(a); let a = 88}

的报错也是 Uncaught ReferenceError: Cannot access 'a' before initialization. 而

let a = 66;
{console.log(a);}

却不报错。

如果 JS 中 let 不存在变量提升,那上述错误从哪里来?该怎么解释?用所谓暂时性死区能解释得通吗?


根据新版的错误,用暂时性死区确实很形象,但总感觉差点意思。

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 5, 2020

关键点剖析:

  • 在 JS 中有一个特殊的值 undefined 以及一种错误:ReferenceError: a is not defined
  • 必须区分的一件事:undefined 不同于 not defined
  • not defined 包含两类错误
    • undeclared
    • declared but uninitialized
  • declared variable can be initialized as undefined

结论:

  • var hoisting: declared variable was initialized as undefined
  • let & const: declared variable will be precomputed, but still uninitialized
  • temporal dead zone 指 let hoisting 中一个变量「已经存在但还未初始化」到「完成初始化」之间这段区域

这里的预计算,指的是「环境」。

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 5, 2020

变量提升存在的意义:

hoisting - Why does JavaScript hoist variables? - Stack Overflow

As Stoyan Stefanov explains in "JavaScript Patterns" book, the hoisting is result of JavaScript interpreter implementation.

The JS code interpretation performed in two passes. During the first pass, the interpreter processes variable and function declarations.

The second pass is the actual code execution step. The interpreter processes function expressions and undeclared variables.

Thus, we can use the "hoisting" concept to describe such behavior.

所以,这是 JS 解释器某个实现造成的。

那,为什么 JS 解释器选择这种实现方案呢?

以我目前的知识水平,先存疑不再深究。

但到这里,可以发现 JS 变量提升和 Python 中的 precompute local variable 的微妙差异:JS 同样会 precompute local variable,但除此之外,还会对某些变量进行提升

再具体点,真正想要提升的是**「函数声明」**。var 的提升是个历史错误,后来的 let const 有意规避掉了这一点。

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 5, 2020

let in MDN

let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope. The other difference between var and let is that the latter is initialized to a value only when a parser evaluates it.

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 6, 2020

JS 中的变量提升是在预计算的基础之上,针对 function 进行提升。

Python 只有预计算,提前使用报错: local variable 'xxx' referenced before assignment

JS 除了 function 的正经提升之外,还有一个 var 的历史性错误,其余的提前使用时,也是报错:Uncaught ReferenceError: Cannot access 'a' before initialization,这和 Python 中的错误一致。

  • var: undefined
  • let: uninitialized
  • const: uninitialized
  • function: function definition
  • class: uninitialized

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 6, 2020

在 JS creator 眼里,变量提升主要是为了「函数式编程的方便」,而 var 声明的变量被初始化为 undefined 是个错误。

BrendanEich on Twitter: "@aravind030792 function hoisting allows top-down program decomposition, 'let rec' for free, call before declare; var hoisting tagged along." / Twitter

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 6, 2020

Python 中一个经典的报错:

def something_maybe_true():
    return False

x = 10
def foo():
    if something_maybe_true():
        x = 1
    print(x)

UnboundLocalError: local variable 'x' referenced before assignment

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 6, 2020

JS 中 var 和 let 作用域的不同:

function something_maybe_true() {
  return false;
}

var x = 10;

function foo() {
  console.log(x)
  if (something_maybe_true()) {
    var x = 1;
  }
  console.log(x)
}

output:

undefined
undefined
function something_maybe_true() {
  return true;
}

let x = 10;

function foo() {
  console.log(x)
  if (something_maybe_true()) {
    let x = 1;
  }
  console.log(x)
}

output:

10
10

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 6, 2020

@xxleyi
Copy link
Owner Author

xxleyi commented Feb 6, 2020

@xxleyi xxleyi mentioned this issue Feb 21, 2020
13 tasks
@xxleyi xxleyi changed the title JS 变量提升 JS 变量提升的存在是为了满足什么目的呢? Mar 28, 2020
@xxleyi xxleyi added the WHAT label Mar 28, 2020
@xxleyi xxleyi moved this from In progress to Done in JS OR FP Sep 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
JS OR FP
  
Done
Development

No branches or pull requests

1 participant