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); // undefinedSymbol
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]); // WorldBigInt
BigInt 是 ES2020(ES11) 中引入的一种新数据类型。它表示一个任意精度的整数。
例如:
            
            
              javascript
              
              
            
          
          const bigInt = 12345678901234567890n;
console.log(bigInt.toString()); // 12345678901234567890BigInt的扩展
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 在更新数据时更加高效。