中级前端面试整理-上篇

JS的基本类型

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

JS的7种基本类型

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

详细介绍

布尔值(Boolean)

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

例如:

javascript 复制代码
const isLoggedIn = true;

if (isLoggedIn) {
  // 用户已登录
} else {
  // 用户未登录
}

数字(Number)

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

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

例如:

javascript 复制代码
const age = 25;
const pi = 3.14;
const invalidNumber = NaN;

console.log(age + pi); // 28.14
console.log(invalidNumber / 2); // NaN

字符串(String)

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

例如:

javascript 复制代码
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);

空(Null)

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

例如:

javascript 复制代码
const variable = null;

console.log(variable === undefined); // false
console.log(variable === null); // true

未定义(Undefined)

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

例如:

javascript 复制代码
let variable;

console.log(variable); // undefined

Symbol

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

例如:

javascript 复制代码
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

BigInt

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

例如:

javascript 复制代码
const bigInt = 12345678901234567890n;

console.log(bigInt.toString()); // 12345678901234567890

BigInt的扩展

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

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

javascript 复制代码
const bigIntNumber = 1234567890123456789012345678901234567890n;

在这个示例中,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)组成。对象的键是字符串类型,值可以是任意类型的数据,包括基本类型和其他引用类型。

例如:

javascript 复制代码
const person = {
  name: "John Doe",
  age: 25,
  address: {
    city: "New York",
    state: "NY",
    zip: "10001"
  }
};

数组(Array)

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

例如:

javascript 复制代码
const numbers = [1, 2, 3, 4, 5];
const fruits = ["apple", "banana", "orange"];

函数(Function)

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

例如:

javascript 复制代码
function add(a, b) {
  return a + b;
}

const result = add(1, 2); // 3

日期(Date)

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

例如:

javascript 复制代码
const date = new Date();
console.log(date.toString()); // "Thu Feb 16 2023 20:07:12 GMT+0800 (中国标准时间)"

正则表达式(RegExp)

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

例如:

javascript 复制代码
const regex = /\d+/g;
const matches = regex.exec("The answer is 42"); // ["42"]

错误(Errror)

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

例如:

javascript 复制代码
try {
  // 这里可能会发生错误
} catch (error) {
  // 处理错误
}

JS中引用类型的特殊性

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

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

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

  • 语法

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

    例如:

javascript 复制代码
function add(a, b) {
    return a + b;
}

const add = (a, b) => a + b;
  • this指向

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

    例如:

javascript 复制代码
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
  • arguments对象

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

​ 例如:

javascript 复制代码
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);
  • 构造函数

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

    例如:

javascript 复制代码
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
  • prototype属性

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

javascript 复制代码
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

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

深拷贝和浅拷贝的区别

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

浅拷贝

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

深拷贝

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

浅拷贝和深拷贝区别

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

例如:

javascript 复制代码
// 原对象
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']

如何实现深拷贝

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

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

JSON.parse() 和 JSON.stringify() 方法

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

递归函数

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

例如:

javascript 复制代码
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;
}

第三方库

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

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

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

类组件

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

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

函数式组件

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

  • 没有生命周期
  • 没有状态
  • 不能使用this访问组件实例

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

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

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

选择类组件还是函数式组件

因该考虑以下几点因素:

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

闭包的理解

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

闭包的形成

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

闭包的示例

javascript 复制代码
function outer() {
  let count = 0;

  function inner() {
    count++;
    console.log(count);
  }
  return inner;
}

const fn = outer();

fn(); // 1
fn(); // 2

console.log(count); // undefined

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

闭包的优点

  • 可以进行封装代码,提高代码的可重用性
  • 可以实现私有变量,提高代码的安全性
  • 可以模拟面向对象编程中的类

闭包的缺点

  • 可能会导致内存泄漏
  • 可能会使代码变得难以理解

闭包的应用场景

  • 实现延迟执行

    javascript 复制代码
    function delay(fn, delay) {
      return setTimeout(fn, delay);
    }
    
    const fn = () => console.log("Hello");
    
    delay(fn, 1000); // 1秒后输出 "Hello"
  • 模拟面向对象编程中的类

    javascript 复制代码
    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"
  • 创建私有变量

    javascript 复制代码
    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
  • 实现事件监听

    javascript 复制代码
    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();

Promise详解

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

Promise状态

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

Promise状态更改

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

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

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

Promise状态实列:

javascript 复制代码
const promise =  new Promise((resolve, reject) => {
    setTimeOut(() => {
        resolve('success');
    }, 1000);
});

promise.then(
    (reslut) => {
        console.log(reslut); // 'success'
    }, 
    (error) => {
        console.log(error);
    }
);

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

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('error');
  }, 1000);
});

promise.then(
  (result) => {
    console.log(result);
  },
  (error) => {
    console.log(error); // 'error'
  }
);

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

Promise场景

  • ajax请求
  • setTimeOut
  • setInterval
  • Web API

Promise特点

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

Promise基本使用

Promise创建

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  // 异步操作
});

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

  • resolve :用于将 Promise 的状态更改为 Fulfilled 的函数。
  • reject :用于将 Promise 的状态更改为 Rejected 的函数。
javascript 复制代码
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);

Promise常见用法

串联执行

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

javascript 复制代码
const promise1 = new Promise((resolve, reject) => {
  // 异步操作 1
});

const promise2 = new Promise((resolve, reject) => {
  // 异步操作 2
});

promise1.then(() => {
  return promise2;
}).then((result) => {
  // 处理最终结果
});

也可以理解为嵌套执行。

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

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

javascript 复制代码
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);
});

串联执行的注意事项:

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

Promise 嵌套的替代方案

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

并行执行

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

javascript 复制代码
const promise1 = new Promise((resolve, reject) => {
  // 异步操作 1
});

const promise2 = new Promise((resolve, reject) => {
  // 异步操作 2
});

Promise.all([promise1, promise2]).then((results) => {
  // 处理所有结果
});

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

javascript 复制代码
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);
  }
);

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 的结果或错误。

javascript 复制代码
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"
});

错误处理

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

javascript 复制代码
promise.then(
  (result) => {
    // 处理成功结果
  }
).catch((error) => {
  // 处理失败结果
});

最终处理

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

javascript 复制代码
promise.then(
  (result) => {
    // 处理成功结果
  },
  (error) => {
    // 处理失败结果
  }
).finally(() => {
  // 无论成功或失败都会执行的代码
});

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 方法
  • 可以用于加载数据、更新页面内容等

比较

特性 Promise Fetch Ajax
用途 处理异步操作 发送 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 算法如何工作:

html 复制代码
<div id="app">
  <p>{{ count }}</p>
  <button @click="increment">+</button>
</div>
javascript 复制代码
const app = Vue.createApp({
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
});

app.mount("#app");

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

html 复制代码
<div id="app">
  <p>0</p>
  <button>+</button>
</div>

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

html 复制代码
<div id="app">
  <p>1</p>
  <button>+</button>
</div>

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

json 复制代码
[
  {
    type: "update",
    node: {
      id: "app",
    },
    attr: {
      textContent: "1",
    },
  },
]

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

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

相关推荐
zqx_710 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己27 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2341 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河1 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai2 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端