网站备案证书安装,怎么查询网站的域名备案,企业类网站,长沙市建网站JavaScript作为网络的核心语言之一#xff0c;近年来发展迅速。从ES6(ECMAScript 2015)开始#xff0c;JavaScript几乎每年都有新的语言特性加入#xff0c;极大地改善了开发体验和代码质量。本文主要内容包括#xff1a;
ES6关键特性#xff1a;解构赋值与扩展运算符近年来发展迅速。从ES6(ECMAScript 2015)开始JavaScript几乎每年都有新的语言特性加入极大地改善了开发体验和代码质量。本文主要内容包括
ES6关键特性解构赋值与扩展运算符模块化开发Proxy与Reflect新的API与语法糖兼容性与Polyfill
1、ES6关键特性
ES6是JavaScript发展的一个重要里程碑带来了大量革命性的改变后续版本继续添加了许多实用特性。
箭头函数
箭头函数提供了更简洁的函数定义语法并且自动绑定词法上下文的this。
// 传统函数
function add(a, b) {return a b;
}// 箭头函数
const add (a, b) a b;// 箭头函数与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);}
};箭头函数的特点
更简洁的语法没有自己的this继承外围作用域的this没有arguments对象不能用作构造函数没有prototype属性
let与const
ES6引入了块级作用域的变量声明方式
// var的问题没有块级作用域
if (true) {var x 10;
}
console.log(x); // 10x泄漏到外部作用域// let的块级作用域
if (true) {let y 20;
}
// console.log(y); // ReferenceError: y未定义// const声明常量值不可重新赋值
const PI 3.14159;
// PI 3.14; // TypeError: 赋值给常量变量// const声明的对象内容可以修改
const person { name: 李四 };
person.name 王五; // 这是允许的
// person {}; // 错误不能重新赋值let/const与var的主要区别
块级作用域vs函数作用域没有变量提升实际上有暂时性死区禁止重复声明全局声明的变量不会成为window的属性
类语法
ES6引入了类语法使面向对象编程更直观
class Animal {// 构造函数constructor(name) {this.name name;}// 实例方法speak() {return ${this.name}发出声音;}// 静态方法static isAnimal(obj) {return obj instanceof Animal;}// ES2022私有字段#privateField private;getPrivate() {return this.#privateField;}
}// 继承
class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数this.breed breed;}speak() {return ${this.name}汪汪叫;}// 获取器方法get description() {return ${this.breed}犬${this.name};}
}const dog new Dog(旺财, 哈士奇);
console.log(dog.speak()); // 旺财汪汪叫
console.log(dog.description); // 哈士奇犬旺财Promise及异步编程
Promise为异步操作提供了更优雅的处理方式
// Promise基本用法
function fetchData(url) {return new Promise((resolve, reject) {const success Math.random() 0.3;setTimeout(() {if (success) {resolve(来自${url}的数据);} 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(请求结束无论成功或失败);});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(获取数据失败:, error);} finally {console.log(操作完成);}
}// 调用异步函数
fetchAllData().then(result {console.log(所有数据:, result);
});可选链与空值合并
ES2020引入了两个非常实用的运算符
// 可选链运算符 ?.
const user {profile: {// address: {// city: 北京// }}
};// 使用前防御性编程
const city user user.profile user.profile.address user.profile.address.city || 未知城市;// 使用后简洁优雅
const city2 user?.profile?.address?.city || 未知城市;// 空值合并运算符 ??
// 与 || 不同?? 只在值为 null 或 undefined 时生效
const count 0;
console.log(count || 10); // 10因为0是假值
console.log(count ?? 10); // 0因为0不是null或undefined2、解构赋值与扩展运算符
解构赋值和扩展运算符是现代JavaScript中最常用的语法特性之一。
数组解构
// 基本数组解构
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]// 交换变量无需临时变量
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// 设置默认值
const [p 0, q 0] [10];
console.log(p, q); // 10 0对象解构
// 基本对象解构
const person {name: 张三,age: 30,job: 开发者,address: {city: 上海,street: 南京路}
};const { name, age, hobby 阅读 } person;
console.log(name, age, hobby); // 张三 30 阅读// 重命名
const { name: fullName, job: occupation } person;
console.log(fullName, occupation); // 张三 开发者// 嵌套解构
const { address: { city } } person;
console.log(city); // 上海// 解构与函数参数
function printPerson({ name, age, job 无业 }) {console.log(${name}, ${age}岁, 职业: ${job});
}
printPerson(person); // 张三, 30岁, 职业: 开发者扩展运算符
扩展运算符(...)在数组、对象和函数参数中非常有用
// 数组合并
const arr1 [1, 2, 3];
const arr2 [4, 5, 6];
const combined [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]// 数组克隆
const original [1, 2, 3];
const copy [...original];
original.push(4);
console.log(original); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3]// 对象合并与克隆
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 }// 对象克隆浅拷贝
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 (受影响因为是浅拷贝)实际应用解构与扩展
// React组件中的状态更新
const [state, setState] useState({ count: 0, loaded: false });
setState(prevState ({ ...prevState, count: prevState.count 1 }));// API响应处理
async function fetchUser(id) {const response await fetch(/api/users/${id});const data await response.json();// 只提取需要的字段并设置默认值const { name, email, roles [user], settings: { theme light } {} } data;return { name, email, roles, theme };
}3、模块化开发
JavaScript模块化经历了从无到有的发展过程ES6正式将模块系统引入语言标准。
模块系统的演进 ES模块基本语法
// 导出语法 (math.js)
// 命名导出
export function add(a, b) {return a b;
}export function subtract(a, b) {return a - b;
}// 默认导出
export default function multiply(a, b) {return a * b;
}// 导入语法 (app.js)
// 导入默认导出
import multiply from ./math.js;// 导入命名导出
import { add, subtract } from ./math.js;// 导入所有导出为一个对象
import * as mathUtils from ./math.js;// 重命名导入
import { add as sum, subtract as minus } from ./math.js;// 混合导入
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动态导入
ES2020标准化了动态导入支持按需加载模块
// 静态导入在顶层
import { feature1 } from ./features.js;// 动态导入按需
button.addEventListener(click, async () {try {// 只有用户点击按钮后才加载const { feature2 } await import(./features.js);feature2();} catch (error) {console.error(模块加载失败:, error);}
});CommonJS与ES模块的区别
// 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) { /* ... */ }主要区别
加载时机CommonJS是运行时加载ES模块是静态加载导入类型CommonJS导入的是值的拷贝ES模块导入的是值的引用this指向CommonJS顶层this指向当前模块ES模块顶层this是undefined语法位置CommonJS可以在条件语句中requireES静态import必须在顶层
模块设计最佳实践
单一职责原则每个模块只负责一个功能显式导出API明确声明公共API减少副作用避免循环依赖可能导致未初始化的变量合理使用默认导出每个模块最多一个默认导出保持一致的命名约定文件名与默认导出内容一致
// 模块设计示例// api.js - 负责API调用
export async function fetchUsers() { /* ... */ }
export async function fetchPosts() { /* ... */ }// formatters.js - 负责数据格式化
export function formatDate(date) { /* ... */ }
export function formatCurrency(amount) { /* ... */ }// index.js - 聚合模块简化导入
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引入的强大特性允许拦截和自定义对象的基本操作。
Proxy基础
Proxy允许创建一个对象的代理拦截并重新定义该对象的基本操作
const target {name: 张三,age: 30
};const handler {// 拦截属性读取get(target, prop, receiver) {console.log(正在获取${prop}属性);return target[prop];},// 拦截属性设置set(target, prop, value, receiver) {console.log(正在设置${prop}属性为${value});// 可以添加验证逻辑if (prop age typeof value ! number) {throw new TypeError(age必须是一个数字);}target[prop] value;return true; // 表示设置成功}
};// 创建代理
const proxy new Proxy(target, handler);// 使用代理
console.log(proxy.name); // 输出: 正在获取name属性 张三
proxy.age 31; // 输出: 正在设置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操作// 函数操作apply(target, thisArg, argumentsList) {}, // 拦截函数调用construct(target, args, newTarget) {}, // 拦截new操作// 原型操作getPrototypeOf(target) {},setPrototypeOf(target, prototype) {},// 扩展/配置isExtensible(target) {},preventExtensions(target) {},// 属性描述符getOwnPropertyDescriptor(target, prop) {},defineProperty(target, prop, descriptor) {},// 获取所有键ownKeys(target) {} // 拦截Object.keys等
};Reflect API
Reflect是一个内置对象提供与Proxy处理程序方法相同的静态方法
// 使用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(设置${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; // 设置z属性使用Reflect的好处
操作返回状态成功/失败而非抛出错误接收相同的参数方便与Proxy配合提供函数式的对象操作API
Proxy实际应用
1. 数据验证
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; // 抛出错误2. 响应式数据系统
// 简化版Vue 3响应式系统
function reactive(obj) {return new Proxy(obj, {get(target, prop, receiver) {const result Reflect.get(target, prop, receiver);track(target, prop); // 跟踪依赖// 深层响应式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); // 触发更新}return result;}});
}// 模拟实现
function track(target, prop) {console.log(追踪 ${prop} 变化);
}function trigger(target, prop) {console.log(属性 ${prop} 已变化触发更新);
}const state reactive({ count: 0, user: { name: 张三 } });
state.count; // 触发更新
state.user.name 李四; // 深层响应式触发更新5、新的API与语法糖
除了主要特性外现代JavaScript还引入了大量实用的API和语法糖。
数组新方法
// ES6引入的数组方法
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// 数组填充
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]// 平铺数组
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 (从末尾数)对象新方法
// 对象新方法
const person { name: 张三, age: 30, job: 工程师 };
const skills { programming: true, design: false };// Object.assign: 合并对象
const merged Object.assign({}, person, skills);// 对象属性描述
console.log(Object.getOwnPropertyDescriptors(person));// 对象条目、值、键
console.log(Object.entries(person)); // [[name,张三], [age,30], [job,工程师]]
console.log(Object.values(person)); // [张三, 30, 工程师]
console.log(Object.keys(person)); // [name, age, job]// 从条目创建对象
const entries [[name, 李四], [age, 25]];
const obj Object.fromEntries(entries); // { name: 李四, age: 25 }// 对象扩展运算符(ES2018)
const personWithSkills { ...person, ...skills };
const { name, ...rest } person; // 解构与剩余属性字符串与正则新特性
// 字符串模板字面量
const name 张三;
const greeting 你好${name}!;// 标签模板字面量
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我今年em30/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// 正则表达式命名捕获组(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数值与数学增强
// 数值分隔符(ES2021)
const billion 1_000_000_000; // 更易读
const bytes 0xFF_FF_FF_FF; // 字节分组// BigInt(ES2020)
const bigNumber 9007199254740991n; // BigInt字面量
const anotherBig BigInt(9007199254740991); // 构造函数console.log(bigNumber 1n); // 9007199254740992n
// console.log(bigNumber 1); // 错误不能混合BigInt和Number// 指数运算符
console.log(2 ** 10); // 1024
let num 2;
num ** 3; // 8// 数学方法
console.log(Math.trunc(3.9)); // 3 (去除小数部分)
console.log(Math.sign(-5)); // -1 (符号函数)
console.log(Math.log10(1000)); // 3 (底数为10的对数)
console.log(Math.log2(8)); // 3 (底数为2的对数)其他语法特性
// 逻辑赋值运算符(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 {// 可能抛出错误的代码
} catch { // 不需要错误变量时可以省略console.log(发生错误);
}// 顶层await(ES2022)
// 可以在模块顶层使用await无需async函数包装
// 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 新特性
数组新增反向查找方法
findLast() 和 findLastIndex()从数组末尾开始查找元素
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数组的非修改性方法
以下方法返回新数组而不修改原数组
toReversed(): 返回反转后的新数组toSorted(): 返回排序后的新数组toSpliced(): 返回删除/替换元素后的新数组with(): 返回替换指定索引元素后的新数组
const arr [1, 2, 3, 4, 5];
console.log(arr.toReversed()); // [5, 4, 3, 2, 1]
console.log(arr); // [1, 2, 3, 4, 5] - 原数组不变
console.log(arr.with(2, 10)); // [1, 2, 10, 4, 5]Hashbang 语法
允许在JavaScript文件开头使用#!/usr/bin/env node这样的语法使JS文件可作为可执行脚本。
Symbol 作为 WeakMap 键
允许使用Symbol作为WeakMap的键前提是该Symbol通过Symbol.for()创建。
ES2024 新特性
Promise.withResolvers()
提供了更简洁的Promise创建方式返回promise及其控制函数
const { promise, resolve, reject } Promise.withResolvers();
// 等同于:
// let resolve, reject;
// const promise new Promise((res, rej) {
// resolve res;
// reject rej;
// });数组分组方法
Object.groupBy(): 按条件将数组元素分组为对象Map.groupBy(): 按条件将数组元素分组为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语句中添加元数据
import data from ./data.json with { type: json };装饰器Decorators
类装饰器正式标准化可以修改或扩展类和类成员的行为
function logged(value, { kind, name }) {if (kind method) {return function(...args) {console.log(正在调用 ${name} 方法);return value.call(this, ...args);};}
}class Person {loggedgreet() {return 你好;}
}String Unicode 处理
isWellFormed(): 检查字符串是否包含有效的UTF-16代码点toWellFormed(): 替换无效代码点返回格式正确的字符串
ArrayBuffer 传输
transfer() 和 transferToFixedLength(): 转移ArrayBuffer所有权避免内存复制
Temporal API
新的日期时间API解决了现有Date对象的问题提供更丰富的时间和日期操作功能。
RegExp Unicode 集合支持
新增v标志支持Unicode属性转义和集合操作。
// 匹配所有希腊字母但不包括元音
const regex /\p{ScriptGreek}--[αεηιοωυ]/v;6、兼容性与Polyfill
现代JavaScript特性在不同环境中的支持情况各不相同需要通过工具确保代码的兼容性。
浏览器兼容性现状
主流浏览器已经支持大部分ES6特性但旧版浏览器不支持。Node.js各版本支持的特性也不同。
什么是Polyfill与转译
为了在不支持新特性的环境中使用现代JavaScript我们需要两种解决方案
转译Transpilation: 将新语法转换为等效的旧语法Polyfill: 为缺失的API提供兼容实现
// 转译示例箭头函数
// 现代代码
const add (a, b) a b;// 转译后
var add function(a, b) {return a b;
};// Polyfill示例Array.prototype.includes
if (!Array.prototype.includes) {Array.prototype.includes function(searchElement, fromIndex) {// 实现逻辑...if (this null) {throw new TypeError(this is null or not defined);}var o Object(this);var len o.length 0;// 更多实现代码...return false;};
}Babel: JavaScript转译器
Babel是最流行的JavaScript转译工具能将现代JavaScript转为兼容性更好的代码
// 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标准库polyfillregenerator-runtime: 为生成器和async/await提供运行时支持whatwg-fetch: Fetch API的polyfillpromise-polyfill: Promise的polyfill实现
// 手动引入polyfill
import core-js/stable;
import regenerator-runtime/runtime;
import whatwg-fetch;// 或者在webpack配置中
entry: [core-js/stable, regenerator-runtime/runtime, ./src/index.js]构建工具与兼容性
现代前端工具链通常集成了转译和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}]]}}}]}
};差异化加载策略
为现代浏览器提供现代代码为旧浏览器提供兼容代码可以优化性能
!-- 现代浏览器使用模块版本 --
script typemodule srcapp.modern.js/script
!-- 旧浏览器使用nomodule后备 --
script nomodule srcapp.legacy.js/script// 在Webpack或Rollup中配置差异化构建
// webpack.config.js
module.exports [{// 现代浏览器构建output: {filename: [name].modern.js},module: {rules: [{test: /\.js$/,use: {loader: babel-loader,options: {targets: { esmodules: true }}}}]}},{// 旧浏览器构建output: {filename: [name].legacy.js},module: {rules: [{test: /\.js$/,use: {loader: babel-loader,options: {targets: 0.25%, not dead}}}]}}
];特性检测
除了构建时处理兼容性还可以在运行时检测特性并提供替代方案
// 特性检测示例
if (IntersectionObserver in window) {// 使用IntersectionObserverconst observer new IntersectionObserver(entries {// ...});
} else {// 降级处理如监听scroll事件window.addEventListener(scroll, handleScroll);
}// 动态加载polyfill
(async function() {if (!window.fetch) {await import(whatwg-fetch);}// 现在可以安全使用fetchfetch(/api/data).then(/* ... */);
})();总结现代JavaScript特性的价值
现代JavaScript极大改善了开发体验使代码更加简洁、可维护同时提高了应用性能和可扩展性。主要优势包括
开发效率提升
更简洁的语法箭头函数、解构赋值、模板字符串等减少了样板代码强大的异步处理Promise、async/await让异步代码更易理解和维护模块化系统原生支持模块化开发代码组织更合理
代码质量改善
更好的作用域控制let/const减少作用域问题不可变编程支持扩展运算符简化了不可变数据更新可选链与空值合并大幅减少防御性编程的样板代码
可维护性提高
类语法更直观的面向对象编程模型模块化明确的依赖关系降低耦合标准化的API统一的实现减少框架依赖