很久没上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 这个方法上。git
slice这个方法是js原生方法,天然而然,我会想到去找找 es 的规范,看看这个方法是怎样定义,以及如何实现的。github
如下是es对该方法定义的截图,图片看不清楚的童鞋能够看看连接segmentfault
嫌英文字母太多的能够直接看如下我写的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、ToString
slice 方法不要求 this 必须是数组,所以类数组对象也能够调用该方法,在本例中入参 start 和 end 均为 undefined,其实是根据 类数组对象 的 length 属性,从0到length-1去把类数组对象对应的值取出来,放到前面声明的数组 A 里,最终再return A
所以,类数组对象的 length 属性很重要,而且该对象里的数序也很重要,都会影响到转换为数组的结果。如如下例子:app
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]
第一次写文章,想到哪写到哪,写得有点乱,各位看官勿怪,若是对你有用,还请点个赞~函数