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

JavaScript-TDZ与变量提升 #1

Open
renjie1996 opened this issue Nov 10, 2017 · 1 comment
Open

JavaScript-TDZ与变量提升 #1

renjie1996 opened this issue Nov 10, 2017 · 1 comment

Comments

@renjie1996
Copy link
Owner

renjie1996 commented Nov 10, 2017

JavaScript-TDZ & 一次混淆

let会不会把变量提升

image

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

The difference between var/function/function* declarations and let/const/class declara­tions is the initialisation.

MDN let文档

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

TDZ 临时死区

  1. No Redeclaring
  2. In ECMAScript 2015, let bindings are not subject to Variable Hoisting, which means that let declarations do not move to the top of the current execution context. Referencing the variable in the block before the initialization results in a ReferenceError (contrary to a variable declared with var, which will just have the undefined value). The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.

image

Notice that in terms of variables lifecycle, declaration phase is a different term than generally speaking variable declaration. In simple words, the engine processes the variable declaration in 3 phases: declaration phase, initialization phase and assignment phase.

image

Suppose a scenario when JavaScript encounters a function scope with var variable statement inside. The variable passes the declaration phase and right away the initialization phase at the beginning of the scope, before any statements are executed .

image

If you try to access variable at this stage, JavaScript will throw ReferenceError: variable is not defined. It happens because the variable state is uninitialized.
variable is in the temporal dead zone.

@renjie1996
Copy link
Owner Author

renjie1996 commented Dec 13, 2017

Vue methods 中的变量提升问题

还原现场与参考:https://babeljs.io/repl/

在Vue的methods中和一个普通的js中执行如下代码会得到不同的结果;

在Vue的methods中:

test() {
    console.log(a); // undefine (竟然没有报错)
    let a = 1; 
}

在普通的js文件中:

test() {
    console.log(a); // Uncaught ReferenceError: a is not defined(报错了)
    let a = 1; 
}

对比,我们可能会觉得在Vue的methods中用let和Vue声明的变量是存在变量提升的,但是提升到哪里我们得继续探讨,在Vue的methods中继续执行如下的方法:

test() {
    console.log(a); // Uncaught ReferenceError: a is not defined(报错了)
    if(true) { 
        let a = 1; 
    }
}

我们可以发现此时代码报错了,说明就算存在变量提升,至少也不是提升到函数作用域,我们继续执行下面的代码:

test() {
    if(true) { 
        console.log(a); // undefined(没有报错)
        let a = 1; 
    }
}

发现没有,没有报错,这可能会使我们更坚定的认为在Vue的methods中用let和const声明变量是存在变量提升的,但不是提升到函数作用域,那是提升到变量所在的块级作用域?在下最后的结论之前,我们分别在普通的JS和Vue的methods方法中分别执行如下代码。

test(value) {
    if (value === 1) {
        let a = 1;
    } else if(value === 2) {
        let a = 2;
    } else if (value === 3) {
        let a = 3;
    } else {
        let a = 4;
    }
}

执行结果如下图:
image
image

上图是执行的Vue的实例方法,下图是在普通的JS中执行的同样的方法,我们把关注点放在作用域部分。可以看中普通的JS中a(仅有一个a)是放在Block作用域中的。而在Vue的实例方法中,a是放在Local作用域中的,并且创建了三个临时变量(在这里姑且先称作临时变量吧)_a、_a2、_a3,并且当代码执行到断点处时,_a3的值为4,而a的值却为undefined,但是当我们试图在变量a所在的作用域访问这些临时变量时我们发现代码报错了,入下:

// test() 
test(value) {
    if (value === 1) {
        let a = 1;
    } else if(value === 2) {
        let a = 2;
    } else if (value === 3) {
        let a = 3;
    } else {
        console.log(a); 
        console.log(_a3); // ReferenceError: _a3 is not defined
        console.log(_a); 
        console.log(_a3);
        let a = 4; 
        console.log(a); 
        debugger;
    }  
}

那我们就会想Vue中的实例方法为啥有着和普通JS中如此不同的表现?为啥要创造这些临时变量呢?为啥Vue的methods中的方法用let声明的变量竟然没有块级作用域呢?竟然是放在局部作用域Local中的,这与我们之前的认知有很大的不同,我们前面的那些猜测是对的吗?

带着这些疑问,我们继续往下探索,最后终于发现问题的源头竟然出在Babel,因为我们这里的代码其实是经过Babel编译的,Babel会把如下图中左侧的代码编译成下面的这样。
image

这样就能解释清楚为啥Vue中let声明的变量的作用域是在函数的局部作用域Local中,变量_a、_a2、_a3也就顺理成章的解释清楚了。上面说的在Vue中执行的方法,只要在Babel中编译一下你就能理解它们不正常的行为其实是没有问题的,因为Balel编译了它们。

总结

总结一下:
浏览器调试器中之所以显示的依然是我们的源码,是因为启用了webpack的souceMap,调试的时候能定位到你的源码,使用源码调试,但是实际运行的还是Babel编译后的代码。
在正常使用let和const声明变量时(不用Babel编译)我觉得依然是不会提升变量的,而且是存在块级作用域的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant