JavaScript 高级特性
JavaScript 高级特性是指那些超出基础语法的概念和技术,如闭包、原型链、异步编程、模块化等。掌握这些高级特性可以让你编写更加优雅、高效、可维护的 JavaScript 代码。
🎯 高级特性的重要性
高级特性是区分初级和高级 JavaScript 开发者的关键。掌握这些特性可以让你解决更复杂的问题,编写更优质的代码,并且更容易理解和使用现代 JavaScript 框架和库。
闭包
闭包是指函数能够访问其外部作用域中的变量,即使外部函数已经执行完毕:
// 基本闭包示例
function outerFunction() {
let outerVariable = "外部变量";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closure = outerFunction();
closure(); // 输出: 外部变量
// 闭包应用:计数器
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 输出: 1
console.log(counter.increment()); // 输出: 2
console.log(counter.decrement()); // 输出: 1
console.log(counter.getCount()); // 输出: 1
// 闭包应用:模块化
const module = (function() {
// 私有变量
let privateVariable = "私有变量";
// 私有方法
function privateMethod() {
console.log(privateVariable);
}
// 公开API
return {
publicMethod: function() {
privateMethod();
},
setPrivateVariable: function(value) {
privateVariable = value;
}
};
})();
module.publicMethod(); // 输出: 私有变量
module.setPrivateVariable("新的私有变量");
module.publicMethod(); // 输出: 新的私有变量
// console.log(module.privateVariable); // 错误: privateVariable is not defined
原型和原型链
JavaScript 是一种基于原型的语言,每个对象都有一个原型对象,原型对象也有自己的原型,形成原型链:
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法
Person.prototype.greet = function() {
return `你好,我是 ${this.name},今年 ${this.age} 岁。`;
};
// 创建实例
const person1 = new Person("张三", 25);
const person2 = new Person("李四", 30);
console.log(person1.greet()); // 输出: 你好,我是 张三,今年 25 岁。
console.log(person2.greet()); // 输出: 你好,我是 李四,今年 30 岁。
// 检查原型
console.log(Object.getPrototypeOf(person1)); // 输出: Person.prototype
console.log(person1.__proto__); // 输出: Person.prototype
console.log(Person.prototype.isPrototypeOf(person1)); // 输出: true
// 原型链
console.log(Object.prototype.isPrototypeOf(Person.prototype)); // 输出: true
console.log(Object.prototype.isPrototypeOf(person1)); // 输出: true
// 实例属性和原型属性
function Animal(name) {
this.name = name;
}
Animal.prototype.species = "动物";
const cat = new Animal("猫");
console.log(cat.name); // 输出: 猫(实例属性)
console.log(cat.species); // 输出: 动物(原型属性)
// 修改原型
Animal.prototype.species = "哺乳动物";
console.log(cat.species); // 输出: 哺乳动物(原型属性被修改)
// 覆盖原型属性
cat.species = "猫科动物";
console.log(cat.species); // 输出: 猫科动物(实例属性覆盖原型属性)
ES6 类
ES6 引入了类语法,提供了更简洁、更面向对象的编程方式:
// 基本类定义
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
return `你好,我是 ${this.name},今年 ${this.age} 岁。`;
}
// 静态方法
static createPerson(name, age) {
return new Person(name, age);
}
// getter
get description() {
return `${this.name} (${this.age}岁)`;
}
// setter
set age(value) {
if (value >= 0) {
this._age = value;
} else {
throw new Error("年龄不能为负数");
}
}
get age() {
return this._age;
}
}
// 创建实例
const person1 = new Person("张三", 25);
console.log(person1.greet()); // 输出: 你好,我是 张三,今年 25 岁。
console.log(person1.description); // 输出: 张三 (25岁)
// 使用静态方法
const person2 = Person.createPerson("李四", 30);
console.log(person2.greet()); // 输出: 你好,我是 李四,今年 30 岁。
// 继承
class Employee extends Person {
constructor(name, age, job, salary) {
// 调用父类构造函数
super(name, age);
this.job = job;
this.salary = salary;
}
// 重写父类方法
greet() {
return `${super.greet()} 我的工作是 ${this.job},薪资是 ${this.salary}。`;
}
// 新方法
work() {
return `${this.name} 正在工作。`;
}
}
const employee = new Employee("王五", 35, "工程师", 10000);
console.log(employee.greet()); // 输出: 你好,我是 王五,今年 35 岁。 我的工作是 工程师,薪资是 10000。
console.log(employee.work()); // 输出: 王五 正在工作。
console.log(employee.description); // 输出: 王五 (35岁)(继承自父类的 getter)
异步编程
JavaScript 是单线程语言,但通过异步编程可以处理耗时操作:
1. 回调函数
// 回调函数示例
function fetchData(url, callback) {
setTimeout(function() {
console.log(`从 ${url} 获取数据`);
callback({ data: "模拟数据" });
}, 1000);
}
// 调用
fetchData("https://api.example.com/data", function(data) {
console.log("获取到数据:", data);
});
// 回调地狱
function step1(callback) {
setTimeout(function() {
console.log("步骤 1 完成");
callback();
}, 1000);
}
function step2(callback) {
setTimeout(function() {
console.log("步骤 2 完成");
callback();
}, 1000);
}
function step3(callback) {
setTimeout(function() {
console.log("步骤 3 完成");
callback();
}, 1000);
}
// 回调地狱
step1(function() {
step2(function() {
step3(function() {
console.log("所有步骤完成");
});
});
});
2. Promise
// 基本 Promise
const promise = new Promise(function(resolve, reject) {
setTimeout(function() {
const success = true;
if (success) {
resolve("操作成功");
} else {
reject("操作失败");
}
}, 1000);
});
// 处理 Promise
promise
.then(function(result) {
console.log(result); // 输出: 操作成功
})
.catch(function(error) {
console.log(error); // 输出: 操作失败
});
// Promise 链式调用
function step1() {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("步骤 1 完成");
resolve("步骤 1 结果");
}, 1000);
});
}
function step2(data) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("步骤 2 完成,使用数据:", data);
resolve("步骤 2 结果");
}, 1000);
});
}
function step3(data) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("步骤 3 完成,使用数据:", data);
resolve("步骤 3 结果");
}, 1000);
});
}
// 链式调用
step1()
.then(step2)
.then(step3)
.then(function(result) {
console.log("所有步骤完成,最终结果:", result);
});
// Promise 静态方法
Promise.resolve("成功值")
.then(function(value) {
console.log(value); // 输出: 成功值
});
Promise.reject("错误值")
.catch(function(error) {
console.log(error); // 输出: 错误值
});
// Promise.all:等待所有 Promise 完成
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then(function(values) {
console.log(values); // 输出: [1, 2, 3]
});
// Promise.race:等待第一个 Promise 完成
const promiseA = new Promise(resolve => setTimeout(() => resolve("A"), 300));
const promiseB = new Promise(resolve => setTimeout(() => resolve("B"), 100));
Promise.race([promiseA, promiseB])
.then(function(value) {
console.log(value); // 输出: B
});
3. async/await
// 基本 async/await
async function fetchData() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve("数据获取成功");
}, 1000);
});
}
async function processData() {
console.log("开始获取数据");
const data = await fetchData();
console.log(data); // 输出: 数据获取成功
console.log("数据处理完成");
}
processData();
// async/await 与 Promise 结合
function step1() {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("步骤 1 完成");
resolve("步骤 1 结果");
}, 1000);
});
}
function step2(data) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("步骤 2 完成,使用数据:", data);
resolve("步骤 2 结果");
}, 1000);
});
}
function step3(data) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("步骤 3 完成,使用数据:", data);
resolve("步骤 3 结果");
}, 1000);
});
}
async function runSteps() {
try {
console.log("开始执行步骤");
const result1 = await step1();
const result2 = await step2(result1);
const result3 = await step3(result2);
console.log("所有步骤完成,最终结果:", result3);
} catch (error) {
console.error("执行出错:", error);
}
}
runSteps();
// 并行执行
async function runParallel() {
console.log("开始并行执行");
// 同时开始所有步骤
const promise1 = step1();
const promise2 = step2("初始数据");
const promise3 = step3("初始数据");
// 等待所有步骤完成
const results = await Promise.all([promise1, promise2, promise3]);
console.log("所有步骤完成,结果:", results);
}
runParallel();
模块化
模块化是将代码分割成独立、可重用的模块的过程:
1. 立即执行函数表达式 (IIFE)
// IIFE 模块化
const Module = (function() {
// 私有变量
let privateVar = "私有变量";
// 私有方法
function privateMethod() {
return privateVar;
}
// 公开 API
return {
publicVar: "公开变量",
publicMethod: function() {
return privateMethod();
},
setPrivateVar: function(value) {
privateVar = value;
}
};
})();
console.log(Module.publicVar); // 输出: 公开变量
console.log(Module.publicMethod()); // 输出: 私有变量
Module.setPrivateVar("新的私有变量");
console.log(Module.publicMethod()); // 输出: 新的私有变量
// console.log(Module.privateVar); // 错误: Module.privateVar is undefined
// console.log(Module.privateMethod); // 错误: Module.privateMethod is not a function
2. CommonJS 模块(Node.js)
// 模块文件: math.js
/*
// 导出单个函数
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// 导出多个函数
module.exports = {
add,
subtract
};
// 或者导出单个函数
// module.exports = add;
*/
// 使用模块
/*
// 导入整个模块
const math = require('./math');
console.log(math.add(5, 3)); // 输出: 8
console.log(math.subtract(10, 4)); // 输出: 6
// 导入特定函数
const { add, subtract } = require('./math');
console.log(add(5, 3)); // 输出: 8
console.log(subtract(10, 4)); // 输出: 6
*/
3. ES6 模块
// 模块文件: math.js
/*
// 导出单个函数
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// 导出默认值
const pi = 3.14159;
export default pi;
*/
// 使用模块
/*
// 导入特定函数
import { add, subtract } from './math.js';
console.log(add(5, 3)); // 输出: 8
console.log(subtract(10, 4)); // 输出: 6
// 导入默认值
import pi from './math.js';
console.log(pi); // 输出: 3.14159
// 导入所有
import * as math from './math.js';
console.log(math.add(5, 3)); // 输出: 8
console.log(math.default); // 输出: 3.14159
*/
// 浏览器中使用 ES6 模块
/*
<script type="module">
import { add } from './math.js';
console.log(add(5, 3)); // 输出: 8
</script>
*/
解构赋值
解构赋值允许我们从对象或数组中提取值并赋给变量:
// 数组解构
const numbers = [1, 2, 3, 4, 5];
// 基本解构
const [a, b, c] = numbers;
console.log(a, b, c); // 输出: 1 2 3
// 跳过元素
const [x, , y] = numbers;
console.log(x, y); // 输出: 1 3
// 剩余元素
const [first, ...rest] = numbers;
console.log(first); // 输出: 1
console.log(rest); // 输出: [2, 3, 4, 5]
// 默认值
const [p, q, r, s, t, u = 6] = numbers;
console.log(u); // 输出: 6
// 对象解构
const person = {
name: "张三",
age: 25,
job: "工程师",
address: {
city: "北京",
district: "朝阳区"
}
};
// 基本解构
const { name, age } = person;
console.log(name, age); // 输出: 张三 25
// 重命名变量
const { job: occupation } = person;
console.log(occupation); // 输出: 工程师
// 嵌套解构
const { address: { city, district } } = person;
console.log(city, district); // 输出: 北京 朝阳区
// 默认值
const { name: userName, salary = 10000 } = person;
console.log(userName, salary); // 输出: 张三 10000
// 函数参数解构
function greet({ name, age }) {
console.log(`你好,我是 ${name},今年 ${age} 岁。`);
}
greet(person); // 输出: 你好,我是 张三,今年 25 岁。
function calculate({ a, b, operation = "add" }) {
switch (operation) {
case "add":
return a + b;
case "subtract":
return a - b;
case "multiply":
return a * b;
case "divide":
return a / b;
default:
return "无效操作";
}
}
console.log(calculate({ a: 10, b: 5 })); // 输出: 15
console.log(calculate({ a: 10, b: 5, operation: "multiply" })); // 输出: 50
展开运算符
展开运算符允许我们将数组或对象展开为多个元素:
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
const mergedArray = [...arr1, ...arr2];
console.log(mergedArray); // 输出: [1, 2, 3, 4, 5, 6]
// 复制数组
const copyArray = [...arr1];
console.log(copyArray); // 输出: [1, 2, 3]
// 函数参数展开
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 输出: 6
// 对象展开
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
// 合并对象
const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj); // 输出: { a: 1, b: 3, c: 4 }
// 复制对象
const copyObj = { ...obj1 };
console.log(copyObj); // 输出: { a: 1, b: 2 }
// 扩展对象
const person = {
name: "张三",
age: 25
};
const extendedPerson = {
...person,
job: "工程师",
address: "北京"
};
console.log(extendedPerson); // 输出: { name: "张三", age: 25, job: "工程师", address: "北京" }
// 与解构结合
const { a, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
console.log(a); // 输出: 1
console.log(rest); // 输出: { b: 2, c: 3, d: 4 }
生成器
生成器是一种特殊的函数,可以暂停执行并在需要时恢复:
// 基本生成器
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const generator = generatorFunction();
console.log(generator.next()); // 输出: { value: 1, done: false }
console.log(generator.next()); // 输出: { value: 2, done: false }
console.log(generator.next()); // 输出: { value: 3, done: false }
console.log(generator.next()); // 输出: { value: undefined, done: true }
// 使用 for...of 遍历
function* generatorFunction2() {
yield "苹果";
yield "香蕉";
yield "橙子";
}
for (const fruit of generatorFunction2()) {
console.log(fruit);
}
// 输出:
// 苹果
// 香蕉
// 橙子
// 无限序列
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const infinite = infiniteSequence();
console.log(infinite.next().value); // 输出: 0
console.log(infinite.next().value); // 输出: 1
console.log(infinite.next().value); // 输出: 2
// 带参数的生成器
function* generatorWithParams() {
const param1 = yield "请输入第一个参数";
console.log("第一个参数:", param1);
const param2 = yield "请输入第二个参数";
console.log("第二个参数:", param2);
yield param1 + param2;
}
const gen = generatorWithParams();
console.log(gen.next()); // 输出: { value: "请输入第一个参数", done: false }
console.log(gen.next(5)); // 输出: { value: "请输入第二个参数", done: false }
console.log(gen.next(3)); // 输出: { value: 8, done: false }
console.log(gen.next()); // 输出: { value: undefined, done: true }
// 错误处理
function* generatorWithError() {
try {
yield 1;
yield 2;
throw new Error("生成器错误");
yield 3;
} catch (error) {
console.log("捕获到错误:", error.message);
yield "错误已处理";
}
yield 4;
}
const genWithError = generatorWithError();
console.log(genWithError.next()); // 输出: { value: 1, done: false }
console.log(genWithError.next()); // 输出: { value: 2, done: false }
console.log(genWithError.next()); // 输出: 捕获到错误: 生成器错误, { value: "错误已处理", done: false }
console.log(genWithError.next()); // 输出: { value: 4, done: false }
console.log(genWithError.next()); // 输出: { value: undefined, done: true }
Symbol
Symbol 是一种新的原始数据类型,用于创建唯一的标识符:
// 创建 Symbol
const symbol1 = Symbol();
const symbol2 = Symbol("描述");
const symbol3 = Symbol("描述");
console.log(symbol1); // 输出: Symbol()
console.log(symbol2); // 输出: Symbol(描述)
console.log(symbol2 === symbol3); // 输出: false(即使描述相同,Symbol 也是唯一的)
// 作为对象属性
const id = Symbol("id");
const person = {
[id]: 123,
name: "张三"
};
console.log(person[id]); // 输出: 123
console.log(Object.keys(person)); // 输出: ["name"](Symbol 属性不会出现在 Object.keys 中)
console.log(Object.getOwnPropertySymbols(person)); // 输出: [Symbol(id)]
// 内置 Symbol
const obj = {
[Symbol.toStringTag]: "自定义对象"
};
console.log(obj.toString()); // 输出: [object 自定义对象]
// Symbol.iterator
const iterableObj = {
items: [1, 2, 3],
[Symbol.iterator]: function* () {
for (const item of this.items) {
yield item;
}
}
};
for (const item of iterableObj) {
console.log(item);
}
// 输出:
// 1
// 2
// 3
Proxy 和 Reflect
Proxy 用于创建对象的代理,Reflect 提供了操作对象的方法:
// 基本 Proxy
const target = {
name: "张三",
age: 25
};
const proxy = new Proxy(target, {
// 拦截属性访问
get: function(target, property) {
console.log(`访问属性: ${property}`);
return target[property];
},
// 拦截属性设置
set: function(target, property, value) {
console.log(`设置属性: ${property} = ${value}`);
target[property] = value;
return true;
},
// 拦截属性删除
deleteProperty: function(target, property) {
console.log(`删除属性: ${property}`);
delete target[property];
return true;
},
// 拦截 in 操作符
has: function(target, property) {
console.log(`检查属性: ${property}`);
return property in target;
}
});
// 使用代理
console.log(proxy.name); // 输出: 访问属性: name, 张三
proxy.age = 26; // 输出: 设置属性: age = 26
console.log("age" in proxy); // 输出: 检查属性: age, true
delete proxy.age; // 输出: 删除属性: age
console.log(proxy.age); // 输出: 访问属性: age, undefined
// Reflect
const obj = {
name: "李四",
age: 30
};
// 使用 Reflect 访问属性
console.log(Reflect.get(obj, "name")); // 输出: 李四
// 使用 Reflect 设置属性
Reflect.set(obj, "age", 31);
console.log(obj.age); // 输出: 31
// 使用 Reflect 删除属性
Reflect.deleteProperty(obj, "age");
console.log(obj.age); // 输出: undefined
// 使用 Reflect 检查属性
console.log(Reflect.has(obj, "name")); // 输出: true
console.log(Reflect.has(obj, "age")); // 输出: false
// 使用 Reflect 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
const person = Reflect.construct(Person, ["王五", 35]);
console.log(person); // 输出: Person { name: "王五", age: 35 }
高级特性最佳实践
- 合理使用闭包:闭包可以创建私有变量和模块化代码,但过度使用可能导致内存泄漏
- 理解原型链:了解原型链的工作原理,避免修改内置对象的原型
- 使用 ES6+ 语法:优先使用 ES6+ 的新特性,如类、箭头函数、解构赋值等
- 掌握异步编程:理解回调、Promise 和 async/await 的使用场景和区别
- 模块化代码:使用 ES6 模块或 CommonJS 模块组织代码,提高可维护性
- 使用展开运算符:展开运算符可以简化数组和对象的操作
- 合理使用生成器:生成器适合处理异步操作和迭代器场景
- 使用 Symbol:Symbol 适合创建唯一标识符和对象的私有属性
- 使用 Proxy:Proxy 适合实现对象的拦截和自定义行为
- 编写清晰的代码:即使使用高级特性,也要保持代码的可读性和可维护性
高级特性常见问题
- 问题 1: 闭包内存泄漏
- 问题 2: 原型链污染
- 问题 3: 异步编程错误处理
- 问题 4: 模块化兼容性
- 问题 5: 性能问题
解决方案:避免在循环中创建闭包,及时释放不再需要的引用,特别是在长生命周期的对象中。
解决方案:不要修改内置对象的原型,使用 Object.create() 创建干净的对象。
解决方案:使用 try-catch 处理 async/await 错误,使用 Promise.catch() 处理 Promise 错误。
解决方案:了解不同模块化系统的区别,使用打包工具如 Webpack 处理兼容性问题。
解决方案:避免过度使用高级特性,如 Proxy 在性能敏感场景可能不是最佳选择。
📝 学习检查
通过本章节的学习,你应该了解:
- 闭包的概念和应用
- 原型和原型链的工作原理
- ES6 类的使用
- 异步编程(回调、Promise、async/await)
- 模块化的不同方式
- 解构赋值和展开运算符
- 生成器的使用
- Symbol 的用途
- Proxy 和 Reflect 的应用
- 高级特性的最佳实践和常见问题