從一道面試題談 Array.prototype.push()

YanceyOfficial發表於2019-04-02

引子

今天早晨翻木易楊大佬的 GitHub,發現他更新了一道面試題。

昨天在寫 《JavaScript API 全解析》 系列時寫到了 Array.from() 這一章,並惡補了一下類陣列相關的知識,正好可以拿這道題練練手。

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push,
}
obj.push(1)
obj.push(2)

// 列印出什麼?
console.log(obj)
複製程式碼

分析

一步一步來,我們暫時去掉 splice 屬性,只探討 Array.prototype.push 會給類陣列物件帶來什麼影響。

push 方法有意具有通用性。該方法和 call() 或 apply() 一起使用時,可應用在類似陣列的物件上。push 方法根據 length 屬性來決定從哪裡開始插入給定的值。如果 length 不能被轉成一個數值,則插入的元素索引為 0,包括 length 不存在時。當 length 不存在時,將會建立它。

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'push': Array.prototype.push,
}
obj.push(1)
obj.push(2)

console.log(obj) // {2: 1, 3: 2, length: 4, push: ƒ}
複製程式碼

根據 MDN 描述,例子中的 length 屬性值為 2,意味著 將從屬性為 2 的地方開始增加鍵值對,正巧 obj 有一個 '2' 屬性了,所以 obj.push(1) 會將其屬性值覆蓋成 1,並且 length 屬性的屬性值變成 3,第二個 push 同樣,將 '3' 的屬性值覆蓋成 2,同時 length 變成 4

我們做一下變種,將類陣列物件的 length 變為 3,可見它從屬性為 3 的地方開始插入鍵值對,因為沒有屬性 4,所以新增一個屬性為 4 的鍵值對。

var obj = {
    '2': 3,
    '3': 4,
    'length': 3,
    'push': Array.prototype.push,
}
obj.push(1)
obj.push(2)

console.log(obj) // {2: 3, 3: 1, 4: 2, length: 5, push: ƒ}
複製程式碼

綜合這兩個例子,當給類陣列物件 push 元素時,將把 length 的屬性值作為第一個屬性名,如果有重複屬性則覆蓋其屬性值,否則向後新增一個鍵值對。

解決了 push,我們繼續看,這裡直接列印出原題的結果。我們發現 物件 變成了 類陣列,注意這裡不是真正的陣列,我們使用 Array.isArray(obj) 會返回 false。

結果不難理解,因為在上面的例子中我們知道 length 變成了 4,所以索引為 0 和 1 會變成 empty。

從一道面試題談 Array.prototype.push()

最後

學習過類陣列物件的同學肯定知道類陣列裡要有一個 length 屬性,且屬性值應為大於等於0的整數。但為什麼有 splice 屬性且其屬性值為函式時就能轉換為類陣列呢?我們列印出下面一組示例

// 只要屬性名是 splice ,屬性名隨便是個函式就可以轉變為類陣列
var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice'(){},
}

obj; // [empty × 2, 2: 3, 3: 4, splice: ƒ]

// 屬性名不是函式則不行
var obj1 = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': 123,
}

obj1; // {2: 3, 3: 4, length: 2, splice: 123}

// 屬性名不是 splice 不行
var obj2 = {
    '2': 3,
    '3': 4,
    'length': 2,
    'slice': 123,
}

obj2; // {2: 3, 3: 4, length: 2, slice: 123}

// 屬性名不是 splice 即使屬性值是函式也不行
var obj3 = {
    '2': 3,
    '3': 4,
    'length': 2,
    'slice'(){},
}

obj3; //{2: 3, 3: 4, length: 2, slice: ƒ}

// 沒有length一定不行
var obj4 = {
    '2': 3,
    '3': 4,
    'splice'(){},
}

obj4; // {2: 3, 3: 4, splice: ƒ}
複製程式碼

好吧,我也不知道,看木易楊大佬這道題底下的評論也是卡在這裡了,歡迎大家討論。

從一道面試題談 Array.prototype.push()

相關文章