ES6-2 块级作用域与嵌套、let、暂行性死区

本文探讨了ES6中的块级作用域,强调let关键字带来的变化。let不允许在同一作用域重复声明,并引入了暂时性死区的概念,使得typeof操作不再总是安全。在for循环中,let确保每次迭代都有独立的作用域。文章还提醒,不同浏览器对ES6的支持程度不同,可能需要Babel转译。
摘要由CSDN通过智能技术生成

注意,写在开头

function test(x = 1) {
    var x  // 不报错
    console.log(x)
}
function test1(x = 1) {
    let x = 10 // 报错
    console.log(x)
}

let的变量名不可以和参数中的名称相同。而var并不限制,说白了就是希望你规范使用变量名。
形参原则上数组函数内部的临时变量,但是形参其实在内存中有独立的空间存储。

阮一峰ES6 - let

思考,在编写代码时,有es5、es6的语法,究竟是否有块级作用域?
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。

1. var关键字可以重复声明

var num1 = 1;
var num1 = 100;
console.log(num1) // 100

2. let块级作用域

2.1 同一作用域下不能重复声明(无论是let/var/const声明)

let num1 = 1;
var num1 = 100; // 报错 重复定义
var a = 10;
let a = 10; // 报错
function test(a){
    let a = 10; // 预编译时,形参a已定义,重复声明报错
}
let a = 1;
function a() { } // 报错
let a = 1;
{
    function a() { } // 不报错
}
// 函数提升是在当前(块级)作用域下提升
// 转译后
"use strict";

var a = 1;
{
  var _a = function _a() {}; // 不报错

}
function test(a) {
    {
        let a = 10;
    }
    console.log(a) // undefined
}
test()

2.2 let不会提升,会产生一个暂时性死区,在变量声明前访问会报错

var a = a;
console.log(a) // undefined
let x = x; // 报错
// 在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。
// 这里不要把赋值语句拆分为声明和赋值2步理解
  • 以下3种情况
var x = 1;
{
	// 此处=右的x取的是块级作用域内的
	// 暂时性死区
    let x = x;
    console.log(x) // 报错
    // Uncaught ReferenceError: Cannot access 'x' before initialization
}

// 转译ES5
"use strict";

var x = 1;
{
  var _x = _x;
  console.log(_x); // undefined
}

这里注意: 因为es5不存在暂时死区。let x = x 的问题在于,右侧x是取值并赋值的操作,而这个时候在es6里,x并没有完成初始化,所以取值x的时候就会失败。
而var是不存在这种问题的。因为es6严格规定了初始化流程就是变量声明必须初始化且不允许取值。也就是说声明语句不可以有对该变量的引用。

转译并不能百分百还原。对应let转var这里就出现了以上的情况。
有些浏览器不支持块级作用域(大括号)。
转译结果和babel的版本也有关系,有可能转译后去除了大括号或转成IIFE或其他形式。

var x = 1;
{
    let x;
    x = x;
    console.log(x) // undefined
}

"use strict";

var x = 1;
{
  var _x;

  _x = _x;
  console.log(_x); // undefined
}
// 这个本身就是ES5不需要转
var x = 1;
{
    x = x;
    console.log(x) // 1
}
let a ;
a = a
console.log(a) // undefined
if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}
function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 报错 
// 参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。如果y的默认值是x,就不会报错,因为此时x已经声明了。

2.3 typeof不再是一个百分之百安全的操作

typeof x; // ReferenceError
let x;

注意

for (var i = 0; i < 10; i++) {
    arr[i] = function () {
        console.log(i)
    }
}
for (var i = 0; i < 10; i++) {
    arr[i]() // 打印0-9  
}
// 第二个for循环里,var i重新赋值了,恰好是i

在这里插入图片描述

var arr = [];
for (var i = 0; i < 10; i++) {
    arr[i] = function () {
        console.log(i)
    }
}
// 上个for循环var i是全局的,退出循环后为10
for (var index = 0; index < 10; index++) {
    arr[index]() // 10个10
}

==================================================

let是在块中声明的变量,每当声明一个function都会传入当前for的块中单独的i进去。也就是说let是块变量,在自己的块,或者子块中使用。而不是只在函数或全局作用域中使用。立即执行函数是为了将每次的i作为函数作用域中的局部变量传入与外界的i隔离。

在这里插入图片描述

// 用let声明,由于存在父子级作用域,相当于也形成了闭包
var arr = [];
for (let i = 0; i < 10; i++) {
    arr[i] = function () {
        console.log(i)
    }
}
// 即使index用let打印的也是0-9
for (var index = 0; index < 10; index++) {
    arr[index]() // 0-9
}
// 转译之后
"use strict";

var arr = [];

var _loop = function _loop(i) {
  arr[i] = function () {
    console.log(i);
  };
};

for (var i = 0; i < 10; i++) {
  _loop(i); // 这里立即执行了
}

for (var index = 0; index < 10; index++) {
  arr[index](); // 0-9
}

2.4 for循环作用域

  • for循环的特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for (var i = 0; i < 10; i++) {
    let i = 'a' 
    console.log(i) // 10个a 
    // 循环的index i是声明在全局的
}
console.log(i) // 10
for (let i = 0; i < 10; i++) {
    // for循环内的块级作用域
    // for花括号内的块级作用域并不相同
    let i = 'a'
    console.log(i) // 10个a
}
for (let i = 0; i < 10; i++) {
    var i = 'a' // 报错 
    // 因为for循环内,这里var声明的i会提升到全局
    // 而for循环条件又用let声明一次i,重复声明了
    console.log(i) 
}
if (1) {
    let a = 1;
    console.log(a) // 1
    {
        let a = 10;
        console.log(a) // 10
    }
}

以前遇到的有IIFE里不用关键字声明的吗

let a = 1;
(function(){
    a = 10; 
    console.log(a) // 10
})()
console.log(a) // 10
let a = 1;
(function(){
    let a = 10;
    console.log(a) // 10
})()
console.log(a) // 1

var a;
(function () {
    a = 10;
    console.log(a) // 10
})();
(function () {
    a = 100;
    console.log(a) // 100
})();
console.log(a) // 100
(function () {
    var a = 10;
    console.log(a) // 10
})();
(function () {
    var a = 100;
    console.log(a) // 100
})();
console.log(a) // 报错 
// Uncaught ReferenceError: a is not defined

思考:在index.html文件的script标签里编码,既有es5的语法,又有es6的语法,在浏览器中打开时,浏览器会将所有代码转译成es6吗
不会转译,浏览器不同的版本对es的支持不一样。(现代浏览器基本都支持ES6)
现在普遍兼容es6的语法。但对特殊的语法需要babel转译, 包括对象的拓展,类的修饰等等,这些是需要babel转译的。

块级作用域等于匿名函数的立即调用吗?并不,块级作用域没有返回值。二者本质不同。

思考总结,在块级作用域{}内,用let/const声明的和父作用域同名的变量x,在转译ES6的时候,会被编译成另一变量_x

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值