前端 ES6 与 ESNext 特性全解析
ES6(ECMAScript 2015)作为JavaScript发展的重要转折点,带来了诸多革命性特性,显著提升了开发效率。此后,ES7、ES8等后续版本以及ESNext持续推动着JavaScript的演进。
ES6(ES2015)核心特性
1. 块级作用域:let 和 const
let和const解决了 ES5 中var的作用域问题。
let 关键字
// let 声明的变量具有块级作用域for(leti=0;i<3;i++){console.log(i);// 0, 1, 2}// console.log(i); // ReferenceError: i is not defined// 不会变量提升console.log(a);// ReferenceErrorleta=1;// 不允许重复声明letb=2;// let b = 3; // SyntaxError// 暂时性死区(Temporal Dead Zone){console.log(c);// ReferenceErrorletc=3;}const 关键字
// const 声明常量,值不可修改(对于基本类型)constPI=3.14159;// PI = 3.14; // TypeError// 对于对象/数组,内容可以修改constobj={name:'JavaScript'};obj.name='ES6';// 允许// obj = {}; // TypeError// 同样具有块级作用域{constAPI_KEY='abc123';console.log(API_KEY);// abc123}// console.log(API_KEY); // ReferenceError2. 解构赋值(Destructuring)
数组解构
// 基本数组解构const[a,b,c]=[1,2,3];console.log(a,b,c);// 1, 2, 3// 跳过元素const[x,,z]=[1,2,3];console.log(x,z);// 1, 3// 剩余操作符const[first,...rest]=[1,2,3,4];console.log(first,rest);// 1, [2, 3, 4]// 默认值const[p=1,q=2]=[5];console.log(p,q);// 5, 2// 交换变量letm=1,n=2;[m,n]=[n,m];console.log(m,n);// 2, 1对象解构
// 基本对象解构const{name,age}={name:'Alice',age:25};console.log(name,age);// Alice, 25// 变量重命名const{name:userName,age:userAge}={name:'Bob',age:30};console.log(userName,userAge);// Bob, 30// 默认值const{score=0,grade='A'}={score:95};console.log(score,grade);// 95, 'A'// 嵌套解构const{info:{city,country}}={info:{city:'Beijing',country:'China'}};console.log(city,country);// Beijing, China// 剩余属性const{x,y,...coords}={x:1,y:2,z:3,w:4};console.log(x,y,coords);// 1, 2, { z: 3, w: 4 }3. 箭头函数(Arrow Functions)
// 传统函数functionadd(a,b){returna+b;}// 箭头函数constadd=(a,b)=>a+b;// 多个参数constmultiply=(x,y)=>x*y;// 单个参数(可省略括号)constsquare=x=>x*x;// 无参数constgetRandom=()=>Math.random();// 函数体多条语句constcalculate=(a,b)=>{constsum=a+b;constproduct=a*b;return{sum,product};};// 返回对象字面量constcreateUser=(name,age)=>({name,age});// 立即执行函数表达式(IIFE)(()=>console.log('IIFE'))();// 作为回调函数constnumbers=[1,2,3,4,5];constdoubled=numbers.map(n=>n*2);constevens=numbers.filter(n=>n%2===0);constsum=numbers.reduce((acc,n)=>acc+n,0);箭头函数的特性
// 1. 没有自己的 this,继承自外层作用域constobj={value:10,methods:function(){// this 指向 objsetTimeout(()=>{console.log(this.value);// 10},100);}};// 2. 没有 arguments 对象functionouter(){constinner=()=>{// arguments 来自外层函数console.log(arguments);// [1, 2, 3]};inner();}outer(1,2,3);// 3. 不能用作构造函数constPerson=(name)=>{this.name=name;};// const p = new Person('John'); // TypeError// 4. 没有 prototype 属性constfn=()=>{};console.log(fn.prototype);// undefined4. 模板字符串(Template Literals)
// 基本用法constname='World';constgreeting=`Hello,${name}!`;console.log(greeting);// Hello, World!// 表达式计算consta=5,b=10;console.log(`${a}+${b}=${a+b}`);// 5 + 10 = 15// 多行字符串constmultiline=`这是一个 多行字符串 不需要转义\n换行`;console.log(multiline);// 嵌套模板constvalue=20;constresult=`值是:${value>10?'大于10':'小于等于10'}`;// 标签模板(Tagged Templates)functiontag(strings,...values){console.log(strings);// ['前缀', ' 是 ', '']console.log(values);// [42]return'处理结果';}constresult=tag`前缀${42}`;// 原始字符串(保留转义字符)constraw=String.raw`第一行\n第二行`;console.log(raw);// 第一行\n第二行(而不是换行)5. 类(Class)
基本类定义
// 类的定义classPerson{// 构造函数constructor(name,age){this.name=name;this.age=age;}// 实例方法greet(){return`Hello, I'm${this.name}`;}// 静态方法staticspecies(){return'Homo sapiens';}// gettergetinfo(){return`${this.name},${this.age}years old`;}// settersetinfo(data){const[name,age]=data.split(',');this.name=name;this.age=parseInt(age);}}constalice=newPerson('Alice',25);console.log(alice.greet());// Hello, I'm Aliceconsole.log(alice.info);// Alice, 25 years oldconsole.log(Person.species());// Homo sapiensalice.info='Bob, 30';console.log(alice.info);// Bob, 30 years old继承
// 继承classStudentextendsPerson{constructor(name,age,major){super(name,age);// 调用父类构造函数this.major=major;}// 重写父类方法greet(){return`${super.greet()}and I study${this.major}`;}// 子类特有方法study(){return`${this.name}is studying${this.major}`;}}constbob=newStudent('Bob',20,'Computer Science');console.log(bob.greet());// Hello, I'm Bob and I study Computer Scienceconsole.log(bob.study());// Bob is studying Computer Science// instanceof 检查console.log(bobinstanceofStudent);// trueconsole.log(bobinstanceofPerson);// true私有字段(ES2022)
classBankAccount{// 私有字段(ES2022)#balance=0;#accountNumber;constructor(accountNumber){this.#accountNumber=accountNumber;}deposit(amount){if(amount>0){this.#balance+=amount;returntrue;}returnfalse;}withdraw(amount){if(amount>0&&amount<=this.#balance){this.#balance-=amount;returntrue;}returnfalse;}getbalance(){returnthis.#balance;}getaccountNumber(){returnthis.#accountNumber;}}constaccount=newBankAccount('123456');account.deposit(1000);console.log(account.balance);// 1000// console.log(account.#balance); // SyntaxError: Private field6. Promise
Promise 基本用法
// 创建 Promiseconstpromise=newPromise((resolve,reject)=>{// 异步操作setTimeout(()=>{constsuccess=true;if(success){resolve('操作成功');}else{reject(newError('操作失败'));}},1000);});// 使用 Promisepromise.then(result=>{console.log(result);// 操作成功}).catch(error=>{console.error(error.message);// 操作失败}).finally(()=>{console.log('无论成功或失败都会执行');});Promise 静态方法
// Promise.resolve()constp1=Promise.resolve('成功');p1.then(v=>console.log(v));// 成功// Promise.reject()constp2=Promise.reject(newError('失败'));p2.catch(e=>console.log(e.message));// 失败// Promise.all() - 所有 Promise 都成功才成功constp3=Promise.resolve(1);constp4=Promise.resolve(2);constp5=Promise.resolve(3);Promise.all([p3,p4,p5]).then(values=>console.log(values));// [1, 2, 3]// Promise.allSettled() - 等待所有 Promise 完成Promise.allSettled([Promise.resolve(1),Promise.reject(2),Promise.resolve(3)]).then(results=>{console.log(results);// [// { status: 'fulfilled', value: 1 },// { status: 'rejected', reason: 2 },// { status: 'fulfilled', value: 3 }// ]});// Promise.race() - 返回最先完成的 PromisePromise.race([newPromise(resolve=>setTimeout(()=>resolve(1),1000)),newPromise(resolve=>setTimeout(()=>resolve(2),500))]).then(value=>console.log(value));// 2// Promise.any() - 返回最先成功的 PromisePromise.any([Promise.reject(1),Promise.resolve(2),Promise.resolve(3)]).then(value=>console.log(value));// 27. 模块化(Module)
导出(Export)
// 命名导出// 方式1:声明时直接导出exportconstPI=3.14159;exportfunctionadd(a,b){returna+b;}exportclassCalculator{multiply(a,b){returna*b;}}// 方式2:统一导出constPI=3.14159;functionadd(a,b){returna+b;}export{PI,add};// 导出时重命名export{PIaspi,addassum};// 默认导出(每个模块只能有一个)exportdefaultfunctionsubtract(a,b){returna-b;}导入(Import)
// 导入命名导出import{add,Calculator}from'./math.js';console.log(add(2,3));// 5constcalc=newCalculator();console.log(calc.multiply(4,5));// 20// 导入时重命名import{addassum}from'./math.js';console.log(sum(2,3));// 5// 导入所有import*asmathfrom'./math.js';console.log(math.add(2,3));// 5console.log(math.PI);// 3.14159// 导入默认导出importsubtractfrom'./math.js';console.log(subtract(5,3));// 2// 混合导入importsubtract,{add,Calculator}from'./math.js';// 动态导入asyncfunctionloadModule(){const{add}=awaitimport('./math.js');console.log(add(2,3));}loadModule();8. 新的数据结构
Map
// 创建 MapconstuserMap=newMap();// 添加元素userMap.set('id001',{name:'Alice',age:25});userMap.set('id002',{name:'Bob',age:30});userMap.set('id003',{name:'Charlie',age:35});// 获取元素console.log(userMap.get('id001'));// { name: 'Alice', age: 25 }// 检查键是否存在console.log(userMap.has('id001'));// true// 获取大小console.log(userMap.size);// 3// 删除元素userMap.delete('id003');// 遍历userMap.forEach((value,key)=>{console.log(`${key}:${value.name}`);});// 使用 for...offor(const[key,value]ofuserMap){console.log(`${key}:${value.name}`);}// 获取所有键、值或键值对console.log([...userMap.keys()]);console.log([...userMap.values()]);console.log([...userMap.entries()]);WeakMap
// WeakMap 的键必须是对象,且不可枚举constweakMap=newWeakMap();constobj1={id:1};constobj2={id:2};weakMap.set(obj1,'value1');weakMap.set(obj2,'value2');console.log(weakMap.get(obj1));// value1console.log(weakMap.has(obj1));// true// 垃圾回收:如果没有其他引用,键值对会被自动清除obj1=null;// 垃圾回收器会清除 weakMap 中的对应项// 应用:缓存数据constcache=newWeakMap();functionexpensiveOperation(obj){if(cache.has(obj)){returncache.get(obj);}constresult=obj.id*100;cache.set(obj,result);returnresult;}Set
// 创建 SetconstnumberSet=newSet([1,2,3,3,4,5]);console.log(numberSet);// Set(5) { 1, 2, 3, 4, 5 }// 添加元素numberSet.add(6);numberSet.add(3);// 重复,不会添加// 检查存在console.log(numberSet.has(3));// true// 删除元素numberSet.delete(6);// 大小console.log(numberSet.size);// 5// 遍历numberSet.forEach(value=>{console.log(value);});// 使用 for...offor(constvalueofnumberSet){console.log(value);}// 数组去重constuniqueNumbers=[...newSet([1,2,2,3,3,4])];console.log(uniqueNumbers);// [1, 2, 3, 4]WeakSet
// WeakSet 只能存储对象constweakSet=newWeakSet();constobj1={name:'Alice'};constobj2={name:'Bob'};constobj3={name:'Charlie'};weakSet.add(obj1);weakSet.add(obj2);weakSet.add(obj1);// 重复,不会添加console.log(weakSet.has(obj1));// true// 垃圾回收:没有其他引用时自动清除obj1=null;// 应用:追踪对象constvisited=newWeakSet();functionmarkVisited(obj){visited.add(obj);}functioncheckVisited(obj){returnvisited.has(obj);}9. 迭代器与生成器
迭代器
// 创建可迭代对象constmyIterable={[Symbol.iterator](){letindex=0;return{next(){index++;if(index<=3){return{value:index,done:false};}return{value:undefined,done:true};}};}};for(constvalueofmyIterable){console.log(value);// 1, 2, 3}// 使用迭代器constiterator=myIterable[Symbol.iterator]();console.log(iterator.next());// { value: 1, done: false }console.log(iterator.next());// { value: 2, done: false }console.log(iterator.next());// { value: 3, done: false }console.log(iterator.next());// { value: undefined, done: true }生成器函数
// 生成器函数function*numberGenerator(){yield1;yield2;yield3;}constgen=numberGenerator();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 }// 使用 for...of 遍历for(constnumofnumberGenerator()){console.log(num);// 1, 2, 3}// 无限序列function*infiniteSequence(){leti=0;while(true){yieldi++;}}// 斐波那契数列function*fibonacci(){leta=0,b=1;while(true){yielda;[a,b]=[b,a+b];}}// 生成器作为可迭代对象function*range(start,end){for(leti=start;i<end;i++){yieldi;}}for(constnumofrange(0,5)){console.log(num);// 0, 1, 2, 3, 4}生成器方法
// throw 方法function*errorGenerator(){try{yield1;yield2;}catch(e){console.log('捕获错误:',e.message);}yield3;}constgen2=errorGenerator();console.log(gen2.next());// { value: 1, done: false }console.log(gen2.throw(newError('测试错误')));// { value: 3, done: false }console.log(gen2.next());// { value: undefined, done: true }// return 方法function*returnGenerator(){yield1;yield2;yield3;}constgen3=returnGenerator();console.log(gen3.next());// { value: 1, done: false }console.log(gen3.return('结束'));// { value: '结束', done: true }console.log(gen3.next());// { value: undefined, done: true }10. Symbol
// 创建 Symbolconstsymbol1=Symbol();constsymbol2=Symbol('描述');constsymbol3=Symbol('描述');console.log(symbol2===symbol3);// false(每个 Symbol 都是唯一的)// Symbol.for() - 全局 Symbol 注册表constsymbol4=Symbol.for('global');constsymbol5=Symbol.for('global');console.log(symbol4===symbol5);// true// Symbol.keyFor() - 获取 Symbol 的键console.log(Symbol.keyFor(symbol4));// 'global'// 作为对象属性名constobj={[Symbol('私有属性')]:'值1',[Symbol('私有属性')]:'值2',public:'值3'};console.log(Object.getOwnPropertySymbols(obj));// [Symbol(私有属性), Symbol(私有属性)]// 内置 SymbolconstmyArray=[1,2,3];constiterator=myArray[Symbol.iterator]();console.log(iterator.next());// { value: 1, done: false }// Symbol.toPrimitiveconstobj2={value:10,[Symbol.toPrimitive](hint){if(hint==='number'){returnthis.value;}returnString(this.value);}};console.log(+obj2);// 10console.log(`${obj2}`);// 10// Symbol.asyncIteratorconstasyncIterable={[Symbol.asyncIterator](){return{asyncnext(){return{value:'异步值',done:false};}};}};11. Proxy
// 创建 Proxyconsttarget={};consthandler={get(target,prop){console.log(`获取属性:${prop}`);returnpropintarget?target[prop]:'默认值';},set(target,prop,value){console.log(`设置属性:${prop}=${value}`);target[prop]=value;returntrue;}};constproxy=newProxy(target,handler);proxy.name='Alice';console.log(proxy.name);// Aliceconsole.log(proxy.age);// 获取属性: age; 默认值// 验证constvalidator={set(target,prop,value){if(prop==='age'){if(typeofvalue!=='number'||value<0||value>150){thrownewError('年龄必须是0-150之间的数字');}}target[prop]=value;returntrue;}};constperson=newProxy({},validator);person.age=25;// 正常// person.age = -5; // Error// 隐藏属性consthiddenProps=newWeakSet(['password','token']);constsecureObj=newProxy({},{get(target,prop){if(hiddenProps.has(prop)){thrownewError('禁止访问私有属性');}returntarget[prop];}});// 响应式系统(简化版)constreactive=(obj)=>{consthandlers=newMap();returnnewProxy(obj,{set(target,prop,value){target[prop]=value;constpropHandlers=handlers.get(prop);if(propHandlers){propHandlers.forEach(handler=>handler(value));}returntrue;},get(target,prop){if(typeoftarget[prop]==='object'&&target[prop]!==null){returnreactive(target[prop]);}returntarget[prop];}});};conststate=reactive({count:0});handlers.get('count')?.forEach(handler=>handler(1));state.count=1;12. Reflect
// Reflect 是 ES6 引入的新对象,提供了一系列静态方法// 1. 替代 Object 上的方法constobj={a:1,b:2};// Reflect.applyconsole.log(Reflect.apply(Math.max,null,[1,2,3]));// 3// Reflect.constructclassMyClass{}constinstance=Reflect.construct(MyClass,[]);console.log(instanceinstanceofMyClass);// true// Reflect.definePropertyReflect.defineProperty(obj,'c',{value:3});// Reflect.deletePropertyReflect.deleteProperty(obj,'b');// Reflect.getconsole.log(Reflect.get(obj,'a'));// 1// Reflect.getOwnPropertyDescriptorconsole.log(Reflect.getOwnPropertyDescriptor(obj,'a'));// Reflect.getPrototypeOfconsole.log(Reflect.getPrototypeOf(obj));// Reflect.hasconsole.log(Reflect.has(obj,'a'));// true// Reflect.isExtensibleconsole.log(Reflect.isExtensible(obj));// Reflect.ownKeysconsole.log(Reflect.ownKeys(obj));// Reflect.preventExtensionsReflect.preventExtensions(obj);// Reflect.setReflect.set(obj,'d',4);// Reflect.setPrototypeOfReflect.setPrototypeOf(obj,MyClass.prototype);// 2. 与 Proxy 配合使用constproxy=newProxy({},{get(target,prop,receiver){returnReflect.get(target,prop,receiver);},set(target,prop,value,receiver){returnReflect.set(target,prop,value,receiver);}});ES7(ES2016)特性
1. Array.prototype.includes()
// 检查数组是否包含指定值constarray=[1,2,3,NaN];console.log(array.includes(2));// trueconsole.log(array.includes(4));// falseconsole.log(array.includes(NaN));// true(includes 可以检测 NaN)// 与 indexOf 对比console.log(array.indexOf(2)!==-1);// trueconsole.log(array.indexOf(NaN)!==-1);// false(indexOf 无法检测 NaN)2. 幂运算符(**)
// 基本用法console.log(2**3);// 8console.log(10**2);// 100// 等同于 Math.powconsole.log(Math.pow(2,3));// 8// 右结合console.log(2**3**2);// 2 ** (3 ** 2) = 2 ** 9 = 512// 赋值运算符letbase=2;base**=3;console.log(base);// 8ES8(ES2017)特性
1. async/await
// 基本用法asyncfunctionfetchData(){try{constresponse=awaitfetch('https://api.example.com/data');constdata=awaitresponse.json();returndata;}catch(error){console.error('获取数据失败:',error);throwerror;}}// 箭头函数constfetchData=async()=>{constdata=awaitsomeAsyncOperation();returndata;};// 并行执行异步操作asyncfunctionfetchMultipleData(){const[data1,data2,data3]=awaitPromise.all([fetch('/api1'),fetch('/api2'),fetch('/api3')]);return[data1,data2,data3];}// 错误处理asyncfunctionsafeFetch(){try{constdata=awaitmightFail();returndata;}catch(error){// 处理错误returndefaultValue;}}2. Object.entries() 和 Object.values()
constobj={a:1,b:2,c:3};// Object.entries() - 返回键值对数组console.log(Object.entries(obj));// [['a', 1], ['b', 2], ['c', 3]]// Object.values() - 返回值数组console.log(Object.values(obj));// [1, 2, 3]// 遍历对象for(const[key,value]ofObject.entries(obj)){console.log(`${key}:${value}`);}// 将对象转换为 Mapconstmap=newMap(Object.entries(obj));console.log(map.get('a'));// 13. Object.getOwnPropertyDescriptors()
constobj={name:'Alice',age:25,getinfo(){return`${this.name},${this.age}`;}};constdescriptors=Object.getOwnPropertyDescriptors(obj);console.log(descriptors);/* { name: { value: 'Alice', writable: true, enumerable: true, configurable: true }, age: { value: 25, writable: true, enumerable: true, configurable: true }, info: { get: [Function: get info], set: undefined, enumerable: true, configurable: true } } */// 浅拷贝constcopy=Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj));4. String padding
// padStart()console.log('5'.padStart(3,'0'));// '005'console.log('abc'.padStart(5));// ' abc'// padEnd()console.log('abc'.padEnd(5,'*'));// 'abc**'console.log('abc'.padEnd(8));// 'abc '// 实际应用:对齐输出constprices=[{name:'苹果',price:5.99},{name:'香蕉',price:2.50},{name:'榴莲',price:19.99}];prices.forEach(item=>{console.log(`${item.name.padEnd(5)}- $${item.price.toFixed(2)}`);});/* 苹果 - $5.99 香蕉 - $2.50 榴莲 - $19.99 */5. Object.getOwnPropertyDescriptors()
constobj={a:1,b:2};// 获取所有属性的描述符constdescriptors=Object.getOwnPropertyDescriptors(obj);// 浅拷贝对象(包括 getter/setter)constcopy=Object.create(Object.getPrototypeOf(obj),descriptors);ES9(ES2018)特性
1. 异步迭代
// 异步迭代器constasyncIterable={[Symbol.asyncIterator](){return{current:0,asyncnext(){awaitnewPromise(resolve=>setTimeout(resolve,100));if(this.current<3){return{value:this.current++,done:false};}return{value:undefined,done:true};}};}};// 使用 for-await-ofasyncfunctioniterateAsync(){forawait(constvalueofasyncIterable){console.log(value);// 0, 1, 2}}iterateAsync();// 处理异步操作数组asyncfunctionprocessPromises(){constpromises=[Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)];forawait(constresultofpromises){console.log(result);// 1, 2, 3}}2. Rest/Spread 属性
// Rest 参数(用于解构)const{x,y,...coords}={x:1,y:2,z:3,w:4};console.log(x,y,coords);// 1, 2, { z: 3, w: 4 }// Spread 语法(用于对象)constobj1={a:1,b:2};constobj2={c:3,d:4};constcombined={...obj1,...obj2};console.log(combined);// { a: 1, b: 2, c: 3, d: 4 }// 对象合并(后面的属性会覆盖前面的)constmerged={x:1,y:2,...{y:3,z:4}};console.log(merged);// { x: 1, y: 3, z: 4 }// 克隆对象constoriginal={a:1,b:2};constclone={...original};// 添加属性constuser={name:'Alice'};constuserWithAge={...user,age:25};// 函数参数functionsum(x,y,...rest){returnx+y+rest.reduce((a,b)=>a+b,0);}console.log(sum(1,2,3,4,5));// 153. Promise.prototype.finally()
// 无论成功或失败都会执行fetch('/api/data').then(response=>response.json()).then(data=>console.log(data)).catch(error=>console.error(error)).finally(()=>{console.log('请求完成');hideLoadingSpinner();});4. 正则表达式增强
s 标志(dotAll)
// . 不匹配换行符console.log(/^.{3}$/.test('abc'));// trueconsole.log(/^.{3}$/.test('a\nc'));// false// s 标志让 . 匹配换行符console.log(/^.{3}$/s.test('a\nc'));// true命名捕获组
// ES2018 引入constdateRegex=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;constmatch=dateRegex.exec('2023-12-13');console.log(match.groups.year);// 2023console.log(match.groups.month);// 12console.log(match.groups.day);// 13先行断言和后行断言
// 正向后行断言 (?<=...)constregex1=/(?<=\$)\d+/;console.log(regex1.exec('$100')[0]);// 100console.log(regex1.exec('€100')[0]);// null// 负向后行断言 (?<!...)constregex2=/(?<!\$)\d+/;console.log(regex2.exec('$100')[0]);// 0(不是100)console.log(regex2.exec('€100')[0]);// 100// 正向前瞻 (?=...)constregex3=/foo(?=bar)/;console.log(regex3.test('foobar'));// trueconsole.log(regex3.test('foobaz'));// false// 负向前瞻 (?!...)constregex4=/foo(?!bar)/;console.log(regex4.test('foobar'));// falseconsole.log(regex4.test('foobaz'));// trueES10(ES2019)特性
1. Array.prototype.flat() 和 flatMap()
// flat() - 多维数组扁平化constnestedArray=[1,[2,3],[[4,5]]];console.log(nestedArray.flat());// [1, 2, 3, [4, 5]]console.log(nestedArray.flat(2));// [1, 2, 3, 4, 5]// flatMap() - 先 map 再 flatconstnumbers=[1,2,3];constresult=numbers.flatMap(n=>[n,n*2]);console.log(result);// [1, 2, 2, 4, 3, 6]// 对比 mapconstmapResult=numbers.map(n=>[n,n*2]);console.log(mapResult);// [[1, 2], [2, 4], [3, 6]]// 实际应用:移除空值constarrayWithHoles=[1,,2,,3];console.log(arrayWithHoles.flat());// [1, 2, 3]2. Object.fromEntries()
// 将键值对数组转换为对象constentries=[['name','Alice'],['age',25],['city','Beijing']];constobj=Object.fromEntries(entries);console.log(obj);// { name: 'Alice', age: 25, city: 'Beijing' }// 应用:转换 Mapconstmap=newMap([['key1','value1'],['key2','value2']]);constfromMap=Object.fromEntries(map);console.log(fromMap);// { key1: 'value1', key2: 'value2' }// 应用:对象属性转换constobj={a:1,b:2,c:3};consttransformed=Object.fromEntries(Object.entries(obj).map(([key,value])=>[key.toUpperCase(),value*2]));console.log(transformed);// { A: 2, B: 4, C: 6 }3. String.prototype.trimStart() 和 trimEnd()
conststr=' hello world ';console.log(str.trimStart());// 'hello world 'console.log(str.trimEnd());// ' hello world'// 别名console.log(str.trimLeft());// 'hello world '(trimStart 的别名)console.log(str.trimRight());// ' hello world'(trimEnd 的别名)4. Symbol.prototype.description
constsym=Symbol('描述');console.log(sym.description);// '描述'// 无描述的 Symbolconstsym2=Symbol();console.log(sym2.description);// undefined5. 可选 catch 参数
// 以前必须声明参数try{// 可能失败的代码}catch(error){console.error(error);}// 现在可以省略参数try{// 可能失败的代码}catch{// 即使不使用 error 参数console.error('发生了错误');}ES11(ES2020)特性
1. BigInt
// 创建 BigIntconstbigInt1=123456789012345678901234567890n;constbigInt2=BigInt('123456789012345678901234567890');// 基本运算console.log(bigInt1+bigInt2);console.log(bigInt1*2n);console.log(bigInt1/3n);// 不能与 Number 混合运算// console.log(bigInt1 + 1); // TypeError// 比较console.log(1n<2);// trueconsole.log(1n===1);// falseconsole.log(1n==1);// true// Number 与 BigInt 转换constnum=Number(bigInt1);constbig=BigInt(Number.MAX_SAFE_INTEGER);2. 空值合并运算符(??)
// ?? 只有在值为 null 或 undefined 时才使用默认值consta=0;constb=null;constc=undefined;constd='default';console.log(a??d);// 0(0 不是 null 或 undefined)console.log(b??d);// 'default'console.log(c??d);// 'default'// 与 || 对比console.log(a||d);// 'default'(|| 将 0 视为 falsy)console.log(a??d);// 0(?? 只识别 null/undefined)// 实际应用constconfig={timeout:0,retries:null};console.log(config.timeout??5000);// 0console.log(config.retries??3);// 3console.log(config.maxRetries??5);// 53. 可选链操作符(?.)
constuser={name:'Alice',address:{city:'Beijing'},getName(){returnthis.name;}};// 属性访问console.log(user.address?.city);// 'Beijing'console.log(user.profile?.age);// undefined// 方法调用console.log(user.getName?.());// 'Alice'console.log(user.getAge?.());// undefined// 动态属性constprop='address';console.log(user?.[prop]?.city);// 'Beijing'// 数组访问constarr=[1,2,3];console.log(arr?.[0]);// 1console.log(arr?.[10]);// undefined// 实际应用constresponse=awaitfetch('/api/user');constuserData=response?.data;constuserName=userData?.profile?.name;4. dynamic-import
// 动态导入模块asyncfunctionloadFeature(){const{default:myModule,someFunction}=awaitimport('./module.js');constresult=someFunction();returnresult;}// 根据条件导入if(condition){const{utility}=awaitimport('./utility.js');utility();}// 导入模块路径constmodulePath='./utils.js';constutils=awaitimport(modulePath);// 导入缓存constmodule1=awaitimport('./module.js');constmodule2=awaitimport('./module.js');// 重新导入(可以缓存)console.log(module1===module2);// trueES12(ES2021)特性
1. 数字分隔符(Numeric Separators)
// 改善大数字的可读性constbillion=1_000_000_000;constbytes=0xFF_FF_FF_FF;constbinary=0b1111_0000;constpi=3.14159_26535;console.log(billion);// 1000000000console.log(bytes);// 42949672952. String.prototype.replaceAll()
conststr='foo bar foo baz foo';// 以前需要使用正则表达式console.log(str.replace(/foo/g,'bar'));// 'bar bar bar baz bar'// 现在可以直接使用 replaceAllconsole.log(str.replaceAll('foo','bar'));// 'bar bar bar baz bar'3. Promise.any()
// Promise.any() 等待第一个成功的 Promiseconstp1=Promise.reject('错误1');constp2=Promise.resolve('成功2');constp3=Promise.resolve('成功3');Promise.any([p1,p2,p3]).then(value=>console.log(value))// '成功2'.catch(error=>{console.log(error.errors);// ['错误1', ...]});// 所有 Promise 都失败时抛出 AggregateErrorconstp4=Promise.reject('失败1');constp5=Promise.reject('失败2');Promise.any([p4,p5]).catch(error=>{console.log(errorinstanceofAggregateError);// trueconsole.log(error.errors);// ['失败1', '失败2']});4. 逻辑赋值运算符
// ||= 逻辑或赋值leta=0;a||=10;console.log(a);// 10(0 是 falsy)letb=1;b||=10;console.log(b);// 1(1 是 truthy)// &&= 逻辑与赋值letc=1;c&&=10;console.log(c);// 10(1 是 truthy)letd=0;d&&=10;console.log(d);// 0(0 是 falsy)// ??= 逻辑空赋值lete=undefined;e??=10;console.log(e);// 10letf=0;f??=10;console.log(f);// 0(0 不是 null/undefined)// 实际应用config.timeout??=5000;config.retries&&=10;config.debug||=false;ES13(ES2022)特性
1. 类静态初始化块
classMyClass{staticproperty1='值1';staticproperty2='值2';static{// 静态初始化块// 可以在此处执行复杂的静态属性初始化this.computed=this.property1+this.property2;console.log('静态初始化完成');}static{// 可以有多个静态块this.timestamp=Date.now();}}console.log(MyClass.computed);// '值1值2'console.log(MyClass.timestamp);// 当前时间戳2. 私有字段检查
classBankAccount{#balance=0;deposit(amount){this.#balance+=amount;}checkBalance(){if(this.#balance<0){thrownewError('余额不能为负');}}// 检查私有字段是否存在hasBalance(){return#balanceinthis;}}constaccount=newBankAccount();console.log('#balance'inaccount);// false(私有字段不通过 in 检查)console.log(account.hasBalance());// true(使用 # 前缀语法检查)3. at() 方法
constarr=[1,2,3,4,5];// 支持从末尾访问console.log(arr.at(-1));// 5console.log(arr.at(-2));// 4// 字符串也支持conststr='hello';console.log(str.at(-1));// 'o'console.log(str.at(-2));// 'l'// 对比传统方法console.log(arr[arr.length-1]);// 5(需要计算长度)console.log(arr.at(-1));// 5(更简洁)4. Object.hasOwn()
constobj={a:1,b:2};Object.prototype.c=3;console.log(Object.hasOwn(obj,'a'));// trueconsole.log(Object.hasOwn(obj,'c'));// false(在原型上)console.log(Object.prototype.hasOwnProperty.call(obj,'c'));// false// 更简洁、更安全constobj2=Object.create(null);obj2.prop='exists';console.log(Object.hasOwn(obj2,'prop'));// true// console.log(obj2.hasOwnProperty('prop')); // TypeError(无 hasOwnProperty)5. 错误原因(Error.cause)
try{try{JSON.parse('invalid json');}catch(error){thrownewError('JSON 解析失败',{cause:error});}}catch(error){console.log(error.message);// 'JSON 解析失败'console.log(error.cause);// SyntaxError: Unexpected token}实际开发应用案例
1. 异步数据获取与缓存
classDataCache{constructor(){this.cache=newMap();}asyncfetch(key,fetcher,ttl=60000){// 检查缓存if(this.cache.has(key)){const{data,timestamp}=this.cache.get(key);if(Date.now()-timestamp<ttl){returndata;}}// 获取新数据try{constdata=awaitfetcher();this.cache.set(key,{data,timestamp:Date.now()});returndata;}catch(error){// 如果有缓存数据,返回过期数据作为降级if(this.cache.has(key)){console.warn('请求失败,使用过期缓存');returnthis.cache.get(key).data;}throwerror;}}clear(key){this.cache.delete(key);}clearAll(){this.cache.clear();}}constdataCache=newDataCache();// 使用constuserData=awaitdataCache.fetch('user:123',()=>fetch('/api/user/123').then(r=>r.json()),300000// 5分钟缓存);2. 响应式状态管理
functioncreateStore(initialState){letstate=initialState;constlisteners=newSet();conststore={getState(){returnstate;},setState(patcher){constnewState=patcher(state);if(newState!==state){state=newState;listeners.forEach(listener=>listener(state));}},subscribe(listener){listeners.add(listener);return()=>listeners.delete(listener);}};returnstore;}conststore=createStore({count:0});// 订阅状态变化constunsubscribe=store.subscribe(state=>{console.log('状态更新:',state);});// 更新状态store.setState(state=>({...state,count:state.count+1}));store.setState(state=>({...state,count:state.count*2}));// 取消订阅unsubscribe();3. 异步并发控制
// 限制并发数量的异步函数asyncfunctionconcurrentMap(items,limit,mapper){constresults=[];constexecuting=newSet();for(constitemofitems){constpromise=mapper(item);results.push(promise);executing.add(promise);promise.then(()=>executing.delete(promise));if(executing.size>=limit){awaitPromise.race(executing);}}returnPromise.all(results);}// 使用示例consturls=['url1','url2','url3','url4','url5'];constfetchWithLimit=concurrentMap(urls,2,// 最多同时2个请求url=>fetch(url).then(r=>r.json()));4. 错误边界处理
classErrorBoundary{constructor(fallback){this.fallback=fallback;this.errors=newWeakMap();}wrap(fn){returnasync(...args)=>{try{returnawaitfn(...args);}catch(error){consterrorId=Symbol('error');this.errors.set(errorId,error);returnthis.fallback(error,errorId);}};}getError(id){returnthis.errors.get(id);}}consterrorBoundary=newErrorBoundary((error,id)=>{console.error('捕获错误:',error.message);return{error:true,id};});constriskyOperation=errorBoundary.wrap(async()=>{thrownewError('可能失败的操作');});constresult=awaitriskyOperation();5. 资源加载管理器
classResourceLoader{constructor(){this.cache=newMap();this.loading=newMap();}asyncload(key,fetcher){// 返回缓存if(this.cache.has(key)){returnthis.cache.get(key);}// 返回正在加载的 Promiseif(this.loading.has(key)){returnthis.loading.get(key);}// 开始加载constpromise=fetcher().then(data=>{this.cache.set(key,data);this.loading.delete(key);returndata;}).catch(error=>{this.loading.delete(key);throwerror;});this.loading.set(key,promise);returnpromise;}preload(key,fetcher){// 预加载但不等待this.load(key,fetcher);}unload(key){this.cache.delete(key);this.loading.delete(key);}clear(){this.cache.clear();this.loading.clear();}}// 使用constloader=newResourceLoader();constimage=awaitloader.load('hero-image',()=>fetch('/images/hero.jpg').then(r=>r.blob()));兼容性说明
Babel 转译
// .babelrc 配置{"presets":[["@babel/preset-env",{"targets":{"browsers":["> 1%","last 2 versions"]},"useBuiltIns":"usage","corejs":3}]],"plugins":[["@babel/plugin-proposal-decorators",{"legacy":true}],"@babel/plugin-proposal-class-properties"]}使用 Polyfill
// 引入 core-js 作为 polyfillimport'core-js/stable';// 或者按需引入import'core-js/features/promise';import'core-js/features/array/includes';import'core-js/features/object/entries';浏览器支持检查
// 检查 ES6 支持if(typeofPromise==='undefined'){import('promise-polyfill').then(PromisePolyfill=>{window.Promise=PromisePolyfill;});}// 检查可选链if(!('optionalChaining'in({}))){// 使用 Babel 转译}总结
ES6 带来的核心改进
- 语法现代化:let/const、箭头函数、类、解构赋值
- 模块化支持:原生 ES6 模块系统
- 异步编程:Promise、生成器
- 数据结构:Map/Set、Symbol、Proxy、Reflect
- 更好的开发体验:模板字符串、迭代器
ES7-ES12 的增强
- 更简洁的 API:includes、Object.entries、flat
- 更好的异步支持:async/await、Promise 新方法
- 可选链和空值合并:?. 和 ??
- 性能优化:BigInt、数字分隔符
- 开发者体验提升:String padding、Error.cause