首页 > 其他分享 >ES6 的20 个新特性的详细介绍(完整版)

ES6 的20 个新特性的详细介绍(完整版)

时间:2024-11-13 13:49:32浏览次数:3  
标签:ES6 20 log 迭代 输出 数组 console 完整版 const

ES6 的20 个新特性的详细介绍以及相关知识拓展:

目录

一. 块级作用域

二. 箭头函数

三. 类(Class)

四. 模板字符串

五. 解构赋值

六. 默认参数

七. 扩展运算符

八. 剩余参数

九. Promise

十. 模块化

十一. Symbol

十二. Iterators

十三. Generators

十四. Map 数据结构

十五. Set 数据结构

十六. WeakMap 和 WeakSet

十七. Array.from

十八. Array.of

十九. Array.prototype.find

二十. Array.prototype.fill


一. 块级作用域

块级作用域是 JavaScript 中的一个重要概念,特别是在 ES6(ECMAScript 2015)引入了 let 和 const 关键字之后。块级作用域使得变量的作用域限制在一个特定的代码块内,这样可以更精确地控制变量的生命周期和可访问性。以下是对块级作用域的详细介绍:

1. 什么是块级作用域

块级作用域是指变量仅在特定的代码块(由 {} 括起来的部分)内可见和有效。与函数作用域不同,块级作用域只在最近的块内有效。这对于防止变量的意外冲突和易于维护代码非常重要。

2. 如何定义块级作用域

在 ES6 之前,JavaScript 只支持全局作用域和函数作用域,这意味着使用 var 声明的变量在全局或函数内都是可见的。ES6 新增的 let 和 const 关键字允许开发者在代码块中声明变量,这样变量将只在该代码块内有效。

示例:

{
    let x = 10; // x 在这个块的作用域内有效
    const y = 20; // y 也是在这个块的作用域内有效
    console.log(x); // 输出: 10
    console.log(y); // 输出: 20
}

// 下面的代码会导致错误,因为 x 和 y 超出了它们的作用域
console.log(x); // ReferenceError: x is not defined
console.log(y); // ReferenceError: y is not defined
3. 使用 let 和 const
  • let:用于声明变量,允许在块内重新赋值,但不会被提升。
  • const:用于声明常量,常量在声明时必须初始化且不可重新赋值。
let a = 1;

{
    let a = 2; // 这是一个新的块级作用域中的 a
    console.log(a); // 输出: 2
}

console.log(a); // 输出: 1,因为外部 a 与内部 a 是不同的
4. 变量提升

在 JavaScript 中,使用 var 声明的变量会被提升到函数或全局作用域的顶部。而使用 let 和 const 声明的变量则不会被提升。尝试在声明之前访问它们会导致错误。

console.log(z); // 输出: undefined
var z = 5;

console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 10;
5. 块级作用域与循环

块级作用域特别适用于循环中的变量声明。例如,在使用 let 声明循环变量时,每次迭代都会生成一个新的块作用域。

示例:

for (let i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i); // 输出: 0, 1, 2
    }, 1000);
}

在这个例子中,使用 let 声明 i,每一次循环创建一个新的作用域,因此每个 setTimeout 函数都能正确地记录当前的 i 值。

6. 总结
  • 块级作用域提供了更精确的变量管理,减少了变量冲突的可能性。
  • 使用 let 和 const 声明的变量在定义的代码块内部有效,外部无法访问。
  • 通过改善作用域的管理,块级作用域使得 JavaScript 代码更清晰和更可维护。

二. 箭头函数

箭头函数是 ES6(ECMAScript 2015)引入的一种简化的函数表达式语法,旨在提供更清晰、更简洁的函数定义方式。箭头函数的语法易于编写,同时还解决了一些在传统函数中常见的 this 绑定问题。以下是对箭头函数的详细介绍。

1. 基本语法

箭头函数使用 => 符号来定义,具有以下几种基本形式:

1.1 无参数

如果没有参数,可以使用空括号 ():

const noParam = () => {
    return 'Hello World';
};

console.log(noParam()); // 输出: Hello World

1.2 单参数

如果只有一个参数,可以省略括号:

const square = x => x * x;

console.log(square(5)); // 输出: 25

1.3 多个参数

如果有多个参数,要使用括号:

const add = (a, b) => a + b;

console.log(add(2, 3)); // 输出: 5

1.4 多行语句

如果函数体包含多个语句,需要使用大括号 {},并在函数体中使用 return 返回值:

const multiply = (a, b) => {
    const result = a * b;
    return result;
};

console.log(multiply(3, 4)); // 输出: 12
2. 特性与优点

2.1 this 绑定

传统函数会根据调用方式动态地绑定 this,而箭头函数不具有自己的 this 值,它通过词法作用域(lexical scoping)继承外部函数的 this 值。箭头函数特别适合在回调函数和一些事件处理中使用,以确保 this 指向期望的对象。

示例:

function Person() {
    this.age = 0;

    setInterval(() => {
        this.age++; // 这里的 `this` 指向 Person
        console.log(this.age);
    }, 1000);
}

const person = new Person(); // 每秒输出增加的年龄

在这个例子中,this.age 正确地指向了 Person 的实例。

2.2 简洁的语法

箭头函数的语法比传统函数更简洁,有助于提高代码的可读性,特别是在需要编写简单的回调函数时。

2.3 没有自己的 arguments

箭头函数没有自己的 arguments 对象。如果需要使用参数对象,可以使用剩余参数(...args)来接收。

const logArgs = (...args) => {
    console.log(args);
};

logArgs(1, 2, 3); // 输出: [1, 2, 3]
3. 缺点

虽然箭头函数在很多地方都很有用,但也存在一些限制和缺点:

  • 不适合作为构造函数:箭头函数没有 prototype 属性,不能被用作构造函数。

    const Person = () => {};
    const john = new Person(); // TypeError: Person is not a constructor
    
  • 不适合定义对象方法:当定义对象方法时,使用箭头函数可能导致 this 指向问题,因为它会将 this 绑定到创建箭头函数时的上下文。

    const obj = {
        value: 10,
        getValue: () => {
            return this.value; // `this` 并不指向 obj,所以返回 undefined
        }
    };
    console.log(obj.getValue()); // 输出: undefined
    
  • 4. 总结
  • 没有 thissuperarguments 和 new.target:在箭头函数中是不可用的。

  • 箭头函数提供了一种简洁而强大的函数定义方式,特别是在需要处理回调和方法时。
  • 它的关键特性是 this 没有自己的绑定,而是继承自外层作用域,使得代码更易读和易于维护。
  • 然而,箭头函数并不是万能的,不适合用作构造函数或某些方法的定义。使用时需要根据实际情况选择合适的函数形式。

