提升——你不知道的JavaScript笔记系列

1、先有鸡还是先有蛋

  在最开始学习javaScript的时候,我们得知,javaScript的执行顺序是从由上往下执行,从而导致我们的认知中 JavaScript 的代码在执行时由上往下执行。但实际上这并不完全正确。

  看下面这个两个例子:

// 例1:
a = 2;
var a;
console.log( a );       // 2

// ---------------------- 分割线 ----------------------

// 例2:
console.log( a );       // Uncaught ReferenceError: a is not defined
var a = 2;
复制代码

  第一个例子输出了 2,第二个例子抛出了 ReferenceError 异常。

  我们从编译阶段开始,分析代码

// 例1:实际代码如下
var a;          // 声明a;
a = 2;          // LHS引用  先在当前的作用域内查找a,找到了 并将2赋值给a
console.log(a)  // 2,console =>RHS引用, a=>RHS引用
// ---------------------- 分割线 ----------------------

// 例2:实际代码如下
var a;
console.log( a ); // undefined;console =>RHS引用,console是一个内置的对象;a=>RHS引用,在作用域内找到了a 但是还未赋值,
a = 2;
复制代码

  当编译器在进行词法分析的过程中,会找到所有的声明,并且用合适的作用域将他们串起来,形成词法作用域。

  所以,上述两个例子中的 var a 的声明会在任何其他代码执行之前,先被处理,当你看到 var a=2 的时候,你可能会认为他只是一个声明,但是 JavaScript 会将其看成两句代码,var a;和 a = 2; 第一个声明在词法化的过程中被处理了,第二个赋值会被留在原地等待代码自上而下执行

  打个比方,这个过程就好像变量和函数的声明从他们在代码中出现的位置被“移动”到最上面。这个过程我们称作“提升”。(注意在 JavaScript 这门语言中并没有提升这个词,只是把词法化过程中处理变量和函数的声明的过程,称之为提升)

  只有声明本身会被提升,而赋值或者其他的运行逻辑会留在原地,如果提升改变了代码的执行顺序,那么会带来毁灭性的灾难

  我们在看下面的例子:

// 例子1:
foo()
function foo(){
    var a;
    console.log(a) // undefined;
    a = 2;
}

// ---------------------- 分割线 ----------------------

// 例子2:
foo(2);                 // Uncaught TypeError: foo is not a function
var foo=function (b){
    console.log(b)
}
复制代码

  上述的代码会被引擎理解成如形式

// 例子1:
function foo(){
    var a;
    console.log(a) // undefined;
    a = 2;
}
foo()
// ---------------------- 分割线 ----------------------

// 例子2:
var foo;
foo(2);     // Uncaught TypeError: foo is not a function
foo=function (b){
    // b=2;   隐式代码
    console.log(b)
}
复制代码

  例子1:函数声明foo,在词法分析阶段被“移动”到了最前面。函数声明提升了。

  例子2:代码var foo = function( b ){ … }被拆成了两个部分(var foo和foo = function( b ){ … })var foo声明在词法化的过程中被处理了,出现了变量提升,此时foo的值是undefined;而foo = function( b ){ … }会被留在原地等待代码自上而下执行,当代码执行到foo( 2 ),我们对 undefined 值进行函数调用,结果抛出了一个类型错误。

2、函数优先

  函数声明和变量声明都会被提升,但是需要注意,存在重复声明的时候函数会先被提升,然后才是变量。

  看下面的代码

foo();      // 1;
var foo;
function foo(){
    console.log(1);
}

foo=function(){
    console.log(2);
}
复制代码

  结果输出的是 1,上述的代码会被引擎理解成如形式

function foo(){
    console.log(2)
}
foo();
foo=function(){
    console.log(2)
}
复制代码

  我们发现 var foo 不见了,在这之前已经存在function foo(){ … }的具名函数声明,并且函数声明会变量声明之前先被处理了,所以变量声明var foo不会在重复出现出现

总结

  无论作用域中的声明出现在什么地方,都将在代码本身被执行前(词法阶段)首先进行处理。可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升

  声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升

  存在重复声明的时候函数会先被提升,然后才是变量,所以要注意避免重复声明。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享