JavaScript 的 var let const 变量声

变量声明

ES6 之后,JavaScript 的变量声明经历了翻天覆地的变化。直到 ECMAScript 5.1,var 都是声明变量
的唯一关键字。ES6 不仅增加了 let 和 const 两个关键字,而且还让这两个关键字压倒性地超越 var
成为首选。

1. 使用 var 的函数作用域声明

在使用 var 声明变量时,变量会被自动添加到最接近的上下文。在函数中,最接近的上下文就是函
数的局部上下文。在 with 语句中,最接近的上下文也是函数上下文。如果变量未经声明就被初始化了,
那么它就会自动被添加到全局上下文,如下面的例子所示:

function add(num1, num2) { 
 var sum = num1 + num2; 
 return sum; 
} 
let result = add(10, 20); // 30 
console.log(sum); // 报错:sum 在这里不是有效变量

这里,函数 add()定义了一个局部变量 sum,保存加法操作的结果。这个值作为函数的值被返回,
但变量 sum 在函数外部是访问不到的。如果省略上面例子中的关键字 var,那么 sum 在 add()被调用
之后就变成可以访问的了,如下所示:

function add(num1, num2) { 
 sum = num1 + num2; 
 return sum; 
} 
let result = add(10, 20); // 30 
console.log(sum); // 30 

这一次,变量 sum 被用加法操作的结果初始化时并没有使用 var 声明。在调用 add()之后,sum
被添加到了全局上下文,在函数退出之后依然存在,从而在后面可以访问到。
注意 未经声明而初始化变量是 JavaScript 编程中一个非常常见的错误,会导致很多问题。
为此,读者在初始化变量之前一定要先声明变量。在严格模式下,未经声明就初始化变量
会报错。

var 声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前。这个现象叫作“提升”
(hoisting)。提升让同一作用域中的代码不必考虑变量是否已经声明就可以直接使用。可是在实践中,提
升也会导致合法却奇怪的现象,即在变量声明之前使用变量。下面的例子展示了在全局作用域中两段等
价的代码:

var name = "Jake"; 
// 等价于:
name = 'Jake'; 
var name; 

下面是两个等价的函数:

function fn1() { 
 var name = 'Jake'; 
} 
// 等价于:
function fn2() { 
 var name; 
 name = 'Jake'; 
} 

通过在声明之前打印变量,可以验证变量会被提升。声明的提升意味着会输出 undefined 而不是

Reference Error:
console.log(name); // undefined 
var name = 'Jake'; 
function() { 
 console.log(name); // undefined 
 var name = 'Jake'; 
}

2. 使用 let 的块级作用域声明

ES6 新增的 let 关键字跟 var 很相似,但它的作用域是块级的,这也是 JavaScript 中的新概念。块
级作用域由最近的一对包含花括号{}界定。换句话说,if 块、while 块、function 块,甚至连单独
的块也是 let 声明变量的作用域。

if (true) { 
 let a; 
} 
console.log(a); // ReferenceError: a 没有定义
while (true) { 
 let b; 
}
console.log(b); // ReferenceError: b 没有定义
function foo() { 
 let c; 
} 
console.log(c); // ReferenceError: c 没有定义
 // 这没什么可奇怪的
 // var 声明也会导致报错
// 这不是对象字面量,而是一个独立的块
// JavaScript 解释器会根据其中内容识别出它来
{ 
 let d; 
} 
console.log(d); // ReferenceError: d 没有定义

let 与 var 的另一个不同之处是在同一作用域内不能声明两次。重复的 var 声明会被忽略,而重
复的 let 声明会抛出 SyntaxError。

var a; 
var a; 
// 不会报错
{ 
 let b; 
 let b; 
} 
// SyntaxError: 标识符 b 已经声明过了
let 的行为非常适合在循环中声明迭代变量。使用 var 声明的迭代变量会泄漏到循环外部,这种情
况应该避免。来看下面两个例子:
for (var i = 0; i < 10; ++i) {} 
console.log(i); // 10 
for (let j = 0; j < 10; ++j) {} 
console.log(j); // ReferenceError: j 没有定义

严格来讲,let 在 JavaScript 运行时中也会被提升,但由于“暂时性死区”(temporal dead zone)的
缘故,实际上不能在声明之前使用 let 变量。因此,从写 JavaScript 代码的角度说,let 的提升跟 var
是不一样的。

3. 使用 const 的常量声明

除了 let,ES6 同时还增加了 const 关键字。使用 const 声明的变量必须同时初始化为某个值。
一经声明,在其生命周期的任何时候都不能再重新赋予新值。

const a; // SyntaxError: 常量声明时没有初始化
const b = 3; 
console.log(b); // 3 
b = 4; // TypeError: 给常量赋值
const 除了要遵循以上规则,其他方面与 let 声明是一样的:
if (true) { 
 const a = 0; 
} 
console.log(a); // ReferenceError: a 没有定义
while (true) { 
 const b = 1; 
} 
console.log(b); // ReferenceError: b 没有定义
function foo() { 
 const c = 2; 
} 
console.log(c); // ReferenceError: c 没有定义
{ 
 const d = 3; 
} 
console.log(d); // ReferenceError: d 没有定义

const 声明只应用到顶级原语或者对象。换句话说,赋值为对象的 const 变量不能再被重新赋值
为其他引用值,但对象的键则不受限制。

const o1 = {}; 
o1 = {}; // TypeError: 给常量赋值
const o2 = {}; 
o2.name = 'Jake'; 
console.log(o2.name); // 'Jake' 

如果想让整个对象都不能修改,可以使用 Object.freeze(),这样再给属性赋值时虽然不会报错,
但会静默失败:

const o3 = Object.freeze({}); 
o3.name = 'Jake'; 
console.log(o3.name); // undefined 

由于 const 声明暗示变量的值是单一类型且不可修改,JavaScript 运行时编译器可以将其所有实例
都替换成实际的值,而不会通过查询表进行变量查找。谷歌的 V8 引擎就执行这种优化。
注意 开发实践表明,如果开发流程并不会因此而受很大影响,就应该尽可能地多使用
const 声明,除非确实需要一个将来会重新赋值的变量。这样可以从根本上保证提前发现
重新赋值导致的 bug。

参考资料:
《JavaScript高级程序设计(第4版)》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值