三. 类(Class)

  • ES6(ECMAScript 2015)引入了类(Class)的概念,使得 JavaScript 的面向对象编程更为清晰和可维护。类 Syntax 提供了一种更简洁、易读的方式来创建对象和处理继承。以下是对 ES6 类的详细介绍。

    1. 类的基本语法

    在 ES6 中,使用 class 关键字定义类:

    class ClassName {
        constructor(parameters) {
            // 初始化代码
        }
    
        method1() {
            // 方法1
        }
    
        method2() {
            // 方法2
        }
    }
    

    示例:

    class Person {
        constructor(name, age) {
            this.name = name; // 实例属性
            this.age = age;   // 实例属性
        }
    
        greet() {
            console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
        }
    }
    
    const john = new Person('John', 30);
    john.greet(); // 输出: Hello, my name is John and I am 30 years old.
    
    2. 构造函数

    构造函数是类中一个特殊的方法,用于创建和初始化对象。当使用 new 关键字实例化一个类时,构造函数会被自动调用。每个类可以有一个构造函数,构造函数可以接受参数。

    class Rectangle {
        constructor(width, height) {
            this.width = width;
            this.height = height;
        }
    
        area() {
            return this.width * this.height;
        }
    }
    
    const rect = new Rectangle(10, 5);
    console.log(rect.area()); // 输出: 50
    
    3. 类的继承

    ES6 类支持继承,可以通过 extends 关键字让一个类继承另一个类。子类会继承父类的属性和方法,并可以添加自己的属性和方法。

    class Animal {
        constructor(name) {
            this.name = name;
        }
    
        speak() {
            console.log(`${this.name} makes a noise.`);
        }
    }
    
    class Dog extends Animal {
        speak() {
            console.log(`${this.name} barks.`);
        }
    }
    
    const dog = new Dog('Rex');
    dog.speak(); // 输出: Rex barks.
    
    4. 方法的简写

    在类中定义方法时,可以直接书写,不需要加 function 前缀。

    class User {
        constructor(name) {
            this.name = name;
        }
    
        sayHello() {
            console.log(`Hello, ${this.name}`);
        }
    }
    
    const user = new User('Alice');
    user.sayHello(); // 输出: Hello, Alice
    
    5. Getter 和 Setter

    类可以定义访问器属性 getter 和 setter 来读取和修改对象的属性。

    class Circle {
        constructor(radius) {
            this.radius = radius;
        }
    
        get area() {
            return Math.PI * this.radius * this.radius; // 计算面积
        }
    
        set newRadius(value) {
            this.radius = value; // 更新半径
        }
    }
    
    const circle = new Circle(5);
    console.log(circle.area); // 输出: 78.53981633974483
    circle.newRadius = 10; // 更新半径
    console.log(circle.area); // 输出: 314.1592653589793
    
    6. 静态方法

    使用 static 关键字定义的静态方法属于类本身,而不是气态实例。静态方法不可在实例上调用。

    class MathUtils {
        static add(x, y) {
            return x + y;
        }
    }
    
    console.log(MathUtils.add(5, 10)); // 输出: 15
    
    7. 类的继承与 super

    在子类的构造函数中,可以使用 super() 调用父类的构造函数。调用 super() 是子类构造函数中必须的步骤,以确保父类部分正确初始化。

    class Animal {
        constructor(name) {
            this.name = name;
        }
    }
    
    class Cat extends Animal {
        constructor(name, breed) {
            super(name); // 调用父类构造函数
            this.breed = breed;
        }
    
        describe() {
            console.log(`This is a ${this.breed} cat named ${this.name}.`);
        }
    }
    
    const cat = new Cat('Luna', 'Siamese');
    cat.describe(); // 输出: This is a Siamese cat named Luna.
    
    8. 总结
  • 定义:使用 class 关键字定义类,支持构造函数和方法的定义。
  • 继承:支持通过 extends 和 super() 进行类的继承。
  • 简化语法:允许直接定义方法,支持 Getter 和 Setter。
  • 静态方法:通过静态方法定义属于类本身的方法。

四. 模板字符串

模板字符串(Template Literals)是 ES6 引入的一种新的字符串表示法,提供了一种更丰富的方式来处理字符串。模板字符串增强了字符串的可读性和可维护性,支持多行文本和嵌入表达式。以下是对模板字符串的详细介绍。

1. 基础语法

