一、为什么需要块级作用域?
ES5 只有 全局作用域 和 函数作用域,没有块级作用域,这导致了很多不合理的场景;
场景1、
内层变量可能会覆盖外层变量:
var temp = new Date();
console.info(temp); // Thu Jun 30 2022 15:29:49 GMT+0800 (中国标准时间)
function f() {
console.info(temp); // undefined
if ( false ) { // 替换 false 和 true,查看不同的情况
var temp = 'hello world' ;
console.info(temp);
}
console.info(temp); // undefined
}
f();
console.info(temp); // Thu Jun 30 2022 15:29:49 GMT+0800 (中国标准时间)
var temp = new Date();
console.info(temp); // Thu Jun 30 2022 15:33:47 GMT+0800 (中国标准时间)
function f() {
console.info(temp); // undefined
if ( true ) { // 替换 false 和 true,查看不同的情况
var temp = 'hello world' ;
console.info(temp); // hello world
}
console.info(temp); // hello world
}
f();
console.info(temp); // Thu Jun 30 2022 15:33:47 GMT+0800 (中国标准时间)
上述代码的意思指:
在 if 代码块 外部时使用外部声明的 temp 变量,在 if 代码块内部时使用内部声明的 temp 变量。
结果为:
但是在 f() 函数执行后,if 代码块外部的 temp 变量打印为 undefined;如果进入过 if 代码块的话,会在 if 代码块 外,f() 函数代码块 中 打印出 if 代码块中 赋予的值;
原因:
由于内部变量提升,内部 if 代码块中的 temp 变量,覆盖了外部 f() 代码块中的 temp 变量(即 f() 代码块外声明的 temp变量);
场景2、
用于 计数 的循环变量,泄露为全局变量:
var temp = 'hello world';
console.info(i); // undefined
for (var i = 0; i < temp.length; i++) {
console.info(temp[i]);
}
console.info(i); // 11
二、ES6 的块级作用域:
1、let 命令 实际上为 JavaScript 新增了块级作用域;
var temp = new Date();
function f() {
console.info(temp); // Thu Jun 30 2022 16:04:47 GMT+0800 (中国标准时间)
if (true) {
let temp = 10;
console.info(temp); // 10
}
console.info(temp); // Thu Jun 30 2022 16:04:47 GMT+0800 (中国标准时间)
}
f();
上述的函数有两个代码快,都声明了 temp,在 if 代码块外部 使用的是外部的 temp ,在 if 代码块内部 使用的是内部的 temp 内部的值,表示外层代码块不受内层代码快的影响。如果两次都使用 var 定义变量 temp ,最后的值会是 10;
注意:实际使用时,尽量别 在代码快内部的变量名称 和代码块外部的变量名称设置为一样,在TypeScript 文件中会报错,同时在看代码时容易搞错逻辑;
2、ES6 允许块级作用域的任意嵌套:
{{{
console.info(temp); // ReferenceError: temp is not defined
{
console.info(temp); // ReferenceError: temp is not defined
let temp = 10;
console.info(temp); // 10
{
console.info(temp); // ReferenceError: Cannot access 'temp' before initialization
let temp = 11;
console.info(temp); // 11
}
}
console.info(temp); // ReferenceError: temp is not defined
}}}
上述代码,使用了一个 5层 的块级作用域,每一层都是一个单独的作用域。第三层作用域无法读取到第四层和第五层的内部变量,同理第四层无法读取第五层的内部变量;
注意:实际使用时,不建议不同层级的作用域,定义相同的 同步变量名称;
原理:实际上是广泛应用的匿名立即执行函数表达式;
3、块级作用域 与 函数声明:
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明;
ES6 引入了块级作用域,明确允许了会计作用中可以声明函数。
ES6 规定,块级作用域中,函数声明语句的行为类似于 let,在块级作用域之外不可引用;
function f() {
console.info('out');
}
(
function() {
function f() {
console.info('in');
}
f();
}
)
f(); // out
上述代码在 ES6 中运行,我在 谷歌浏览器中 打印的结果是 out,但是在参考文档中解释为 会出现报错;
这是由于环境导致的行为差异;
结论:应该避免在块级作用域内 声明函数;如果确实需要,也应该写成函数表达式,而不是函数声明语句;比如
// 块级作用域内部的函数声明语句,建议不要使用
{
let a = 'secret1';
function f() {
return a;
}
}
console.info(f()); // secret1
// 块级作用域内部,优先使用函数表达式
{
let a = 'secret2';
let f = function () {
return a;
}
console.info(f()); // secret2
}
console.info(f()); // secret1
ES6 块级作用域必须有大括号,如果没有大括号,JavaScrpt 引擎就认为不存在块级作用域;
//ES6 块级作用域必须有大括号
// 第一种写法,未添加块级作用的的大括号({}),报错
if (true) let x = 1; // Lexical declaration cannot appear in a single-statement context
// 第二种写法,不报错
if (true) {
let x = 1;
}
参考文档:https://es6.ruanyifeng.com/#docs/let#%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F
浙公网安备 33010602011771号