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。
- 整数:例如
1
、2
、3
等。 - 浮点数:例如
1.2
、3.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
后缀来创建,例如:
javascriptconst 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
变量。
闭包的优点
- 可以进行封装代码,提高代码的可重用性
- 可以实现私有变量,提高代码的安全性
- 可以模拟面向对象编程中的类
闭包的缺点
- 可能会导致内存泄漏
- 可能会使代码变得难以理解
闭包的应用场景
-
实现延迟执行
javascriptfunction delay(fn, delay) { return setTimeout(fn, delay); } const fn = () => console.log("Hello"); delay(fn, 1000); // 1秒后输出 "Hello"
-
模拟面向对象编程中的类
javascriptfunction 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"
-
创建私有变量
javascriptfunction 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
-
实现事件监听
javascriptfunction 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.resolve
和Promise.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扩展
Promise 、Fetch 和 Ajax 都是 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中受数据影响的最小部分。实现步骤:
- 深度优先遍历虚拟DOM树:从根节点开始,深度优先遍历虚拟DOM树,同时每个节点生成一个唯一的Key。
- 比较新旧虚拟DOM树:使用key匹配新旧虚拟DOM树中的节点,如果节点的 key 相同,则比较它们的属性和子节点。如果节点的 key 不同,则认为该节点已删除或新增。
- 生成更新操作列表 :根据新旧虚拟 DOM 树的差异,生成一个更新操作列表。更新操作包括:
- 插入节点
- 删除节点
- 更新节点属性
- 移动节点
- 应用更新操作:根据更新操作列表,更新真实 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 在更新数据时更加高效。