模板字符串使用反引号(`)来定义,而不是传统的单引号或双引号。基本语法如下:

const templateString = `这是一个模板字符串`;
2. 多行字符串

模板字符串允许在字符串内自由换行,而不需要使用转义字符 \n。其格式显得更加直观。

示例:

const multiLineString = `这是一段多行字符串。
可以在不同的行中书写文本。
这样更容易阅读。`;

console.log(multiLineString);
3. 字符串插值

模板字符串可以直接嵌入变量和表达式,使用 ${} 语法,将表达式放入其中,执行结果将被插入到字符串中。

示例:

const name = 'Alice';
const age = 30;
const greeting = `你好,我是 ${name},我 ${age} 岁。`;

console.log(greeting); // 输出: 你好,我是 Alice,我 30 岁。
4. 嵌入表达式

模板字符串不仅支持变量,也支持执行任何 JavaScript 表达式,包括函数调用、数学运算等。

示例:

const a = 5;
const b = 10;
const result = `五加十分的结果是:${a + b}`; // 内部可以进行计算

console.log(result); // 输出: 五加十分的结果是:15
5. 标签模板(Tagged Templates)

标签模板允许您定义一个函数来处理模板字符串。标签函数会接收字符串片段和嵌入的表达式,允许自定义逻辑处理。

示例:

function tag(strings, ...values) {
    let str = '';
    strings.forEach((string, index) => {
        str += string + (values[index] || '');
    });
    return str.toUpperCase(); // 将字符串转换为大写
}

const name = 'Bob';
const message = tag`你好,${name}!欢迎回来。`;

console.log(message); // 输出: 你好,BOB!欢迎回来。
6. 处理嵌套模板

模板字符串可以进行嵌套,允许在一个模板字符串内部使用另一个模板字符串。

示例:

const user = {
    name: 'Charlie',
    info: `姓名: ${name}`
};

const greeting = `欢迎回来,${user.info}`;
console.log(greeting); // 输出: 欢迎回来,姓名: Charlie
7. 使用场景

模板字符串在以下场景中非常便利:

  • 生成 HTML 字符串,构建基于 DOM 的动态内容。
  • 在应用中插入复杂的文本内容,减少字符串拼接的复杂性。
  • 日志、错误信息等,使用多行字符串提高可读性。
8. 总结
  • 模板字符串 使用反引号定义,支持多行文本和字符串插值。
  • 嵌入表达式 使用 ${} 语法,执行变量和表达式,结果会被插入到字符串中。
  • 标签模板 提供了一种处理模板字符串的灵活方式,可以自定义输出格式。
  • 可读性和维护性:模板字符串使得书写字符串更加简洁清晰,尤其在处理复杂文本时。

五. 解构赋值

  • 解构赋值是 ES6(ECMAScript 2015)引入的一种语法,允许从数组或对象中提取值并将其分配给变量。这种语法极大地提高了代码的可读性和简洁性,特别是在处理复杂数据结构时。以下是对解构赋值的详细介绍。

    1. 数组解构

    数组解构允许从数组中提取元素,并将它们赋值给变量。

    基本语法

    const array = [1, 2, 3];
    const [a, b, c] = array; // a = 1, b = 2, c = 3
    

    示例:

    const fruits = ['apple', 'banana', 'cherry'];
    const [first, second, third] = fruits;
    
    console.log(first); // 输出: apple
    console.log(second); // 输出: banana
    console.log(third); // 输出: cherry
    
    2. 默认值

    如果解构时某个元素没有对应的值,可以设置默认值。

    示例:

    const colors = ['red', 'green'];
    const [primary, secondary, tert = 'blue'] = colors; // tert 将默认设为 'blue'
    
    console.log(primary); // 输出: red
    console.log(secondary); // 输出: green
    console.log(tert); // 输出: blue
    
    3. 交换变量的值

    使用解构赋值,可以方便地交换变量的值。

    示例:

    let x = 5;
    let y = 10;
    [x, y] = [y, x]; // 交换 x 和 y 的值
    
    console.log(x); // 输出: 10
    console.log(y); // 输出: 5
    
    4. 对象解构

    对象解构允许从对象中提取属性,并将它们赋值给变量。与数组解构不同,对象解构使用字段名匹配。

    基本语法

    const object = { name: 'John', age: 30 };
    const { name, age } = object; // name = 'John', age = 30
    

    示例:

    const user = {
        username: 'Alice',
        age: 25,
        email: '[email protected]'
    };
    
    const { username, age } = user;
    
    console.log(username); // 输出: Alice
    console.log(age); // 输出: 25
    
    5. 使用不同的变量名

    在对象解构中,可以使用不同的变量名来接收值。

    示例:

    const car = {
        make: 'Toyota',
        model: 'Corolla',
        year: 2021
    };
    
    const { make: brand, model: type } = car; // 将 make 分配给 brand,model 分配给 type
    
    console.log(brand); // 输出: Toyota
    console.log(type); // 输出: Corolla
    
    6. 嵌套解构

    解构赋值支持嵌套对象和数组。

    示例:

    const person = {
        name: 'Bob',
        address: {
            city: 'New York',
            country: 'USA'
        }
    };
    
    const { name, address: { city } } = person;
    
    console.log(name); // 输出: Bob
    console.log(city); // 输出: New York
    
    7. 解构函数参数

    可以在函数参数中使用解构赋值,这在处理带有多个参数的函数时非常方便。

    示例:

    function printUser({ name, age }) {
        console.log(`Name: ${name}, Age: ${age}`);
    }
    
    const user = { name: 'Charlie', age: 20 };
    printUser(user); // 输出: Name: Charlie, Age: 20
    
    8. 总结
  • 解构赋值 提供了一种简洁的方式来从数组和对象中提取数据。
  • 通过解构,可以方便地设置默认值、交换变量、处理嵌套结构以及在函数参数中使用。
  • 解构赋值极大地提升了代码的可读性和维护性。

六. 默认参数

默认参数是 ES6(ECMAScript 2015)引入的一项功能,允许开发者为函数的参数指定默认值。当函数调用时没有提供该参数的值或者提供的是 undefined,则使用预设的默认值。默认参数简化了代码的编写,提升了可读性与可维护性。

1. 基本语法

默认参数的定义方式是在函数参数表中为某个参数指定一个值:

function functionName(parameter = defaultValue) {
    // 函数体
}
2. 示例

2.1 简单例子

下面是一个使用默认参数的简单示例:

function greet(name = 'Guest') {
    console.log(`Hello, ${name}!`);
}

greet();          // 输出: Hello, Guest!
greet('Alice');  // 输出: Hello, Alice!

在这个示例中,如果调用 greet() 时没有提供参数,name 的默认值为 'Guest'。

2.2 与常规参数结合使用

默认参数可以与常规参数结合使用。默认参数是可以灵活设定的:

function multiply(a, b = 1) {
    return a * b;  // 乘法函数,b 默认值为 1
}

console.log(multiply(5));     // 输出: 5
console.log(multiply(5, 10)); // 输出: 50

在上面的例子中,第二个参数 b 的默认值为 1,当只传递一个参数时,b 会自动使用该默认值。

3. 表达式作为默认值

默认参数不仅可以是简单的值,还可以是表达式。例如,可以使用其他参数、函数调用等作为默认值:

function add(a, b = a) {
    return a + b;
}

console.log(add(5));       // 输出: 10 (5 + 5)
console.log(add(5, 3));    // 输出: 8  (5 + 3)

在这个示例中,当 b 没有提供时,它的默认值为参数 a 的值。

4. 默认参数与 undefined

需要注意的是,默认参数的值仅在参数未被定义或为 undefined 时生效。传递 null 或其他假值(如 0false 等)时,将不会使用默认值。

function log(value = 'default') {
    console.log(value);
}

log();        // 输出: default
log(undefined); // 输出: default
log(null);   // 输出: null
log(0);      // 输出: 0
log(false);  // 输出: false
5. 总结
  • 简化参数处理:默认参数使得函数参数处理更加方便。特别是在需要多个参数的情况下,可以避免对每个参数进行单独的检测。
  • 提高可读性:提供了清晰的函数接口,使得函数的意图更加明显。
  • 灵活性:可以与其他参数结合使用,支持表达式作为默认值,增强了函数的灵活性。

七. 扩展运算符

扩展运算符(Spread Operator)是 ES6(ECMAScript 2015)引入的一种新语法,使用三个点(...)表示。扩展运算符可以将数组或对象轻松展开,以便于在数组或函数参数中使用。它使得集合操作更加简洁和直观,广泛应用于数组和对象的操作中。以下是对扩展运算符的详细介绍。

1. 数组扩展

扩展运算符可以将数组展开为单个元素,常用于合并数组、复制数组和作为函数参数传递。

1.1 合并数组

通过扩展运算符,可以方便地合并多个数组:

const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const combined = [...array1, ...array2]; // 合并数组

console.log(combined); // 输出: [1, 2, 3, 4, 5, 6]

1.2 复制数组

使用扩展运算符可以简便地复制数组:

const original = [1, 2, 3];
const copy = [...original]; // 复制数组

console.log(copy); // 输出: [1, 2, 3]

1.3 作为函数参数

扩展运算符可用于将数组元素作为参数传递给函数,避免使用 apply 方法。

const nums = [1, 2, 3, 4];
const sum = (a, b, c, d) => a + b + c + d;

console.log(sum(...nums)); // 输出: 10
2. 对象扩展

扩展运算符同样可以用于对象,允许更灵活地合并和复制对象。

2.1 合并对象

通过扩展运算符,可以将多个对象合并为一个新的对象:

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 };

console.log(merged); // 输出: { a: 1, b: 3, c: 4 }

注意:在合并对象时,如果存在同名属性,后面的对象属性会覆盖前面的。

2.2 复制对象

扩展运算符也可以用于复制对象:

const original = { x: 1, y: 2 };
const copy = { ...original };

console.log(copy); // 输出: { x: 1, y: 2 }
3. 处理嵌套结构

扩展运算符在处理嵌套数据结构时,帮助简化操作,例如合并嵌套数组或对象:

const user = {
    name: 'Alice',
    info: {
        age: 25,
        city: 'New York'
    }
};

const updatedUser = {
    ...user,
    info: {
        ...user.info,
        country: 'USA' // 添加新属性
    }
};

console.log(updatedUser);
/* 输出:
{
    name: 'Alice',
    info: { age: 25, city: 'New York', country: 'USA' }
}
*/
4. 需要注意的事项
  • 浅拷贝:使用扩展运算符进行对象和数组的复制会创建一个新的引用,但对于嵌套的对象和数组,仍然是浅拷贝。这意味着引用对象中的更改会影响原始对象。
const original = { a: 1, b: [2, 3] };
const copy = { ...original };

copy.b[0] = 5; // 修改拷贝的数组

console.log(original.b); // 输出: [5, 3],影响了原对象
  • 不适合所有场合:扩展运算符无法用于类数组对象(如 NodeListarguments 等),需使用 Array.from() 转换。
5. 总结
  • 扩展运算符 提供了一种简洁的方式来操作数组和对象。
  • 允许方便地合并、复制、展开数组和对象。
  • 在处理函数参数时,非常实用。

八. 剩余参数

“剩余参数”(Rest Parameters)是 ES6(ECMAScript 2015)引入的一种语法,用于收集函数传入的剩余参数并将其打包为一个数组。它解决了传统函数使用 arguments 对象所带来的局限性和不便之处,使得函数参数处理更加灵活和简洁。下面是对剩余参数的详细介绍。

1. 基本语法

剩余参数的语法使用三个点 (...) 后跟参数名。示例如下:

function myFunction(...args) {
    console.log(args);
}

在这个示例中,args 将是一个数组,包含所有传递给 myFunction 的参数。

2. 用法示例

2.1 简单示例

function sum(...numbers) {
    return numbers.reduce((acc, curr) => acc + curr, 0); // 对所有参数求和
}

console.log(sum(1, 2, 3, 4)); // 输出: 10
console.log(sum(5, 10)); // 输出: 15

这里,sum 函数使用剩余参数来接收任意数量的参数,并返回它们的总和。

2.2 与传统参数结合使用

剩余参数可以与其他常规参数结合使用,但必须将其放在参数列表的最后。

function multiply(factor, ...numbers) {
    return numbers.map(num => num * factor); // 每个数乘以给定因子
}

console.log(multiply(2, 1, 2, 3)); // 输出: [2, 4, 6]

在这个示例中,factor 是一个普通参数,而 numbers 是使用剩余参数收集的其他所有参数。

3. 特点与优势
  • 数组:剩余参数实际是一个真实的数组,可以使用数组方法(如 mapfilterreduce)进行操作。

  • 清晰语法:使用剩余参数使得函数参数处理更加直观,避免了使用 arguments 对象的繁琐操作。

  • 减少冲突:使用剩余参数可以避免与其他参数发生冲突,因为它会收集所有剩余参数为一个数组。

4. 对比 arguments 对象

使用 arguments 对象时,一些局限性会使得代码的 readability 降低:

  • arguments 不是数组,而是一个类数组对象,不能直接使用数组方法。
  • arguments 包含了所有传入函数的参数,包括那些未命名的参数。
  • arguments 在箭头函数和某些其他场景中不可用。

示例对比:

使用 arguments

function logArguments() {
    console.log(arguments);
}

logArguments(1, 2, 3); // 输出: [1, 2, 3] (类数组)

使用剩余参数:

function logArgs(...args) {
    console.log(args);
}

logArgs(1, 2, 3); // 输出: [1, 2, 3] (真正的数组)
5. 总结
  • 剩余参数 是一种强大的功能,简化了处理不定数量的函数参数。
  • 与传统的 arguments 对象相比,剩余参数提供了更直接、易于操作的解决方案。
  • 剩余参数的灵活性和可读性使得它在现代 JavaScript 开发中得到广泛应用。

九. Promise

Promise 是 ES6(ECMAScript 2015)引入的一种用于处理异步操作的对象,它表示一个可能会在将来某个时间点成功或失败的操作及其结果值。Promise 允许我们编写更清晰和结构化的异步代码,避免了传统回调函数造成的“回调地狱"(Callback Hell)。

1. Promise 的状态

Promise 有三种状态:

  • Pending(等待中):初始状态,既不是成功也不是失败。
  • Fulfilled(已完成):操作成功的状态,Promise 对象的值可用。
  • Rejected(已拒绝):操作失败的状态,Promise 对象的原因(错误信息)可用。

状态的转换可视化如下:

Pending → Fulfilled
Pending → Rejected
2. 创建 Promise

我们可以使用 new Promise 来创建一个 Promise 对象。构造函数接受一个执行器函数(executor),该函数有两个参数:resolve 和 reject

const myPromise = new Promise((resolve, reject) => {
    // 异步操作
    const success = true; // 假设这是一个成功的操作

    if (success) {
        resolve('操作成功'); // 调用 resolve 以改变状态为已完成
    } else {
        reject('操作失败');   // 调用 reject 以改变状态为已拒绝
    }
});
3. 使用 Promise

Promise 提供了 .then() 和 .catch() 方法来处理结果和错误。

  • then():用于处理成功的情况,可链式调用。
  • catch():用于处理失败的情况。

示例:

myPromise
    .then(result => {
        console.log(result); // 输出: 操作成功
    })
    .catch(error => {
        console.error(error); // 如果操作失败,输出错误信息
    });
4. Promise 的链式调用

Promise 允许通过链式调用来避免嵌套结构,使得代码更清晰:

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000); // 1秒后成功
});

const promise2 = promise1
    .then(result => {
        console.log(result); // 输出: 1
        return result + 1;   // 返回新的值
    })
    .then(result => {
        console.log(result); // 输出: 2
        return result + 2;   // 返回新的值
    });

promise2.then(result => {
    console.log(result); // 输出: 4
});
5. Promise.all() 和 Promise.race()
  • Promise.all(iterable):接收一个可迭代对象(如数组)作为参数,返回一个新的 Promise,只有在所有输入的 Promise 都成功时,才会解析为成功。如果有一个 Promise 失败,则立即拒绝。
const promiseA = Promise.resolve(1);
const promiseB = Promise.resolve(2);
const promiseC = new Promise((resolve, reject) => setTimeout(resolve, 100, 3));

Promise.all([promiseA, promiseB, promiseC])
    .then(results => {
        console.log(results); // 输出: [1, 2, 3]
    })
    .catch(error => {
        console.error(error);
    });
  • Promise.race(iterable):返回一个新的 Promise,解析为第一个成功或失败的 Promise 的结果。这使得我们能够处理多个 Promise 中的第一个完成。
const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'A'));
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'B'));

Promise.race([promise1, promise2])
    .then(result => {
        console.log(result); // 输出: A,因为 promise1 先完成
    });
6. 总结
  • Promise 是一个表示异步操作的对象,具有三种状态(待定、已完成、已拒绝)。
  • 提供 .then() 和 .catch() 方法,能够进行链式调用,使得异步代码更加清晰。
  • 方法 Promise.all() 和 Promise.race() 允许处理多个 Promise 的情况,进一步增强了处理异步操作的能力。

十. 模块化

ES6(ECMAScript 2015)引入了模块化功能,提供了一种组织和管理 JavaScript 代码的新方式。模块化不仅有助于更清晰地分离代码逻辑,还提高了代码的可重用性、可维护性和团队协作效率。以下是对 ES6 模块化的详细介绍。

1. 模块的基本概念

在 ES6 中,模块是指一个独立的 JavaScript 文件,模块中的变量、函数和类通过 export 和 import 进行导出和导入。每个模块都是独立的作用域,模块内的内容不会污染全局作用域,从而避免命名冲突。

2. 导出(Export)

ES6 提供了两种导出方式:命名导出(Named Exports)和默认导出(Default Export)。

2.1 命名导出

可以通过 export 关键字导出多个变量、函数或类。

// math.js
export const PI = 3.14;

export function add(x, y) {
    return x + y;
}

export class Calculator {
    constructor() {}
    
    multiply(x, y) {
        return x * y;
    }
}

2.2 默认导出

模块可以有一个默认导出,使用 export default 关键字。每个模块只能有一个默认导出。

// logger.js
export default function log(message) {
    console.log(message);
}
3. 导入(Import)

通过 import 语句来引入其他模块中的变量、函数和类。导入的名称必须和导出时使用的一致。

3.1 导入命名导出

// main.js
import { PI, add, Calculator } from './math.js';

console.log(PI); // 输出: 3.14
console.log(add(2, 3)); // 输出: 5

const calc = new Calculator();
console.log(calc.multiply(2, 3)); // 输出: 6

3.2 导入默认导出

// main.js
import log from './logger.js';

log('Hello, World!'); // 输出: Hello, World!
4. 重新命名导入和导出

可以在导入时使用 as 关键字重新命名导入的内容,避免命名冲突:

import { add as sum } from './math.js';

console.log(sum(2, 3)); // 输出: 5

在导出时也可以使用 as 进行重新命名:

export { add as sum };
5. 整体导入

可以使用 * 符号整体导入模块中的所有导出,并将其绑定到一个对象上:

import * as math from './math.js';

console.log(math.PI); // 输出: 3.14
console.log(math.add(2, 3)); // 输出: 5
6. 模块的加载方式

ES6 模块是异步加载的,这意味着模块导入会在后台进行,而不会阻塞主线程。这与传统的 <script> 标签不同,后者会在解析时阻塞。

7. 模块化的优点
  1. 代码组织:将代码分割成多个模块,能够使结构更加清晰,易于管理。
  2. 命名冲突:每个模块都有自己的作用域,减少了变量和函数的命名冲突。
  3. 重用性:模块可以被其他模块导入,促进代码的重用。
  4. 团队协作:多个团队成员可以独立开发不同的模块,最后将其组合成整体应用。
8. 兼容性

虽然 ES6 模块在现代浏览器和 Node.js 中得到了广泛支持,但在需要兼容旧版本的环境中,可以使用工具(如 Babel)将 ES6 模块转换为 CommonJS 模块或其他格式。

9.总结
  • 模块化的引入:ES6 的模块化功能增强了 JavaScript 的开发能力,提供了更清晰的代码结构和易于管理的方式。
  • 灵活性:通过 export 和 import 语句,可以方便地定义和使用模块。
  • 现代 JavaScript 开发:模块化已成为现代应用程序开发的标准,尤其是在构建大型应用时,可以提高开发效率和代码质量。

十一. Symbol

Symbol 是 ES6(ECMAScript 2015)中引入的一种新的原始数据类型,用于创建唯一的标识符。与其他基本数据类型(例如 NumberStringBoolean)相比,Symbol 提供了一个更加安全和隐秘的方法来定义对象的属性,避免属性名称的冲突。以下是对 Symbol 的详细介绍。

1. 基本概念
  • 唯一性Symbol 的值是唯一的。每次调用 Symbol() 都会创建一个新的、独特的符号。
  • 不可变性:一旦创建,Symbol 的值是不可变的,不能被修改。
2. 创建 Symbol

可以通过 Symbol() 函数创建一个新的 Symbol。可以选择性地给每个 Symbol 提供一个描述(或名称),但这个描述是调试用,不影响 Symbol 的唯一性。

示例:

const sym1 = Symbol('description');
const sym2 = Symbol('description');

console.log(sym1 === sym2); // 输出: false,因为每个 Symbol 都是唯一的

即使两个符号的描述相同,它们的值仍然不同。

3. 将 Symbol 用作对象的属性

使用 Symbol 可以定义对象的属性,确保这些属性名称不会与任何其他属性冲突。

示例:

const uniqueKey = Symbol('uniqueKey');

const obj = {
    [uniqueKey]: 'value'
};

console.log(obj[uniqueKey]); // 输出: value

在这个例子中,uniqueKey 作为属性名,避免了与传统字符串属性名称的冲突。

4. Symbol 的应用场景

4.1 定义私有属性

虽然 JavaScript 没有真正的私有属性,但可以通过 Symbol 来模拟私有属性,因为它们不会被其他代码轻易访问:

const privateMethod = Symbol('privateMethod');

class Example {
    constructor() {
        this.publicProperty = 'I am public';
    }

    [privateMethod]() {
        console.log('This is a private method');
    }

    callPrivateMethod() {
        this[privateMethod](); // 通过公有方法调用私有方法
    }
}

const example = new Example();
example.callPrivateMethod(); // 输出: This is a private method

4.2 与常规属性区分

在某些情况下,可以使用 Symbol 定义的属性来区分常规属性,避免名称冲突。

const sym1 = Symbol('id');
const sym2 = Symbol('id');

const user = {
    name: 'Alice',
    [sym1]: 1,
    [sym2]: 2
};

console.log(user[sym1]); // 输出: 1
console.log(user[sym2]); // 输出: 2
5. Symbol.for() 和 Symbol.keyFor()
  • Symbol.for(key): 在全局范围内注册或获取 Symbol。通过该方法创建的 Symbol 是共享的,具有相同的键始终返回同一个 Symbol。
const globalSym1 = Symbol.for('appSymbol');
const globalSym2 = Symbol.for('appSymbol');

console.log(globalSym1 === globalSym2); // 输出: true
  • Symbol.keyFor(symbol): 获取与共享 Symbol 关联的键。如果给定的 Symbol 不是共享的,将返回 undefined。
const sym = Symbol.for('appSymbol');
console.log(Symbol.keyFor(sym)); // 输出: appSymbol
6. 总结
  • 唯一性: 每个 Symbol 值都是唯一的,适用于防止命名冲突。
  • 不可变性: 一旦创建,Symbol 的值不能更改。
  • 用途: 可用于定义对象的私有属性、避免与其它属性的冲突,以及作为全局唯一标识符。
  • 共享 Symbol: 通过 Symbol.for() 方法可以在全局范围内共享 Symbol。

十二. Iterators

在 ES6(ECMAScript 2015)中,引入了迭代器(Iterator)和可迭代对象(Iterable)的概念。这些特性提供了一种统一的机制,使得不同的数据结构(如数组、字符串、Map、Set 等)能够进行迭代,极大地提升了 JavaScript 的灵活性和表达能力。以下是对迭代器的详细介绍。

1. 可迭代对象

可迭代对象(Iterable)是指可以通过 for...of 循环进行迭代的对象。每个可迭代对象都实现了一个名为 Symbol.iterator 的方法,该方法返回一个迭代器。

常见的可迭代对象包括:

  • 数组
  • 字符串
  • Map
  • Set
  • Arguments 对象

示例:

const array = [1, 2, 3];
const string = "hello";

console.log(array[Symbol.iterator]); // 输出: f () { [native code] }
console.log(string[Symbol.iterator]); // 输出: f () { [native code] }
2. 迭代器

迭代器是一个对象,它定义了进行迭代的接口。迭代器对象必须实现一个返回对象的方法,该对象具有 next() 方法。每次调用 next() 方法时,都会返回一个包含两个属性的对象:

  • value: 当前迭代的值。
  • done: 一个布尔值,表示迭代是否完成。
3. 自定义迭代器

可以通过实现 Symbol.iterator 方法,自定义一个可迭代对象。

示例:

const myIterable = {
    *[Symbol.iterator]() {
        yield 1;
        yield 2;
        yield 3;
    }
};

for (let value of myIterable) {
    console.log(value); // 输出: 1, 2, 3
}

在这个例子中,通过生成器(Generator)语法 * 实现了可迭代对象。每次使用 yield,就会返回一个值,并在下次调用时从上一次返回的位置继续执行。

4. 迭代器的使用场景

4.1 数组的迭代

可通过 Array.prototype.entries()Array.prototype.keys() 和 Array.prototype.values() 等方法获取数组的迭代器:

const array = [10, 20, 30];

const iterator = array[Symbol.iterator]();

console.log(iterator.next()); // 输出: { value: 10, done: false }
console.log(iterator.next()); // 输出: { value: 20, done: false }
console.log(iterator.next()); // 输出: { value: 30, done: false }
console.log(iterator.next()); // 输出: { value: undefined, done: true }

4.2 Map 和 Set 的迭代

Map 和 Set 对象内置了迭代器,可以直接使用:

const map = new Map([['key1', 'value1'], ['key2', 'value2']]);

for (const [key, value] of map) {
    console.log(key, value);
}
// 输出:
// key1 value1
// key2 value2

const set = new Set([1, 2, 3]);

for (const value of set) {
    console.log(value);
}
// 输出:
// 1
// 2
// 3
5. 生成器(Generator)

生成器是 ES6 中引入的一种特殊函数,能够通过 yield 关键字,逐步返回值并且可以多次暂停和恢复。生成器在实现迭代器时非常方便,因为它们自动实现了 next() 方法。

示例:

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 }
6. 总结
  • 可迭代对象 是指能够使用 for...of 循环的对象,需实现 Symbol.iterator 方法。
  • 迭代器 提供了一种访问集合中每个元素的标准方法,返回 next() 方法的对象包含当前值和完成状态。
  • 自定义迭代器 允许开发者为任何对象定义迭代行为,提供更灵活的数据处理方式。
  • 生成器 是一种简化迭代器实现的工具,使用 yield 关键字可暂停和恢复函数的执行。

十三. Generators

生成器(Generators)是 ES6(ECMAScript 2015)引入的一种特殊类型的函数,使得可以生成可迭代的序列。与传统函数相比,生成器的最大的特点是能够在执行过程中暂停和恢复,这为控制函数的执行提供了更大的灵活性。以下是对生成器的详细介绍。

1. 定义生成器

生成器使用 function* 语法定义,函数体内部可以使用 yield 关键字来暂停函数的执行并返回值。每次调用 next() 方法时,生成器将从上次暂停的位置继续执行并返回下一个值。

语法示例:

function* generatorFunction() {
    yield 1; // 暂停并返回 1
    yield 2; // 暂停并返回 2
    yield 3; // 暂停并返回 3
}
2. 创建生成器

生成器的实例是一个可迭代对象,通过调用生成器函数可以获得该实例:

const gen = generatorFunction(); // 创建生成器对象
3. 使用生成器

调用生成器对象的 next() 方法可获取一个包含 value 和 done 这两个属性的对象:

  • value: 当前 yield 表达式的值。
  • done: 如果生成器已经执行完所有 yield 表达式,则为 true;否则为 false

示例:

const gen = generatorFunction();

console.log(gen.next()); // 输出: { value: 1, done: false }
console.log(gen.next()); // 输出: { value: 2, done: false }
console.log(gen.next()); // 输出: { value: 3, done: false }
console.log(gen.next()); // 输出: { value: undefined, done: true },函数已结束
4. 生成器的使用场景

生成器有多种应用场景,以下是一些常见的用法:

4.1 生成迭代序列

生成器非常适合用于生成无限序列或自定义的迭代器:

function* fibonacci() {
    let a = 0, b = 1;
    while (true) {
        yield a;
        [a, b] = [b, a + b]; // 生成下一个 Fibonacci 数
    }
}

const fib = fibonacci();
console.log(fib.next().value); // 输出: 0
console.log(fib.next().value); // 输出: 1
console.log(fib.next().value); // 输出: 1
console.log(fib.next().value); // 输出: 2

4.2 改善异步编程

生成器可以与 Promise 搭配使用,为异步代码提供更直观的控制风格,尽管这通常需要一个运行时(如 co.js)来执行生成器:

function* fetchData() {
    const data1 = yield fetch('url1'); // 暂停并等待 Promise
    const data2 = yield fetch('url2');
    return [data1, data2];
}

// 假设我们有一个函数来处理 Promise
function run(generator) {
    const iterator = generator();

    function handle(result) {
        if (result.done) return result.value; // 或处理返回结果

        result.value.then(data => {
            handle(iterator.next(data)); // 传递数据到下一个 yield
        });
    }

    handle(iterator.next()); // 暂停执行
}

run(fetchData);

4.3 生成器可以取消产生的值

通过在生成器的运行期间传递参数,可以通过 next() 或 return() 方法向生成器返回数据:

function* counter() {
    let count = 0;
    while (true) {
        const increment = yield count; // 暂停并返回当前计数
        if (increment) count += increment; // 增加计数
    }
}

const countGen = counter();
console.log(countGen.next().value); // 输出: 0
console.log(countGen.next(5).value); // 输出: 5
console.log(countGen.next().value); // 输出: 5
console.log(countGen.next(3).value); // 输出: 8
5. 生成器的优势和注意事项
  • 状态机:生成器可以用作状态机,通过维护状态来控制流程。
  • 更简单的代码:使用生成器可以让异步代码更清晰,而不需要混乱的回调层级。
  • 性能注意:生成器是懒操作的,这意味着只有在调用 next() 方法时,生成器才会继续执行。
6. 总结

生成器是 ES6 中非常强大且灵活的特性,适合处理复杂的异步操作和自定义的迭代逻辑。它们提供了一种简便的方式来管理状态和控制代码执行流程,尤其在处理长时间运行的操作时显得尤为重要。通过理解和使用生成器,开发者可以编写更高效且易读的代码。

十四. Map 数据结构

ES6(ECMAScript 2015)引入了一种新的数据结构——Map。Map 是一种集合,它存储键值对,键和值都可以是任意类型的对象,包括基本数据类型(如字符串、数字等)和引用类型(如对象、数组等)。

1.Map 的特点:
  1. 有序性:Map 中的键值对是有序的,会按照插入的顺序进行迭代。
  2. 键的唯一性:Map 中的每个键都是唯一的。如果使用相同的键插入新的值,那么之前的值会被替换。
  3. 任意类型的键:与对象不同,Map 允许使用任何类型的键,而不仅仅是字符串或符号。
2.创建 Map

可以使用 new Map() 创建一个 Map 实例。此外,也可以通过传入一个可迭代对象(如数组)来初始化 Map:

const map1 = new Map();
const map2 = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);
3.添加和获取元素
  • 添加元素:使用 set(key, value) 方法。
  • 获取元素:使用 get(key) 方法。
map1.set('name', 'Alice');
console.log(map1.get('name')); // 输出: Alice

4.删除元素

  • 使用 delete(key) 方法来删除指定键的值。
  • 使用 clear() 方法可以清空整个 Map。
map1.delete('name'); // 删除 key 为 'name' 的元素
map1.clear(); // 清除所有元素
5.查询元素

可以使用 has(key) 方法检查 Map 中是否存在某个键:

console.log(map1.has('name')); // 输出: false
6.迭代 Map

Map 提供了多种方法来迭代其中的元素:

  • keys():返回一个迭代器,包含 Map 中的所有键。
  • values():返回一个迭代器,包含 Map 中的所有值。
  • entries():返回一个迭代器,包含 Map 中的所有键值对。
for (const key of map2.keys()) {
  console.log(key); // 输出: key1, key2
}

for (const value of map2.values()) {
  console.log(value); // 输出: value1, value2
}

for (const [key, value] of map2.entries()) {
  console.log(`${key}: ${value}`); // 输出: key1: value1, key2: value2
}
7.Map 的大小

可以使用 size 属性获取 Map 中元素的数量:

console.log(map2.size); // 输出: 2
8.总结

Map 是一种功能强大且灵活的数据结构,适用于需要频繁插入和删除操作的场景以及需要保留元素插入顺序的情况。与对象相比,Map 提供了更好的性能和更灵活的键类型支持。

十五. Set 数据结构

ES6(ECMAScript 2015)引入了 Set 数据结构,它是一种集合,允许存储唯一的值。Set 的值可以是任意类型的对象,包括基本数据类型(如字符串、数字等)和引用类型(如对象、数组等)。

1.Set 的特点:
  1. 唯一性:Set 中的每个值都是唯一的,如果尝试添加重复的值,它将被忽略。
  2. 无序性:Set 中的元素是无序的,不能通过索引访问。
  3. 可迭代性:Set 是可迭代的,可以使用 for...of 循环进行遍历。
2.创建 Set

可以使用 new Set() 创建一个空的 Set 实例,也可以传入一个可迭代对象(如数组)进行初始化:

const set1 = new Set();
const set2 = new Set([1, 2, 3, 4, 4]); // 4 只会存储一次
3.添加和获取元素
  • 添加元素:使用 add(value) 方法。
  • 获取元素的存在性:使用 has(value) 方法。
set1.add('hello');
console.log(set1.has('hello')); // 输出: true
4.删除元素
  • 使用 delete(value) 方法来删除指定的值。
  • 使用 clear() 方法可以清空整个 Set。
set1.delete('hello'); // 删除 'hello'
set1.clear(); // 清空所有元素
5.查询元素的数量

可以使用 size 属性获取 Set 中元素的数量:

console.log(set2.size); // 输出: 4
6.迭代 Set

Set 提供了几种方法来迭代其中的元素:

  • values():返回一个迭代器,包含 Set 中的所有值。
  • entries():返回一个迭代器,包含 Set 中的所有键值对(在 Set 中,键和值是相同的)。

可以使用 for...of 循环遍历 Set:

for (const value of set2) {
  console.log(value); // 输出: 1, 2, 3, 4
}
7.Set 的运用

Set 特别适合以下场景:

  1. 去重:通过将数组转换为 Set,可以轻松地对数组去重。

    const arr = [1, 2, 2, 3, 4, 4];
    const uniqueArr = [...new Set(arr)]; // [1, 2, 3, 4]
    
  2. 集合运算:可以利用 Set 来进行一些基本的集合运算(如交集、并集等)。

8.总结

Set 是一种非常有用的数据结构,特别适合需要唯一值集合的场景。它的操作简单且性能优越,相较于传统的数组,对于去重和集合操作提供了更高效的方案。

十六. WeakMap 和 WeakSet

ES6 引入了 WeakMap 和 WeakSet 数据结构,它们的设计主要是为了解决某些特殊的内存管理问题。这两种数据结构与 Map 和 Set 类似,但有一些重要的不同之处,特别是在垃圾回收和对象引用方面。

1.WeakMap

1.特点:

  1. 键的弱引用WeakMap 的键是弱引用。这意味着,如果没有其他引用指向一个键所关联的对象,这个键-值对将会被垃圾回收。而值仍然是强引用。
  2. 键必须是对象WeakMap 的键只能是对象,不能是基本数据类型。
  3. 无序性WeakMap 中的元素是无序的,不能通过索引访问。
  4. 不可迭代WeakMap 不能被迭代,因此不提供 keysvalues 或 entries 方法。

2.创建 WeakMap

可以通过 new WeakMap() 创建一个空的 WeakMap。也可以传入一个可迭代对象(数组)进行初始化,但每一对必须是一个包含两个元素的数组,其中第一个元素为键(对象),第二个元素为值:

const weakMap = new WeakMap();

const obj1 = {};
const obj2 = {};
weakMap.set(obj1, 'some data');
weakMap.set(obj2, 'other data');

3.添加和获取元素

  • 添加元素:使用 set(key, value) 方法。
  • 获取元素:使用 get(key) 方法。
console.log(weakMap.get(obj1)); // 输出: 'some data'

4.删除元素

  • 使用 delete(key) 方法来删除指定的键。
  • WeakMap 没有 clear() 方法。
2.WeakSet

1.特点:

  1. 值的弱引用WeakSet 的值是弱引用,类似于 WeakMap 的键。当没有其他引用指向该对象时,它将被垃圾回收。
  2. 值必须是对象WeakSet 中的值也只能是对象,不能是基本数据类型。
  3. 无序性WeakSet 中的元素是无序的,不能通过索引访问。
  4. 不可迭代WeakSet 不能被迭代,因此不提供 adddelete, 或 has 方法的迭代相关的方法。

2.创建 WeakSet

可以通过 new WeakSet() 创建一个空的 WeakSet。同样,可以传入一个可迭代对象(数组)进行初始化,但每个元素必须是对象:

const weakSet = new WeakSet();

const obj1 = {};
const obj2 = {};
weakSet.add(obj1);
weakSet.add(obj2);

3.添加和获取元素

  • 添加元素:使用 add(value) 方法。
  • 查询元素的存在性:使用 has(value) 方法。
console.log(weakSet.has(obj1)); // 输出: true

4.删除元素

  • 使用 delete(value) 方法来删除指定的值。
  • WeakSet 也没有 clear() 方法。
3.WeakMap 和 WeakSet 的应用场景
  1. 内存管理:使用 WeakMap 和 WeakSet 来防止内存泄漏。当需要将对象与某些数据绑定时,如果这些对象不再使用,它们可以被垃圾回收,避免不必要的内存占用。

  2. 缓存机制:在某些缓存机制中,可以使用 WeakMap 存储对象的元数据,只要对象被垃圾回收,其相关数据也会被清除。

  3. 事件处理:在事件监听中,可以使用 WeakMap 绑定事件处理函数,这样当对象被删除时,事件处理程序可以自动解除绑定。

4.总结

WeakMap 和 WeakSet 提供了对对象的弱引用,与 Map 和 Set 相比,它们避免了潜在的内存泄漏问题,适用于需要动态管理对象生命周期的场景。由于它们的键或值是弱引用,因此在设计应用时使用它们可以更有效地管理内存。

十七. Array.from

Array.from 是 ES6 新增的一个静态方法,用于将类似数组的对象或可迭代对象转换为数组。这个方法非常灵活,支持将多种数据结构转换为数组,常用于处理伪数组(如 NodeList、arguments 等)和其他可迭代对象(如 Set、Map 等)。

1.语法
Array.from(arrayLike, mapFunction, thisArg)
  • arrayLike:必需。要转换成数组的类似数组或可迭代对象。
  • mapFunction:可选。一个映射函数,用于对每个元素进行处理。
  • thisArg:可选。执行回调函数时的 this 值。
2.基本用法
  1. 将伪数组转为数组

伪数组是指类似数组的对象,例如 argumentsNodeList 等。使用 Array.from() 方法可以方便地将其转换为真正的数组。

function example() {
  const args = Array.from(arguments);
  console.log(args);
}

example(1, 2, 3); // 输出: [1, 2, 3]
  1. 将字符串转换为数组

字符串本质上是可迭代的,使用 Array.from() 可以轻松将字符串转换为字符数组。

const str = "hello";
const charArray = Array.from(str);
console.log(charArray); // 输出: ['h', 'e', 'l', 'l', 'o']
  1. 将 Set 和 Map 转换为数组

可以直接将 Set 和 Map 对象转换为数组。

const set = new Set([1, 2, 3]);
const setArray = Array.from(set);
console.log(setArray); // 输出: [1, 2, 3]

const map = new Map([[1, 'one'], [2, 'two']]);
const mapArray = Array.from(map); // 转换为二维数组
console.log(mapArray); // 输出: [[1, 'one'], [2, 'two']]
  1. 使用映射函数

mapFunction 参数允许对每个元素进行处理,可以用来在转换数组的同时对元素进行变换。

const numbers = [1, 2, 3];
const doubled = Array.from(numbers, x => x * 2);
console.log(doubled); // 输出: [2, 4, 6]
3.使用示例
  1. 从数组类对象创建数组
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

const arr = Array.from(arrayLike);
console.log(arr); // 输出: ['a', 'b', 'c']
  1. 使用 thisArg

可以通过 thisArg 参数为 mapFunction 提供一个 this 绑定。

const obj = {
  multiplier: 2
};

const numbers = [1, 2, 3];
const results = Array.from(numbers, function(x) {
  return x * this.multiplier;
}, obj);

console.log(results); // 输出: [2, 4, 6]
4.注意事项
  • Array.from 不会修改原始对象,返回的是一个新数组。
  • 它可以处理任何可迭代对象,不仅限于数组或伪数组。
5.总结

Array.from 是一个功能强大的方法,用于将类似数组的对象和可迭代对象转换为数组。它允许传入可选的映射函数,使得在转换过程中对元素进行处理变得轻松。这使得 Array.from 成为处理数据结构转换时非常有用的工具。

十八. Array.of

Array.of 是 ES6 引入的一个静态方法,用于创建一个新的数组实例,而不论参数的数量或类型。它可以接受任意数量的参数,并将这些参数转换为数组中的元素。这为数组的创建提供了更大的灵活性。

1.语法
Array.of(element1, element2, ..., elementN)
  • element1, element2, ..., elementN:需要添加到新数组中的元素,可以是任意类型。
2.特点
  1. 不同于 Array 构造函数Array.of 和 Array 构造函数在参数处理上有显著不同。例如,Array 构造函数接收单个数字参数时,将创建一个指定长度的空数组,而 Array.of 总是创建含有元素的新数组。

    // Array.of
    console.log(Array.of(7)); // 输出: [7]
    console.log(Array.of()); // 输出: []
    
    // Array 构造函数
    console.log(Array(7)); // 输出: [empty × 7]
    console.log(Array()); // 输出: []
    
  2. 可以处理任意类型的参数Array.of 能够接受任意数量的参数,包括数字、字符串、对象等。

3.用法示例
  1. 创建数组
const arr1 = Array.of(1, 2, 3);
console.log(arr1); // 输出: [1, 2, 3]

const arr2 = Array.of('a', 'b', 'c');
console.log(arr2); // 输出: ['a', 'b', 'c']

const arr3 = Array.of(true, null, {});
console.log(arr3); // 输出: [true, null, {}]
  1. 创建空数组
const emptyArray = Array.of();
console.log(emptyArray); // 输出: []
  1. 创建包含多个不同类型的元素的数组
const mixedArray = Array.of(1, 'hello', { name: 'Alice' }, [1, 2, 3]);
console.log(mixedArray); // 输出: [1, 'hello', { name: 'Alice' }, [1, 2, 3]]
  1. 使用其他数据类型
const numberArray = Array.of(1, 2, 3);
const stringArray = Array.of('one', 'two', 'three');
console.log(numberArray); // 输出: [1, 2, 3]
console.log(stringArray); // 输出: ['one', 'two', 'three']
4.总结

Array.of 提供了一种简单而灵活的方式来创建数组,无需担心是否提供了数字参数或其他数据类型。这使得它在处理各种数据输入时非常方便,能够有效地创建包含任意类型元素的新数组。与传统的 Array 构造函数相比,Array.of 解决了参数处理的歧义性,使得数组的创建更加直观。

十九. Array.prototype.find

Array.prototype.find 是 ES6 引入的一个数组方法,用于查找满足条件的数组元素。它返回数组中第一个满足提供的测试函数的元素,如果没有找到满足条件的元素,则返回 undefined

1.语法
array.find(callback(element[, index[, array]])[, thisArg])
  • callback: 一个函数,执行的测试函数。它接受三个参数:
    • element: 当前正在处理的数组元素。
    • index: 当前正在处理的数组元素的索引(可选)。
    • array: 原数组(可选)。
  • thisArg: 可选。当执行 callback 时用作 this 的值。
2.返回值

find 方法返回数组中第一个满足条件的元素;如果没有满足条件的元素,返回 undefined

3.特点
  1. 只返回第一个满足条件的元素find 方法在找到满足条件的元素后会立即返回,不会继续遍历后续的元素。
  2. 不会改变原数组find 方法不会对原始数组进行任何更改。
  3. 支持可选参数:可以提供 thisArg 以指定 callback 中 this 的值。
4.例子
  1. 基本用法
const numbers = [5, 12, 8, 130, 44];

const found = numbers.find(element => element > 10);
console.log(found); // 输出: 12
  1. 对象数组查找

可以用 find 方法在对象数组中查找特定条件的对象。

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
];

const user = users.find(user => user.id === 2);
console.log(user); // 输出: { id: 2, name: 'Bob' }
  1. 使用索引和原数组参数

find 的回调函数还可以使用索引和原数组。

const numbers = [4, 9, 16, 25];

const found = numbers.find((element, index, array) => {
  console.log(`Index: ${index}, Value: ${element}`);
  return element > 10;
});
console.log(found); // 输出: 16
  1. 未找到元素的情况

如果没有找到满足条件的元素,返回 undefined

const animals = ['dog', 'cat', 'rabbit'];

const result = animals.find(animal => animal === 'lion');
console.log(result); // 输出: undefined
5.总结

Array.prototype.find 方法是一个非常实用的数组工具,用于快速查找满足条件的元素。它使得在数组中查找特定元素的操作变得简单且高效。借助这个方法,开发者能够更容易地处理数组数据,增强代码的可读性和可维护性。

二十. Array.prototype.fill

Array.prototype.fill 是 ES6 引入的一个数组方法,用于用静态值填充数组的一部分。这个方法会修改原数组并返回该数组。

1.语法
arr.fill(value[, start[, end]])
  • value: 必需。用于填充数组的值。
  • start: 可选。填充的开始索引,默认为 0。如果是负值,start 会从数组尾部计算。
  • end: 可选。填充的结束索引,默认为数组的长度。结束索引是非包含的,即不包括该索引对应的元素。如果是负值,会从数组尾部计算。
2.返回值

返回被填充后的数组。

3.特点
  1. 修改原数组fill 方法会直接修改调用它的数组。
  2. 支持负索引start 和 end 参数可以为负值,表示从数组的末尾开始计算。
  3. 可以用于填充部分数组:可通过指定 start 和 end 来限制填充的范围。
4.用法示例
  1. 基本用法
const arr = new Array(5).fill(0);
console.log(arr); // 输出: [0, 0, 0, 0, 0]
  1. 填充部分数组
const arr = [1, 2, 3, 4, 5];
arr.fill(0, 1, 4);
console.log(arr); // 输出: [1, 0, 0, 0, 5]
  1. 负索引

使用负索引从数组的末尾开始填充。

const arr = [1, 2, 3, 4, 5];
arr.fill(0, -3, -1);
console.log(arr); // 输出: [1, 2, 0, 0, 5]
  1. 填充整个数组

如果没有指定 start 和 end,则会填充整个数组。

const arr = [1, 2, 3];
arr.fill(9);
console.log(arr); // 输出: [9, 9, 9]
  1. 填充不同类型的值

可以使用不同的数据类型作为填充值。

const arr = [1, 2, 3];
arr.fill('a');
console.log(arr); // 输出: ['a', 'a', 'a']

const arr2 = [1, 2, 3];
arr2.fill(true);
console.log(arr2); // 输出: [true, true, true]
5.注意事项
  • fill 方法并不适用于 undefined 的填充,因为若源数组本来就有 undefined 的元素则会被填充的值覆盖。
  • fill 方法对于空数组、数组边界和负索引的处理有其特别之处。
6.总结

Array.prototype.fill 方法提供了一种简单而有效的方式来填充数组,能够快速将数组中的元素替换成指定的值。它尤其适合在需要对大规模数组进行初始化或重置时使用。通过灵活使用 start 和 end 参数,开发者可以精确控制填充的范围,满足不同的需求。

以上是对 ES6 中 20 个新特性的详细介绍以及知识拓展,帮助理解这些特性的应用和优势。

标签:ES6,20,log,迭代,输出,数组,console,完整版,const
From: https://blog.csdn.net/fengyiyangdeli/article/details/143730038

相关文章

  • 【会议资讯】第六届建筑学研究前沿与生态环境国际研讨会(ARFEE 2024)
    第六届建筑学研究前沿与生态环境国际研讨会(ARFEE2024)20246th InternationalSymposiumonArchitectureResearchFrontiersandEcologicalEnvironment第六届建筑学研究前沿与生态环境国际研讨会(ARFEE2024)由中南大学土木工程学院主办,将于2024年12月27-29日在中国三亚召......
  • 一同掀起人工智能艺术新浪潮!无问芯穹即将出席2024东京ComfyUI全球社区峰会
    ......
  • 2024 同一个网段,反弹shell四种方法【linux版本】bash、python、nc、villian反弹shell
    实验环境准备(同一个网段下,我是桥接的虚拟机)一、bash反弹shell二、python反弹shell三、nc反弹shell四、villain反弹shell实验环境准备(同一个网段下,我是桥接的虚拟机)      一台kali的linux(攻击者)        一台centos7/debian[另一个linux](受害者)一、b......
  • 空气开关(空气断路器)根据额定电流的不同,可以选择不同规格的开关。家用230V电路中,常见的
    空气开关(空气断路器)根据额定电流的不同,可以选择不同规格的开关。家用230V电路中,常见的额定电流规格有6A、10A、16A、20A、25A、32A、40A、50A、63A等。这些规格的空气开关主要区别在于它们适应的电流负荷大小,从而保护不同功率的家用电器和电路。以下是这些常见规格的比较表格:......
  • [2024.11.13]NOIP 模拟赛
    T1怎么自然溢出被卡了啊(upd:不是哈希被卡了,是大数据里塞小数据被坑了)T2怎么看不清题目要求啊T3怎么都记得欧拉定理啊T4怎么暴力全机房就我一个写挂了啊……赛时T1题目上说是背包,但是数据范围给到了\(2^{18000}\),所以一眼是结论题。题目上\(a_i\)全部互质的条件很独特,所以我......
  • 2024年入职/转行网络安全,该如何规划?_网络安全职业规划
     前言前段时间,知名机构麦可思研究院发布了 《2022年中国本科生就业报告》,其中详细列出近五年的本科绿牌专业,其中,信息安全位列第一。网络安全前景对于网络安全的发展与就业前景,想必无需我多言,作为当下应届生收入较高的专业之一,网络安全同样也在转行领域中占据热门位置,主要......
  • 2024年入职/转行网络安全,该如何规划?_网络安全职业规划
     前言前段时间,知名机构麦可思研究院发布了 《2022年中国本科生就业报告》,其中详细列出近五年的本科绿牌专业,其中,信息安全位列第一。网络安全前景对于网络安全的发展与就业前景,想必无需我多言,作为当下应届生收入较高的专业之一,网络安全同样也在转行领域中占据热门位置,主要......
  • 2024最新网络安全自学路线,内容涵盖3-5年技能提升
     01什么是网络安全网络安全可以基于攻击和防御视角来分类,我们经常听到的“红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性,例如Web安全技术,既有Web渗透,也......
  • 【JetBrains Rider 2024软件下载与安装教程】
    1、安装包Rider2024:链接:https://pan.quark.cn/s/f3b3360dccc0提取码:Z8gARider-2023.3.2:链接:https://pan.quark.cn/s/82b63a1e0df3提取码:XdA82、安装教程(建议关闭杀毒软件)1)       双击下载安装包exe文件安装,弹窗安装对话框  2)       点击下一步......
  • 鸿蒙高质量代码静态检测200条三
    @performance/hp-arkts-no-use-any-export-current避免使用export*导出当前module中定义的类型和数据@performance/hp-arkts-no-use-any-export-other避免使用export*导出其他module中定义的类型和数据@performance/hp-arkui-avoid-empty-callback避免设置空的系统......