優(yōu)化對(duì)網(wǎng)站真的非常有用嗎廣告聯(lián)盟怎么加入
JavaScript作為網(wǎng)絡(luò)的核心語言之一,近年來發(fā)展迅速。從ES6(ECMAScript 2015)開始,JavaScript幾乎每年都有新的語言特性加入,極大地改善了開發(fā)體驗(yàn)和代碼質(zhì)量。本文主要內(nèi)容包括:
- ES6+關(guān)鍵特性:
- 解構(gòu)賦值與擴(kuò)展運(yùn)算符:
- 模塊化開發(fā):
- Proxy與Reflect:
- 新的API與語法糖:
- 兼容性與Polyfill:
1、ES6+關(guān)鍵特性
ES6是JavaScript發(fā)展的一個(gè)重要里程碑,帶來了大量革命性的改變,后續(xù)版本繼續(xù)添加了許多實(shí)用特性。
箭頭函數(shù)
箭頭函數(shù)提供了更簡(jiǎn)潔的函數(shù)定義語法,并且自動(dòng)綁定詞法上下文的this
。
// 傳統(tǒng)函數(shù)
function add(a, b) {return a + b;
}// 箭頭函數(shù)
const add = (a, b) => a + b;// 箭頭函數(shù)與this
const person = {name: '張三',sayHiTraditional: function() {setTimeout(function() {console.log(`你好,我是${this.name}`); // this不是person}, 1000);},sayHiArrow: function() {setTimeout(() => {console.log(`你好,我是${this.name}`); // this綁定到person}, 1000);}
};
箭頭函數(shù)的特點(diǎn):
- 更簡(jiǎn)潔的語法
- 沒有自己的
this
,繼承外圍作用域的this
- 沒有
arguments
對(duì)象 - 不能用作構(gòu)造函數(shù)
- 沒有
prototype
屬性
let與const
ES6引入了塊級(jí)作用域的變量聲明方式:
// var的問題:沒有塊級(jí)作用域
if (true) {var x = 10;
}
console.log(x); // 10,x泄漏到外部作用域// let的塊級(jí)作用域
if (true) {let y = 20;
}
// console.log(y); // ReferenceError: y未定義// const聲明常量,值不可重新賦值
const PI = 3.14159;
// PI = 3.14; // TypeError: 賦值給常量變量// const聲明的對(duì)象內(nèi)容可以修改
const person = { name: '李四' };
person.name = '王五'; // 這是允許的
// person = {}; // 錯(cuò)誤:不能重新賦值
let/const與var的主要區(qū)別:
- 塊級(jí)作用域vs函數(shù)作用域
- 沒有變量提升(實(shí)際上有"暫時(shí)性死區(qū)")
- 禁止重復(fù)聲明
- 全局聲明的變量不會(huì)成為window的屬性
類語法
ES6引入了類語法,使面向?qū)ο缶幊谈庇^:
class Animal {// 構(gòu)造函數(shù)constructor(name) {this.name = name;}// 實(shí)例方法speak() {return `${this.name}發(fā)出聲音`;}// 靜態(tài)方法static isAnimal(obj) {return obj instanceof Animal;}// ES2022+:私有字段#privateField = 'private';getPrivate() {return this.#privateField;}
}// 繼承
class Dog extends Animal {constructor(name, breed) {super(name); // 調(diào)用父類構(gòu)造函數(shù)this.breed = breed;}speak() {return `${this.name}汪汪叫`;}// 獲取器方法get description() {return `${this.breed}犬${this.name}`;}
}const dog = new Dog('旺財(cái)', '哈士奇');
console.log(dog.speak()); // "旺財(cái)汪汪叫"
console.log(dog.description); // "哈士奇犬旺財(cái)"
Promise及異步編程
Promise為異步操作提供了更優(yōu)雅的處理方式:
// Promise基本用法
function fetchData(url) {return new Promise((resolve, reject) => {const success = Math.random() > 0.3;setTimeout(() => {if (success) {resolve(`來自${url}的數(shù)據(jù)`);} else {reject(`獲取${url}失敗`);}}, 1000);});
}fetchData('api/users').then(data => {console.log(data);return fetchData('api/orders');}).then(data => {console.log(data);}).catch(error => {console.error(error);}).finally(() => {console.log('請(qǐng)求結(jié)束,無論成功或失敗');});
ES2017引入的async/await使異步代碼更像同步代碼:
async function fetchAllData() {try {const users = await fetchData('api/users');console.log(users);const orders = await fetchData('api/orders');console.log(orders);return { users, orders };} catch (error) {console.error('獲取數(shù)據(jù)失敗:', error);} finally {console.log('操作完成');}
}// 調(diào)用異步函數(shù)
fetchAllData().then(result => {console.log('所有數(shù)據(jù):', result);
});
可選鏈與空值合并
ES2020引入了兩個(gè)非常實(shí)用的運(yùn)算符:
// 可選鏈運(yùn)算符 ?.
const user = {profile: {// address: {// city: '北京'// }}
};// 使用前:防御性編程
const city = user && user.profile && user.profile.address && user.profile.address.city || '未知城市';// 使用后:簡(jiǎn)潔優(yōu)雅
const city2 = user?.profile?.address?.city || '未知城市';// 空值合并運(yùn)算符 ??
// 與 || 不同,?? 只在值為 null 或 undefined 時(shí)生效
const count = 0;
console.log(count || 10); // 10,因?yàn)?是假值
console.log(count ?? 10); // 0,因?yàn)?不是null或undefined
2、解構(gòu)賦值與擴(kuò)展運(yùn)算符
解構(gòu)賦值和擴(kuò)展運(yùn)算符是現(xiàn)代JavaScript中最常用的語法特性之一。
數(shù)組解構(gòu)
// 基本數(shù)組解構(gòu)
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]// 交換變量,無需臨時(shí)變量
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1// 忽略某些值
const [x, , z] = [1, 2, 3];
console.log(x, z); // 1 3// 設(shè)置默認(rèn)值
const [p = 0, q = 0] = [10];
console.log(p, q); // 10 0
對(duì)象解構(gòu)
// 基本對(duì)象解構(gòu)
const person = {name: '張三',age: 30,job: '開發(fā)者',address: {city: '上海',street: '南京路'}
};const { name, age, hobby = '閱讀' } = person;
console.log(name, age, hobby); // "張三" 30 "閱讀"// 重命名
const { name: fullName, job: occupation } = person;
console.log(fullName, occupation); // "張三" "開發(fā)者"// 嵌套解構(gòu)
const { address: { city } } = person;
console.log(city); // "上海"// 解構(gòu)與函數(shù)參數(shù)
function printPerson({ name, age, job = '無業(yè)' }) {console.log(`${name}, ${age}歲, 職業(yè): ${job}`);
}
printPerson(person); // "張三, 30歲, 職業(yè): 開發(fā)者"
擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符(...
)在數(shù)組、對(duì)象和函數(shù)參數(shù)中非常有用:
// 數(shù)組合并
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]// 數(shù)組克隆
const original = [1, 2, 3];
const copy = [...original];
original.push(4);
console.log(original); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3]// 對(duì)象合并與克隆
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 }; // b將覆蓋obj1的b
const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj); // { a: 1, b: 3, c: 4 }// 對(duì)象克隆(淺拷貝)
const originalObj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...originalObj };
originalObj.a = 10;
originalObj.b.c = 20;
console.log(shallowCopy.a); // 1 (不受影響)
console.log(shallowCopy.b.c); // 20 (受影響,因?yàn)槭菧\拷貝)
實(shí)際應(yīng)用解構(gòu)與擴(kuò)展
// React組件中的狀態(tài)更新
const [state, setState] = useState({ count: 0, loaded: false });
setState(prevState => ({ ...prevState, count: prevState.count + 1 }));// API響應(yīng)處理
async function fetchUser(id) {const response = await fetch(`/api/users/${id}`);const data = await response.json();// 只提取需要的字段并設(shè)置默認(rèn)值const { name, email, roles = ['user'], settings: { theme = 'light' } = {} } = data;return { name, email, roles, theme };
}
3、模塊化開發(fā)
JavaScript模塊化經(jīng)歷了從無到有的發(fā)展過程,ES6正式將模塊系統(tǒng)引入語言標(biāo)準(zhǔn)。
模塊系統(tǒng)的演進(jìn)
ES模塊基本語法
// 導(dǎo)出語法 (math.js)
// 命名導(dǎo)出
export function add(a, b) {return a + b;
}export function subtract(a, b) {return a - b;
}// 默認(rèn)導(dǎo)出
export default function multiply(a, b) {return a * b;
}// 導(dǎo)入語法 (app.js)
// 導(dǎo)入默認(rèn)導(dǎo)出
import multiply from './math.js';// 導(dǎo)入命名導(dǎo)出
import { add, subtract } from './math.js';// 導(dǎo)入所有導(dǎo)出為一個(gè)對(duì)象
import * as mathUtils from './math.js';// 重命名導(dǎo)入
import { add as sum, subtract as minus } from './math.js';// 混合導(dǎo)入
import multiply, { add, subtract } from './math.js';// 使用
console.log(add(5, 3)); // 8
console.log(multiply(4, 2)); // 8
console.log(mathUtils.subtract(10, 5)); // 5
動(dòng)態(tài)導(dǎo)入
ES2020標(biāo)準(zhǔn)化了動(dòng)態(tài)導(dǎo)入,支持按需加載模塊:
// 靜態(tài)導(dǎo)入(在頂層)
import { feature1 } from './features.js';// 動(dòng)態(tài)導(dǎo)入(按需)
button.addEventListener('click', async () => {try {// 只有用戶點(diǎn)擊按鈕后才加載const { feature2 } = await import('./features.js');feature2();} catch (error) {console.error('模塊加載失敗:', error);}
});
CommonJS與ES模塊的區(qū)別
// CommonJS (Node.js)
const fs = require('fs');
const { join } = require('path');module.exports = {readFile: function(path) { /* ... */ },writeFile: function(path, data) { /* ... */ }
};// ES模塊 (ES6+)
import fs from 'fs';
import { join } from 'path';export function readFile(path) { /* ... */ }
export function writeFile(path, data) { /* ... */ }
主要區(qū)別:
- 加載時(shí)機(jī):CommonJS是運(yùn)行時(shí)加載,ES模塊是靜態(tài)加載
- 導(dǎo)入類型:CommonJS導(dǎo)入的是值的拷貝,ES模塊導(dǎo)入的是值的引用
- this指向:CommonJS頂層this指向當(dāng)前模塊,ES模塊頂層this是undefined
- 語法位置:CommonJS可以在條件語句中require,ES靜態(tài)import必須在頂層
模塊設(shè)計(jì)最佳實(shí)踐
- 單一職責(zé)原則:每個(gè)模塊只負(fù)責(zé)一個(gè)功能
- 顯式導(dǎo)出API:明確聲明公共API,減少副作用
- 避免循環(huán)依賴:可能導(dǎo)致未初始化的變量
- 合理使用默認(rèn)導(dǎo)出:每個(gè)模塊最多一個(gè)默認(rèn)導(dǎo)出
- 保持一致的命名約定:文件名與默認(rèn)導(dǎo)出內(nèi)容一致
// 模塊設(shè)計(jì)示例// api.js - 負(fù)責(zé)API調(diào)用
export async function fetchUsers() { /* ... */ }
export async function fetchPosts() { /* ... */ }// formatters.js - 負(fù)責(zé)數(shù)據(jù)格式化
export function formatDate(date) { /* ... */ }
export function formatCurrency(amount) { /* ... */ }// index.js - 聚合模塊,簡(jiǎn)化導(dǎo)入
export { fetchUsers, fetchPosts } from './api.js';
export { formatDate, formatCurrency } from './formatters.js';
export { default as utils } from './utils.js';
4、Proxy與Reflect
Proxy和Reflect是ES6引入的強(qiáng)大特性,允許攔截和自定義對(duì)象的基本操作。
Proxy基礎(chǔ)
Proxy允許創(chuàng)建一個(gè)對(duì)象的代理,攔截并重新定義該對(duì)象的基本操作:
const target = {name: '張三',age: 30
};const handler = {// 攔截屬性讀取get(target, prop, receiver) {console.log(`正在獲取${prop}屬性`);return target[prop];},// 攔截屬性設(shè)置set(target, prop, value, receiver) {console.log(`正在設(shè)置${prop}屬性為${value}`);// 可以添加驗(yàn)證邏輯if (prop === 'age' && typeof value !== 'number') {throw new TypeError('age必須是一個(gè)數(shù)字');}target[prop] = value;return true; // 表示設(shè)置成功}
};// 創(chuàng)建代理
const proxy = new Proxy(target, handler);// 使用代理
console.log(proxy.name); // 輸出: 正在獲取name屬性 張三
proxy.age = 31; // 輸出: 正在設(shè)置age屬性為31
try {proxy.age = '很老'; // 拋出TypeError
} catch (e) {console.error(e.message);
}
Proxy提供13種可攔截的基本操作:
const handler = {// 屬性操作get(target, prop, receiver) {},set(target, prop, value, receiver) {},has(target, prop) {}, // 攔截in操作符deleteProperty(target, prop) {}, // 攔截delete操作// 函數(shù)操作apply(target, thisArg, argumentsList) {}, // 攔截函數(shù)調(diào)用construct(target, args, newTarget) {}, // 攔截new操作// 原型操作getPrototypeOf(target) {},setPrototypeOf(target, prototype) {},// 擴(kuò)展/配置isExtensible(target) {},preventExtensions(target) {},// 屬性描述符getOwnPropertyDescriptor(target, prop) {},defineProperty(target, prop, descriptor) {},// 獲取所有鍵ownKeys(target) {} // 攔截Object.keys等
};
Reflect API
Reflect是一個(gè)內(nèi)置對(duì)象,提供與Proxy處理程序方法相同的靜態(tài)方法:
// 使用Reflect的Proxy
const handler = {get(target, prop, receiver) {console.log(`獲取${prop}屬性`);// 使用Reflect.get代替target[prop]return Reflect.get(target, prop, receiver);},set(target, prop, value, receiver) {console.log(`設(shè)置${prop}屬性`);// 使用Reflect.set代替target[prop] = valuereturn Reflect.set(target, prop, value, receiver);}
};const proxy = new Proxy({x: 1, y: 2}, handler);
console.log(proxy.x); // 獲取x屬性 1
proxy.z = 3; // 設(shè)置z屬性
使用Reflect的好處:
- 操作返回狀態(tài)(成功/失敗)而非拋出錯(cuò)誤
- 接收相同的參數(shù),方便與Proxy配合
- 提供函數(shù)式的對(duì)象操作API
Proxy實(shí)際應(yīng)用
1. 數(shù)據(jù)驗(yàn)證
function createValidator(target, validations) {return new Proxy(target, {set(target, prop, value) {if (validations[prop]) {const validateFn = validations[prop];if (!validateFn(value)) {throw new Error(`Invalid value for ${prop}`);}}target[prop] = value;return true;}});
}const user = createValidator({ name: '張三', age: 30 },{name: value => typeof value === 'string' && value.length > 0,age: value => typeof value === 'number' && value >= 0 && value < 150}
);user.name = '李四'; // 有效
// user.age = -5; // 拋出錯(cuò)誤
2. 響應(yīng)式數(shù)據(jù)系統(tǒng)
// 簡(jiǎn)化版Vue 3響應(yīng)式系統(tǒng)
function reactive(obj) {return new Proxy(obj, {get(target, prop, receiver) {const result = Reflect.get(target, prop, receiver);track(target, prop); // 跟蹤依賴// 深層響應(yīng)式return typeof result === 'object' && result !== null ? reactive(result) : result;},set(target, prop, value, receiver) {const oldValue = target[prop];const result = Reflect.set(target, prop, value, receiver);if (oldValue !== value) {trigger(target, prop); // 觸發(fā)更新}return result;}});
}// 模擬實(shí)現(xiàn)
function track(target, prop) {console.log(`追蹤 ${prop} 變化`);
}function trigger(target, prop) {console.log(`屬性 ${prop} 已變化,觸發(fā)更新`);
}const state = reactive({ count: 0, user: { name: '張三' } });
state.count++; // 觸發(fā)更新
state.user.name = '李四'; // 深層響應(yīng)式,觸發(fā)更新
5、新的API與語法糖
除了主要特性外,現(xiàn)代JavaScript還引入了大量實(shí)用的API和語法糖。
數(shù)組新方法
// ES6+引入的數(shù)組方法
const numbers = [1, 2, 3, 4, 5];// 查找元素
const found = numbers.find(n => n > 3); // 4
const foundIndex = numbers.findIndex(n => n > 3); // 3// 檢查包含
const includes = numbers.includes(3); // true// 數(shù)組填充
const filled = new Array(5).fill(0); // [0, 0, 0, 0, 0]
const modified = [1, 2, 3, 4, 5].fill(0, 2, 4); // [1, 2, 0, 0, 5]// 平鋪數(shù)組
const nested = [1, [2, [3, 4]]];
const flat1 = nested.flat(); // [1, 2, [3, 4]]
const flatAll = nested.flat(Infinity); // [1, 2, 3, 4]// 映射后平鋪
const mappedAndFlat = [1, 2, 3].flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]// ES2022: 按索引位置替換元素,并返回替換的元素
const arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.at(1)); // 'b'
console.log(arr.at(-1)); // 'e' (從末尾數(shù))
對(duì)象新方法
// 對(duì)象新方法
const person = { name: '張三', age: 30, job: '工程師' };
const skills = { programming: true, design: false };// Object.assign: 合并對(duì)象
const merged = Object.assign({}, person, skills);// 對(duì)象屬性描述
console.log(Object.getOwnPropertyDescriptors(person));// 對(duì)象條目、值、鍵
console.log(Object.entries(person)); // [['name','張三'], ['age',30], ['job','工程師']]
console.log(Object.values(person)); // ['張三', 30, '工程師']
console.log(Object.keys(person)); // ['name', 'age', 'job']// 從條目創(chuàng)建對(duì)象
const entries = [['name', '李四'], ['age', 25]];
const obj = Object.fromEntries(entries); // { name: '李四', age: 25 }// 對(duì)象擴(kuò)展運(yùn)算符(ES2018)
const personWithSkills = { ...person, ...skills };
const { name, ...rest } = person; // 解構(gòu)與剩余屬性
字符串與正則新特性
// 字符串模板字面量
const name = '張三';
const greeting = `你好,${name}!`;// 標(biāo)簽?zāi)0遄置媪?/span>
function highlight(strings, ...values) {return strings.reduce((result, str, i) => {return result + str + (values[i] ? `<em>${values[i]}</em>` : '');}, '');
}const highlighted = highlight`我叫${name},我今年${30}歲。`;
// "我叫<em>張三</em>,我今年<em>30</em>歲。"// 字符串新方法
console.log('Hello'.padStart(10, '*')); // '*****Hello'
console.log('Hello'.padEnd(10, '*')); // 'Hello*****'console.log('Hello'.startsWith('He')); // true
console.log('Hello'.endsWith('lo')); // true
console.log('Hello'.includes('ll')); // trueconsole.log(' trim '.trim()); // 'trim'
console.log(' trim '.trimStart()); // 'trim '
console.log(' trim '.trimEnd()); // ' trim'// 正則表達(dá)式命名捕獲組(ES2018)
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = pattern.exec('2023-05-15');
console.log(match.groups.year); // '2023'
console.log(match.groups.month); // '05'
數(shù)值與數(shù)學(xué)增強(qiáng)
// 數(shù)值分隔符(ES2021)
const billion = 1_000_000_000; // 更易讀
const bytes = 0xFF_FF_FF_FF; // 字節(jié)分組// BigInt(ES2020)
const bigNumber = 9007199254740991n; // BigInt字面量
const anotherBig = BigInt('9007199254740991'); // 構(gòu)造函數(shù)console.log(bigNumber + 1n); // 9007199254740992n
// console.log(bigNumber + 1); // 錯(cuò)誤:不能混合BigInt和Number// 指數(shù)運(yùn)算符
console.log(2 ** 10); // 1024
let num = 2;
num **= 3; // 8// 數(shù)學(xué)方法
console.log(Math.trunc(3.9)); // 3 (去除小數(shù)部分)
console.log(Math.sign(-5)); // -1 (符號(hào)函數(shù))
console.log(Math.log10(1000)); // 3 (底數(shù)為10的對(duì)數(shù))
console.log(Math.log2(8)); // 3 (底數(shù)為2的對(duì)數(shù))
其他語法特性
// 邏輯賦值運(yùn)算符(ES2021)
let a = null;
a ||= 10; // 等同于 a = a || 10
console.log(a); // 10let b = 5;
b &&= 20; // 等同于 b = b && 20
console.log(b); // 20let c;
c ??= 30; // 等同于 c = c ?? 30
console.log(c); // 30// 可選catch綁定(ES2019)
try {// 可能拋出錯(cuò)誤的代碼
} catch { // 不需要錯(cuò)誤變量時(shí)可以省略console.log('發(fā)生錯(cuò)誤');
}// 頂層await(ES2022)
// 可以在模塊頂層使用await,無需async函數(shù)包裝
// module.js
const response = await fetch('https://api.example.com/data');
export const data = await response.json();// 私有類字段和方法(ES2022)
class Counter {#count = 0; // 私有字段get value() {return this.#count;}#increment() { // 私有方法this.#count++;}increment() {this.#increment();}
}
ES2023 新特性
數(shù)組新增反向查找方法
- findLast() 和 findLastIndex():從數(shù)組末尾開始查找元素
const array = [1, 2, 3, 4, 5];
console.log(array.findLast(x => x % 2 === 1)); // 5
console.log(array.findLastIndex(x => x % 2 === 1)); // 4
數(shù)組的非修改性方法
以下方法返回新數(shù)組而不修改原數(shù)組:
- toReversed(): 返回反轉(zhuǎn)后的新數(shù)組
- toSorted(): 返回排序后的新數(shù)組
- toSpliced(): 返回刪除/替換元素后的新數(shù)組
- with(): 返回替換指定索引元素后的新數(shù)組
const arr = [1, 2, 3, 4, 5];
console.log(arr.toReversed()); // [5, 4, 3, 2, 1]
console.log(arr); // [1, 2, 3, 4, 5] - 原數(shù)組不變
console.log(arr.with(2, 10)); // [1, 2, 10, 4, 5]
Hashbang 語法
允許在JavaScript文件開頭使用#!/usr/bin/env node
這樣的語法,使JS文件可作為可執(zhí)行腳本。
Symbol 作為 WeakMap 鍵
允許使用Symbol作為WeakMap的鍵,前提是該Symbol通過Symbol.for()創(chuàng)建。
ES2024 新特性
Promise.withResolvers()
提供了更簡(jiǎn)潔的Promise創(chuàng)建方式,返回promise及其控制函數(shù):
const { promise, resolve, reject } = Promise.withResolvers();
// 等同于:
// let resolve, reject;
// const promise = new Promise((res, rej) => {
// resolve = res;
// reject = rej;
// });
數(shù)組分組方法
- Object.groupBy(): 按條件將數(shù)組元素分組為對(duì)象
- Map.groupBy(): 按條件將數(shù)組元素分組為Map
const people = [{ name: "張三", age: 25 },{ name: "李四", age: 30 },{ name: "王五", age: 25 }
];const groupedByAge = Object.groupBy(people, person => person.age);
// { "25": [{name:"張三",age:25}, {name:"王五",age:25}], "30": [{name:"李四",age:30}] }
Import 屬性
允許在import語句中添加元數(shù)據(jù):
import data from "./data.json" with { type: "json" };
裝飾器(Decorators)
類裝飾器正式標(biāo)準(zhǔn)化,可以修改或擴(kuò)展類和類成員的行為:
function logged(value, { kind, name }) {if (kind === "method") {return function(...args) {console.log(`正在調(diào)用 ${name} 方法`);return value.call(this, ...args);};}
}class Person {@loggedgreet() {return "你好!";}
}
String Unicode 處理
- isWellFormed(): 檢查字符串是否包含有效的UTF-16代碼點(diǎn)
- toWellFormed(): 替換無效代碼點(diǎn),返回格式正確的字符串
ArrayBuffer 傳輸
- transfer() 和 transferToFixedLength(): 轉(zhuǎn)移ArrayBuffer所有權(quán),避免內(nèi)存復(fù)制
Temporal API
新的日期時(shí)間API,解決了現(xiàn)有Date對(duì)象的問題,提供更豐富的時(shí)間和日期操作功能。
RegExp Unicode 集合支持
新增v標(biāo)志,支持Unicode屬性轉(zhuǎn)義和集合操作。
// 匹配所有希臘字母但不包括元音
const regex = /\p{Script=Greek}--[αεηιοωυ]/v;
6、兼容性與Polyfill
現(xiàn)代JavaScript特性在不同環(huán)境中的支持情況各不相同,需要通過工具確保代碼的兼容性。
瀏覽器兼容性現(xiàn)狀
主流瀏覽器已經(jīng)支持大部分ES6+特性,但舊版瀏覽器不支持。Node.js各版本支持的特性也不同。
什么是Polyfill與轉(zhuǎn)譯
為了在不支持新特性的環(huán)境中使用現(xiàn)代JavaScript,我們需要兩種解決方案:
- 轉(zhuǎn)譯(Transpilation): 將新語法轉(zhuǎn)換為等效的舊語法
- Polyfill: 為缺失的API提供兼容實(shí)現(xiàn)
// 轉(zhuǎn)譯示例:箭頭函數(shù)
// 現(xiàn)代代碼
const add = (a, b) => a + b;// 轉(zhuǎn)譯后
var add = function(a, b) {return a + b;
};// Polyfill示例:Array.prototype.includes
if (!Array.prototype.includes) {Array.prototype.includes = function(searchElement, fromIndex) {// 實(shí)現(xiàn)邏輯...if (this === null) {throw new TypeError('"this" is null or not defined');}var o = Object(this);var len = o.length >>> 0;// 更多實(shí)現(xiàn)代碼...return false;};
}
Babel: JavaScript轉(zhuǎn)譯器
Babel是最流行的JavaScript轉(zhuǎn)譯工具,能將現(xiàn)代JavaScript轉(zhuǎn)為兼容性更好的代碼:
// babel.config.js
module.exports = {presets: [["@babel/preset-env", {targets: {browsers: ["> 1%", "last 2 versions", "not dead"],node: "10"},useBuiltIns: "usage",corejs: 3}]],plugins: ["@babel/plugin-proposal-class-properties","@babel/plugin-proposal-private-methods"]
};
Babel工作流程:
常用Polyfill庫
- core-js: 最全面的JavaScript標(biāo)準(zhǔn)庫polyfill
- regenerator-runtime: 為生成器和async/await提供運(yùn)行時(shí)支持
- whatwg-fetch: Fetch API的polyfill
- promise-polyfill: Promise的polyfill實(shí)現(xiàn)
// 手動(dòng)引入polyfill
import "core-js/stable";
import "regenerator-runtime/runtime";
import "whatwg-fetch";// 或者在webpack配置中
entry: ["core-js/stable", "regenerator-runtime/runtime", "./src/index.js"]
構(gòu)建工具與兼容性
現(xiàn)代前端工具鏈通常集成了轉(zhuǎn)譯和polyfill功能:
// webpack.config.js
module.exports = {// ...module: {rules: [{test: /\.js$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {presets: [['@babel/preset-env', {targets: '> 0.25%, not dead',useBuiltIns: 'usage',corejs: 3}]]}}}]}
};
差異化加載策略
為現(xiàn)代瀏覽器提供現(xiàn)代代碼,為舊瀏覽器提供兼容代碼,可以優(yōu)化性能:
<!-- 現(xiàn)代瀏覽器使用模塊版本 -->
<script type="module" src="app.modern.js"></script>
<!-- 舊瀏覽器使用nomodule后備 -->
<script nomodule src="app.legacy.js"></script>
// 在Webpack或Rollup中配置差異化構(gòu)建
// webpack.config.js
module.exports = [{// 現(xiàn)代瀏覽器構(gòu)建output: {filename: '[name].modern.js'},module: {rules: [{test: /\.js$/,use: {loader: 'babel-loader',options: {targets: { esmodules: true }}}}]}},{// 舊瀏覽器構(gòu)建output: {filename: '[name].legacy.js'},module: {rules: [{test: /\.js$/,use: {loader: 'babel-loader',options: {targets: '> 0.25%, not dead'}}}]}}
];
特性檢測(cè)
除了構(gòu)建時(shí)處理兼容性,還可以在運(yùn)行時(shí)檢測(cè)特性并提供替代方案:
// 特性檢測(cè)示例
if ('IntersectionObserver' in window) {// 使用IntersectionObserverconst observer = new IntersectionObserver(entries => {// ...});
} else {// 降級(jí)處理,如監(jiān)聽scroll事件window.addEventListener('scroll', handleScroll);
}// 動(dòng)態(tài)加載polyfill
(async function() {if (!window.fetch) {await import('whatwg-fetch');}// 現(xiàn)在可以安全使用fetchfetch('/api/data').then(/* ... */);
})();
總結(jié):現(xiàn)代JavaScript特性的價(jià)值
現(xiàn)代JavaScript極大改善了開發(fā)體驗(yàn),使代碼更加簡(jiǎn)潔、可維護(hù),同時(shí)提高了應(yīng)用性能和可擴(kuò)展性。主要優(yōu)勢(shì)包括:
開發(fā)效率提升
- 更簡(jiǎn)潔的語法:箭頭函數(shù)、解構(gòu)賦值、模板字符串等減少了樣板代碼
- 強(qiáng)大的異步處理:Promise、async/await讓異步代碼更易理解和維護(hù)
- 模塊化系統(tǒng):原生支持模塊化開發(fā),代碼組織更合理
代碼質(zhì)量改善
- 更好的作用域控制:let/const減少作用域問題
- 不可變編程支持:擴(kuò)展運(yùn)算符簡(jiǎn)化了不可變數(shù)據(jù)更新
- 可選鏈與空值合并:大幅減少防御性編程的樣板代碼
可維護(hù)性提高
- 類語法:更直觀的面向?qū)ο缶幊棠P?/li>
- 模塊化:明確的依賴關(guān)系,降低耦合
- 標(biāo)準(zhǔn)化的API:統(tǒng)一的實(shí)現(xiàn),減少框架依賴