Skip to content

ECMAScript 2015(ES6)

let 和 const

新的变量声明方式,具有块级作用域和更严格的约束。

javascript
{
  // let 特性
  // 1. 块级作用域
  for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100) // 0, 1, 2
  }

  for (var j = 0; j < 3; j++) {
    setTimeout(() => console.log(j), 100) // 3, 3, 3
  }

  // 2. 不存在变量提升
  // console.log(a) // ReferenceError: Cannot access 'a' before initialization
  let a = 1

  // 3. 暂时性死区
  let x = 1
  {
    // console.log(x) // ReferenceError: Cannot access 'x' before initialization
    let x = 2
  }

  // 4. 不允许重复声明
  // let name = 'Alice'
  // let name = 'Bob' // SyntaxError: Identifier 'name' has already been declared

  // const 特性
  // 1. 声明时必须赋值
  const PI = 3.14159

  // 2. 不能重新赋值
  // PI = 3.14 // TypeError: Assignment to constant variable

  // 3. 对象内容可变
  const user = { name: 'Alice' }
  user.name = 'Bob' // 允许
  user.age = 25     // 允许
  console.log(user) // { name: 'Bob', age: 25 }

  // 冻结对象使其完全不可变
  const frozenUser = Object.freeze({ name: 'Charlie' })
  frozenUser.name = 'David' // 静默失败(严格模式下报错)
  console.log(frozenUser.name) // 'Charlie'
}

解构赋值

从数组或对象中提取值,赋给变量的语法糖。

javascript
{
  // 数组解构
  const [a, b, c] = [1, 2, 3]
  console.log(a, b, c) // 1 2 3

  // 跳过元素
  const [first, , third] = [1, 2, 3]
  console.log(first, third) // 1 3

  // 剩余元素
  const [head, ...tail] = [1, 2, 3, 4, 5]
  console.log(head, tail) // 1 [2, 3, 4, 5]

  // 默认值
  const [x = 1, y = 2] = [undefined, null]
  console.log(x, y) // 1 null

  // 对象解构
  const { name, age } = { name: 'Alice', age: 25, city: 'NYC' }
  console.log(name, age) // 'Alice' 25

  // 重命名
  const { name: userName, age: userAge } = { name: 'Bob', age: 30 }
  console.log(userName, userAge) // 'Bob' 30

  // 嵌套解构
  const { user: { profile: { avatar } } } = {
    user: { profile: { avatar: 'avatar.jpg' } }
  }
  console.log(avatar) // 'avatar.jpg'

  // 函数参数解构
  function greet({ name, age = 18 }) {
    console.log(`Hello ${name}, you are ${age} years old`)
  }
  greet({ name: 'Charlie' }) // Hello Charlie, you are 18 years old

  // 交换变量
  let m = 1, n = 2;
  [m, n] = [n, m]
  console.log(m, n) // 2 1

  // 字符串解构
  const [char1, char2] = 'hello'
  console.log(char1, char2) // 'h' 'e'
}

字符串扩展

Unicode表示法

javascript
{
  console.log("a", "\u0061"); // a a
  console.log("s", "\u20BB7"); // s ₻7

  console.log("s", "\u{20BB7}"); // s 𠮷
}

模板字符串

javascript
{
  // 标签模板
  let user = {
    name: "wcd",
    info: "hello world"
  };
  console.log(abc`i am ${user.name},${user.info}`); // i am ,,,wcdhello world
  function abc(s, v1, v2) {
    console.log(s, v1, v2); // [ 'i am ', ',', '' ] 'wcd' 'hello world' 
    return s + v1 + v2;
  }
}

{
  // String.raw 方法往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字符串。
  console.log(String.raw`Hi\n${1 + 2}`); // Hi\n3
  console.log(`Hi\n${1 + 2}`); // Hi 3
}

String.prototype.fromCodePoint()

javascript
{
  // es5
  console.log(String.fromCharCode("0x20bb7")); // ஷ
  // es6
  console.log(String.fromCodePoint("0x20bb7")); // 𠮷


  let str = "\u{20bb7}abc";

  // es5
  for (let i = 0; i < str.length; i++) {
    console.log("es5", str[i]);
  }
  // es5 �
  // es5 �
  // es5 a
  // es5 b
  // es5 c

  // es6
  for (let code of str) {
    console.log("es6", code);
  }
  // es6 𠮷
  // es6 a
  // es6 b
  // es6 c
}

String.prototype.includes()/startsWith/endsWith

