JavaScript-提升

一般认为JS在页面执行代码自上而下执行,但实际上并不是完全正确,在JS变量和函数提升的情况下,会导致这个假设是错误的。

a = 1;
var a;
console.log( a ); //1

一般这种情况下会认为undefined 因为var a 声明在a = 1之后,被认为重新赋值,所以被认为输入undefined,但真正输入的是1。

console.log( a );  //undefined
var a = 1;

考虑下另上面这行代码。

鉴于上次的代码结果、可能以为是 1 或者会报错(ReferenceError);但正确的输出却是undefined

好! 那鉴于上面两行代码的特殊的输出结果、那么问题是什么原因呢?

为了搞明白这个问题、我们要先了解下编译器的相关内容。

“JS引擎会在解释JavaScript代码之前进行其编译一遍、编译的阶段中 一部分就是要找到所有声明,并用合适的作用域将它们关联起来。”

因此正确的思考路径应该是,变量和函数在内的所有声明相关的都会在代码执行前进行一次处理。

所以当你看到 var a = 1;时、可能认为是一个声明、但JavaScript实际会分为两步声明、var a; 与 a = 1; 第一个定义变量的声明会在编译阶段进行、而第二个赋值的声明会被留在原地、等待执行阶段。

因此、我们回到上面那两行代码问题、真正的处理形式应该是:

var a;
a = 1;
console.log( a ); //1

第一部分是是将其编译、第二部分执行;

类似的、第二段代码实际按照:

var a = 1;
console.log( a );  //undefined

因此、在JS执行过程中 好像变量和函数声明从他们的代码中好像提到了代码的最上面、这个过程就叫做**“提升”**。

只有声明本身会提升、而赋值或其他的运行逻辑会留在原地等待执行 不会改变、如果提升改变了代码的执行顺序、会造成严重的破坏和紊乱。

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

foo函数的声明被提升了,因此第一行调用是可以正常执行的。

另外代码中值得注意的是、每个作用域都会进行提升操作、我们前面做的演示都是基于全局作用域、而这次我们是在foo的函数内部 对 a 进行了提升、但是并不是提升到了 程序的最上方、因此这个段代码 会被理解为:

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

可以看到函数声明会被提升、但是函数表达式不会。

foo();  //typeError
var foo = function() {
 ...
};

TypeErrpr : 当传入函数的操作数或参数的类型并非操作符或函数所预期的类型时,将抛出一个 TypeError 类型错误。

这段程序中的变量标识符 foo 被提升并分配给所在作用域(这段代码中是全局作用域)、因此foo() 并不会导致ReferenceError报错、但是此时并没有赋值(如果它是一个函数声明而不是表达式、那么就会赋值)。foo() 由于对 undefined值进行函数调用 从而导致非法操作、因此报出TypeError异常。

同时也要注意、即使使用具名的函数表达式、名称标识符在赋值之前也无法在所在的作用域使用。

foo();  //TypeError
bar();  //ReferenceError
var foo = function bar() {
  ...
}

这段代码经过提升后、实际会被理解为:

var foo;
foo(); //TypeError
bar(); //ReferenceError

foo = function() {
 var bar = ...self
}

值得注意的既然函数和变量都存在提升、那么那个优先级高呢?

函数会首先被提升、然后才是变量。

考虑以下代码:

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

var foo;

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

foo = 2;

会输出的是 foo函数而不是变量2、这段代码片段会被理解为:

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

注意、尽管var foo 出现在 function foo() { … } 声明之前、但它是重复声明的、因此可以被忽略,因为函数声明被提升到了变量声明之前。

值得注意的是:尽管重复的var 声明会被 函数声明覆盖掉、但是出现多个重复的函数声明之后的可以覆盖前面的。 列如:

console.log(foo); // function foo() { console.log( 'hello' ); };

var foo;

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

foo = 2;

function foo() {
 console.log( 'hello' );
};

一个块级作用域的函数声明会被提升到所在作用域顶部、这个过程并不会像下面代码所表达的那样执行。

foo(); // "b"

var a = true;

if ( a ) {
  function foo() { console.log( 'a' ) };
} else { 
  function foo() { console.log( 'b' ) };
}

需要注意这样操作并不可靠。

* 测试Chrome 75.0.3770.100 已经解决上段代码提升问题;

foo(); // TypeError: foo is not a function

var a = true;

if ( a ) {
  function foo() { console.log( 'a' ) };
} else { 
  function foo(){ console.log( 'b' ) };
}

 

第一次写技术文章,前端技术不断提升,但还是不变的JS,今天开始不定期更新JS方面技术了解,提升自己的见识。