• 中级前端面试整理-上篇


    JS的基本类型

    JavaScript中的基本类型是指:那些存储在内存中的值类型,他们是不可以变的、意味着一旦创建,值类型就不能改变。

    JS的7种基本类型

    • 布尔类型(Boolean): 表示true或者false
    • 数字类型(Number): 表示整数、浮点或者NaN
    • 字符串类型(String): 表示一串字符串
    • 空(Null): 表示一个空置。
    • 未定义(undefined): 表示一个未赋值的变量
    • Symbol: 表示一个独一无二的值(ES6新增)
    • BigInt: 表示一个任意精度的整数(ES11新增)

    详细介绍

    布尔值(Boolean)

    布尔值只有两个:true或者false。它们通常用于表示条件或开关。

    例如:

    const isLoggedIn = true;
    
    if (isLoggedIn) {
      // 用户已登录
    } else {
      // 用户未登录
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    数字(Number)

    数字可以是整数、浮点数或者NaN。

    • 整数:例如 123 等。
    • 浮点数:例如 1.23.14-0.5 等。
    • NaN:表示一个非数字值。

    例如:

    const age = 25;
    const pi = 3.14;
    const invalidNumber = NaN;
    
    console.log(age + pi); // 28.14
    console.log(invalidNumber / 2); // NaN
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    字符串(String)

    字符串是一串字符的集合。它们可以用单引号、双引号或反引号括起来。

    例如:

    const name = "John Doe";
    const message = "Hello, world!";
    const multilineString = `This is a
    multiline string.`;
    
    console.log(name + " says " + message); // John Doe says Hello, world!
    console.log(multilineString);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    空(Null)

    空值表示一个空值。它与未定义不同,未定义表示一个变量未赋值。

    例如:

    const variable = null;
    
    console.log(variable === undefined); // false
    console.log(variable === null); // true
    
    • 1
    • 2
    • 3
    • 4
    未定义(Undefined)

    未定义表示一个变量未赋值。

    例如:

    let variable;
    
    console.log(variable); // undefined
    
    • 1
    • 2
    • 3
    Symbol

    Symbol是ES6引入的一种新的数据类型,它表示一个独一无二的值。Symbol值通常用于表示对象的属性键(key)或者私有成员。

    例如:

    const symbol1 = Symbol();
    const symbol2 = Symbol();
    
    console.log(symbol1 === symbol2); // false
    
    const object = {
      [symbol1]: "Hello",
      [symbol2]: "World"
    };
    
    console.log(object[symbol1]); // Hello
    console.log(object[symbol2]); // World
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    BigInt

    BigInt 是 ES2020(ES11) 中引入的一种新数据类型。它表示一个任意精度的整数。

    例如:

    const bigInt = 12345678901234567890n;
    
    console.log(bigInt.toString()); // 12345678901234567890
    
    • 1
    • 2
    • 3

    BigInt的扩展

    BigInt 是 JavaScript 中的一种基本类型,用于表示任意精度的整数。它可以用来表示超出了 Number 类型能够表示范围的整数值,即 ±(253-1)(即 Number.MAX_SAFE_INTEGER)以外的整数。BigInt 是在 ECMAScript 2020(ES11)中引入的新特性。

    BigInt 可以通过在数字后面加上 n 后缀来创建,例如:

    const bigIntNumber = 1234567890123456789012345678901234567890n;
    
    • 1

    在这个示例中,1234567890123456789012345678901234567890n 就是一个 BigInt 类型的整数。

    由于 BigInt 表示的整数没有范围限制,因此可以用来处理大整数的运算,例如在加密算法、数值计算等领域有着广泛的应用。

    尽管 BigInt 和 Number 类型很相似,但是它们之间并不是完全兼容的。BigInt 不能直接和 Number 类型进行混合运算,需要通过 BigInt 的方法和运算符来进行操作。另外,BigInt 也不支持使用 Math 对象中的方法。

    BigInt 支持的操作和运算符包括:加法(+)、减法(-)、乘法(*)、除法(/)、取余(%)等,以及比较运算符(><>=<======!=!==)等。此外,BigInt 还提供了一些用于操作和转换的方法,如 BigInt.prototype.toString()BigInt.prototype.valueOf() 等。

    需要注意的是,由于 BigInt 是在 ES11 中引入的新特性,因此在一些较旧的浏览器中可能不被支持。在使用 BigInt 时,应该确保你的运行环境支持该特性,或者使用适当的 polyfill 或转译工具进行兼容处理。

    JS的引用类型

    在JavaScript中引用类型是指:那些存储在堆内存中的对象,它们是可变的,这意味这一旦创建,他们的值就可以被改变。

    JS的常见引用类型:

    • 对象(Object)
    • 数组(Array)
    • 函数(Function)
    • 日期(Date)
    • 正则表达式(RegExp)
    • 错误(Error)
    • 其他内置对象

    详细介绍

    对象(Object)

    对象是 JavaScript 中最基本的引用类型,它是一种无序的集合,由键值对(key-value pairs)组成。对象的键是字符串类型,值可以是任意类型的数据,包括基本类型和其他引用类型。

    例如:

    const person = {
      name: "John Doe",
      age: 25,
      address: {
        city: "New York",
        state: "NY",
        zip: "10001"
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    数组(Array)

    数组是一种特殊的对象,用来存储有序的数据集合。数组的每个元素可以是任意类型的数据,包括基本类型和其他引用类型。

    例如:

    const numbers = [1, 2, 3, 4, 5];
    const fruits = ["apple", "banana", "orange"];
    
    • 1
    • 2
    函数(Function)

    函数是一种特殊的对象,它可以被调用执行代码。函数可以接受参数并返回值,也可以作为对象的属性和方法使用。

    例如:

    function add(a, b) {
      return a + b;
    }
    
    const result = add(1, 2); // 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    日期(Date)

    Date 类型用来表示日期和时间。它是 JavaScript 中的内置对象,可以创建表示特定日期和时间的实例。

    例如:

    const date = new Date();
    console.log(date.toString()); // "Thu Feb 16 2023 20:07:12 GMT+0800 (中国标准时间)"
    
    • 1
    • 2
    正则表达式(RegExp)

    正则表达式是一种用于匹配字符串模式的对象。它可以用来搜索、替换和提取字符串中的特定部分。

    例如:

    const regex = /\d+/g;
    const matches = regex.exec("The answer is 42"); // ["42"]
    
    • 1
    • 2
    错误(Errror)

    错误对象表示程序运行时发生的错误。

    例如:

    try {
      // 这里可能会发生错误
    } catch (error) {
      // 处理错误
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JS中引用类型的特殊性

    • 引用: 引用类型的值是通过引用传递的。这意味着对引用类型的赋值实际上是复制了对该对象的引用,而不是复制对象的本身。
    • 可变性: 引用类型的值是可以改变的。对引用类型属性的更改会影响所有引用该对象的变量。
    • 原型: 每个引用类型都具有一个原型对象,该对象包含该类型的所有默认属性和方法。

    箭头函数和普通函数的区别

    箭头函数和普通函数的区别主要包括:语法、this绑定、arguments对象、构造函数、prototype等5个方面有区别;

    • 语法

      箭头函数使用"=>"来定义函数,普通函数则使用function关键字来进行定义。

      例如:

    function add(a, b) {
        return a + b;
    }
    
    const add = (a, b) => a + b;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • this指向

      箭头函数的this指向始终是定义时的上下文对象,而普通函数的this指向会根据调用方式发生变化。

      例如:

    const obj = {
      name: 'obj',
      add: function(a, b) {
        console.log(this.name);
        return a + b;
      }
    };
    
    const add = (a, b) => {
      console.log(this.name);
      return a + b;
    };
    
    obj.add(1, 2); // 'obj'
    add(1, 2); // undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • arguments对象

      箭头函数中没有arguments对象,而普通函数中则可以通过arguments对象访问函数的参数列表。

    ​ 例如:

    const add = function(a, b) {
      console.log(arguments);
      return a + b;
    };
    
    const add = (a, b) => {
      console.log(arguments); // ReferenceError: arguments is not defined
      return a + b;
    };
    
    add(1, 2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 构造函数

      箭头函数不能作为构造函数使用,而普通函数则可以。

      例如:

    const Person = function(name) {
      this.name = name;
    };
    
    const Person = () => {
      this.name = name;
    };
    
    const person = new Person('John'); // TypeError: Arrow functions cannot be used as constructors
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • prototype属性

      箭头函数没有prototype属性,而普通函数则可以通过prototype属性为函数添加方法。
      例如:

    const Person = function(name) {
      this.name = name;
    };
    
    Person.prototype.sayHello = function() {
      console.log(`Hello, my name is ${this.name}`);
    };
    
    const person = new Person('John');
    person.sayHello(); // 'Hello, my name is John'
    
    const Person = () => {
      this.name = name;
    };
    
    const person = new Person('John');
    person.sayHello(); // TypeError: person.sayHello is not a function
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    总结来说,箭头函数和普通函数各有优缺点。箭头函数语法更加简洁,this指向更加明确,但不能作为构造函数使用,没有prototype属性,也不能使用yield命令。普通函数功能更加强大,但语法更加繁琐,this指向容易混淆。在实际开发中,应根据具体需求选择合适的函数定义方式。

    深拷贝和浅拷贝的区别

    浅拷贝和深拷贝是常见的两种不同的对象复制方式。它们的主要区别在于浅拷贝只复制对象的引用,深拷贝则会复制对象的整个值,包括所有的属性和子对象。

    浅拷贝

    浅拷贝是指将一个对象的引用赋值给另一个对象,这就意味着两个对象将会指向同一个内存地址,如果修改一个对象的属性,另一个对象的属性也会随之改变。

    深拷贝

    深拷贝是指创建一个新的对象,并将其所有属性和子对象的值复制到一个新的对象中。这意味着两个对象拥有完全不同的内存地址,互不影响。

    浅拷贝和深拷贝区别

    区别浅拷贝深拷贝
    复制方式复制引用复制值
    内存地址相同不同
    修改影响相互影响互不影响

    例如:

    // 原对象
    const originalObj = {
        name: 'John',
        age: 30,
        hobbies: ['reading', 'music']
    };
    
    // 浅拷贝
    const shallowCopyObj = Object.assign({}, originalObj);
    shallowCopyObj.hobbies.push('sports');
    
    console.log(originalObj.hobbies); // ['reading', 'music', 'sports']
    console.log(shallowCopyObj.hobbies); // ['reading', 'music', 'sports']
    
    // 深拷贝
    const deepCopyObj = JSON.parse(JSON.stringify(originalObj));
    deepCopyObj.hobbies.push('drawing');
    
    console.log(originalObj.hobbies); // ['reading', 'music', 'sports']
    console.log(deepCopyObj.hobbies); // ['reading', 'music', 'drawing']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如何实现深拷贝

    在 JavaScript 中,可以通过以下几种方式实现深拷贝:

    • 使用 JSON.parse() 和 JSON.stringify() 方法
    • 使用递归函数
    • 使用第三方库
    JSON.parse() 和 JSON.stringify() 方法

    JSON.parse() 和 JSON.stringify() 方法可以将对象转换为 JSON 字符串,然后再将 JSON 字符串解析为对象。这种方法可以实现深拷贝,但需要注意的是,它会忽略对象的原型和循环引用。

    递归函数

    使用递归函数可以实现深拷贝。递归函数会遍历对象的每个属性,并将其值复制到新对象中。这种方法可以实现深拷贝,但需要注意的是,它可能会递归调用,导致性能问题。

    例如:

    function deepCopy(obj, visited =  new WeakMap()) {
        // 基本类型或 null,直接返回
        if (typeof obj !== 'object' || obj === null)   return obj;
        
        // 如果已拷贝的对象则直接进行返回
        if (visited.has(obj)) return visited.get(obj);
        
        // 对日期类型和正则表达式进行特殊处理,直接复制构造函数
        if (obj instanceof Date || obj instanceof RegExp) return new obj.constructor(obj);
        
        let copy = Array.isArray(obj) ? [] : {}; // 创建目标对象
        
        visited.set(obj, copy); // 记录已拷贝的对象
        
        for (let key in obj) { // 遍历对象的属性
            // 只复制自身属性,非原型链上的属性
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                copy[key] = deepCopy(obj[key], visited); // 递归拷贝
            }
        }
        
        return copy;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    第三方库

    可以使用第三方库来实现深拷贝。例如,Underscore.js 和 Lodash 都提供了深拷贝的方法。

    类组件和函数式组件的区别

    在React中,有两种创建组件的方式,一种式类组件,一种是函数式组件。类组件是使用ES6类创建的,而函数是组件是使用JavaScript函数进行创建的。

    类组件

    使用ES6类进行创建,并集成于React.Component类。同时具有一下特点:

    • 具有生命周期
    • 可以使用状态
    • 可以使用this关键字访问组件示例
    函数式组件

    函数式组件是React中较新类型的组件,React 16.8新增的特性。它使用JS函数进行创建,但是没有生命周期和状态。

    • 没有生命周期
    • 没有状态
    • 不能使用this访问组件实例
    类组件和函数式组件的区别

    以下是类组件和函数式组件的主要区别:

    特性类组件函数式组件
    创建方式使用 ES6 类使用 JavaScript 函数
    继承继承自 React.Component
    生命周期方法具有生命周期方法无生命周期方法
    状态可以使用状态不能使用状态
    this 关键字可以使用 this 关键字访问组件实例不能使用 this 关键字访问组件实例
    性能性能略差性能略好
    可读性可读性略差可读性略好
    选择类组件还是函数式组件

    因该考虑以下几点因素:

    • 组件是否需要状态
    • 组件是否需要生命周期方法
    • 组件的复杂度
    • 代码的可读性

    闭包的理解

    在JS中,闭包是指一个函数可以访问其外部函数的作用域中的变量,换句话说,闭包使函数能够记住其创建时的环境。

    闭包的形成

    闭包是在函数内部定义另一个函数时形成的,内部函数可以访问外部函数作用域中的变量,即使外包函数已经执行完毕。

    闭包的示例
    function outer() {
      let count = 0;
    
      function inner() {
        count++;
        console.log(count);
      }
      return inner;
    }
    
    const fn = outer();
    
    fn(); // 1
    fn(); // 2
    
    console.log(count); // undefined
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这个例子中,inner 函数是一个闭包,它可以访问外部函数 outer 中的变量 count。即使 outer 函数已经执行完毕,inner 函数仍然可以访问 count 变量。

    闭包的优点
    • 可以进行封装代码,提高代码的可重用性
    • 可以实现私有变量,提高代码的安全性
    • 可以模拟面向对象编程中的类
    闭包的缺点
    • 可能会导致内存泄漏
    • 可能会使代码变得难以理解
    闭包的应用场景
    • 实现延迟执行

      function delay(fn, delay) {
        return setTimeout(fn, delay);
      }
      
      const fn = () => console.log("Hello");
      
      delay(fn, 1000); // 1秒后输出 "Hello"
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 模拟面向对象编程中的类

      function Person(name) {
        this.name = name;
      
        const sayHello = () => console.log(`Hello, my name is ${this.name}`);
      
        return {
          sayHello,
        };
      }
      
      const person = new Person("John Doe");
      
      person.sayHello(); // 输出 "Hello, my name is John Doe"
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    • 创建私有变量

      function counter() {
        let count = 0;
      
        function increment() {
          count++;
        }
      
        function getCount() {
          return count;
        }
      
        return {
          increment,
          getCount,
        };
      }
      
      const counter = counter();
      
      counter.increment();
      counter.increment();
      
      console.log(counter.getCount()); // 2
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    • 实现事件监听

      function addEventListener(element, type, listener) {
        element.addEventListener(type, listener);
      
        return () => element.removeEventListener(type, listener);
      }
      
      const element = document.getElementById("button");
      
      const removeListener = addEventListener(element, "click", () => console.log("Button clicked"));
      
      // 移除事件监听
      removeListener();
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

    Promise详解

    Promise是ES6中引入的一种用于处理异步操作的对象。它代表了一个异步操作的最终完成(或者失败)及其结果的表示。

    Promise状态

    • Pending(进行中): 初始状态,表示异步操作尚未完成,正在进行中。
    • Fulfilled(已完成): 表示异步操作已经完成
    • Rejected(已拒绝): 表示异步操作失败

    Promise状态更改

    Promise的状态只能由resolve和reject进行更改。

    • resolve函数将Promise的状态更改为Fulfilled,并且传入一个结果作为值。
    • reject函数将Promise的状态更改为Rejected,并转入一个错误对象作为原因。

    Promise状态改变之后不可以逆转!

    Promise状态实列:

    const promise =  new Promise((resolve, reject) => {
        setTimeOut(() => {
            resolve('success');
        }, 1000);
    });
    
    promise.then(
        (reslut) => {
            console.log(reslut); // 'success'
        }, 
        (error) => {
            console.log(error);
        }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ​ 在这个例子中,Promise 的初始状态是 Pending。1 秒后,resolve 函数被调用,将 Promise 的状态更改为 Fulfilled,并传入 success 作为结果。然后,then 方法的第一个回调函数被调用,并输出 success

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('error');
      }, 1000);
    });
    
    promise.then(
      (result) => {
        console.log(result);
      },
      (error) => {
        console.log(error); // 'error'
      }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ​ 在这个例子中,Promise 的初始状态是 Pending。1 秒后,reject 函数被调用,将 Promise 的状态更改为 Rejected,并传入 error 作为原因。然后,then 方法的第二个回调函数被调用,并输出 error

    Promise场景

    • ajax请求
    • setTimeOut
    • setInterval
    • Web API

    Promise特点

    • 不可变性: 一旦状态变为 Fulfilled 或 Rejected,就不会再改变。
    • 链式调用: 可以通过 then() 方法进行链式调用,依次处理异步操作的成功或失败情况。
    • 错误捕获: 可以通过 catch() 方法捕获异步操作中的错误。

    Promise基本使用

    Promise创建

    const promise = new Promise((resolve, reject) => {
      // 异步操作
    });
    
    • 1
    • 2
    • 3

    new Promise 语句接受一个函数作为参数,该函数有两个参数:

    • resolve:用于将 Promise 的状态更改为 Fulfilled 的函数。
    • reject:用于将 Promise 的状态更改为 Rejected 的函数。
    let promise = new Promise((resolve, reject) => {
        // 异步操作
        if (/* 异步操作成功 */) {
            resolve("操作成功"); // 将 Promise 状态从 Pending 变为 Fulfilled
        } else {
            reject("操作失败"); // 将 Promise 状态从 Pending 变为 Rejected
        }
    });
    
    // 第一种写法(个人喜欢这种写法,使用命名函数来处理成功和失败,可读性高,方便代码重用)
    promise.then((result) => {
        console.log("成功:" + result);
    }).catch((error) => {
        console.error("失败:" + error);
    });
    
    // 第二种写法(使用匿名函数处理成功和失败)
    promise.then(
     (result) => {
        // 处理成功结果
      },
      (error) => {
        // 处理失败结果
      }
    );
    
    // 第三种写法(使用命名函数处理结果)
    function handleSuccess(result) {}
    function handleError(error) {}
    promise.then(handleSuccess, handleError);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    Promise常见用法

    串联执行

    使用then方法可以将多个异步操作串联起来。

    const promise1 = new Promise((resolve, reject) => {
      // 异步操作 1
    });
    
    const promise2 = new Promise((resolve, reject) => {
      // 异步操作 2
    });
    
    promise1.then(() => {
      return promise2;
    }).then((result) => {
      // 处理最终结果
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    也可以理解为嵌套执行。

    Promise 嵌套是指在一个 Promise 实例的 then 方法中,又返回一个新的 Promise 实例。这种嵌套可以用于处理多个异步操作的依赖关系。

    以下是一个简单的 Promise 嵌套示例:

    const promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success1');
      }, 1000);
    });
    
    promise1.then((result) => {
      console.log('promise1 成功fulfilled:', result);
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('success2');
        }, 2000);
      });
    }).then((result) => {
      console.log('promise2 成功fulfilled:', result);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    串联执行的注意事项:

    • Promise 嵌套会导致代码结构变得复杂,因此需要谨慎使用。
    • 在使用 Promise 嵌套时,需要考虑错误处理的情况。
    • 如果嵌套层级过深,可能会导致性能问题。

    Promise 嵌套的替代方案

    • 使用 async/await 语法
    • 使用 Promise.all 方法
    • 使用 Promise 的静态方法 Promise.resolvePromise.reject
    并行执行

    可以使用 Promise.all 方法并行执行多个异步操作。

    const promise1 = new Promise((resolve, reject) => {
      // 异步操作 1
    });
    
    const promise2 = new Promise((resolve, reject) => {
      // 异步操作 2
    });
    
    Promise.all([promise1, promise2]).then((results) => {
      // 处理所有结果
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Promise.all 是 JavaScript 中 Promise 的一个静态方法,用于将多个 Promise 实例包装成一个新的 Promise 实例。该方法会等到所有传入的 Promise 实例都成功 fulfilled 或其中一个 Promise 实例被 rejected 时,才会返回一个新的 Promise 实例。

    const promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success1');
      }, 1000);
    });
    
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success2');
      }, 2000);
    });
    
    const promise3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('error');
      }, 3000);
    });
    
    const allPromise = Promise.all([promise1, promise2, promise3]);
    
    allPromise.then(
      (results) => {
        console.log('所有 Promise 实例都成功fulfilled:', results);
      },
      (error) => {
        console.error('其中一个 Promise 实例被 rejected:', error);
      }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    Promise.all 的返回值

    Promise.all 方法返回的新 Promise 实例的返回值取决于传入的 Promise 实例的状态:

    • 如果所有传入的 Promise 实例都成功 fulfilled,则新的 Promise 实例会成功 fulfilled,并返回一个包含所有 Promise 实例的成功结果的数组。
    • 如果其中一个 Promise 实例被 rejected,则新的 Promise 实例会立即被 rejected,并返回第一个被 rejected 的 Promise 实例的错误原因。

    Promise.all 的注意事项

    • Promise.all 方法只会等待所有传入的 Promise 实例都完成,不会根据传入的顺序执行它们。
    • Promise.all 方法不会改变传入 Promise 实例的状态。
    • 如果传入的 Promise 实例不是一个可迭代对象,则 Promise.all 方法会抛出一个 TypeError 错误

    关于返回结果是否有先后顺序

    Promise.all 方法返回的新 Promise 实例的返回值是一个数组,该数组的顺序与传入 Promise 实例的顺序一致

    但是,请注意Promise.all 方法不会根据传入的顺序执行 Promise 实例。它们是并行执行的,因此它们的完成顺序可能与传入的顺序不同。

    竞速执行

    Promise.race() 是 Promise 提供的一个静态方法,用于在多个 Promise 实例中,只要有一个实例率先改变状态(无论是 Fulfilled 还是 Rejected),就返回那个率先改变状态的 Promise 实例的返回值。换句话说,Promise.race() 用于竞速,返回最先完成的 Promise 的结果或错误。

    let promise1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("promise1 resolved");
        }, 1000);
    });
    
    let promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("promise2 resolved");
        }, 500);
    });
    
    Promise.race([promise1, promise2]).then((result) => {
        console.log(result); // 输出 "promise2 resolved"
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    错误处理

    可以使用 catch 方法处理 Promise 的错误。

    promise.then(
      (result) => {
        // 处理成功结果
      }
    ).catch((error) => {
      // 处理失败结果
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    最终处理

    finally 方法:无论 Promise 成功或失败,finally 方法都会被调用。

    promise.then(
      (result) => {
        // 处理成功结果
      },
      (error) => {
        // 处理失败结果
      }
    ).finally(() => {
      // 无论成功或失败都会执行的代码
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Promise扩展

    PromiseFetchAjax 都是 JavaScript 中用于处理异步操作的技术。它们之间存在一些关键的区别:

    Promise

    Promise 是 JavaScript 中用于处理异步操作的对象。它可以使异步代码更加易读易写,并帮助你避免回调地狱。

    Promise 的主要特点:

    • 可以将异步操作的结果封装成一个对象
    • 可以使用 then 方法来处理异步操作的结果
    • 可以使用 catch 方法来处理异步操作的错误
    • 可以使用 finally 方法来执行一些无论成功或失败都会执行的代码
    Fetch

    Fetch 是 JavaScript 中用于发送 HTTP 请求的 API。它比传统的 XMLHttpRequest API 更易于使用,并提供了更多的功能。

    Fetch 的主要特点:

    • 使用 Promise 来处理请求的结果
    • 支持多种 HTTP 方法
    • 支持请求头和响应头
    • 支持流式数据传输
    Ajax

    Ajax 是 Asynchronous JavaScript and XML 的缩写,是一种使用 JavaScript 在不刷新页面的情况下与服务器通信的技术。

    Ajax 的主要特点:

    • 使用 XMLHttpRequest 对象来发送 HTTP 请求
    • 使用回调函数来处理请求的结果
    • 可以用于发送 GET、POST、PUT、DELETE 等 HTTP 方法
    • 可以用于加载数据、更新页面内容等
    比较
    特性PromiseFetchAjax
    用途处理异步操作发送 HTTP 请求与服务器通信
    返回值Promise 对象Promise 对象XMLHttpRequest 对象
    处理结果then 方法then 方法回调函数
    错误处理catch 方法catch 方法错误回调函数
    优势易读易写,避免回调地狱简洁易用,功能强大兼容性好,支持多种浏览器
    劣势嵌套层级过深会导致性能问题不支持 IE 9 及更早版本代码结构混乱,难以维护
    总结

    Promise、Fetch 和 Ajax 都是用于处理异步操作的技术。它们各有优势,适合不同的场景。

    • 如果需要处理通用的异步操作,可以使用 Promise。
    • 如果需要发送 HTTP 请求,可以使用 Fetch。
    • 如果需要兼容旧浏览器,可以使用 Ajax。

    建议根据实际需求选择合适的技术。

    Vue3中diff算法的认识

    Vue3 Diff 算法详细介绍

    Vue3中的Diff算法是一种用于计算虚拟DOM树差异的算法,它用于在更新数据时尽可能高效地更新真实DOM。

    DIFF算法的核心思想是最小路径更新,即只更新虚拟dom中受数据影响的最小部分。实现步骤:

    1. 深度优先遍历虚拟DOM树:从根节点开始,深度优先遍历虚拟DOM树,同时每个节点生成一个唯一的Key。
    2. 比较新旧虚拟DOM树:使用key匹配新旧虚拟DOM树中的节点,如果节点的 key 相同,则比较它们的属性和子节点。如果节点的 key 不同,则认为该节点已删除或新增。
    3. 生成更新操作列表:根据新旧虚拟 DOM 树的差异,生成一个更新操作列表。更新操作包括:
      • 插入节点
      • 删除节点
      • 更新节点属性
      • 移动节点
    4. 应用更新操作:根据更新操作列表,更新真实 DOM。

    Vue3 Diff 算法相比 Vue2 中的 Diff 算法有以下改进:

    • 更快: 由于采用了“最小路径更新”的思想,Vue3 Diff 算法可以只更新受数据更新影响的最小部分虚拟 DOM 树,因此速度更快。
    • 更少内存使用: Vue3 Diff 算法不再使用“最小公共子序列”算法来比较两个虚拟 DOM 树,因此内存使用更少。
    • 更易于理解: Vue3 Diff 算法的实现更加简单易懂。
    Vue3 Diff 算法示例

    以下示例演示了 Vue3 Diff 算法如何工作:

    <div id="app">
      <p>{{ count }}p>
      <button @click="increment">+button>
    div>
    
    • 1
    • 2
    • 3
    • 4
    const app = Vue.createApp({
      data() {
        return {
          count: 0,
        };
      },
      methods: {
        increment() {
          this.count++;
        },
      },
    });
    
    app.mount("#app");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    初始状态下,虚拟 DOM 树如下:

    <div id="app">
      <p>0p>
      <button>+button>
    div>
    
    • 1
    • 2
    • 3
    • 4

    当用户点击按钮时,count 值增加 1,虚拟 DOM 树变为:

    <div id="app">
      <p>1p>
      <button>+button>
    div>
    
    • 1
    • 2
    • 3
    • 4

    Vue3 Diff 算法会计算两个虚拟 DOM 树之间的差异,并生成更新操作列表。更新操作列表如下:

    [
      {
        type: "update",
        node: {
          id: "app",
        },
        attr: {
          textContent: "1",
        },
      },
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    根据更新操作列表,Vue3 会更新真实 DOM,将文本内容更新为 “1”。

    总结:Vue3 Diff 算法是一种高效的虚拟 DOM 树差异计算算法,它可以使 Vue3 在更新数据时更加高效。

  • 相关阅读:
    7、Docker网络
    Python学习DAY14_文档处理_Excel
    Python8-使用json模块解析JSON文件
    springboot中@Async的使用
    Maven ,命令行中,字符编码 设置
    Python爬虫爬图片测试1
    Spring——依赖注入
    深度学习 RNN架构解析
    基于深度学习的车型识别系统(Python+清新界面+数据集)
    【数据结构】优先级队列(堆)
  • 原文地址:https://blog.csdn.net/qq_26941173/article/details/136475100