【阅文笔记】块作用域【你不知道的JavaScript(上)】

块作用域

函数作用域是最常见的作用域单元,但其他类型的作用域单元也是存在的。

除 JavaScript 外的很多编程语言都支持块作用域,因此对于主要使用 JavaScript 的开发者来说, 块作用域这个概念会很陌生

尽管你可能连一行带有块作用域风格的代码都没有写过, 但下面代码一定很熟悉:

for (var i=0; i<10; i++) {
  console.log( i );
}

我们在 for 循环的头部直接定义了变量 i, 通常是因为只想在 for 循环内部的上下文中使用 i, 而忽略了 i 会被绑定在外部作用域(函数或全局) 中的事实

而这👆就是块作用域的用处。 【变量的声明应该距离使用的地方越近越好, 并最大限度地本地化】

例子:

var foo = true;
if (foo) {
	var bar = foo * 2;
	bar = something( bar );
	console.log( bar );
}

【bar 变量仅在 if 声明的上下文中使用, 因此如果能将它声明在 if 块内部中会是一个很有意义的事情】 但是当使用 var 声明变量时, 它写在哪里都是一样的, 因为它们最终都会属于外部作用域。 【这段代码是为了风格更易读而伪装出的形式上的块作用域】, 如果使用这种形式, 要确保没在作用域其他地方意外地使用 bar 只能依靠自觉性

在这里插入图片描述

块作用域是一个用来对之前的最小暴露原则进行扩展的工具, 将代码从在函数中隐藏信息扩展为在块中隐藏信息

在看下面代码:

for (var i=0; i<10; i++) {
  console.log( i );
}

为什么要把一个只在 for 循环内部使用(至少是应该只在内部使用) 的变量 i 污染到整个函数作用域中呢?
在这里插入图片描述

但可惜, 表面上看 JavaScript 并没有块作用域的相关功能,除深入研究

一、with

with 关键字不仅是一个难于理解的结构, 同时也是块作用域的一个例子(块作用域的一种形式), 【用 with 从对象中创建出的作用域仅在 with 声明中而非外部作用域中有效】

二、try/catch

JavaScript 的 ES3 规范中规定 try/catch 的 catch 分句会创建一个块作用域, 其中声明的变量仅在 catch 内部有效

例子:👇

try {
undefined(); // 执行一个非法操作来强制制造一个异常
}
catch (err) {
console.log( err ); // 能够正常执行!
}
console.log( err ); // ReferenceError: err not found

err 仅存在 catch 分句内部, 当试图从别处引用它时会抛出错误(目前此行为被修复,但静态检查工具会发出警告

三、let

在这里插入图片描述
ES6 改变了现状引入了新的 let 关键字, 提供了除 var 以外的另一种变量声明方式

let 关键字可以将变量绑定到所在的任意作用域中(通常是 { … } 内部)。 换句话说, let为其声明的变量隐式地了所在的块作用域

var foo = true;
if (foo) {
	let bar = foo * 2;
	bar = something( bar );
	console.log( bar );
} 
console.log( bar ); // ReferenceError

用 let 将变量附加在一个已经存在的块作用域上的行为是隐式的👇

在开发和修改代码的过程中, 如果没有密切关注哪些块作用域中有绑定的变量, 并且习惯性地移动这些块或者将其包含在其他的块中, 就会导致代码变得混乱!!

为块作用域显式地创建块可以部分解决这个问题, 使变量的附属关系变得更加清晰。显式的块作用域风格和其他语言中块作用域的工作原理一致:👇

var foo = true;

if (foo) {
	{ // <-- 显式的快
		let bar = foo * 2;
		bar = something( bar );
		console.log( bar );
	}
} 
console.log( bar ); // ReferenceError

只要声明是有效的, 在声明中的任意位置都可以使用 { … } 括号来为 let 创建一个用于绑定的块。 在这个例子中, 我们在 if 声明内部显式地创建了一个块

提升是指声明会被视为存在于其所出现的作用域的整个范围内

但是使用 let 进行的声明不会在块作用域中进行提升。 声明的代码被运行之前, 声明并不“存在”】

{
	console.log( bar ); // ReferenceError!
	let bar = 2;
}
let循环

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

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

【for 循环头部的 let 不仅将 i 绑定到了 for 循环的块中, 事实上它将其重新绑定到了循环的每一个迭代中, 确保使用上一个循环迭代结束时的值重新进行赋值

在这里插入图片描述
在这里插入图片描述

四、const

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!

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值