javascript
{
  // includes():返回布尔值,表示是否找到了参数字符串。
  // startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  // endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
  let str = "string";
  console.log("includes", str.includes("r")); // includes true
  console.log("includes", str.includes("c")); // includes false

  console.log("start", str.startsWith("str")); // start true
  console.log("end", str.endsWith("str")); // end false
}

String.prototype.repeat()

javascript
{
  // repeat 方法返回一个新字符串,表示将原字符串重复n次。
  let str = "abc";
  console.log(str.repeat(2)); // abcabc
  console.log('na'.repeat(0);); // ""

  // 参数如果是小数,会被取整。
  console.log('na'.repeat(2.9)); // "nana"

  // 如果repeat的参数是负数或者Infinity,会报错。
  console.log('na'.repeat(Infinity)); // RangeError
  console.log('na'.repeat(-1)); // RangeError

  // but 如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于-0,repeat视同为 0。
  console.log('na'.repeat(-0.9)); // ""

  // 参数NaN等同于 0。
  console.log('na'.repeat(NaN)); // ""

  // 如果repeat的参数是字符串,则会先转换成数字。
  console.log('na'.repeat('na')); // ""
  console.log('na'.repeat('3')); // "nanana"
}

String.raw

javascript
// String.raw
console.log(String.raw`Hello\nWorld`) // Hello\nWorld (不转义)
console.log(`Hello\nWorld`)           // Hello
                                      // World (转义)

数值扩展

新的数值表示法和数学方法。

二进制0b(或0B) 八进制0o(或0O)

javascript
{
  // es5
  // 十进制 -> 二进制
  const a = 5
  console.log(a.toString(2)) // 101

  // 二进制 -> 十进制
  const b = 101
  console.log(parseInt(b, 2)) // 5
}

{
  // es6
  // 0b(或0B) 和0o(或0O)
  const a = 0b0101
  console.log(a)

  const b = 0o777
  console.log(b)
}

Number.isFinite()

用来检查一个数值是否为有限的(finite),即不是Infinity

javascript
{
  console.log(Number.isFinite(15)) // true
  console.log(Number.isFinite(0.8)) // true
  console.log(Number.isFinite(NaN)) // false
  console.log(Number.isFinite(Infinity)) // false
  console.log(Number.isFinite(-Infinity)) // false
  console.log(Number.isFinite('foo')) // false
  console.log(Number.isFinite('15')) // false
  console.log(Number.isFinite(true)) // false
}

Number.isNaN()

用来检查一个值是否为NaN

javascript
{
  console.log(Number.isNaN(NaN)) // true
  console.log(Number.isNaN(15)) // false
  console.log(Number.isNaN('15')) // false
  console.log(Number.isNaN(true)) // false
  console.log(Number.isNaN(9 / NaN)) // false
  console.log(Number.isNaN('true' / 0)) // true
  console.log(Number.isNaN('true' / 'true')) // true
}

Number.parseInt()

  • Number.parseInt() 方法依据指定基数解析成整数。
  • 注:
    • 这个方法和全局的 parseInt() 函数具有一样的函数功能
    • Number.parseInt === parseInt; // true
    • ES6 将全局方法parseInt()移植到Number对象上面,其目的是对全局变量进行模块化
javascript
{
  // ES5的写法
  console.log(parseInt('12.34')) // 12

  // ES6的写法
  console.log(Number.parseInt('12.34')) // 12
}

Number.parseFloat()

Number.parseFloat() 方法可以把一个字符串解析成浮点数。该方法与全局的

parseFloat() 函数相同,并且处于 ECMAScript 6 规范中(用于全局变量的模块化)

javascript
{
  // ES5的写法
  console.log(parseFloat('12.34#')) // 12.34

  // ES6的写法
  console.log(Number.parseFloat('12.34#')) // 12.34
}

Number.isInteger()

Number.isInteger() 方法用来判断给定的参数是否为整数

javascript
{
  console.log(Number.isInteger(25)) // true
  console.log(Number.isInteger(25.1)) // false
  console.log(Number.isInteger(null)) // false
  console.log(Number.isInteger('15')) // false
  console.log(Number.isInteger(true)) // false
}

Number.MAX_SAFE_INTEGER

Number.MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的安全整数(maxinum safe integer)(253 - 1)

javascript
{
  console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
  console.log(Math.pow(2, 53) - 1) // 9007199254740991
}

Number.MIN_SAFE_INTEGER

Number.MIN_SAFE_INTEGER 代表在 JavaScript中最小的安全的integer型数字 (-(253 - 1))

