ECMAScript 特性概览
1. 扩展运算符的应用
在数组中查找最大值时,使用扩展运算符非常方便,示例代码如下:
const maxValue = Math.max(...myArray);
在某些情况下,我们需要克隆对象。比如,有一个存储应用状态的对象,当其中一个状态属性发生变化时,我们不想改变原始对象,而是创建一个修改了一个或多个属性的克隆对象。实现不可变对象的一种方法是使用
Object.assign()
函数。示例如下:
// 使用 Object.assign() 克隆
const myObject = {name: "Mary" , lastName: "Smith"};
const clone = Object.assign({}, myObject);
console.log(clone);
// 克隆并修改 `lastName` 属性
const cloneModified = Object.assign({}, myObject, {lastName: "Lee"});
console.log(cloneModified);
扩展运算符提供了更简洁的语法来实现相同的目标,示例如下:
// 使用扩展运算符克隆
const myObject = { name: "Mary" , lastName: "Smith"};
const cloneSpread = {...myObject};
console.log(cloneSpread);
// 克隆并修改 `lastName`
const cloneSpreadModified = {...myObject, lastName: "Lee"};
console.log(cloneSpreadModified);
2. 生成器函数
当浏览器执行 JavaScript 函数时,它会不间断地运行到结束。但生成器函数的执行可以暂停和多次恢复。生成器函数可以将控制权交给在同一线程上运行的调用脚本。
要将普通函数转换为生成器,需要在
function
关键字和函数名之间放置一个星号。示例如下:
function* doSomething() {
console.log("Started processing");
yield;
console.log("Resumed processing");
}
调用这个函数时,它不会立即执行函数代码,而是返回一个特殊的
Generator
对象,该对象用作迭代器。示例如下:
var iterator = doSomething();
要开始执行函数体,需要在生成器上调用
next()
方法:
iterator.next();
生成器函数在需要编写一个生成数据流的函数,但又想控制何时处理下一个流值时非常有用。例如,模拟获取股票价格的生成器函数:
function* getStockPrice(symbol) {
while (true) {
yield Math.random()*100;
console.log(`resuming for ${symbol}`);
}
}
以下是调用该生成器函数的示例:
const priceGenerator = getStockPrice("IBM");
const limitPrice = 15;
let price = 100;
while (price > limitPrice) {
price = priceGenerator.next().value;
console.log (`The generator returned ${price}`);
}
console.log(`buying at ${price} !!!`);
3. 解构赋值
解构赋值允许我们通过指定匹配模式,在一个简单的表达式中从对象的属性或数组中提取数据。
3.1 对象解构
在 ES5 中,如果要将对象的属性值赋给单独的变量,需要先创建一个变量来存储对象,然后分别赋值。示例如下:
var stock = getStock();
var symbol = stock.symbol;
var price = stock.price;
在 ES6 中,只需要在等号左边编写匹配模式,并将对象赋给它。示例如下:
let {symbol, price} = getStock();
如果变量名与对象属性名不同,可以使用别名。示例如下:
let {sym: symbol, price} = getStock();
如果提供的变量比对象的属性多,多余的变量将被初始化为
undefined
。可以为变量设置默认值。示例如下:
let {symbol, price, stockExchange = "NASDAQ"} = getStock();
还可以解构嵌套对象。示例如下:
let msft = {
symbol: "MSFT",
lastPrice: 50.00,
exchange: {
name: "NASDAQ",
tradingHours: "9:30am - 4pm"
}
};
function printStockInfo(stock) {
let {symbol, exchange: {name}} = stock;
console.log(`The ${symbol} stock is traded at ${name}`);
}
printStockInfo(msft);
3.2 数组解构
数组解构与对象解构类似,但使用方括号。示例如下:
let [name1, name2] = ["Smith", "Clinton"];
console.log(`name1 = ${name1}, name2 = ${name2}`);
如果只提取数组的第二个元素,匹配模式如下:
let [, name2] = ["Smith", "Clinton"];
如果函数返回一个数组,解构语法可以将其转换为具有多个返回值的函数。示例如下:
function getCustomers() {
return ["Smith", , , "Gonzales"];
}
let [firstCustomer, , , lastCustomer] = getCustomers();
console.log(`The first customer is ${firstCustomer} and the last one is ${lastCustomer}`);
还可以将数组解构与剩余参数结合使用。示例如下:
let customers = ["Smith", "Clinton", "Lou", "Gonzales"];
let [firstCust, secondCust, ...otherCust] = customers;
console.log(`The first customer is ${firstCust} and the second one is ${secondCust}`);
console.log(`Other customers are ${otherCust}`);
4. 迭代方法
可以使用不同的 JavaScript 关键字和 API 来遍历对象集合,以下介绍三种常见的迭代方法。
4.1 使用
forEach()
方法
forEach()
方法接受一个函数作为参数,会正确打印数组中的元素,忽略额外的属性。示例如下:
var numbersArray = [1, 2, 3, 4];
numbersArray.description = "four numbers";
numbersArray.forEach((n) => console.log(n));
forEach()
方法的一个局限性是不能提前中断循环。
4.2 使用
for-in
循环
for-in
循环遍历对象和数据集合的属性名。示例如下:
var numbersArray = [1, 2, 3, 4];
numbersArray.description = "four numbers";
for (let n in numbersArray) {
console.log(n);
}
要查看属性的实际值,可以使用
numbersArray[n]
表示法。示例如下:
var numbersArray = [1, 2, 3, 4];
numbersArray.description = "four numbers";
for (let n in numbersArray) {
console.log(numbersArray[n]);
}
for-in
循环会遍历所有属性,可能不是我们想要的结果。
4.3 使用
for-of
循环
ES6 引入了
for-of
循环,允许我们遍历数据,而不考虑数据集合的其他属性。可以使用
break
关键字提前中断循环。示例如下:
var numbersArray = [1, 2, 3, 4];
numbersArray.description = "four numbers";
console.log("Running for-of for the entire array");
for (let n of numbersArray) {
console.log(n);
}
console.log("Running for-of with a break");
for (let n of numbersArray) {
if (n > 2) break;
console.log(n);
}
for-of
循环适用于任何可迭代对象,包括数组、映射、集合等。示例如下:
for (let char of "John") {
console.log(char);
}
5. 类和继承
虽然 ES5 支持面向对象编程和继承,但 ES6 的类使代码更易于阅读和编写。
在 ES5 中,对象可以从头创建或通过继承其他对象创建。默认情况下,所有 JavaScript 对象都继承自
Object
,这种对象继承通过
prototype
属性实现,称为原型继承。示例如下:
function Tax() {
// 税务对象的代码放在这里
}
function NJTax() {
// 新泽西州税务对象的代码放在这里
}
NJTax.prototype = new Tax();
var njTax = new NJTax();
ES6 引入了
class
和
extends
关键字,使语法与其他面向对象语言(如 Java 和 C#)保持一致。示例如下:
class Tax {
// 税务类的代码放在这里
}
class NJTax extends Tax {
// 新泽西州税务对象的代码放在这里
}
let njTax = new NJTax();
类声明不会被提升,需要先声明类,然后再使用它。
在实例化过程中,类会执行构造函数中的代码。在 ES6 中,使用
constructor
关键字指定类的构造函数。需要注意的是,ES6 语法不支持声明类成员变量。
以下是几种迭代方法的对比表格:
| 迭代方法 | 特点 | 能否提前中断循环 |
| ---- | ---- | ---- |
|
forEach()
| 忽略额外属性,接受函数作为参数 | 否 |
|
for-in
| 遍历所有属性名 | 否 |
|
for-of
| 遍历数据,不考虑其他属性 | 是 |
mermaid 流程图展示生成器函数执行流程:
graph TD;
A[调用生成器函数] --> B[返回 Generator 对象];
B --> C[调用 next() 方法];
C --> D[执行函数体到 yield 处暂停];
D --> E[再次调用 next() 方法];
E --> F[继续执行函数体];
ECMAScript 特性概览
6. 各特性的使用场景总结
为了更清晰地了解上述 ECMAScript 特性的使用场景,我们进行如下总结:
| 特性 | 使用场景 | 示例代码 |
|---|---|---|
| 扩展运算符 | 查找数组最大值、克隆对象 |
const maxValue = Math.max(...myArray); const clone = {...myObject};
|
| 生成器函数 | 生成数据流并控制数据处理时机 |
function* getStockPrice(symbol) { while (true) { yield Math.random()*100; } }
|
| 解构赋值 | 从对象或数组中提取数据 |
let {symbol, price} = getStock(); let [name1, name2] = ["Smith", "Clinton"];
|
forEach()
方法
| 遍历数组元素,忽略额外属性 |
numbersArray.forEach((n) => console.log(n));
|
for-in
循环
| 遍历对象或数组的所有属性名 |
for (let n in numbersArray) { console.log(n); }
|
for-of
循环
| 遍历可迭代对象的数据,可提前中断循环 |
for (let n of numbersArray) { if (n > 2) break; console.log(n); }
|
| 类和继承 | 实现面向对象编程,代码更易读易写 |
class Tax {} class NJTax extends Tax {}
|
7. 特性的综合应用示例
以下是一个综合应用上述特性的示例,模拟一个股票交易系统:
// 生成器函数:生成股票价格
function* getStockPrice(symbol) {
while (true) {
yield Math.random() * 100;
console.log(`resuming for ${symbol}`);
}
}
// 股票对象解构示例
function getStock() {
return {
symbol: "IBM",
price: 100.00
};
}
// 类和继承示例:税务计算
class Tax {
constructor(income) {
this.income = income;
}
calcTax() {
return this.income * 0.1; // 简单示例,税率 10%
}
}
class NJTax extends Tax {
constructor(income) {
super(income);
}
calcTax() {
return super.calcTax() + 10; // 额外加 10 美元
}
}
// 主程序
const priceGenerator = getStockPrice("IBM");
const limitPrice = 15;
let price = 100;
while (price > limitPrice) {
price = priceGenerator.next().value;
console.log(`The generator returned ${price}`);
}
console.log(`buying at ${price} !!!`);
let { symbol, price: stockPrice } = getStock();
console.log(`The price of ${symbol} is ${stockPrice}`);
let tax = new Tax(50000);
let njTax = new NJTax(50000);
console.log(`Regular tax: ${tax.calcTax()}`);
console.log(`NJ tax: ${njTax.calcTax()}`);
8. 注意事项
在使用这些特性时,有一些注意事项需要牢记:
-
扩展运算符
:在克隆对象时,它进行的是浅克隆。如果对象包含嵌套对象,修改克隆对象的嵌套对象会影响原始对象。
-
生成器函数
:生成器函数的状态会被保留,多次调用
next()
方法时会继续之前的状态执行。
-
解构赋值
:变量名要与对象属性名匹配,否则需要使用别名;多余的变量会被初始化为
undefined
。
-
forEach()
方法
:不能提前中断循环,如果需要提前中断,可考虑使用
every()
方法。
-
for-in
循环
:会遍历对象的所有可枚举属性,包括原型链上的属性,可能会导致意外结果。
-
for-of
循环
:只能用于可迭代对象,如果对象不可迭代,需要先将其转换为可迭代对象。
-
类和继承
:类声明不会被提升,需要先声明类,然后再使用;ES6 语法不支持声明类成员变量。
mermaid 流程图展示综合应用示例的执行流程:
graph TD;
A[开始] --> B[调用生成器函数获取股票价格];
B --> C{价格是否低于限制价格};
C -- 否 --> B;
C -- 是 --> D[打印购买信息];
D --> E[解构获取股票对象属性];
E --> F[创建税务对象];
F --> G[计算并打印税务信息];
G --> H[结束];
通过以上对 ECMAScript 各特性的介绍、使用场景总结、综合应用示例以及注意事项的说明,希望能帮助你更好地理解和使用这些特性,提升 JavaScript 编程能力。
超级会员免费看
1339

被折叠的 条评论
为什么被折叠?



