前言
JS 中数组和字符串身上的很多相同的方法,两种类型也经常相互转换
这次就对比总结一下双方的共同方法并介绍如何通过 call 来借用对方的方法
相同方法
at
at 方法等价于通过数字索引去访问,区别在于可以接受一个负值,表示从最后一个元素开始倒数。
当你需要访问栈顶/末尾元素的时候,不妨用其代替麻烦的 arr[arr.length-1]
let arr = [1, 2, 3]
let str = '123'
arr.at(-1) // 3
str.at(-1) // '3'
concat
concat 方法用于拼接多个元素
数组的 concat 和 push 很像,区别在于前者只会返回拼接好的新数组而不改变原数组,而且如果传参是数组的话则会展开一层
let arr = [1, 2, 3]
let str = '123'
arr.concat([4, 5], 6, 7) // [1, 2, 3, 4, 5, 6, 7]
arr.concat([4, 5, [6], 7]) // [1, 2, 3, 4, 5, [6], 7]
str.concat('45', '6', 7) // '1234567'
有一个特殊的符号属性 Symbol.isConcatSpreadable 可以将类数组视为数组展开,用的很少,类数组一般都通过 Array.from 来处理
function fun() {
let args = [].concat(arguments)
console.log(args) // [ [Arguments] { '0': 1, '1': 2, '2': 3 } ]
arguments[Symbol.isConcatSpreadable] = true
args = [].concat(arguments)
console.log(args) // [1, 2, 3]
}
fun(1, 2, 3)
出于性能原因,强烈建议使用赋值操作符(
+,+=)代替字符串concat方法。
查询方法
查找元素的三个方法,includes 判断元素是否存在,返回布尔值,indexOf 查找元素的索引,lastIndexOf 从后向前寻找,三个方法都支持传入第二个参数设置开始查找的索引
数组的 include 方法相比另外两个有一些区别,其还可以判断 NaN,也会将稀疏数组的空值视为 undefined
数组还有功能更强大的 find 和 findIndex 方法,会在下一节中介绍
slice
slice 用于切割数组/字符串,依次传递开始和结束索引,允许负值,默认值分别为 0 和 length,返回值是个左闭右开区间 [start, end)
字符串还有个相同功能的 subString 方法(参数不接受负值)和一个已废弃的 subStr 方法
需要注意的是,执行 slice 方法的数组如果它的构造函数(constructor)是一个类的话,会调用这个构造函数返回该类的实例,否则会返回一个数组
// 类继承
class MyArray1 extends Array {
constructor() {
super(...arguments)
console.log('MyArray1的构造函数被调用了')
}
}
// 组合寄生继承
function MyArray2() {
Array.call(this, ...arguments)
console.log('MyArray2的构造函数被调用了')
}
MyArray2.prototype = Object.create(Array.prototype)
MyArray2.prototype.constructor = MyArray2
let arr1 = new MyArray1(1, 2, 3) // MyArray1的构造函数被调用了
let clone1 = arr1.slice() // MyArray1的构造函数被调用了
console.log(clone1 instanceof MyArray1) // true
let arr2 = new MyArray2() // MyArray2的构造函数被调用了
let clone2 = arr1.slice()
console.log(clone2 instanceof MyArray2) // false
console.log(clone2 instanceof Array) // true
迭代
数组和字符串都实现了迭代接口(Symbol.iterator),属于可迭代对象,可以使用 for of 遍历
let arr = [1, 2, 3]
let str = '123'
for (const num of arr) {
console.log(num) // 1 2 3
}
for (const char of str) {
console.log(char) // '1' '2' '3'
}
借用方法
有很多强大的方法只存在于数组的原型上,字符串想使用的话需要先转换成数组,调用完再转回来,有点麻烦
我们可以通过 call 改变方法的 this 指向,直接调用数组的一些方法
findIndex
字符串查找索引的 indexOf 并不够强大,当你想查找第一个元音字母的时候,可以用数组的 findIndex 方法,find findLast findLastIndex 也同理
let str = 'Hello World'
let index = [].findIndex.call(str, (val) => 'aeiouAEIOU'.includes(val))
console.log(index) // 1
join
如果你想在字符串每个字母中间插入一个字符,数组的 join 方法刚好可以满足
let str = 'Hello'
let s = [].join.call(str, '-')
console.log(s) // 'H-e-l-l-o'
map
数组的 map 方法也字符串也可以使用
let str = 'Hello World'
let s = [].map.call(str, switchChar).join('')
console.log(s) // 'hELLO wORLD'
function switchChar(char) {
if (char >= 'a' && char <= 'z') {
return char.toUpperCase()
} else if (char >= 'A' && char <= 'Z') {
return char.toLowerCase()
} else {
return char
}
}
reverse
很遗憾,reverse 方法会修改原数组,不能通过 call 去使用,想要反转字符串,还是得老老实实转换成类型转换
let str = '123456'
let s = Array.from(str).reverse().join('')
console.log(s) // '654321'
数组也能以相同的方式借用字符串的方法,不过数组有更方便的
mapforEach,完全不需要通过 call 去调用
结语
以上就是本文的所有内容了,如果文中有错误或不严谨的地方,请务必指正,十分感谢。