javascript
{
  console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
  console.log(-Math.pow(2, 53) - 1) // -9007199254740991
}

Number.isSafeInteger()

Number.isSafeInteger() 方法用来判断传入的参数值是否是一个“安全整数”(safe integer)

javascript
{
  console.log(Number.isSafeInteger(3)) // true
  console.log(Number.isSafeInteger(Math.pow(2, 53))) // true
  console.log(Number.isSafeInteger(Math.pow(2, 53) - 1)) // true
  console.log(Number.isSafeInteger(NaN)) // false
  console.log(Number.isSafeInteger(Infinity)) // false
  console.log(Number.isSafeInteger('3')) // false
  console.log(Number.isSafeInteger(3.1)) // false
  console.log(Number.isSafeInteger(3.1)) // false
  console.log(Number.isSafeInteger(3.0)) // true
}

Math扩展

Math.trunc()

Math.trunc() 方法会将数字的小数部分去掉,只保留整数部分

javascript
{
  console.log(Math.trunc(13.37)) // 13
  console.log(Math.trunc(42.84)) // 42
  console.log(Math.trunc(0.123)) // 0
  console.log(Math.trunc(-0.123)) // 0
  console.log(Math.trunc(true)) // 1
  console.log(Math.trunc(false)) // 0
  console.log(Math.trunc(NaN)) // NaN
  console.log(Math.trunc(undefined)) // NaN
  console.log(Math.trunc('foo')) // NaN
  console.log(Math.trunc()) // NaN
}

Math.sign()

Math.sign() 函数返回一个数字的符号, 指示数字是正数,负数还是零 注: 此函数共有5种返回值, 分别是 1, -1, 0, -0, NaN. 代表的各是正数, 负数, 正零, 负零, NaN 传入该函数的参数会被隐式转换成数字类型

javascript
{
  console.log(Math.sign(3)) // 1
  console.log(Math.sign(-3)) // -1
  console.log(Math.sign('-3')) // -1
  console.log(Math.sign(0)) //  0
  console.log(Math.sign(-0)) // -0
  console.log(Math.sign(NaN)) // NaN
  console.log(Math.sign(true)) // 1
  console.log(Math.sign(false)) // 0
  console.log(Math.sign('foo')) // NaN
  console.log(Math.sign()) // NaN
}

Math.cbrt()

Math.cbrt() 函数返回任意数字的立方根

javascript
{
  console.log(Math.cbrt(8)) // 2
  console.log(Math.cbrt(NaN)) // NaN
  console.log(Math.cbrt(-1)) // -1
  console.log(Math.cbrt(-0)) // -0
  console.log(Math.cbrt(-Infinity)) // -Infinity
  console.log(Math.cbrt(0)) // 0
  console.log(Math.cbrt(1)) // 1
  console.log(Math.cbrt(Infinity)) // Infinity
  console.log(Math.cbrt(null)) // 0
  console.log(Math.cbrt(2)) // 1.2599210498948734
}

数组扩展 (类数组/伪数组)

Array.from()

javascript
{
  // Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。
  let p = document.querySelectorAll('p')
  let pArr = Array.from(p)
  pArr.forEach(function (item) {
    console.log(item.textContent) //
  })

  console.log(
    Array.from([1, 3, 5], function (item) {
      return item * 2
    })
  ) // [ 2, 6, 10 ]
}

Array.of()

javascript
{
  // Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
  let arr = Array.of(3, 4, 7, 9, 11)
  console.log('arr=', arr) // arr= [ 3, 4, 7, 9, 11 ]

  let empty = Array.of()
  console.log('empty', empty) // empty []
}

Array.copyWithin()

javascript
{
  // copyWithin函数,用于操作当前数组自身,用来把某些个位置的元素复制并覆盖到其他位置上去

  // Array.prototype.copyWithin(target, start = 0, end = this.length)
  // target:目的起始位置。
  // start:复制源的起始位置,可以省略,可以是负数。
  // end:复制源的结束位置,可以省略,可以是负数,实际结束位置是end-1。

  console.log([1, 2, 3, 4, 5].copyWithin(0, 3, 4)) // [ 4, 2, 3, 4, 5 ]
}

Array.fill()

javascript
{
  // fill()函数,使用制定的元素填充数组,其实就是用默认内容初始化数组

  // arr.fill(value, start, end)
  // value:填充值
  // start:填充起始位置,可以省略
  // end:填充结束位置,可以省略,实际结束位置是end-1
  console.log('fill-7', [1, 'a', undefined].fill(7)) // fill-7 [ 7, 7, 7 ]
  console.log('fill,pos', ['a', 'b', 'c'].fill(7, 1, 3)) // fill,pos [ 'a', 7, 7 ]
}

