Array.prototype.slice.call 將僞數組轉成真數組的原理是什麼?

很久沒上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
}

ToObjectToUnit32ToIntegerToString
slice 方法不要求 this 必須是數組,所以類數組對象也能夠調用該方法,在本例中入參 startend 均爲 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]

第一次寫文章,想到哪寫到哪,寫得有點亂,各位看官勿怪,若是對你有用,還請點個贊~函數

相關文章
相關標籤/搜索