中级前端面试整理-上篇

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 {// 用户未登录
}
数字(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
字符串(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);
空(Null)

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

例如:

const variable = null;console.log(variable === undefined); // false
console.log(variable === null); // true
未定义(Undefined)

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

例如:

let variable;console.log(variable); // undefined
Symbol

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

例如:

const symbol1 = Symbol();
const symbol2 = Symbol();console.log(symbol1 === symbol2); // falseconst object = {[symbol1]: "Hello",[symbol2]: "World"
};console.log(object[symbol1]); // Hello
console.log(object[symbol2]); // World
BigInt

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

例如:

const bigInt = 12345678901234567890n;console.log(bigInt.toString()); // 12345678901234567890

BigInt的扩展

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

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

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

例如:

const person = {name: "John Doe",age: 25,address: {city: "New York",state: "NY",zip: "10001"}
};
数组(Array)

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

例如:

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

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

例如:

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

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

例如:

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

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

例如:

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

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

例如:

try {// 这里可能会发生错误
} catch (error) {// 处理错误
}

JS中引用类型的特殊性

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

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

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

  • 语法

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

    例如:

function add(a, b) {return a + b;
}const add = (a, b) => a + b;
  • 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
  • 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 definedreturn a + b;
};add(1, 2);
  • 构造函数

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

    例如:

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属性为函数添加方法。
    例如:

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指向容易混淆。在实际开发中,应根据具体需求选择合适的函数定义方式。

深拷贝和浅拷贝的区别

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

浅拷贝

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

深拷贝

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

浅拷贝和深拷贝区别

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

例如:

// 原对象
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 字符串解析为对象。这种方法可以实现深拷贝,但需要注意的是,它会忽略对象的原型和循环引用。

递归函数

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

例如:

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中,闭包是指一个函数可以访问其外部函数的作用域中的变量,换句话说,闭包使函数能够记住其创建时的环境。

闭包的形成

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

闭包的示例
function outer() {let count = 0;function inner() {count++;console.log(count);}return inner;
}const fn = outer();fn(); // 1
fn(); // 2console.log(count); // undefined

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

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

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

    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"
    
  • 创建私有变量

    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
    
  • 实现事件监听

    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状态实列:

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

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创建

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

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);

Promise常见用法

串联执行

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

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 嵌套示例:

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 方法并行执行多个异步操作。

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 实例。

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

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