Array.find()/Array.findIndex()

javascript
{
  // 数组实例的find方法,用于找出符合条件的数组的值
  // 数组实例的findIndex方法,用于找出符合条件的数组下标
  console.log(
    [1, 2, 3, 4, 5, 6].find(function (item) {
      return item > 3
    })
  ) // 4
  console.log(
    [1, 2, 3, 4, 5, 6].findIndex(function (item) {
      return item > 3
    })
  ) // 3
}

Array.includes()

javascript
{
  // ES6提供了Array.includes()函数判断是否包含某一元素,除了不能定位外,解决了indexOf的上述的两个问题。它直接返回true或者false表示是否包含元素,对NaN一样能有有效。
  console.log('number', [1, 2, NaN].includes(1)) // number true
  console.log('number', [1, 2, NaN].includes(NaN)) // number true
}

Array.entries/Array.keys/Array.values

javascript
{
  // ES6 提供三个新的方法 —— entries(),keys()和values() —— 用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

  // values() 方法在 Chrome 下面运行会报错未定义(not a function)

  for (let index of ['1', 'c', 'ks'].keys()) {
    console.log('keys', index)
    // keys 0
    // keys 1
    // keys 2
  }

  for (let values of ['1', 'c', 'ks']) {
    console.log('values', values)
    // values 1
    // values c
    // values ks
  }

  for (let [index, values] of ['1', 'c', 'ks'].entries()) {
    console.log('index,values', index, values)
    // index,values 0 1
    // index,values 1 c
    // index,values 2 ks
  }
}

函数扩展

参数默认值、rest 参数、扩展运算符和箭头函数。

箭头函数和普通函数的区别

  1. 箭头函数和普通函数的样式不同,箭头函数语法更加简洁、清晰,箭头函数是=>定义函数,普通函数是function定义函数;
  2. 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,定义的时候就确定并固定了;
  3. 箭头函数不能作为构造函数使用,也不能使用new关键字(因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会改变,作为构造函数其的this要是指向创建的新对象);
  4. 箭头函数没有自己的arguments。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值;
  5. call、apply、bind 并不会影响其 this 的指向;
  6. 箭头函数没有原型prototype;
  7. 箭头函数不能当作 Generator 函数,不能使用 yield 关键字;
javascript
{
  // 参数默认值
  function greet(name = 'World', age = 18) {
    console.log(`Hello ${name}, age ${age}`)
  }
  greet() // Hello World, age 18
  greet('Alice', 25) // Hello Alice, age 25

  // 默认值可以是表达式
  function createId(prefix = 'user', suffix = Date.now()) {
    return `${prefix}_${suffix}`
  }

  // rest 参数
  function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0)
  }
  console.log(sum(1, 2, 3, 4)) // 10

  // 扩展运算符
  const arr1 = [1, 2, 3]
  const arr2 = [4, 5, 6]
  const combined = [...arr1, ...arr2]
  console.log(combined) // [1, 2, 3, 4, 5, 6]

  // 函数调用
  function multiply(a, b, c) {
    return a * b * c
  }
  console.log(multiply(...[2, 3, 4])) // 24

  // 箭头函数
  const double = x => x * 2
  const add = (a, b) => a + b
  const createUser = name => ({ name, createdAt: new Date() })

  // 箭头函数的 this
  const obj = {
    name: 'Alice',
    greet: function() {
      setTimeout(() => {
        console.log(`Hello, I'm ${this.name}`) // this 指向 obj
      }, 100)
    }
  }

  // 尾调用优化
  function factorial(n, acc = 1) {
    if (n <= 1) return acc
    return factorial(n - 1, n * acc) // 尾调用
  }
}

对象扩展

简洁表示法、计算属性名和新的对象方法。

