JavaScript 变量声明提拔

JavaScript 变量声明提拔

原文链接

一个小例子

先来看个例子:

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

为何是如许的效果呢?这是由于 JavaScript 代码在实行之前会有一个 预剖析 阶段,在这个阶段,诠释器会将统统 变量声明函数声明 提拔到他们各自的作用域顶部。

注:变量声明提拔只是预剖析阶段的一部份行动!

假如变量在函数体内声明,它的作用域是函数作用域(function-level scope)。不然,它就是全局作用域。

继承上面的例子,由于这个预剖析阶段,上面的代码会被诠释器预剖析成下面的代码:

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

变量的声明

在 ES6 之前,一般经由过程 var 来声明一个变量,然则 ES6 宣布后,又新添了2个关键字来声明一个变量:letconst

  • var 声清楚明了一个变量,这个变量的作用域是当前实行位置的上下文:一个函数的内部(声明在函数内)或许全局(声明在函数外)

  • let 声清楚明了一个块级域的局部变量,而且它声明的变量只在地点的代码块内有用

  • const 声清楚明了一个只读的块级域的常量,而且它声明的常量也只在地点的代码块内有用

{ // 代码块
  var a = 1;
  let b = 2;
  const c = 3;
  
  console.log(a);   // 1
  console.log(b);   // 2
  console.log(c);   // 3
}

console.log(a);   // 1
console.log(b);   // 报错,ReferenceError: b is not defined(…)
console.log(c);   // 未实行, 由于上面语句失足,所以这条语句不再实行,假如上面的语句不报错,那末这里就会报错
(function (){
  var a = 1;
  let b = 2;
  const c = 3;
  
  console.log(a);   // 1
  console.log(b);   // 2
  console.log(c);   // 3
})();   // 为了轻易,这里运用了自实行函数

console.log(a);   // 报错,ReferenceError: a is not defined(…)
console.log(b);   // 未实行
console.log(c);   // 未实行
  • let const 不像 var 那样会发作“变量提拔”征象。

console.log(a);    // 报错,ReferenceError: a is not defined
let a = 2;
console.log(a);    // 待实行
console.log(a);    // 报错,ReferenceError: a is not defined
const a = 2;
console.log(a);    // 待实行

ES6明确规定,假如区块中存在let和const敕令,这个区块对这些敕令声明的变量,从一最先就形成了关闭作用域。通常在声明之前就运用这些变量,就会报错。

变量声明提拔(Variable hoisting)

提拔(hoisting)影响了变量的生命周期,一个变量的生命周期包括3个阶段:

  • 声明 – 建立一个新变量,比方 var myValue

  • 初始化 – 用一个值初始化变量 比方 myValue = 150

  • 运用 – 运用变量的值 比方 alert(myValue)

当代码根据这三个步骤的递次实行的时刻,统统看起来都很简朴,天然。

  • 变量提拔的部份只是变量的声明,赋值语句和可实行的代码逻辑还保持在原地不动

console.log(a);     // undefined
var a = 111;
function fun(){
  console.log(a);   // undefined
  var a = 222;
  console.log(a);   // 222
}
fun();
console.log(a);     // 111

// --------------
//变量提拔后

function fun(){
  var a;
  console.log(a);   // undefined
  a = 222;
  console.log(a);   // 222
}
var a;
console.log(a);     // undefined
a = 111;
fun();
console.log(a);     // 111
  • 在基础的语句(或许说代码块)中(比方:if语句for语句while语句switch语句for...in语句等),不存在变量声明提拔

var a = "aaa";
{
  console.log(a);       // aaa
  var a = "bbb";
}
console.log(a);         // bbb

//---------------

var a = "aaa";
if (true) {
  console.log(a);       // aaa
  var a = "bbb";
}
console.log(a);         // bbb

//---------------

var a = "aaa";
for(let x in window){
  console.log(a);       // aaa
  var a = "bbb";
  break;
}
console.log(a);         // bbb

函数声明提拔(Function Hoisting)

函数声明(function declarations) 和 函数表达式(function expressions)在语法上实际上是等价的,然则有一点差别,就是 JavaScript 引擎 加载他们的体式格局不一样。简朴讲,就是函数声明会被提拔到其作用域顶部,而函数表达式不会。

更多细节

  • 函数声明会提拔,然则函数表达式的函数体就不会提拔了

fun();       // hello 

function fun(){
  console.log("hello");
}

// --------------
// 提拔后

function fun(){
  console.log("hello");
}

fun();       // hello 
fun();       // 报错,TypeError: fun is not a function

var fun = function(){
  console.log("hello");
};

// --------------
// 提拔后

var fun;

fun();        // 报错,TypeError: fun is not a function

fun = function(){
  console.log("hello");
};

当函数表达式的函数不再是匿名函数,而是一个有函数名的函数时,会发作什么?

foo();  // 报错,TypeError "foo is not a function"
bar();  // 有用的
baz();  // 报错,TypeError "baz is not a function"
spam(); // 报错,ReferenceError "spam is not defined"

// anonymous function expression ('foo' gets hoisted)
var foo = function () {};     

// function declaration ('bar' and the function body get hoisted)
function bar() {}; 

// named function expression (only 'baz' gets hoisted)
var baz = function spam() {}; 

foo(); // 有用的
bar(); // 有用的
baz(); // 有用的
spam(); // 报错,ReferenceError "spam is not defined"
  • 假如一个变量和函数同名,函数声明优先于变量声明(毕竟函数是 JavaScript 的第一等国民),而且与函数名同名的变量声明将会被疏忽。

fun();    // 输出的效果为111
function fun(){
  console.log(111);
}
var fun = function(){
  console.log(222);
}
fun();    // 输出的效果为222 

// --------------
// 提拔后

function fun(){
  console.log(111);
}
fun();    // 输出的效果为111
fun = function(){       // 从新定义了变量 fun
  console.log(222);
}
fun();    // 输出的效果为222 
  • 假如定义了雷同的函数变量声明,后定义的声明会覆蓋掉先前的声明,看以下代码:

foo();    //输出3
function foo(){
  console.log(1);
}
var foo = function(){
  console.log(2);
}  
function foo(){
  console.log(3);
} 

演习

  • alert(foo) 的值是多少?

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo); // ?
}
bar();
  • alert(a) 的值是多少?

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);  // ?

第二题的剖析请看 这里

参考资料

    原文作者:percy507
    原文地址: https://segmentfault.com/a/1190000006769038
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