ES6复习(一)

ES6 及以上最新规范 let 和 const 在 ES6 之前,声明变量的关键字只有var,并且没有块级作用域,只有函数作用域和全局作用域。
letconstvar的区别

  • 不允许声明提升
  • 不允许重复声明
  • 不覆盖全局变量
  1. let
let 声明的变量,其声明语句不会再提升。
console.log(outer); console.log(inner); // Uncaught ReferenceError: inner is not defined { console.log(outer); // console.log(inner); // Uncaught ReferenceError: Cannot access 'inner' before initialization var outer = true; let inner = true; console.log(outer); // true console.log(inner); // true } console.log(outer); // true console.log(inner); // Uncaught ReferenceError: inner is not defined

临时死区(Temporal Dead Zone, TDZ),也叫暂时性死区。用letconst声明的变量,在声明前都会放到 TDZ 中,而声明前访问这些变量就会触发运行时错误。
其次,let 不允许重复声明同一个变量。这里有个限制:必须是在同一个作用域时,才不允许同一个变量重复声明。
最后是let在全局作用域中的特性。当用 var 在全局作用域中声明变量的时候,该变量不但会成为全局变量,而且会成为全局对象(如浏览器中 window 对象)的一个属性。ES6 规定用 let 可将全局变量和全局对象断开联系。下面用两组代码分别演示断开联系(第一组)和覆盖已有属性(第二组),注意,在第二组代码中为了方便对比,忽略了重复声明的错误。
// group 1 var global = true; console.log(window.global); // truelet whole = true; console.log(window.whole); // undefined// group 2 var Math = true; console.log(window.Math); //truelet Math = true; console.log(window.Math);

  1. const
const 不但拥有上面所述的 let 的 3 个特性,并且还能声明一个常量。常量是指一个定义了初始值后固定不变的只读变量。constlet不同,声明时必须初始化(即赋值)。并且这设定后其值无法更改。
注意,const 限制的是变量与内存地址之间的绑定。也就是说const让变量无法更改内存地址。如果是基本类型(如布尔值、数字等)的变量,那么对应的内存地址中保存的就是值;如果是引用类型(如对象)的变量,那么对应的内存地址中保存的是指向实际数据的一个指针。由此可知,当用 const 声明的变量,其初始化的值是对象时,可以修改对象中的属性或方法。
  1. 循环中的letconst
ES6 规定了 let 声明在循环内部的行为,以 for 循环为例,
for (let i = 0; i < 3; i++) { setTimeout(function () { console.log(i); }, 0); }

在控制台输出的结果依次为 0、1、2,没有出现循环中的异步回调问题。这是因为在每次循环的时候,都会重新创建一个叫作 i 的同名变量,并将其初始化为计算后的值,而循环体内调用的 i 变量不会受其他同名变量的影响,所以能够在定时器的回调函数中正确显示该变量的值。在 ES5 及之前的版本中,如果要解决异步回调问题,可以借助立即执行函数表达式(IIFE)才能得到预期的效果
for (var i = 0; i < 3; i++) { (function (n) { setTimeout(function () { console.log(n); }, 0); })(i); }

Symbol Symbol,可以像字符串那样作为对象的属性,且具有唯一性的特点,(重要)可以避免属性冲突。
1.创建
【ES6复习(一)】Symbol没有字面量形式,只能通过Symbol()函数创建。该函数有一个可选参数,只是用来描述当前符号,除了便于阅读,没有其他用途。即使 2 个Symbol的描述相同,它们也不相等。Symbol()不是构造函数,不能组合new运算符使用。
var sym1 = Symbol(); var sym2 = Symbol("name"); var sym3 = Symbol("name"); var sym4 = new Symbol(); // 抛出异常错误 cosole.log(sym2 === sym3); // true

typeof识别Symbol
typeof Symbol() === "symbol"; // true

  1. 类型转换
Symbol 无法与字符串和数字进行运算
var sym = Symbol("age"); Number(sym); parseInt(sym); 1 + sym; "" + sym;

但可以显性转成字符串或布尔值。
Boolean(sym); // true !sym; // false sym.toString(); // "Symbol(age)" String(sym); // "Symbol(age)"

  1. 全局共享
ES6 会在内部维护一张全局符号注册表,通过 Symbol.for()方法,可以登记指定符号,使其变成一个全局有效的符号,从而达到全局共享。该方法只接收一个参数,这个参数既是注册表中的键值,同时也是此符号的描述。下面的代码调用了两次 Symbol.for()方法,传递了相同的参数,返回的却是同一个全局符号。
var sym1 = Symbol.for("name"); var sym2 = Symbol.for("name"); console.log(sym1 === sym2); // true

  1. 属性名
常见三种属性名赋值方式:
  1. 内置符号
ES6 提供了一些内置符号,也叫知名符号(Well-Known Symbol)。它们暴露了语言的内部逻辑,允许开发人员修改或拓展规范所描述的对象特征或行为。每一个内置符号对应 Symbol 对象的一个属性,如 Symbol.hasInstance、Symbol.iterator 等.
属性名称 值类型 描述
hasInstance 方法 当使用 instanceof 运算符,该方法会被调用
isConcatSpreadable 布尔值 当对象作为 Array.protorype.concat()方法的参数时,控制改对象是否被展开
iterator 方法 返回一个迭代器用于定义一个可迭代对象
match 方法 当对象作为 String.prototype.match() 方法的参数时,该方法会被调用
replace 方法 当对象作为 String.prototype.replace() 方法的参数时,该方法会被调用
search 方法 当对象作为 String.prototype.search() 方法的参数时,该方法会被调用
split 方法 当对象作为 String.prototype.split() 方法的参数时,该方法会被调用
species 方法 创建派生类的构造函数
toPrimitive 方法 当对象需要转换成原始值(即执行 toPrimitive 抽象操作)时,该方法会被调用
toStringTag 字符串 指定对象的类型,可在调用 Object.prototype.toString()方法的时候返回
unscopables 对象 保存在这个对象中的属性将不能被 width 语句引用
下面给出 4 个内置符号示例:hasInstanceisConcatSpreadablematchtoStringTag:
  • 实现一个可迭代的对象
var people = { name: "test", sex: "male", hobbies: ["ball", "paint", "sing"], [Symbol.iterator]() { const _this = this; const keys = Reflect.ownKeys(_this); // 获取到对象的key值列表 let index = 0; return { next() { if (index < keys.length - 1) { return { value: _this[keys[index++]], // 想返回什么 就返回什么 keys[index++] done: false, }; } return { value: keys[index++], done: true, // 迭代结束 }; }, }; }, }; for (let h of people) { console.log(h); }

    推荐阅读