javascript
{
  // 简洁表示法
  const name = 'Alice'
  const age = 25
  
  const user = {
    name,        // 等同于 name: name
    age,         // 等同于 age: age
    greet() {    // 等同于 greet: function() {}
      console.log(`Hello, I'm ${this.name}`)
    }
  }

  // 计算属性名
  const prop = 'foo'
  const obj = {
    [prop]: 'bar',
    ['hello' + 'world']: 123,
    [Symbol.iterator]: function() {} // symbol 作为属性名
  }

  // Object.is()
  console.log(Object.is(NaN, NaN))     // true
  console.log(Object.is(+0, -0))       // false
  console.log(NaN === NaN)             // false
  console.log(+0 === -0)               // true

  // Object.assign()
  const target = { a: 1, b: 2 }
  const source1 = { b: 3, c: 4 }
  const source2 = { c: 5, d: 6 }
  
  Object.assign(target, source1, source2)
  console.log(target) // { a: 1, b: 3, c: 5, d: 6 }

  // 克隆对象
  const clone = Object.assign({}, user)

  // 合并对象
  const merged = Object.assign({}, source1, source2)

  // 对象的遍历
  const data = { name: 'Bob', age: 30 }
  
  // for...in
  for (const key in data) {
    console.log(key, data[key])
  }

  // Object.keys()
  Object.keys(data).forEach(key => {
    console.log(key, data[key])
  })

  // Object.getOwnPropertyNames()
  Object.getOwnPropertyNames(data).forEach(key => {
    console.log(key, data[key])
  })

  // 扩展运算符(ES2018)
  const { name: userName, ...rest } = user
  const newUser = { ...user, city: 'NYC' }
}

Symbol

新的原始数据类型,表示独一无二的值。

javascript
{
  // 基本用法
  const sym1 = Symbol()
  const sym2 = Symbol('description')
  const sym3 = Symbol('description')

  console.log(sym2 === sym3) // false,每个 Symbol 都是唯一的

  // Symbol.for() 全局注册
  const globalSym1 = Symbol.for('global')
  const globalSym2 = Symbol.for('global')
  console.log(globalSym1 === globalSym2) // true

  // Symbol.keyFor() 获取描述
  console.log(Symbol.keyFor(globalSym1)) // 'global'
  console.log(Symbol.keyFor(sym2))       // undefined

  // 作为对象属性
  const obj = {
    [sym2]: 'symbol property',
    normalProp: 'normal property'
  }

  console.log(obj[sym2])        // 'symbol property'
  console.log(obj.normalProp)   // 'normal property'

  // Symbol 属性不被常规方法遍历
  console.log(Object.keys(obj))                 // ['normalProp']
  console.log(Object.getOwnPropertyNames(obj))  // ['normalProp']
  console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(description)]

  // Reflect.ownKeys() 可以返回所有属性
  console.log(Reflect.ownKeys(obj)) // ['normalProp', Symbol(description)]

  // 内置 Symbol
  const arr = [1, 2, 3]
  console.log(arr[Symbol.iterator]) // 数组的迭代器

  // 自定义迭代器
  const iterable = {
    [Symbol.iterator]() {
      let count = 0
      return {
        next() {
          if (count < 3) {
            return { value: count++, done: false }
          }
          return { done: true }
        }
      }
    }
  }

  for (const value of iterable) {
    console.log(value) // 0 1 2
  }
}

Set 和 Map

新的数据结构,提供更好的集合操作。

javascript
{
  // Set - 值的集合,值是唯一的
  const set = new Set([1, 2, 3, 3, 4])
  console.log(set) // Set {1, 2, 3, 4}

  // Set 方法
  set.add(5)
  console.log(set.has(3))    // true
  console.log(set.size)      // 5
  set.delete(2)
  console.log(set)           // Set {1, 3, 4, 5}

  // 数组去重
  const arr = [1, 2, 2, 3, 3, 4]
  const unique = [...new Set(arr)]
  console.log(unique) // [1, 2, 3, 4]

  // Set 遍历
  for (const value of set) {
    console.log(value)
  }

  set.forEach(value => console.log(value))

  // WeakSet - 弱引用,只能存储对象
  const weakSet = new WeakSet()
  const obj1 = {}
  const obj2 = {}
  
  weakSet.add(obj1)
  weakSet.add(obj2)
  console.log(weakSet.has(obj1)) // true

  // Map - 键值对集合,键可以是任意类型
  const map = new Map()
  const keyObj = {}
  const keyFunc = function() {}

  map.set('string', 'string value')
  map.set(keyObj, 'object value')
  map.set(keyFunc, 'function value')

  console.log(map.get('string'))  // 'string value'
  console.log(map.get(keyObj))    // 'object value'
  console.log(map.size)           // 3

  // Map 构造函数
  const map2 = new Map([
    ['name', 'Alice'],
    ['age', 25]
  ])

  // Map 遍历
  for (const [key, value] of map2) {
    console.log(key, value)
  }

  map2.forEach((value, key) => {
    console.log(key, value)
  })

  // WeakMap - 弱引用,键必须是对象
  const weakMap = new WeakMap()
  const key = {}
  
  weakMap.set(key, 'value')
  console.log(weakMap.get(key)) // 'value'

  // 数据结构比较
  // Map vs Object
  const mapDS = new Map()
  const objDS = {}
  
  // 性能对比示例
  mapDS.set('key1', 'value1')
  objDS.key1 = 'value1'
  
  console.log(mapDS.has('key1'))    // true
  console.log('key1' in objDS)      // true
}

