《你不知道的JavaScript(上卷)》4.11 let关键字继续块作用域总结

《你不知道的JavaScript (上卷)》

第三章继续let关键字

1.在第 4 章, 我们会讨论提升

提升是指声明会被视为存在于其所出现的作用域的整个范围内。
但是使用 let 进行的声明不会在块作用域中进行提升。 声明的代码被运行之前, 声明并不
“存在

{
console.log( bar ); //  ReferenceError!
let bar = 2;
}
  1. let可以用于垃圾收集:

    另一个块作用域非常有用的原因和闭包及回收内存垃圾的回收机制相关。 这里简要说明一
    下, 而内部的实现原理, 也就是闭包的机制会在第 5 章详细解释 。

function process(data) {
// 在这里做点有趣的事情
}
var someReallyBigData = { .. };
process( someReallyBigData );

var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt) {
console.log("button clicked");
}, /*capturingPhase=*/false );

click 函数的点击回调并不需要 someReallyBigData 变量。 理论上这意味着当 process(…) 执
行后, 在内存中占用大量空间的数据结构就可以被垃圾回收了。 但是, 由于 click 函数形成
了一个覆盖整个作用域的闭包, JavaScript 引擎极有可能依然保存着这个结构(取决于具体
实现)。

块作用域可以打消这种顾虑,可以让引擎清楚地知道没有必要继续保存 someReallyBigData 了:

function process(data) {
	// 在这里做点有趣的事情
} 
// 在这个块中定义的内容可以销毁了!
{
	let someReallyBigData = { .. };
	process( someReallyBigData );
}
var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt){
	console.log("button clicked");
}, /*capturingPhase=*/false );
3.let循环:

一个let发挥优势的典型例子就是之前讨论的for循环。

for (let i=0; i<10; i++) {
	console.log( i );
}
console.log( i ); // ReferenceError

for 循环头部的 let 不仅将 i 绑定到了 for 循环的块中, 事实上它将其重新绑定到了循环的每一个迭代中, 确保使用上一个循环迭代结束时的值重新进行赋值。 下面通过另一种方式来说明每次迭代进行重新绑定的行为:

{
    let j;
    for (j=0; j<10; j++) {
    let i = j; // 每个迭代重新绑定!
    console.log( i );
    }
}

每个迭代进行重新绑定的原因非常有趣, 我们会在第 5 章讨论闭包时进行说明。

优缺点 :由于 let 声明附属于一个新的作用域而不是当前的函数作用域( 也不属于全局作用域),
当代码中存在对于函数作用域中 var 声明的隐式依赖时, 就会有很多隐藏的陷阱, 如果用
let 来替代 var 则需要在代码重构的过程中付出额外的精力。

以下代码 :

var foo = true, baz = 10;
if (foo) {
    var bar = 3;
    if (baz > bar) {
    console.log( baz );
    } 
    // ...
}

可以重构为下面的同等形式

var foo = true, baz = 10;
if (foo) {
	var bar = 3;
	// ...
}
// 2 - 5出现了变化
if (baz > bar) {
	console.log( baz );
}	

但是在使用块级作用域的变量时需要注意以下变化 :

var foo = true, baz = 10;
if (foo) {
	let bar = 3;
	if (baz > bar) { // <-- 移动代码时不要忘了 bar!
		console.log( baz );
	}
}
4.const

除了 let 以外, ES6 还引入了 const, 同样可以用来创建块作用域变量, 但其值是固定的
(常量)。 之后任何试图修改值的操作都会引起错误。

以下例子可以很好的说明

var foo = true;
if (foo) {
    var a = 2;
    const b = 3; // 包含在 if 中的块作用域常量
    a = 3; // 正常 !
    b = 4; // 错误 !
} 
console.log( a ); // 3
console.log( b ); // ReferenceError!
5.小结

函数是 JavaScript 中最常见的作用域单元。 本质上, 声明在一个函数内部的变量或函数会
在所处的作用域中“隐藏” 起来, 这是有意为之的良好软件的设计原则。
但函数不是唯一的作用域单元。 块作用域指的是变量和函数不仅可以属于所处的作用域,
也可以属于某个代码块(通常指 { … } 内部)。
从 ES3 开始, try/catch 结构在 catch 分句中具有块作用域。
在 ES6 中引入了 let 关键字(var 关键字的表亲), 用来在任意代码块中声明变量。 if
(…) { let a = 2; } 会声明一个劫持了 if 的 { … } 块的变量, 并且将变量添加到这个块
中。
有些人认为块作用域不应该完全作为函数作用域的替代方案。 两种功能应该同时存在, 开
发者可以并且也应该根据需要选择使用何种作用域, 创造可读、 可维护的优良代码。

Page 33 ~ Page 36 2021.04.11

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值