[1].slice.call({ length: 1, 0: 3 }) 為什麼返回[3]?
[1].slice.call([2]) 返回[2] 這是合情合理的正常使用
這時候call的作用是把this由[1] 改成[2]
所以slice方法執行結果返回[2]
但[1].slice.call({ length: 1, 0: 3 }) 為什麼返回[3]
我認為這種現象已經不是call機制的原因了
是Array.prototype.slice內部原因
或者說slice既然是Array.prototype下的
它應該只作用於數組才對呀
那它為什麼能接收並成功作用於Array實例之外的對象
mdn上並沒有對這個函數的此種用法的原理做解釋
具體實現是怎樣的
如何編寫這樣的方法
V8這個方法的實現是JS版本還是c++
https://www.ecma-international.org/ecma-262/#sec-array.prototype.slice這是slice章節
不是很能看懂
有人稍微幫忙解釋下嗎
謝謝
另外問個問題
ES規範5.1/6/7/8 各個版本slice步驟都有一些差異
怎麼看當前瀏覽器實現的是哪個版本
es 規範
如果你讀到了最後,就會看到:
NOTE 3
The slice
function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.
V8
在 V8 中,有專門針對在非 Array 類型的對象上調用 Array 函數的測試用例:array-functions-non-arrays.js(看文件名就知道是什麼意思了)
shouldBe("Array.prototype.slice.call({}, 0, 1)", "[]");
shouldBe("Array.prototype.slice.call(["b", "a"], 0, 1)", "["b"]");
shouldBe("Array.prototype.slice.call({ length:2, 0:"b", 1:"a" }, 0, 1)", "["b"]");
shouldBe("Array.prototype.slice.call(new TwoItemConstructor, 0, 1)", "["b"]");
不僅如此。不論是你已經想到的,還是你沒有想到了,V8 的 test case 都想到了。
比如:
function f() { return arguments; }
var o = f();
o.length = -100;
Array.prototype.slice.call(o);
再比如:
(function () {
arguments.length = 7;
Array.prototype.slice.call(arguments);
})();
再比如:
shouldThrow("Array.prototype.slice.call(undefined, 0, 1)");
再比如:
var array = [];
var proxy = new Proxy(new Proxy(array, {}), {});
var Ctor = function() {};
var result;
array.constructor = function() {};
array.constructor[Symbol.species] = Ctor;
Array.prototype.slice.call(proxy);
還比如:
var o = { length: Number.MIN_VALUE };
var result = Array.prototype.slice.call(o);
assertEquals(0, result.length);
var o = { length: Number.MIN_VALUE };
var result = Array.prototype.slice.call(o, Number.MAX_VALUE);
assertEquals(0, result.length);
var o = { length: Number.MAX_VALUE };
var result = Array.prototype.slice.call(o, Number.MAX_VALUE - 1);
assertEquals(0, result.length);
由於我英語水平不是很好,上學時沒有好好學,都是工作之後又重新補的英語。很多時候讀那些規範也是很吃力的,所以我就喜歡看 V8 的 test case。
源碼
你也問道了源碼問題。其實一個好消息是,slice 的源碼是使用 js 寫的:v8/src/js/array.js slice。源碼中調用了一些以百分號(%)開頭的函數,在 js 中如果有百分號開頭的函數,說明這個函數不是用 js 寫的,而是直接使用 C++ 寫的。
最後是廣告時間,我的 V8 專欄: V8 源碼及周邊。
題主真的認真看MDN了嗎? Array.prototype.slice()這裡面寫的很清楚了啊,可以把類似數組的對象轉成數組,你這個對象{ length: 1, 0: 3 },有length,有0索引,正好類似數組
類似數組(Array-like)對象
slice
方法可以用來將一個類數組(Array-like)對象/集合轉換成一個數組。你只需將該方法綁定到這個對象上。下述代碼中 list 函數中的arguments
就是一個類數組對象。function list() { return Array.prototype.slice.call(arguments);}var list1 = list(1, 2, 3); // [1, 2, 3]
補充一下,鑒於題主問的是「為什麼會這樣」,可以看看es規範,https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf,22.1.3.23節,最後的note3提到了
The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.
這應該可以解決題主的疑惑了,規範就是這麼規定的,各種實現當然要按照規範去實現了。
這事情跟 duck type 沒什麼關係。標準庫有些方法是會強制檢驗類型,不符合就扔 TypeError,有些則不會,視情(xin)況(qing)而定。
去讀下標準就知道,Array 上的大量方法早在 ES3 時代就是故意設計為 generic 的,也就是並不一定要求 this 為 Array。通常我們稱這種可以被這些 Array 方法操作的類型為 ArrayLike。
BTW,輪子又來提供打臉素材了。
你改用TypeScript來寫,編譯就會失敗。
說明輪子根本沒有用 TS 試過。
直接 Array.prototype.slice.call({ length: 1, 0: 3 }) 調用返回的是 any 類型(因為 call 的類型標註比較『隨便』),所以根本不會爆類型錯。
就算將來 TS 改進之後,也不應爆類型錯,因為 TS 早已經有 ArrayLike&
輪子身為軟狗,真是給巨硬丟人啊!
一種遠古的用法
Array.prototype.slice.call(arguments);
用來把類數組對象arguments轉換為一個真正的數組。
沒有規定Array.prototype下的方法只能作用於數組。
最後是你給的ECMA規範
NOTE 3
Theslice
function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.
這就是duck type啊另外 js的數組就是key是整數並且有length屬性的object
帶有length類數組,沒毛病。當成數組就行。
醒醒,JavaScript的類型都是假的,只要對象長得像就可以了。你改用TypeScript來寫,編譯就會失敗。
類數組就長這樣:{0: "a", 1: "b", length: 2}
[1].slice.call({length: 1, 0: 3}) 其實就是借用Array.prototype.slice.call({length: 1, 0: 3})
然後返回一個純數組 [3] 這裡[1] 就是誤導你的,其實就是借用數組原型, slice省略了傳入的參數
樓主你還沒有真正理解js,js裡面數據類型的邊界是比較模糊的,除了幾個基礎類型,其他都是對象,數組也只是一堆key為數字(實際上是數字字元串)的對象,外加一個length屬性。只不過數組的原型上有一堆「專門」作用於數組的方法而已,說是專門,但是實際上這些方法是很沒有節操的,通過call, apply, bind可以作用於任意擁有length屬性的對象。為什麼一定要有length才能用呢?因為數組的方法基本上都要去讀寫length,沒有length沒辦法幹活。
因為內部的實現是用for循環的,你傳進去的對象是一個類數組。什麼是類數組?就是一個有length屬性並且每一個key都是數字的對象,比如你getElementByClassName獲取到的DOM列表,比如你題目中給call穿進去的參數。for循環並不是數組的專利,只要你有length屬性,只要你的下標(或者key)是一個數字,就可以用for循環。es6之前通常用這個方式把類數組轉成真正的數組從而使用push這種真正數組才能用的方法,用了es6的Array.from就沒必要這樣了。手機碼字沒排版賊不爽,回去排下版
觀V8源碼中的array.js,解析 Array.prototype.slice為什麼能將類數組對象轉為真正的數組?
"call這個神奇的方法、slice的處理缺一不可"....
不知道怎麼解釋更多了 不返回這個還能返回什麼呢
{ length: 1, 0: 3 }是一個類數組,長度為1,第一項為3,可以理解為[3]
看到了length了嗎?每個數組裡都有一個length,你把它改改啊,改了之後就知道,為什麼這裡得到的是【3】了。
slice 可以把類數組轉換成數組
js高設,請看
同為動態語言,在這個問題上可以做一個類比。
推薦閱讀:
※本人前端,剛入手了mac本,以前沒用過,請各位大大推薦一下mac本上做前端的編碼開發或者調試輔助工具?
※哪裡有比較成熟的 React.js 項目案例?
※960px 寬度的網格布局過時了嗎?
※想掌握前端的構建工具,有沒有學習路線推薦?
※如何看待只用CSS畫圖?
TAG:前端開發 | JavaScript | Nodejs | 前端工程師 | V8 |