Iterator 和 for...of

统一的遍历接口和新的遍历语法。

javascript
{
  // 数组的迭代器
  const arr = ['a', 'b', 'c']
  const iterator = arr[Symbol.iterator]()

  console.log(iterator.next()) // { value: 'a', done: false }
  console.log(iterator.next()) // { value: 'b', done: false }
  console.log(iterator.next()) // { value: 'c', done: false }
  console.log(iterator.next()) // { value: undefined, done: true }

  // for...of 循环
  for (const value of arr) {
    console.log(value) // 'a' 'b' 'c'
  }

  // 字符串迭代
  for (const char of 'hello') {
    console.log(char) // 'h' 'e' 'l' 'l' 'o'
  }

  // 自定义迭代器
  const range = {
    from: 1,
    to: 5,
    [Symbol.iterator]() {
      let current = this.from
      const last = this.to
      return {
        next() {
          if (current <= last) {
            return { value: current++, done: false }
          } else {
            return { done: true }
          }
        }
      }
    }
  }

  for (const num of range) {
    console.log(num) // 1 2 3 4 5
  }

  // 类数组对象添加迭代器
  const arrayLike = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3,
    [Symbol.iterator]: Array.prototype[Symbol.iterator]
  }

  for (const value of arrayLike) {
    console.log(value) // 'a' 'b' 'c'
  }
}

Generator

生成器函数,可以暂停和恢复执行的函数。

javascript
{
  // 基本语法
  function* simpleGenerator() {
    yield 1
    yield 2
    yield 3
  }

  const gen = simpleGenerator()
  console.log(gen.next()) // { value: 1, done: false }
  console.log(gen.next()) // { value: 2, done: false }
  console.log(gen.next()) // { value: 3, done: false }
  console.log(gen.next()) // { value: undefined, done: true }

  // 无限序列
  function* fibonacci() {
    let a = 0, b = 1
    while (true) {
      yield a;
      [a, b] = [b, a + b]
    }
  }

  const fib = fibonacci()
  console.log(fib.next().value) // 0
  console.log(fib.next().value) // 1
  console.log(fib.next().value) // 1
  console.log(fib.next().value) // 2

  // yield* 委托
  function* inner() {
    yield 1
    yield 2
  }

  function* outer() {
    yield 0
    yield* inner()
    yield 3
  }

  console.log([...outer()]) // [0, 1, 2, 3]

  // 双向通信
  function* twoWayGen() {
    const input1 = yield 'First yield'
    const input2 = yield 'Second yield'
    return `Inputs: ${input1}, ${input2}`
  }

  const gen2 = twoWayGen()
  console.log(gen2.next())        // { value: 'First yield', done: false }
  console.log(gen2.next('hello')) // { value: 'Second yield', done: false }
  console.log(gen2.next('world')) // { value: 'Inputs: hello, world', done: true }

  // 实际应用:异步流程控制
  function* asyncFlow() {
    try {
      const user = yield fetchUser()
      const posts = yield fetchPosts(user.id)
      return { user, posts }
    } catch (error) {
      console.error('Error:', error)
    }
  }

  function fetchUser() {
    return Promise.resolve({ id: 1, name: 'Alice' })
  }

  function fetchPosts(userId) {
    return Promise.resolve([{ id: 1, title: 'Post 1' }])
  }

  // 执行异步生成器
  function runAsync(genFunc) {
    const gen = genFunc()
    
    function step(value) {
      const result = gen.next(value)
      if (result.done) {
        return result.value
      }
      return Promise.resolve(result.value).then(step)
    }
    
    return step()
  }

  runAsync(asyncFlow).then(result => {
    console.log(result) // { user: {...}, posts: [...] }
  })
}

Promise

异步编程的新解决方案,比传统的回调函数更合理和强大。

