由let、const 语法引申的块级作用域问题

477 阅读2分钟

今天要和大家聊一下,从ES6的声明语法 letconst 来聊一下JavaScript的块级作用域的问题。众所周知varletconst 区别在社区中很多文章都有描述,所以这里我简单用一张表格复述一下。

var/let/const 的区别

varletconst
变量可变性×
定义标识符与作用域全局块级块级
重复声明××

用代码来演示一下:

/*
 * var demo
 */
console.log(testVar);// undefined

var testVar = 'var';

console.log(testVar);// var

testVar = 'var2';

console.log(testVar);// var2

var testVar = 'var3';

console.log(testVar);// var3

/*
 * let demo
 */
console.log(testLet); // ReferenceError: testLet is not defined

let testLet = 'let';

console.log(testLet); // let

testLet = 'let2';

console.log(testLet); // let2

let testLet = 'let3'; // SyntaxError: Identifier 'testLet' has already been declared

/*
 * const demo
 */
console.log(testConst);// ReferenceError: testConst is not defined

const testConst = 'const';

console.log(testConst);// const

testConst = 'const2'; // SyntaxError: Identifier 'testConst' has already been declared

const testConst = 'const3' //  SyntaxError: Identifier 'testConst' has already been declared


块级作用域(scope)

作用域是什么?

当前的执行上下文。值和表达式在其中 "可见" 或可被访问到的上下文。如果一个变量或者其他表达式不 "在当前的作用域中",那么它就是不可用的。 作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,通常是指沿着链式的作用域链查找,而不能从父作用域引用子作用域中的变量和引用。 —— MDN

JavaScript很长时间以来只有函数作用域和全局作用域。是由于var 声明的变量,在距离最近的函数内或全局词法环境中定义,直接忽略了块级作用域。以至于ES6早期以及更早的JavaScript都不存在块级作用域。

什么是块级作用域?

如下图

声明中有三块词法环境(全局作用域、函数作用域、循环)。如果有了块级作用域,便能在循环内部声明变量,而不受函数作用域的影响。那么由此可以把块级作用域看做是函数作用域或全局作用域的子作用域。

词法环境(lexical environment): JavaScript 引擎内部用来跟踪标识符与特定变量的映射关系。

没有块级作用域很多事就不方便,比如你要模块化,只能用函数作用域来伪造块级作用域,以此来防止变量全局污染等问题。


(function(){
    // 从这里开始是函数作用域
    // 语句 
    // 变量
})();
为什么constlet能形成块级作用域?

letconst直接在最近的词法环境中定义变量(可以是在块级作用域内、循环内、函数内或全局环境内)。我们可以使用letconst定义块级别、函数级别、全局级别的变量。

如果本文章对您有所帮助可以点赞关注一下~