2. 常用函数
2024/10/3大约 13 分钟
2. 常用函数
2.1. Object
Object静态方法Object.assign(newObj, ...objs):浅拷贝对象,为newObj添加objs有的属性,如果属性有重复,则后面的属性会覆盖前面的属性
深浅拷贝
- 直接复制引用
const a = b:修改会共享 - 浅拷贝:会创建新对象,但其中的元素只是原对象的引用,不会递归复制内层对象,因此第二层及更深层的元素修改会共享
const a = {...b}Object.assign(a, b)
- 深拷贝:会创建新对象,并递归复制内层对象,使得新对象与原对象完全独立
创建递归函数手动创建新对象
// 简单的递归拷贝函数,仅对数组和对象进行递归处理 function deepCopy(obj, oldObj) { // 使用键遍历 for (let key in obj) { // 必须先判断是否为数组,因为数组也是对象 if (oldObj[key] instanceof Array) { obj[key] = []; deepCopy(obj[key], oldObj[key]); } else if (oldObj[key] instanceof Object) { obj[key] = {}; deepCopy(obj[key], oldObj[key]); } else { obj[key] = oldObj[key]; } } }借助
json格式const a = JSON.parse(JSON.stringify(b)),特殊对象难以表达使用
structuredClone方法:ECMAScript 2021添加的原生方法,通过结构化克隆算法完成拷贝,但有许多类型不支持此算法,比如:函数、许多错误类型、DOM节点、原型链跟踪、属性描述符、正则对象的lastIndex属性等,见结构化克隆算法此方法第二个参数中的选项支持转移对象属性到新对象中
const obj = { a: 1, b: { c: 2 } }; const copy = structuredClone(obj); console.log(copy); // { a: 1, b: { c: 2 } } console.log(copy === obj); // false(深拷贝) // 创建一个 ArrayBuffer const buffer = new ArrayBuffer(8); // 克隆并转移所有权 const cloned = structuredClone({ buf: buffer }, { transfer: [buffer] }); console.log(cloned.buf.byteLength); // 8 console.log(buffer.byteLength); // 0 原 buffer 被清空(转移完成)
借助其他工具库的实现,比如
lodash的cloneDeep方法,处理了许多的特殊类型,但不支持原型链跟踪
Object.create(proto, [descriptors]):创建一个新对象,对象原型为proto,属性描述符为descriptorsproto不能是undefined,如果是null说明对象不继承自Object.prototype,也就没有对应的方法,这种对象可以用作干净的字典,没有任何额外属性,也没有任何冲突
Object.values(obj):返回对象obj的属性值组成的数组Object.getOwnPropertyDescriptors(obj):返回对象obj的所有属性描述符组成的对象,在变量特性部分已经介绍过- 获取对象的属性键:
Object.keys(obj):返回对象obj的自身可迭代属性名组成的数组Object.getOwnPropertyNames(obj):返回对象obj的自身属性名(包括不可迭代属性)组成的数组Object.getOwnPropertySymbols(obj):返回对象obj的所有Symbol属性键组成的数组
Reflect.ownKey()可以获取到自身所有属性名,包括Symbol属性键名 Object.getPrototypeOf(obj):返回对象obj的原型对象Object.is(value1, value2):比===更严格的判断两个值是否相等的方法,只有以下情况被认为是相同的- 都是 undefined
- 都是 null
- 都是 true 或者都是 false
- 都是长度相同、字符相同、顺序相同的字符串
- 都是相同的对象(意味着两个值都引用了内存中的同一对象)
- 都是 BigInt 且具有相同的数值
- 都是 symbol 且引用相同的 symbol 值
- 都是数字且都是相同值时,
+0和-0被认为是不同值(与===的区别),两个NaN被认为是相同的
2.2. Array
- 数组实例方法
arr.push(item1, item2, ...):在数组末尾添加元素,返回新数组长度arr.pop():删除数组最后一个元素并返回该元素arr.shift():删除数组第一个元素并返回该元素arr.unshift(item1, item2, ...):在数组开头添加元素,返回新数组长度arr.splice(start, deleteCount, item1, item2, ...):删除数组的元素,并添加新的元素,start为开始索引(删除含本元素),deleteCount为删除的元素个数,item1...为要添加的元素,返回被删除的元素组成的数组- 如果希望删除元素,仅使用前两个参数
arr.splice(start, deleteCount),deleteCount默认无限制 - 如果希望添加元素,仅使用索引和最后的可变参数
arr.splice(start, 0, item1, item2, ...) - 由于函数返回值是被删除的部分,有时也由于截取数组中部分内容
- 如果希望删除元素,仅使用前两个参数
arr.forEach((val, idx, arr) => void, thisArg):遍历数组arr,对每个元素执行callback函数,元素长度将被预先记录,在修改过程中被添加的元素不会被遍历(与for循环的区别)thisArg:用于修改callback函数的this指向,因为函数作为参数传入之后会丢失对应的obj绑定,所以需要传入一个对象作为this指向,确保调用形式是callback.call(thisArg, val, idx, arr)callback的执行逻辑:forEach、map等这样的函数,都会对数组的每一个元素调用一次callback函数,这个函数的返回值将影响最终返回的新数组或值,函数本身的this有特殊设计过,如果不传入值,那么this指向window(或严格模式下的undefined)- 对空位的处理:因为这种方法可能被稀疏数组使用
Array.prototype.forEach.call([1, , 3], callback),因此较久的方法考虑了空位的处理(跳过),新方法不处理空位(作为undefined),具体见数组方法与空位
arr.map((val, idx, arr) => any, thisArg):遍历数组arr,对每个元素执行callback函数(返回值作为新值),返回一个新数组arr.filter((val, idx, arr) => boolean, thisArg):遍历数组arr,对每个元素执行callback函数,返回一个新数组,新数组的元素是满足callback函数(返回true)的元素arr.reduce((accumulator, curVal, index, arr) => any [, initialValue]):遍历数组arr,对每个元素执行callback函数,返回一个值,用于求和等操作- 初始值
initialValue是可选的,不提供且数组非空就使用数组的第一个元素作为初始值,不对第一个元素执行callback函数 accumulator是上一个回调的返回值,初始值为initialValue或数组的第一个元素
- 初始值
arr.join([separator]):将数组元素连接为一个字符串arr.find((val, idx, arr) => boolean):找到数组中满足条件回调(返回true)的第一个元素,如果没有满足条件的元素,则返回undefinedarr.findIndex((val, idx, arr) => boolean):返回数组中满足条件回调的第一个元素的索引,如果没有满足条件的元素,则返回-1arr.every((val, idx, arr) => boolean):测试数组的所有元素是否都符合指定条件回调arr.some((val, idx, arr) => boolean):测试数组中是否有元素符合指定条件回调arr.sort((a, b) => number):对数组进行排序,返回表示在前,表示在前,时稳定性依赖实现,ES2019起大多数是稳定排序arr.reverse():将数组的元素顺序颠倒arr.slice(start, end):返回数组的片段,不包含end下标的内容end可省略,默认到末尾,start也可以省略,默认为0- 索引可以使用负数,表示从末尾开始
- 返回的是浅拷贝的新数组
arr.concat(item1, item2, ...):连接数组(展开第一层)或元素并返回一个新数组arr.join(separator):将数组元素连接为字符串arr.flat(depth):将数组拍平,最多变为一维depth:拍平的深度,默认为1,如果传入infinity,则拍平所有层- 拍平只处理数组,对类数组对象不会处理
- 拍平会删除空位
- 数组静态方法
Array.isArray(value):判断是否是数组Array.from(arrayLike, mapFn, thisArg):将类数组对象或可遍历对象转换成数组arrayLike:类数组对象或可遍历对象mapFn:转换函数,可以转换映射,用于在转换时对元素加工thisArg:转换函数的this指向,目的是确保mapFn指向正确,默认指向window
2.3. String
字符串实例属性
length:字符串长度
访问每个元素
- 可以直接像访问数组一样访问字符串的每个元素
str[index]:访问字符串的每个元素,但无法修改for (let c of str):ES6特性,遍历字符串的每个元素
- 也可以使用
+拼接字符串,js引擎对这个操作有缓冲优化,当然如果特别极端的情况,优化可能不稳定,使用join方法性能更好
字符串实例方法
str.split(separator, limit):将字符串按separator分割成数组,separator可以是字符串或正则,limit为返回数组的最大长度str.replace(regexpOrSubstr, replacement):替换字符串,regexpOrSubstr可以是字符串或正则表达式,replacement可以是字符串或函数;返回新字符串str.toUpperCase():将字符串转为大写,返回新字符串str.toLowerCase():将字符串转为小写,返回新字符串str.trim():去除字符串首尾空格,返回新字符串str.charAt(index):获取指定索引index的字符,返回单字符字符串- 获得
UTF-16编码、Unicode值需要通过charCodeAt(index)、codePointAt(index)函数
- 获得
str.includes(searchString, position):判断字符串是否包含searchString,从position索引开始搜索,返回布尔值str.startsWith(searchString, position):判断字符串是否以searchString开头,从position索引开始匹配,返回布尔值str.endsWith(searchString, length):判断字符串是否以searchString结尾,length表示只考虑前length个字符,返回布尔值str.substring(start, end):返回从start到end(不包括)之间的子串,end可选,默认到字符串末尾str.slice(start, end):返回从start到end(不包括)之间的子串,支持负数索引表示从末尾开始str.indexOf(searchValue, fromIndex):返回searchValue首次出现的索引,若未找到返回-1,从fromIndex开始搜索str.lastIndexOf(searchValue, fromIndex):返回searchValue最后一次出现的索引,若未找到返回-1,从fromIndex向前搜索str.match(regexp):使用正则表达式regexp匹配字符串,返回匹配结果数组或nullstr.matchAll(regexp):使用全局正则表达式regexp匹配所有结果,返回一个可迭代对象,每个元素是匹配数组str.repeat(count):重复字符串count次,返回新字符串str.padStart(targetLength, padString):在字符串前填充padString到targetLength长度,返回新字符串str.padEnd(targetLength, padString):在字符串后填充padString到targetLength长度,返回新字符串
2.4. Math
Math对象的静态属性中有许多数学常量Math.E:自然对数底数Math.PI:圆周率Math.SQRT2:Math.SQRT1_2:Math.LN2:对数底数
Math中包含常用数学函数,都是静态方法Math.abs(x):返回x的绝对值- 取整函数
Math.ceil(x):向上取整Math.floor(x):向下取整Math.round(x):返回最接近的整数,四舍五入
其他的取整方法
~~(x):按位取反再取反,也相当于x | 0,向0取整,超过二进制32位会被截断,读取到NaN和Infinity会返回Math.trunc(x):返回x的整数部分,向取整,是~~(x)的替代,可以处理更大范围的浮点数parseInt(string, radix):将字符串转换成整数,radix指定转换的进制,默认是10进制,方法会从左往右读取字符串,直到遇到非数字字符或.结束,如果开头就不是数字,则返回NaNNumber.prototype.toFixed(digits):返回一个定点数字符串(比如123000),digits指定小数位数,默认是,方法四舍五入Number.prototype.toPrecision(precision):返回一个定点数或指数表示字符串(如果可以使用指数表示的话,会使用指数表示,比如1.23e+5),precision指定有效位数,默认和当前有效位数一致(仅删去小数末尾的),方法四舍五入
Math.max(x1, x2, ...):返回参数中的最大值,还有对应的Math.min()返回参数中的最小值Math.pow(x, y):返回x的y次方Math.sqrt(x):返回x的平方根Math.random():返回一个随机数,范围是[0, 1)- 各类三角函数,比如
Math.sin(),Math.cos(),Math.tan(),参数需要是弧度制弧度制和角度制的换算:在
js中角度制也是通过数字显示的,比如角度对应的数字为30 * Math.PI / 180
2.5. 时间
Date对象:是目前js用于处理日期和时间的对象,对时区不友好,因此大多数时候都使用外部时间库比如dayjs完成,官方的新时间对象Temporal还在实验中,见MDN Temporal- 静态方法
Date.now():返回当前时间戳,单位是毫秒
- 创建对象
new Date()- 无参数,获取当前时间
- 传入时间戳创建对象
- 传入字符串创建对象,字符串应该为
YYYY-MM-DDTHH:mm:ss.sssZ格式的时间(部分内容可缺省,仅有日期被视为UTC时间,有日期时分秒无时区被视为当地时间),其他格式的时间可能会被部分浏览器解析,但不鼓励使用 - 使用包含年月日时分秒毫秒的参数列表,创建为当地时间
- 年参数传入
0-99时会转换为19xx年,其他转化为对应年份,如果需要公元0-99的年份,需要在之后调用setFullYear()方法设置 - 月参数从
0开始,表示月的索引 - 日参数非必须,默认为1
- 时分秒毫秒参数非必须,默认为0
- 年参数传入
- 获取和设置时间信息
- 获取和设置年:
getFullYear()、setFullYear() - 获取和设置月:
getMonth()、setMonth(),0表示1月 - 获取和设置日:
getDate()、setDate() - 获取和设置时:
getHours()、setHours() - 获取和设置分:
getMinutes()、setMinutes() - 获取和设置秒:
getSeconds()、setSeconds() - 获取和设置毫秒:
getMilliseconds()、setMilliseconds() - 获取星期几:
getUTCDay(),0表示星期天 - 获取时间戳:
getTime()、valueOf(),更早的时间是负数
- 获取和设置年:
- 注意事项
- 传入的时间超过了对应的时间范围,会发生进位
- 如果时间无法解析会得到
Invalid Date toJSON方法定义了将Date对象转为JSON格式字符串的函数,之后时间将为协调世界时UTC的时间Date()可以不带new调用,返回时间字符串- 可以通过
toLocaleString,toLocaleDateString和toLocaleTimeString方法转为本地时间展示字符串 - 已重写
[Symbol.toPrimitive],默认和string返回当地时间字符串,number返回时间戳
2.6. 哈希结构
- 哈希表
Map:可以使用任意类型作为键set(k, v):添加值get():获取值delete(k):删除元素has(k):判断元素是否存在clear():清空表size:表中元素个数entries():获取所有键值对数组[[k,v],...]keys():获取所有键数组values():获取所有值数组fporEach((k,v)=>{}):迭代器
- 哈希集合
Set:集合中的元素不重复,可以传入元素数组作为构造参数new Set([...]),之后可以使用[...new Set()]转为数组add(e):添加元素delete(e):删除元素has(e):判断元素是否存在clear():清空集合size:集合元素个数- 可以直接使用
[...new Set()]或Array.from(new Set())转为数组
WeakMap:弱映射,使用对象作为键,如果键在外面没有引用,则记录会被垃圾回收,因此WeakMap不能遍历也没有长度。常用于存储私有数据,以变量本身作为键,存储私有字段,如果变量被回收,私有字段也会回收set(o, v):设置键值对get(o):获取值delete(o):删除对象has(o):判断对象键是否存在
WeakSet:弱集合,只能存储对象,如果对象在外面没有引用,则会被垃圾回收,因此WeakSet不能遍历也没有长度add(o):添加对象delete(o):删除对象has(o):判断对象是否存在,比较地址