javascript
{
  // 基本用法
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve('Success!')
      } else {
        reject(new Error('Failed!'))
      }
    }, 1000)
  })

  promise
    .then(result => console.log(result))
    .catch(error => console.error(error))

  // 链式调用
  fetchUser()
    .then(user => {
      console.log('User:', user)
      return fetchPosts(user.id)
    })
    .then(posts => {
      console.log('Posts:', posts)
      return posts.map(post => post.title)
    })
    .then(titles => {
      console.log('Titles:', titles)
    })
    .catch(error => {
      console.error('Error:', error)
    })

  function fetchUser() {
    return new Promise(resolve => {
      setTimeout(() => resolve({ id: 1, name: 'Alice' }), 100)
    })
  }

  function fetchPosts(userId) {
    return new Promise(resolve => {
      setTimeout(() => resolve([
        { id: 1, title: 'Post 1', userId },
        { id: 2, title: 'Post 2', userId }
      ]), 100)
    })
  }

  // Promise.all() - 并行执行
  Promise.all([
    fetch('/api/users'),
    fetch('/api/posts'),
    fetch('/api/comments')
  ]).then(responses => {
    console.log('All requests completed')
    return Promise.all(responses.map(r => r.json()))
  }).then(data => {
    console.log('All data:', data)
  })

  // Promise.race() - 竞态
  Promise.race([
    new Promise(resolve => setTimeout(() => resolve('Fast'), 100)),
    new Promise(resolve => setTimeout(() => resolve('Slow'), 500))
  ]).then(result => {
    console.log(result) // 'Fast'
  })

  // 错误处理
  new Promise((resolve, reject) => {
    throw new Error('Something went wrong')
  })
  .then(result => {
    console.log(result) // 不会执行
  })
  .catch(error => {
    console.error('Caught:', error.message)
  })
  .finally(() => {
    console.log('Cleanup') // 总是执行
  })

  // Promise 状态
  console.log(Promise.resolve('resolved'))  // 立即解决
  console.log(Promise.reject('rejected'))   // 立即拒绝
}

Class

更清晰的面向对象编程语法糖。

javascript
{
  // 基本类定义
  class Person {
    constructor(name, age) {
      this.name = name
      this.age = age
    }

    greet() {
      console.log(`Hello, I'm ${this.name}`)
    }

    get info() {
      return `${this.name} is ${this.age} years old`
    }

    set info(value) {
      const [name, age] = value.split(' is ')
      this.name = name
      this.age = parseInt(age)
    }

    static species() {
      return 'Homo sapiens'
    }
  }

  const person = new Person('Alice', 25)
  person.greet() // Hello, I'm Alice
  console.log(person.info) // Alice is 25 years old
  console.log(Person.species()) // Homo sapiens

  // 继承
  class Student extends Person {
    constructor(name, age, school) {
      super(name, age) // 调用父类构造函数
      this.school = school
    }

    greet() {
      super.greet() // 调用父类方法
      console.log(`I study at ${this.school}`)
    }

    study() {
      console.log(`${this.name} is studying`)
    }
  }

  const student = new Student('Bob', 20, 'MIT')
  student.greet() 
  // Hello, I'm Bob
  // I study at MIT
  student.study() // Bob is studying

  // 静态属性(ES2022)
  class MyClass {
    static staticProperty = 'static value'
    
    static staticMethod() {
      return 'static method'
    }
  }

  console.log(MyClass.staticProperty) // 'static value'
  console.log(MyClass.staticMethod()) // 'static method'

  // 私有字段和方法(ES2022)
  class PrivateExample {
    #privateField = 'private'
    
    #privateMethod() {
      return 'private method'
    }
    
    publicMethod() {
      return this.#privateMethod()
    }
  }

  const privateExample = new PrivateExample()
  console.log(privateExample.publicMethod()) // 'private method'
  // console.log(privateExample.#privateField) // SyntaxError
}

Proxy 和 Reflect

对象操作的拦截和自定义。

