用法
对于基本类型的包装对象来说,调用该方法会返回对应的基本类型值,但对于数组一般会直接返回数组本身
const arr = [1,2,3] arr.valueOf() === arr
实现
Array.prototype.myValueOf = function(){
return this
}
用法
将数组中的每个元素转为字符串并用规定好的分隔符进行连接:
undefined,而 undefined 和 null 会进一步被转化为空字符串。[1,2,3].join() // "1,2,3" 缺省是逗号作为连接符
[1,2,3].join('.') // "1.2.3"
[{},{},{}].join('**') // "[object Object]**[object Object]**[object Object]"
实现
Array.prototype.myJoin = function(connector = ','){
let arr = this
let str = ''
for(x of arr){
x = typeof(x) === 'undefined' || x === null ? "" : x
str += x.toString() + connector
}
return str.slice(0,str.length - connector.length)
}
// 或者
Array.prototype.myJoin = function(connector = ','){
let arr = this
let len = arr.length
let str = ''
for(let i = 0;i < len;i++){
arr[i] = typeof(arr[i]) === 'undefined' || arr[i] === null ? "" : arr[i]
// 如果是最后一个元素,则不加连接符(后缀符)
str += arr[i].toString + (i === len - 1 ? '' : connector)
}
return str
}
用法
toString 可以看作是 join 的一种特殊情况,即传入的分隔符是逗号,其它的都一样(包括对 undefined、null 和 empty 元素的处理)
[1,2,3].toString() // "1,2,3"
[{a:1},{b:2}].toString() // "[obejct Object],[object Object]"
实现
Array.prototype.myToString = function(){
let arr = this
let str = ""
for(x of arr){
x = typeof(x) === 'undefined' || x === null ? "" : x
str += `${x.toString()},`
}
return str.slice(0,str.length - 1)
}
用法
concat 可以用于合并数组
[Symbol.isConcatSpreadable]=true,此时会取出这个对象的每一项(除了 length)放入新数组[Symbol.isConcatSpreadable]=false实现
Array.prototype.myConcat = function(...args){
let arr = this
let res = []
let k = 0
const isArrayLike = obj => {
if( typeof o === 'object' &&
isFinite(o.length) &&
o.length >= 0 &&
o.length === Math.floor(o.length) &&
o.length < 4294967296)
return true
else
return false
}
for(let el of arr){
res[k++] = el
}
for(let el of args){
// 如果是数组且没有禁止展开
if(Array.isArray(el) && el[Symbol.isConcatSpreadable] != false){
for(let _el of el){
res[k++] = _el
}
} else {
// 如果是类数组且允许展开
if(isArrayLike(el) && el[Symbol.isConcatSpreadable]){
for(let key in el){
// 把除了 length 之外的键值都放入新数组中
if(key !== 'length'){
res[k++] = el[key]
}
}
} else {
res[k++] = y
}
}
}
return res
}
PS:这里检测类数组对象的方式可能不太严谨,且没有考虑 empty 元素的情况
用法
at 是一个比较新的方法,目前浏览器还没有实现:
相比 arr[2],这个方法的优势在哪里呢?优势在于可以很方便地访问那些数组末尾的元素,比如现在要访问 const arr = [1,2,3,4] 的倒数第二个元素,不再需要使用 arr[arr.length - 2],只需要 arr.at(-2)。
const arr = [1,2,3,4] arr.at(2) // 3 arr.at(-1) // 4
实现
Array.prototype.myAt = function(searchIndex){
let arr = this
let len = arr.length
let searchIndex = searchIndex >= 0 ?
searchIndex : Math.abs(searchIndex) < len ? searchIndex + len : Infinity
return arr[searchIndex]
}
用法
const arr = ['a','b','c','d','a','e']
arr.indexOf('b') // 从前往后查找'b',返回它的索引1
arr,indexOf('b',2) // 从索引2开始,从前往后查找'b',找不到,返回 -1
arr.lastIndexOf('a') // 从后往前查找'a',返回它的索引4
arr.lastIndexOf('a',2) // 从索引2开始,从后往前查找'a',返回它的索引0
arr.includes('c') // 数组存在'c',返回 true
arr.includes('c',3) // 从索引3开始,数组不存在'c',返回 false
arr.includes('c',300) // 超出数组长度,返回 false
arr.includes('c',-2) // 负值=>负值+数组长度=>4,从索引4开始查找,返回 false
arr.includes('c',-100) // 负值=>负值+数组长度=>-94,从头开始查找,返回 true
实现
Array.prototype.myIndexOf = function(target,start = 0){
let arr = this
let len = arr.length
let _start = start >= 0 ? start : Math.abs(start)<= len ? len + start : 0
for(;_start < len;_start++){
if(arr[_start] === target){
return _start
}
}
return -1
}
用法
lastIndexOf 和 indexOf 相比,有些地方是反过来的:
const arr = [1,2,3,2,5] arr.lastIndexof(2) // 3
实现
Array.prototype.myLastIndexOf = function(target,start){
let arr = this
let len = arr.length
start = start || arr[arr.length - 1]
let _start = start < 0 ? len + start : start >= len ? arr.length - 1 : start
for(;_start > 0;_start--){
if(arr[_start] === target){
return _start
}
}
return -1
}
用法
inlcudes 和 indexOf 类似,但是返回的是布尔值。
为什么有了 indexOf 还要引入 inlcudes?一是因为返回布尔值,语义更加清晰;二是因为 includes 内部使用的是类似 Object.is 的比较方式,而非 indexOf 所使用的 ===,所以可以准确判断 NaN。
[1,NaN].indexOf(NaN) // -1 [1,NaN],includes(NaN) // true // 然而,inlcudes 仍然无法准确判断±0,会认为两者相等 [1,+0].includes(-0) // true [1,0].includes(+0) // true
实现
Array.prototype.myIncludes = function(target,start = 0){
let arr = this
let len = arr.length
let _start = start >=0 ? start : Math.abs(start) <= len ? start + len : 0
function isSame(x,y){
return x === y || typeof(x)=='number'&&typeof(y)=='number'&&isNaN(x)&&isNaN(y)
// return x === y || x!=x && y!= y
// return x === y || Number.isNaN(x) && Number.isNaN(y)
}
for(;_start < len;_start++){
if(isSame(arr[_start],target)){
return true
}
}
return false
}
这里判断 NaN 的方式很多,一种是直接利用最准确的 Number.isNaN,一种是使用 isNaN,但要保证参数是数字,还有一种是利用 NaN 自身的特性,即“自己不等于自己”。
用法
slice 用于产生数组切片:
begin 和 end,表示开始位置和结束位置;可以只接受一个参数 begin,表示开始位置;可以不接受任何参数,则缺省开始位置为第一个元素,结束位置为最后一个元素begin 可以是正数或者负数:
end 可以是正数或者负数:
begin 可能大于 end,此时就直接返回一个空数组const arr = [1,2,3,4,5] arr.slice(1) // [2,3,4,5] arr.slice(1,4) // [2,3,4] arr.slice(-4,-1) // [2,3,4] 负值 => 数组长度加负值 arr.slice(4,1) // [] 反向索引,返回空数组
实现
// 通过默认参数值,为 begin 和 end 设置缺省值
Array.prototype.mySlice = function(begin = 0,end = this.length){
let arr = this
let len = arr.length
let res = []
let k = 0
begin = begin >= 0 ? begin : Math.abs(begin) <= len ? begin + len : 0
end = end < 0 ? end + len : Math.min(end,len)
for(;begin < end;begin++){
res[k++] = arr[begin]
}
return res
}
用法
用于数组扁平化(数组降维):
Infinity 可以直接将数组转化为一维数组flat 的实现可以参考数组扁平化的方法,但它实现起来需要更加灵活,可以传参控制降维次数[1,[2,3],[[4,5],6]].flat() // [1,2,3,[4,5],6] [1,[2,3],[[4,5],6]].flat(2) // [1,2,3,4,5,6]
实现
1)reduce + 递归
Array.prototype.myFlat = function(times = 1){
let arr = this
// 如果参数无法转化为数字,或小于等于0,则直接将原数组返回
if(!Number(times) || Number(times) <= 0){
return arr
}
return arr.reduce((acc,cur) => {
return acc.concat(Array.isArray(cur) ? cur.myFlat(times - 1) : cur)
},[])
}
2)forEach + 递归
Array.prototype.myFlat = function(times = 1){
let arr = this
let res = []
if(!Number(times) || Number(times) <= 0){
return arr
}
arr.forEach(el => {
res.concat(Array.isArray(el) ? el.myFlat(times - 1) : el)
})
return res
}
3)for 循环 + in 检查 + 递归
Array.prototype.myFlat = function(times = 1){
let arr = this
let res = []
if(!Number(times) || Number(times) <= 0){
return arr
}
for(let i = 0;i < arr.length;i++){
if(i in arr){
if(Array.isArray(arr[i])){
res = [...res,...arr[i].myFlat(times - 1)]
} else {
res = [...res,arr[i]]
}
}
}
return res
}