JavaScript的作用域(四)块作用域

1.块作用域

尽管函数作用域是最长用的作用域单元,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀、简介的代码。

分析下面代码:

js 复制代码
for(var i = 0; i < 10; i++){
	console.log(i);
}

for循环的头部直接定义了变量i,通常只想在for循环内部的上下文中使用i,但忽略了i会被绑定在外部作用域中的事实。

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

js 复制代码
var foo = true;
if(foo){
	var bar = foo * 2;
	console.log(bar); // 2
}
console.log(bar); // 这里也是 2

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

2 with

with关键字在前面介绍过,它不仅是一个难于理解的结构,同时也是块作用域的一个例子,用with从对象中创建出的作用域仅在它内部声明中而非外部作用域有效。

3 try/catch

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

js 复制代码
try{
	undefined(); // 制造错误
}catch (err) {
	console.log(err);
}
console.log(err); // 这里就会报错

4 let

ES6中引入了新的let关键字,提供了另一种变量声明方式,let关键字可以将变量绑定到所在的任意作用域中(通常是{...}内部)。

js 复制代码
var foo = true;
if(foo){
	let bar = foo * 2;
	bar = 3;
	console.log(bar);
}
console.log(bar); // 这里会报错

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

4.1 let 循环

js 复制代码
for(let i = 0; i < 10; i++) {
	console.log(i);
}

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

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

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

思考一下:

js 复制代码
var foo = true;
var baz = 10;
if(foo){
	var bar = 3;
	if(baz > bar){
		console.log(baz);
	}
}

这段代码可以简单的被重构为下面代码

js 复制代码
var foo = true;
var baz = 10;
if(foo) {
	var bar = 3;
}
if(baz > bar) {
	console.log(baz);
}

但是在使用块级作用域的变量时要注意

js 复制代码
var foo = true;
var baz = 10;
if(foo) {
	let bar = 3;
	if(baz > bar) {
		console.log(baz);
	}
}

如果这时候直接将内部的if提取出来,就会报错找不到bar

5 const

除了let以外,ES6中还引入了const,同样可以用来创建块级作用域,但const主要用来声明常量。之后的对const声明的值进行任何修改都会报错。

js 复制代码
let a = 3;
const b = 5;
a = 4;
b = 6; // 错误
console.log(a); // 4
console.log(b); // 报错

6 小结

函数是JavaScript中最常见的作用域单元,本质上声明一个在函数内部的变量或函数会在所处的作用域中隐藏起来,但是函数不是唯一的作用域单元。块作用域指的是变量和函数不仅可以属于所处的作用域,也可以属于某个代码块。

相关推荐
宅小海6 分钟前
scala String
大数据·开发语言·scala
qq_327342739 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍10 分钟前
Scala的Array数组
开发语言·后端·scala
心仪悦悦13 分钟前
Scala的Array(2)
开发语言·后端·scala
yqcoder36 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
baivfhpwxf20231 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
许嵩661 小时前
IC脚本之perl
开发语言·perl
长亭外的少年1 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
直裾1 小时前
Scala全文单词统计
开发语言·c#·scala
心仪悦悦1 小时前
Scala中的集合复习(1)
开发语言·后端·scala