promise.then((result) => {// 处理成功结果}
).catch((error) => {// 处理失败结果
});
最终处理

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

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 方法
  • 可以用于加载数据、更新页面内容等
比较
特性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>
const app = Vue.createApp({data() {return {count: 0,};},methods: {increment() {this.count++;},},
});app.mount("#app");

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

<div id="app"><p>0</p><button>+</button>
</div>

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

<div id="app"><p>1</p><button>+</button>
</div>

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

[{type: "update",node: {id: "app",},attr: {textContent: "1",},},
]

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/721067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

了解拒绝服务攻击:攻击类型、影响和防御措施

拒绝服务攻击&#xff08;Denial of Service&#xff0c;简称DoS&#xff09;是一种广泛存在的网络安全威胁&#xff0c;旨在使目标系统无法提供正常的服务&#xff0c;使其服务不可用或严重受限。在本文中&#xff0c;我们将深入探讨拒绝服务攻击的不同类型、其对网络系统和业…

安全特性 悬垂指针

英文名称 Dangling point&#xff0c;它还有一个兄弟叫 wild point - 野指针。 简单的对Dangling point做一个类比&#xff1a;我换手机号码了&#xff0c;但是没有通知老板&#xff0c;老板通讯录存的是我的旧号码。然后老板打电话有两种可能&#xff1a;打不通电话或者电话打…

pytorch 自定义函数

pytorch 自定义函数 介绍&#xff1a;https://zhuanlan.zhihu.com/p/344802526 主要构建 static method forward 和 backward 比如 layernorm: 参考&#xff1a;https://github.com/zhangyi-3/KBNet/blob/main/basicsr/models/archs/kb_utils.py 导数的推导&#xff1a;http…

Linux常用命令(超详细)

一、基本命令 1.1 关机和重启 关机 shutdown -h now 立刻关机 shutdown -h 5 5分钟后关机 poweroff 立刻关机 重启 shutdown -r now 立刻重启 shutdown -r 5 5分钟后重启 reboot 立刻重启 1.2 帮助命令 –help命令 shutdown --help&#xff1a; ifconfig --help&#xff1a;查看…

SpringBoot 接口报错该如何解决?

在Spring Boot应用中&#xff0c;接口报错可能由多种原因引起&#xff0c;包括但不限于业务逻辑错误、异常处理不当、依赖库问题、配置错误等。解决接口报错的过程需要分析具体的错误信息、排查可能的原因&#xff0c;并采取相应的调试和修复措施。 以下是解决Spring Boot接口…

AWS ECR(AWS云里面的docker镜像私库)

问题 上一篇文章&#xff0c;在AWS云上面部署了k8s集群&#xff0c;这次接下来&#xff0c;需要在一个docker镜像私库。 步骤 创建docker镜像私库 打开AWS ECR主页&#xff0c;创建一个docker镜像私库&#xff0c;如下图&#xff1a; 设置私有镜像库名称&#xff0c;直接创…

AI短视频矩阵运营软件|抖音视频矩阵控制工具

【罐头鱼AI传单功能介绍】 罐头鱼AI传单是一款专为短视频矩阵运营而设计的智能软件&#xff0c;旨在帮助用户高效管理和运营多个抖音账号&#xff0c;并提供一系列强大的功能来优化视频内容创作和发布流程。QQ:290615413以下是软件框架&#xff0c;详细介绍其功能和特点&#…

第一弹:Flutter安装和配置

目标&#xff1a; 1&#xff09;配置Flutter开发环境 2&#xff09;创建第一个Flutter Demo项目 Flutter中文开发者网站&#xff1a; https://flutter.cn/ 一、配置Flutter开发环境 Flutter开发环境已经提供集成IDE开发环境&#xff0c;因此需要配置开发环境的时候&#xf…

【项目管理】CMMI-质量保证过程

质量保证过程&#xff08;PQA)&#xff1a;通过质量保证活动&#xff0c;确保过程与产品满足过程、规程及相应的要求&#xff0c;确保问题得到关注与解决&#xff0c;使工作人员和管理者能够客观地了解过程与相关的工作产品。QA工程师应实施质量保证策划活动&#xff0c;客观地…

【云呐】固定资产管理系统选型有哪些

固定资产管理是企业经营管理过程中非常重要的任务。它涉及到公司的核心资产&#xff0c;包括土地、建筑、设备、车辆等。许多企业选择引入固定资产管理系统&#xff0c;以确保这些资产的高效管理和使用。但是&#xff0c;面对市场上众多的固定资产管理系统&#xff0c;如何选择…

【自然语言处理六-最重要的模型-transformer-上】

自然语言处理六-最重要的模型-transformer-上 什么是transformer模型transformer 模型在自然语言处理领域的应用transformer 架构encoderinput处理部分&#xff08;词嵌入和postional encoding&#xff09;attention部分addNorm Feedforward & add && NormFeedforw…

固定资产管理系统包括哪些

固定资产管理是企业经营过程中一项非常重要的任务。它涉及到公司的核心资产&#xff0c;包括土地、建筑物、设备、车辆等。为了有效地管理这些资产&#xff0c;许多企业选择使用固定资产管理系统。那么&#xff0c;固定资产管理系统的内容是什么呢&#xff1f;本文将为您进行全…

代码随想录day13(2)栈与队列:用队列实现栈(leetcode225)

题目要求&#xff1a;使用队列实现栈的push、pop、top&#xff08;取栈顶元素&#xff09;、empty操作。 思路&#xff1a;首先的思路就是使用两个队列&#xff0c;入栈的操作不变&#xff0c;如果想弹出一个元素&#xff0c;先将队列1元素出队&#xff0c;只保留一个元素&…

力扣hot100:1.两数之和(哈希表)

输入中可能存在重复值 。 分析&#xff1a; 本题需要返回的是数组下标&#xff0c;因此如果需要使用排序然后双指针的话&#xff0c;需要用到哈希表&#xff0c;但是由于输入中可能存在重复值&#xff0c;因此哈希表的value值必须是vector<int>。 使用双指针求目标值targ…

第十七天-反爬与反反爬-验证码识别

目录 反爬虫介绍 基于身份识别反爬和解决思路 Headers反爬-使用User-agent Headers反爬-使用coookie字段 Headers反爬-使用Referer字段 基于参数反爬 验证码反爬 1.验证码介绍 2.验证码分类&#xff1a; 3.验证码作用 4.处理方案 5.图片识别引擎:ocr 6.使用打码平…

基于springboot+vue的周边游平台个人管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【leetcode】三数之和 双指针

/*** param {number[]} nums* return {number[][]}*/ var threeSum function(nums) {nums.sort((a,b)>a-b);let result[];for(let i0;i<nums.length-2;i){if(nums[i]>0) return result;//因为求三数之和等于0&#xff0c;如果第一个数已经大于0&#xff0c;后面肯定无…

完蛋!这DLC不行啊?!

作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle ACE Associate: Database&#xff08;Oracle与MySQL&#xff09; 国内某科技公司 DBA总监 10年数据库行业经验&#xff0c;现主要从事数据库服务工作 拥有OCM 11g/12c/19c、MySQL 8.0 OCP、Exadata、CDP等认…

pycharm社区版+miniconda 环境配置学习

使用电脑为win10 64bit 一、下载&#xff1a; 1.pycharm社区版地址&#xff1a; 其他版本 - PyCharm 2.miniconda下载地址&#xff1a; Index of /anaconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 3.python下载地址&#xff1a; https://www.python.or…

云原生数据库 GaiaDB 支持新的管理工具啦

GaiaDB 是百度智能云自研的新一代企业级关系型数据库&#xff0c;最大容量可扩展 500TB 以上&#xff0c;吞吐达到 150 万以上 QPS。 作为一款 100% 兼容 MySQL 的云原生数据库产品&#xff0c;用户可以通过多种客户端工具连接 GaiaDB 实例&#xff0c;例如 MySQL Workbench、N…