javascript
{
  // Proxy 基本用法
  const target = {
    name: 'Alice',
    age: 25
  }

  const proxy = new Proxy(target, {
    get(target, property) {
      console.log(`Getting ${property}`)
      return target[property]
    },
    
    set(target, property, value) {
      console.log(`Setting ${property} to ${value}`)
      if (property === 'age' && value < 0) {
        throw new Error('Age cannot be negative')
      }
      target[property] = value
      return true
    },
    
    has(target, property) {
      console.log(`Checking if ${property} exists`)
      return property in target
    },
    
    deleteProperty(target, property) {
      console.log(`Deleting ${property}`)
      if (property.startsWith('_')) {
        delete target[property]
        return true
      }
      return false
    }
  })

  console.log(proxy.name)    // Getting name, Alice
  proxy.age = 30             // Setting age to 30
  console.log('name' in proxy) // Checking if name exists, true

  // Reflect - 提供拦截 JavaScript 操作的方法
  const obj = { name: 'Bob', age: 20 }
  
  console.log(Reflect.get(obj, 'name'))        // Bob
  Reflect.set(obj, 'age', 25)
  console.log(Reflect.has(obj, 'name'))        // true
  console.log(Reflect.ownKeys(obj))            // ['name', 'age']

  // 验证器示例
  function createValidator(target, validators) {
    return new Proxy(target, {
      set(target, property, value) {
        const validator = validators[property]
        if (validator && !validator(value)) {
          throw new Error(`Invalid value for ${property}`)
        }
        return Reflect.set(target, property, value)
      }
    })
  }

  const userValidators = {
    name: val => typeof val === 'string' && val.length > 0,
    age: val => typeof val === 'number' && val >= 0
  }

  const validatedUser = createValidator({}, userValidators)
  validatedUser.name = 'Alice' // OK
  validatedUser.age = 25       // OK
  // validatedUser.age = -1     // Error: Invalid value for age
}

模块化

ES6 原生支持模块化,提供 import/export 语法。

javascript
{
  // 导出方式
  
  // 命名导出
  export const PI = 3.14159
  export function add(a, b) {
    return a + b
  }
  export class Calculator {
    multiply(a, b) {
      return a * b
    }
  }

  // 批量导出
  const name = 'MyModule'
  const version = '1.0.0'
  function init() {
    console.log('Module initialized')
  }
  
  export { name, version, init }

  // 重命名导出
  export { init as initialize, name as moduleName }

  // 默认导出
  export default class DefaultClass {
    constructor() {
      console.log('Default class created')
    }
  }

  // 或者
  class MyClass {}
  export default MyClass

  // 导入方式
  
  // 命名导入
  import { PI, add, Calculator } from './math.js'
  
  // 重命名导入
  import { add as sum, PI as pi } from './math.js'
  
  // 默认导入
  import DefaultClass from './module.js'
  
  // 混合导入
  import DefaultClass, { PI, add } from './module.js'
  
  // 全部导入
  import * as mathModule from './math.js'
  console.log(mathModule.PI)
  console.log(mathModule.add(1, 2))
  
  // 动态导入
  async function loadModule() {
    const module = await import('./math.js')
    console.log(module.PI)
  }
  
  // 或者
  import('./math.js').then(module => {
    console.log(module.add(1, 2))
  })

  // 只执行模块,不导入
  import './init.js'

  // 转发导出
  export { PI, add } from './math.js'
  export { default } from './defaultModule.js'
  export * from './utilities.js'
}

Decorator(装饰器)

函数修饰符,用于扩展类和方法的功能(Stage 3 提案)。

javascript
{
  // 方法装饰器
  function readonly(target, name, descriptor) {
    descriptor.writable = false
    return descriptor
  }

  function log(type) {
    return function(target, name, descriptor) {
      const originalMethod = descriptor.value
      descriptor.value = function(...args) {
        console.log(`[${type}] Calling ${name} with args:`, args)
        const result = originalMethod.apply(this, args)
        console.log(`[${type}] Result:`, result)
        return result
      }
      return descriptor
    }
  }

  class Example {
    @readonly
    getValue() {
      return 'read-only value'
    }

    @log('INFO')
    calculate(a, b) {
      return a + b
    }
  }

  const example = new Example()
  console.log(example.getValue()) // read-only value
  example.calculate(2, 3)         // [INFO] Calling calculate..., [INFO] Result: 5

  // 类装饰器
  function addMetadata(target) {
    target.metadata = { createdAt: new Date() }
    return target
  }

  @addMetadata
  class MetadataClass {
    constructor(name) {
      this.name = name
    }
  }

  console.log(MetadataClass.metadata) // { createdAt: Date }

  // 实际应用:埋点装饰器
  function track(eventName) {
    return function(target, name, descriptor) {
      const originalMethod = descriptor.value
      descriptor.value = function(...args) {
        // 埋点逻辑
        console.log(`Track event: ${eventName}`)
        return originalMethod.apply(this, args)
      }
      return descriptor
    }
  }

  class Button {
    @track('button_click')
    onClick() {
      console.log('Button clicked')
    }

    @track('button_hover')
    onHover() {
      console.log('Button hovered')
    }
  }

  const button = new Button()
  button.onClick() // Track event: button_click, Button clicked
}