page contents

JavaScript 变量声明提升

一个小例子 先来看个例子: console.log(a); // undefined var a = 2; console.log(a); // 2 为什么是这样的结果呢?这是因为 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个关键字来声明一个变量:let  const。

  • 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);
} 
  • 发表于 2020-01-13 16:33
  • 阅读 ( 659 )
  • 分类:Java开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1478 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章