JS
JS数据类型
-
JavaScript共有8种数据类型: Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。其中 Symbol代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。而BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。 -
基本数据类型 & 引用数据类型:
- 堆用于存放引用数据类型,引用数据类型占据空间大、大小不固定。存储在栈中,会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的地址。
- 栈用于存放原始数据类型,栈中的简单数据段,使用空间小,多为被频繁使用的数据。
instanceof运算符 & typeof运算符
instanceof运算符适用于检测构造函数的prototype属性上是否出现在某个实例对象的原型链上。其原理也是基于原型链的查找,直至查找到原型链顶端,即Object.prototype。instanceof运算符只能用于判断某个实例是否属于某个构造函数的实例,不可用于判断基本数据类型。typeof运算符可用于判断运算数的基本数据类型,但不可用于引用类型变量。- 一个矛盾点:
typeof会判断null为Object,这是一个历史遗留问题。与JS关于如何判断类型的初始设定有关。
浮点运算的数据精度问题
比如0.1+0.2!=0.3,这是因为JS处理浮点数时使用二进制数转换成十进制,存在截断等问题,所以0.1不是真正精确的0.1,这就导致了上述误差。
- 通过使用
toFixed()方法来实现精确位数的浮点数 - 转换为整数做运算,再转换回浮点数。
判断数据是否为内置类型(以数组类型Array为例)
- 使用
instanceof运算符,直接判断。 - 使用原型链原理,如:
obj.__proto__ === Array.prototype。 - 使用
Array.isArray(obj)判断。
伪数组
伪数组和数组类似,但是不能调用数组方法,常见的类数组有arguments、通过document.querySelectorAll()等方式获取到的非单一元素的内容,这些类数组具有length属性。
转换方法
-
通过
call调用数组的slice方法来实现转换Array.prototype.slice.call(arrayLike) -
通过
call调用数组的splice方法来实现转换Array.prototype.splice.call(arrayLike, 0) -
通过
apply调用数组的concat方法来实现转换Array.prototype.concat.apply([], arrayLike) -
通过
Array.from方法来实现转换Array.from(arrayLike)
数组原生方法
| 分类 | 方法名 | 功能描述 | 示例代码 | 返回值 | 是否修改原数组 |
|---|---|---|---|---|---|
| 数组创建与转换 | Array.from() | 从类数组对象或可迭代对象创建新的浅拷贝数组实例 | const str = 'hello'; const arr = Array.from(str); | 新数组 | 否 |
Array.of() | 创建具有可变数量参数的新数组实例 | const arr = Array.of(1, 2, 3); | 新数组 | 否 | |
| 数组访问与查找 | at() | 根据索引(支持正负)返回数组元素 | const arr = [10, 20, 30]; console.log(arr.at(-1)); | 元素值或 undefined | 否 |
includes() | 判断数组是否包含指定值 | const arr = [1, 2, 3]; console.log(arr.includes(2)); | 布尔值 | 否 | |
indexOf() | 返回指定元素首次出现的索引,不存在则返回 -1 | const arr = [1, 2, 3]; console.log(arr.indexOf(2)); | 索引值或 -1 | 否 | |
lastIndexOf() | 返回指定元素最后出现的索引,不存在则返回 -1 | const arr = [1, 2, 3, 2]; console.log(arr.lastIndexOf(2)); | 索引值或 -1 | 否 | |
find() | 返回满足测试函数的第一个元素的值,否则返回 undefined | const arr = [1, 2, 3, 4, 5]; const found = arr.find(num => num > 3); | 元素值或 undefined | 否 | |
findIndex() | 返回满足测试函数的第一个元素的索引,未找到则返回 -1 | const arr = [1, 2, 3, 4, 5]; const index = arr.findIndex(num => num > 3); | 索引值或 -1 | 否 | |
| 数组修改 | push() | 在数组末尾添加一个或多个元素 | const arr = [1, 2]; const newLength = arr.push(3); | 新数组长度 | 是 |
pop() | 移除数组最后一个元素并返回该元素 | const arr = [1, 2, 3]; const lastElement = arr.pop(); | 被移除的元素 | 是 | |
unshift() | 在数组开头添加一个或多个元素 | const arr = [2, 3]; const newLength = arr.unshift(1); | 新数组长度 | 是 | |
shift() | 移除数组第一个元素并返回该元素 | const arr = [1, 2, 3]; const firstElement = arr.shift(); | 被移除的元素 | 是 | |
splice() | 删除、替换或添加元素来修改数组 | const arr = [1, 2, 3, 4]; const removed = arr.splice(1, 2, 5, 6); | 被删除元素组成的数组 | 是 | |
fill() | 用固定值填充数组指定范围元素 | const arr = [1, 2, 3]; arr.fill(0, 1, 2); | 修改后的数组 | 是 | |
| 数组排序与反转 | sort() | 对数组元素进行排序 | const arr = [3, 1, 2]; arr.sort(); | 排序后的数组 | 是 |
reverse() | 反转数组元素顺序 | const arr = [1, 2, 3]; arr.reverse(); | 反转后的数组 | 是 | |
| 数组拼接与分割 | concat() | 合并两个或多个数组 | const arr1 = [1, 2]; const arr2 = [3, 4]; const newArr = arr1.concat(arr2); | 合并后的新数组 | 否 |
slice() | 返回数组指定范围的浅拷贝 | const arr = [1, 2, 3, 4]; const newArr = arr.slice(1, 3); | 新数组 | 否 | |
| 数组迭代 | forEach() | 对数组每个元素执行一次提供的函数 | const arr = [1, 2, 3]; arr.forEach(num => console.log(num)); | undefined | 否 |
map() | 创建新数组,元素为原数组元素调用函数后的结果 | const arr = [1, 2, 3]; const newArr = arr.map(num => num * 2); | 新数组 | 否 | |
filter() | 创建新数组,包含通过测试函数的元素 | const arr = [1, 2, 3, 4]; const newArr = arr.filter(num => num > 2); | 新数组 | 否 | |
reduce() | 对数组元素执行 reducer 函数并汇总为单个返回值 | const arr = [1, 2, 3, 4]; const sum = arr.reduce((acc, num) => acc + num, 0); | 汇总结果 | 否 | |
some() | 测试数组是否至少有一个元素通过测试函数 | const arr = [1, 2, 3, 4]; const hasEven = arr.some(num => num % 2 === 0); | 布尔值 | 否 | |
every() | 测试数组所有元素是否都通过测试函数 | const arr = [2, 4, 6]; const allEven = arr.every(num => num % 2 === 0); | 布尔值 | 否 | |
| 数组转换为字符串 | join() | 将数组元素连接成一个字符串 | const arr = [1, 2, 3]; const str = arr.join('-'); | 字符串 | 否 |
Object.assign()
Object.assign()用于将一个或多个源对象的所有可枚举属性复制到目标对象,它会返回目标对象。在复制过程中,对于基本数据类型的属性,会直接复制其值;而对于引用数据类型的属性,只会复制其引用,也就是新对象和原对象中的引用数据类型属性会指向同一个内存地址。所以,当修改新对象中引用数据类型的属性时,原对象中对应的属性也会受到影响,这符合浅拷贝的特征。- 它接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。
扩展运算符
在JS中,使用扩展运算符(...)复制数组或对象时,对于一维数组或只包含基本数据类型属性的对象来说是深拷贝;但对于多维数组或包含引用数据类型属性的对象,它属于浅拷贝。它直接复制基本数据类型,但只复制复杂数据类型的引用。
new关键字
使用 new 关键字调用一个函数(构造函数)时,会依次执行以下步骤:
- 创建一个新对象。
- 将新对象的原型指向构造函数的原型。
- 执行构造函数,并将
this绑定到新对象上。 - 如果构造函数返回一个对象,则返回该对象;否则,返回新创建的对象。
手写实现:
function myNew(constructor, ...args) {
// 步骤 1: 创建一个新对象
const obj = {};
// 步骤 2: 将新对象的原型指向构造函数的原型
obj.__proto__ = constructor.prototype;
// 步骤 3: 执行构造函数,并将 this 绑定到新对象上
const result = constructor.apply(obj, args);
// 步骤 4: 如果构造函数返回一个对象,则返回该对象;否则,返回新创建的对象
return typeof result === 'object' && result!== null? result : obj;
}