Array.prototype.slice.call 将伪数组转成真数组的原理是什么?

40次阅读

共计 1457 个字符,预计需要花费 4 分钟才能阅读完成。

好久没上 SF,昨天上来看到一个问题,引起了我的兴趣。一番探索和研究后,有了此篇文章,也算是对该问题的解答。
let pretendArr = {0:0,1:1,2:2,length:3};
[].slice.call(pretendArr); //[0,1,2]
请看上面的例子可能很多前端童鞋都很知道 Array.prototype.slice.call 可以用于将类数组对象转为数组,call 和 apply 的用法和作用网上一搜一大堆。在这里主要是为了让 pretendArr 借用 Array 构造函数原型上的 slice 方法,并且改变 slice 方法里的 this 的指向。所以这个问题其实不在于 call 或者 apply,关键在于 Array.prototype.slice 这个方法上。
slice 这个方法是 js 原生方法,自然而然,我会想到去找找 es 的规范,看看这个方法是怎样定义,以及如何实现的。
以下是 es 对该方法定义的截图,图片看不清楚的童鞋可以看看链接
嫌英文字母太多的可以直接看以下我写的 slice 方法的伪代码:
Array.prototype.slice = (start, end) => {
let O = ToObject(this)
let A = new Array()
let lenVal = O.length
let len = ToUnit32(lenVal)
let relativeStart = ToInteger(start)
let k, final, relativeEnd
if (relativeStart < 0) {
k = max(len + relativeStart, 0)
} else {
k = min(relativeStart, len)
}
if (end === undefined) {
relativeEnd = len
} else {
relativeEnd = ToInteger(end)
}
if (relativeEnd < 0) {
final = max(len + relativeEnd, 0)
} else {
final = min(relativeEnd, len)
}
let n = 0
while (k < final) {
let Pk = ToString(k)
let kPresent = O.hasOwnProperty(Pk)
if (kPresent) {
let kValue = O[Pk]
Object.defineProperty(A, ToString(n), {
value: kValue,
writable: true,
enumerable: true,
configurable: true
})
}
k++
n++
}
return A
}
ToObject、ToUnit32、ToInteger、ToStringslice 方法不要求 this 必须是数组,因此类数组对象也可以调用该方法,在本例中入参 start 和 end 均为 undefined,实际上是根据 类数组对象 的 length 属性,从 0 到 length- 1 去把类数组对象对应的值取出来,放到前面声明的数组 A 里,最终再 return A 因此,类数组对象的 length 属性很重要,并且该对象里的数序也很重要,都会影响到转换为数组的结果。如以下例子:
let pretendArrA = {0:0, 1:1, 2:2, length:2};
[].slice.call(pretendArrA); //[0,1]

let pretendArrB = {0:0, 2:2, length:3};
[].slice.call(pretendArrB); //[0, undefined, 2]
第一次写文章,想到哪写到哪,写得有点乱,各位看官勿怪,如果对你有用,还请点个赞~

正文完
 0