如何在 JavaScript 中声明变量-你极有可能不知道的时间死区 (Temporal Dead Zone)

1,350 阅读5分钟
  • 2016-9-17 10:46 补充说明 const 关于引用值修改问题

我们在学习 JavaScript 的过程中最基础的一项就是如何使用变量。变量是存储所有可能类型(比如:number, string, array 等)值的容器。每个变量都会有一个被称之为变量名的名字,这个名字会在后面的代码中使用(比如:读出这个变量的值)。

在这篇文章中,你将会了解到如何去使用变量以及不同变量声明方式的区别。

声明、初始化、赋值之间的区别

声明: 变量将会已一个包含相应作用域范围(比如: 在一个函数里面)的名字进行注册。

初始化: 当你声明一个变量的时候会自动的进行初始化,也就是说由 JavaScript 解释引擎为变量分配了一块内存。

赋值: 把一个指定的值指派(赋)给一个变量。

声明的类型

注意: 在 JavaScript 第一个版本 released 的的时候 var 这个关键词就是可以使用的。在 ES6(ES 2015)以及之后的版本中出现了 letconst 关键词

var

语法:

var x; // 声明变量、初始化
x = "Hello World"; // 赋值

// 用一行代码写
var y = "Hello World";

这种方式的变量声明和赋值是非常流行的,直到 ES 2015 出现之后这种方式才有了可替代的方法。在封闭的函数作用域中用 var 的方式声明变量是可以的,但是如果不是在封闭的函数作用域中,声明的变量就是全局的。(换句话中,用 var 声明没有块级作用域的概念)

例子:

function sayHello(){
  var hello = "Hello World";
  return hello;
}
console.log(hello);

这将会抛出一个错误ReferenceError: hello is not defined,变量hello只有在函数sayHello中是可用的。但是下面的这个例子将会正常工作,以变量全局声明的方式运行。

var hello = "Hello World";
function sayHello(){
  return hello;
}
console.log(hello);

let

语法:

let x; // 声明与初始化
x = "Hello World"; // 赋值

// 用一行代码写
let y = "Hello World";

在新的 JavaScript 中出现了 var 的后裔。 let 的作用域不仅在函数中封闭,也在块语句中封闭。块语句就是包含在 {} 中的所有语句(比如: 一个判断或一个循环)。let 的优点就是让变量在一个较小的范围内使用,这样就可以让错误产生的可能性减小。

例子:

var name = "Peter";
if(name === "Peter"){
  let hello = "Hello Peter";
} else {
  let hello = "Hi";
}
console.log(hello);

这将会抛出一个错误:ReferenceError: hello is not defined,变量 hello 只有在块级作用域中是可用的(这个例子中的 if 判断中),但是下面的例子将正常工作。

var name = "Peter";
if(name === "Peter"){
  let hello = "Hello Peter";
  console.log(hello);
} else {
  let hello = "Hi";
  console.log(hello);
}

const

语法:

const x = "Hello World";

技术角度上讲 constant 不是一个变量。constant 的特殊就在于你只能在声明的时候进行初始化(赋值给他),在以后的过程中是无法给他赋新值的。constant 也是限制于块级作用域中(和 let 一样)

constant 应该用于那些在程序运行的时候值一定不能改变的量,当你想重写他的值得时候,应用将抛出一个错误。

注解:

如果 const 的变量是一个引用类型的,比如 Object 或者 Array,会发现这个变量还是可以修改的。但是这并不违反上文所说的内容。

引用类型可以理解为申明了一块地址,然后指针指向一块内容。像 Object 或者 Array 这样的引用类型,我们在修改值得时候并不会改动他的指针地址,变得只是指向的内容里面的东西。

例子:

const a = {};
a['b'] = 1;

const b = [];
b.push(1)

意料之外的全局变量创建

你可能会创建一个全局的变量(比如: 在函数的外面),但是即使你在函数的里面,如果你在声明变量的时候忘记了写 varlet 或者 const,创建的变量也将会是全局的。

例子:

function sayHello(){
  hello = "Hello World";
  return hello;
}
sayHello();
console.log(hello);

上面的例子将会输出 Hello World,这是因为在函数中变量 hello 声明的时候 hello = 前面没有任何的变量声明描述。

要避免这种意料之外的全局变量声明你可以在 JavaScript 中开启 严格模式。

变量提升和时间死区(Temporal Dead Zone)

varlet/const 之间的另外一个重要区别就是 变量提升。变量的声明一直会被提升到当前作用域的顶部,这也就是说:

console.log(hello);
var hello;
hello = "I'm a variable";

等价于

var hello;
console.log(hello);
hello = "I'm a variable";

这种方式的表现方式就是在两个例子中都会输出 undefined。如果不是 var hello, 变量的声明将不会提升到作用域的顶部,并且将抛出一个 ReferenceError 的错误

这种现象叫做hoist,在 var 中有,同样在 let/const 中也有。

就和上面所提及到的一样,在声明代码之前使用后面用 var 声明的变量会返回一个 undefined,这个值是默认的在初始化的时候赋进去的。

但是如果是在声明之前使用后面会用 let/const 声明的变量会抛出一个错误。这个因为变量在声明代码之前是不可用的(没有被初始化)。进入作用域和使用作用域的值得这段时期就叫做时间死区(Temporal Dead Zone)

注解:
时间死区(Temporal Dead Zone): 在进入一个scope之后,let/const也会有一个hoist,这个过程和var 一样,区别是 var 在hoist之后,被初始化为了 undefined,但是let/const没有初始化,直到真正声明它的地方。

结论

无论在什么情况下,为了减少出现不必要的错误你应该使用 letconst。如果你一定要使用 var,最好在作用域的顶部使用,以防止产生不可预料的错误(大多和变量提升相关)

原文: Quick Tip: How to Declare Variables in Javascript

参考资料:


版权声明

  • 请尊重辛勤劳动
  • 禁止不署名完全转载,可单独通过下面的捐赠付版权费
  • 建议署名并进行摘要性质转载

捐赠

写文不易,赠我一杯咖啡增强一下感情可好?

alipay