前言:
在 解開那一層面紗蹦误,js類數(shù)組的小秘密(上篇)里面,已經(jīng)提到類數(shù)組的定義尚镰,以及常見的類數(shù)組類型哪廓,類數(shù)組的轉(zhuǎn)化涡真,接下來我們來探討如何定義一個(gè)類數(shù)組對象?類數(shù)組是否可以借用數(shù)組上面的方法缸剪?jQuery類數(shù)組對象的表現(xiàn)杏节?
1.如何定義一個(gè)類數(shù)組對象典阵?
根據(jù)上面文章的定義:
- 擁有l(wèi)ength屬性壮啊,其它屬性(索引)為非負(fù)整數(shù)
- 不具有數(shù)組所具有的方法(非必須)
如果想自己創(chuàng)一個(gè)類數(shù)組對象是很容易的:
let arrLike = {
0: "a",
1: "b",
2: "c",
length: 3
};
然后我們使用魯大師(lodash.js v4.17.5)
來判斷下,我們創(chuàng)建的是不是滿足條件:
import { isArrayLike, isArrayLikeObject } from "lodash/index";
isArrayLike(arrLike ); // true
isArrayLikeObject(arrLike ); // true
親測是滿足條件的玄渗,那接下來我們就去操作類數(shù)組藤树。
2.“借用”數(shù)組方法來操作類數(shù)組對象
之前已經(jīng)實(shí)現(xiàn)類數(shù)組轉(zhuǎn)成數(shù)組,是借用數(shù)組上面的slice
和splice
方法巡莹,那么在不把類數(shù)組轉(zhuǎn)成數(shù)組的情況下降宅,我們也是可以像操作數(shù)組一樣操作類數(shù)組對象:
let arrLike = {0: "a", 1: "b", 2: "c", length: 3};
Array.prototype.push.call(arrLike, "d"); // 4, arrLike = {0: "a", 1: "b", 2: "c", 3: "d", length: 4}
Array.prototype.pop.call(arrLike); // 3, arrLike = {0: "a", 1: "b", 2: "c", length: 3}
Array.prototype.slice.call(arrLike, 1); // ["b", "c"], arrLike = {0: "a", 1: "b", 2: "c", length: 3}
Array.prototype.splice.call(arrLike, 1, 1); // ["b"], arrLike = {0: "a", 1: "c", length: 2}
....... // other methods eg. map, forEach, filter...
上面測試了“一些方法”(并不是全部)腰根,都是可以實(shí)現(xiàn)我們想要的效果拓型。既然并不是全部方法都能借用以后得到期望的效果劣挫,那么是誰這么特殊呢?
3. 特殊的Array.prototype.concat
上面演示了類數(shù)組對象借用數(shù)組的方法球拦,來實(shí)現(xiàn)一些操作帐我,但是是不是數(shù)組的所有方法拦键,被借用以后都能達(dá)到和操作數(shù)組一樣的效果呢芬为?
我們來看一段代碼:
let arrLike = {0: "a", 1: "b", 2: "c", length: 3};
let arr = ["a", "b", "c"];
arr.concat("d", "e", "f"); // ["a", "b", "c", "d", "e", "f"]
我們期待[].concat.call(arrLike, "d", "e", "f");
運(yùn)行的結(jié)果是{ 0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", length: 6 }
然而結(jié)果卻是:
在看一個(gè)例子:
原因是concat內(nèi)部會新創(chuàng)建一個(gè)數(shù)組媚朦,來接收傳入的每一項(xiàng)莲镣,如果傳入的是數(shù)組涎拉,會先對數(shù)組進(jìn)行一次展平,然后把所有項(xiàng)都放入這個(gè)新數(shù)組半火,結(jié)果就是我們看到的結(jié)果了钮糖,并沒根據(jù)length進(jìn)行操作,所以沒有看到我們期望的效果阎抒。
還好我們可以使用push
來實(shí)現(xiàn)我們想要的效果
let arrLike = {0: "a", 1: "b", 2: "c", length: 3};
Array.prototype.push.call(arrLike, "d", "e", "f"); // 6, arrLike = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", length: 6}
4.jQuery類數(shù)組對象的表現(xiàn)
既然上面我們已經(jīng)對類數(shù)組對象有了一些認(rèn)識且叁,那jQuery獲取到的結(jié)果也是類數(shù)組對象逞带,看下圖:
再看看我們自己創(chuàng)建的類數(shù)組對象:
對比下可以看出很明顯的區(qū)別展氓,我們創(chuàng)建的好像就是對象脸爱,而jQuery得到的結(jié)果卻是“長得像”數(shù)組阅羹,這是為什么呢勺疼,
再看jQuery.fn上面打印出來的結(jié)果,我們可以發(fā)現(xiàn)一些數(shù)組上面的方法:下圖是
jQuery
原型上的部分截圖很面熟捏鱼,好像是數(shù)組的方法我們來驗(yàn)證下:
jQuery.fn.sort === [].sort; // true
jQuery.fn.splice === [].splice; // true
jQuery.fn.slice === [].slice; // false
jQuery.fn.push === [].push; // true
其中有部分方法確實(shí)是來自數(shù)組执庐,但其實(shí)這幾個(gè)方法里面哪一個(gè)才是讓jQuery類數(shù)組對象
和我們自己創(chuàng)建的類數(shù)組對象變相不一樣的原因呢?
進(jìn)過測試:
let arrLike = {0: "a", 1: "b", 2: "c", length: 3, splice: [].splice};
arrLike; // Object(3) ["a", "b", "c", splice: ?]
let arrLike01 = {0: "a", 1: "b", 2: "c", length: 3, sort: [].sort};
arrLike01; // {0: "a", 1: "b", 2: "c", length: 3, sort: ?}
原來是splice
方法导梆,那么是不是必須是Array.prototype.splice
呢轨淌?帶著這個(gè)疑問再次驗(yàn)證:
let arrLike = {0: "a", 1: "b", 2: "c", length: 3, splice: () => {} };
arrLike; // Object(3) ["a", "b", "c", splice: ?]
let arrLike = { 0: "a", 1: "b", 2: "c", length: 3, splice: [].sort };
arrLike; // Object(3) ["a", "b", "c", splice: ?]
let arrLike = { length: 0, splice: () => {} };
arrLike; // Object [splice: ?]
let arrLike = { length: 0, splice: 1 };
arrLike; // {length: 0, splice: 1}
let arrLike = { splice: () => {} };
arrLike; // {splice: ?}
從上面的結(jié)果可以明顯低看出,要想實(shí)現(xiàn)像jQuery一樣的類數(shù)組對象結(jié)構(gòu)看尼,只需要兩個(gè)條件:
- 對象有一個(gè)length屬性递鹉,值為正整數(shù)
- 含有一個(gè)splice方法
其實(shí)jQuery內(nèi)部也是這樣做的藏斩,同樣含有splice
方法躏结,只是jQuery原型上面的splcie
方法,是數(shù)組上面的同名方法狰域。
jQuery.prototype.splice === Array.prototype.splice; // true
到這里關(guān)于“js類數(shù)組(對象)”
的介紹就結(jié)束了媳拴,不得不感嘆js是一門神奇的語言黄橘,各種神秘的東西,繼續(xù)挖掘...