40、ECMAScript 特性概览

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 编程能力。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值