ECMAScript 2015(ES6)
let 和 const
新的变量声明方式,具有块级作用域和更严格的约束。
{
// 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'
}
解构赋值
从数组或对象中提取值,赋给变量的语法糖。
{
// 数组解构
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表示法
{
console.log("a", "\u0061"); // a a
console.log("s", "\u20BB7"); // s ₻7
console.log("s", "\u{20BB7}"); // s 𠮷
}
模板字符串
{
// 标签模板
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()
{
// 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
{
// 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()
{
// 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
// String.raw
console.log(String.raw`Hello\nWorld`) // Hello\nWorld (不转义)
console.log(`Hello\nWorld`) // Hello
// World (转义)
数值扩展
新的数值表示法和数学方法。
二进制0b(或0B) 八进制0o(或0O)
{
// 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
{
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
{
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对象上面,其目的是对全局变量进行模块化
{
// ES5的写法
console.log(parseInt('12.34')) // 12
// ES6的写法
console.log(Number.parseInt('12.34')) // 12
}
Number.parseFloat()
Number.parseFloat() 方法可以把一个字符串解析成浮点数。该方法与全局的
parseFloat() 函数相同,并且处于 ECMAScript 6 规范中(用于全局变量的模块化)
{
// ES5的写法
console.log(parseFloat('12.34#')) // 12.34
// ES6的写法
console.log(Number.parseFloat('12.34#')) // 12.34
}
Number.isInteger()
Number.isInteger() 方法用来判断给定的参数是否为整数
{
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)
{
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))
{
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
console.log(-Math.pow(2, 53) - 1) // -9007199254740991
}
Number.isSafeInteger()
Number.isSafeInteger() 方法用来判断传入的参数值是否是一个“安全整数”(safe integer)
{
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() 方法会将数字的小数部分去掉,只保留整数部分
{
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 传入该函数的参数会被隐式转换成数字类型
{
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() 函数返回任意数字的立方根
{
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()
{
// 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()
{
// 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()
{
// 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()
{
// 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()
{
// 数组实例的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()
{
// 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
{
// 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 参数、扩展运算符和箭头函数。
箭头函数和普通函数的区别
- 箭头函数和普通函数的样式不同,箭头函数语法更加简洁、清晰,箭头函数是=>定义函数,普通函数是function定义函数;
- 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,定义的时候就确定并固定了;
- 箭头函数不能作为构造函数使用,也不能使用new关键字(因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会改变,作为构造函数其的this要是指向创建的新对象);
- 箭头函数没有自己的arguments。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值;
- call、apply、bind 并不会影响其 this 的指向;
- 箭头函数没有原型prototype;
- 箭头函数不能当作 Generator 函数,不能使用 yield 关键字;
{
// 参数默认值
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) // 尾调用
}
}
对象扩展
简洁表示法、计算属性名和新的对象方法。
{
// 简洁表示法
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
新的原始数据类型,表示独一无二的值。
{
// 基本用法
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
新的数据结构,提供更好的集合操作。
{
// 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
统一的遍历接口和新的遍历语法。
{
// 数组的迭代器
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
生成器函数,可以暂停和恢复执行的函数。
{
// 基本语法
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
异步编程的新解决方案,比传统的回调函数更合理和强大。
{
// 基本用法
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
更清晰的面向对象编程语法糖。
{
// 基本类定义
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
对象操作的拦截和自定义。
{
// 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 语法。
{
// 导出方式
// 命名导出
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 提案)。
{
// 方法装饰器
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
}