ECMAScript6(一)块级作用域绑定

系列文章目录

第一章 块级作用域绑定
第二章 字符串和正则表达式


前言

本文主要介绍了ES6中的块级作用域内容。笔记内容总结《深入理解ES6》书。


一、块级作用域绑定

1. var声明及变量提升机制

提升(Hoisting)机制:通过关键字var声明的变量,都会被当成在当前作用域顶部生命的变量。

以下列代码为例:

function getValue(condition){
    
    
	if(condition){
    
    
		var value = "blue";
		console.log(value);
	}else{
    
    
		// 此处可访问变量value,其值为undefined
		return null
	}
	// 此处可访问变量value,其值为undefined
}

事实上,JavaScript引擎会将上面的getValue函数修改为下面这样。变量value的声明被提升至函数顶部初始化操作依旧保留在原处。为此,ES6引入了块级作用域来强化对变量声明周期的控制。

function getValue(condition){
    
    
	var value;
	if(condition){
    
    
		value = "blue";
		console.log(value);
	}else{
    
    
		return null
	}
}

2. 块级声明

块级声明:用于声明在指定块的作用域之外无法访问的变量。

块级作用域存在于:① 函数内部; ②块中({}之间的区域)。

⑴ let声明

  • 用法同var相同,但声明不会被提升;
  • 禁止在同一作用域内重声明;
  • 如果当前作用域内嵌另一个作用域,便可在内嵌的作用域中用let声明同名变量。
function getValue(condition){
    
    
	if(condition){
    
    
		var value = "blue";
		console.log(value);
	}else{
    
    
		// 变量value在此处不存在
		return null
	}
	// 变量value在此处不存在
}
var count = 30;
// 抛出语法错误
let count = 40;

if(condition){
    
    
	// 不会抛出错误
	let count = 40;
}

⑵ const声明

  • 声明的是常量,必须初始化;
  • 禁止在同一作用域内重声明;
  • 不可再赋值(常量对象可修改值);
  • const声明对象时,不允许修改绑定,但可修改值。
// 有效的常量
const maxItems = 30;
// 语法错误,未初始化
const name;

if(condition){
    
    
	const count = 40;
}
// 在此处无法访问count

let age = 20;
// 抛出错误,重声明
const age = 15const maxItems = 30;
// 抛出语法错误,不能重新赋值
maxTtems = 35;


const person = {
    
    
	name: 'Tom'
};
// 可以直接修改对象属性的值
person.name = 'LingLing';
// 直接给person赋值,即要改变person的绑定,会抛出异常。
person = {
    
    
	name: 'DaMing'
}

3. 临时死区(TDZ)

临时死区(Temppral Dead Zone, TDZ)
由于console.log(typeof value)语句会抛出错误,因此用let定义并初始化变量value的语句不会执行。此时的value还位于JavaScript社区所谓的“临时死区”。TDZ通常用来描述let和const的不提升效果。

扫描二维码关注公众号,回复: 15326738 查看本文章
if(condition){
    
    
	console.log(typeof value);// 引用错误
	let value = 40;
}

JS引擎扫描代码发现变量时操作:
JS引擎扫描代码发现变量时操作
下面情况不会报错:typeof是在声明变量value的代码块外执行的,此时value并不在该作用域中的TDZ中,也就意味着不存在value这个绑定。

console.log(typeof value);// undefined
if(condition){
    
    
	let value = 40;
}

4. 循环中的块级作用域绑定

for循环中通过let将计数器变量限制在循环内部。

for(var i = 0; i < 10; i++){
    
    
	// 更多代码
}
// 在这里仍然可以访问变量i
console.log(i); // 10

for(let i = 0; i < 10; i++){
    
    
	// 更多代码
}
// i在这里不可访问,抛出错误
console.log(i); 

5. 循环中的函数、let声明、const声明

let funcs = [];
for(var i = 0; i < 10; i++){
    
    
    funcs.push(function(){
    
    
        console.log(i)
    })
}
/*
每个funcs[]中都存在一个函数:
	ƒ (){ console.log(i) }
*/
funcs.forEach(function(func){
    
    
    func(); // 10个10
})

因为这里的循环里的每次迭代同时共享着变量i,循环内部创建的函数都保留了对相同变量的引用。
解决该问题的两种方案:

  • 在循环中使用立即调用函数表达式(IIFE),以强制生成计数器变量的副本。
let funcs = [];
for(var i = 0; i < 10; i++){
    
    
    funcs.push(function(value){
    
    
        return function(){
    
    
        	console.log(value)
        }
    })
}
funcs.forEach(function(func){
    
    
    func(); // 0 1 2 3......9
})
  • 用let声明计数器:每次迭代循环都会创建一个新变量 i,并以之前迭代同名变量的值将其初始化。所以循环内部创建的每一个函数都能够得到属于自己的 i 的值。let 声明在循环内部的行为是标准中专门定义的,它不一定与let的不提升特性相关,理解这一点至关重要!
let funcs = [];
for(let i = 0; i < 10; i++){
    
    
    funcs.push(function(){
    
    
        console.log(i)
    })
}
funcs.forEach(function(func){
    
    
    func(); // 0 1 2 3...... 9
})

循环中let、const声明

6. 全局块作用域绑定

var、let、const在全局作用域中的行为区别:

  • var会创建一个新的变量作为全局对象(浏览器环境中的window对象),会无意中覆盖已存在的全局属性;
    在这里插入图片描述

  • let、const会在全局作用域下创建一个新的绑定,但该绑定不会添加为全局对象的属性。换句话说,不能覆盖只是遮蔽。
    在这里插入图片描述


总结

当前使用块级绑定最佳实践是:默认使用const,只在确定需要改变变量的值时使用let。这样就可以在某种程度上实现代码的不可变,从而防止某些错误的产生。

猜你喜欢

转载自blog.csdn.net/yan_danfeng/article/details/117606441