Array.prototype.slice.call()詳解及轉換陣列的方法
在翻看以前公司留下的專案時,看到一段程式碼。
var $show=$imgList.filter(':visible');
var showPics=Array.prototype.slice.call('$show',0);
其中第二行雖然能猜出大意,但也有一些費解,不知道這樣的用意為何。於是在網上搜了一下,發現關於這個用法講解的還挺多,選其中一篇較為詳細的,轉存來以備不時之需。在此感謝小平果的分享:http://blog.csdn.net/i10630226。
Array.prototype.slice.call()方法詳解
在很多時候經常看到Array.prototype.slice.call()方法,比如Array.prototype.slice.call(arguments),下面講一下其原理:
1、基本講解
1.在JS裡Array是一個類 slice是此類裡的一個方法 ,那麼使用此方法應該Array.prototype.slice這麼去用
slice從字面上的意思很容易理解就是擷取(當然你不是英肓的話) 這方法如何使用呢?
arrayObj.slice(start, [end]) 很顯然是擷取陣列的一部分。
2.我們再看call
call([thisObj[,arg1[arg2[[argN]]]]])
thisObj是一個物件的方法
arrg1~argN是引數
那麼Array.prototype.slice.call(arguments,1);這句話的意思就是說把呼叫方法的引數截取出來。
如:
function test(a,b,c,d)
{
var arg = Array.prototype.slice.call(arguments,1);
alert(arg);
}
test("a","b","c","d"); //b,c,d
2、疑惑解答
先給個例子,這是jqFloat外掛裡的程式碼:
if (element.data('jDefined')) {
if (options && typeof options === 'object') {
methods.update.apply(this , Array.prototype.slice.call(arguments, 1));
}
} else {
methods.init.apply(this, Array.prototype.slice.call(arguments, 1));
}
多次用到 Array.prototype.slice.call(arguments, 1),不就是等於 arguments.slice(1) 嗎?像前者那樣寫具體的好處是什麼?這個很多js新手最疑惑的地方。那為什麼呢?
因為arguments並不是真正的陣列物件,只是與陣列類似而已,所以它並沒有slice這個方法,而Array.prototype.slice.call(arguments, 1)可以理解成是讓arguments轉換成一個數組物件,讓arguments具有slice()方法。要是直接寫arguments.slice(1)會報錯。
typeof arguments==="Object" //而不是 "Array"
3、真正原理
Array.prototype.slice.call(arguments)能將具有length屬性的物件轉成陣列,除了IE下的節點集合(因為ie下的dom物件是以com物件的形式實現的,js物件與com物件不能進行轉換)
如:
var a={length:2,0:'first',1:'second'};//類陣列,有length屬性,長度為2,第0個是first,第1個是second
console.log(Array.prototype.slice.call(a,0));// ["first", "second"],呼叫陣列的slice(0);
var a={length:2,0:'first',1:'second'};
console.log(Array.prototype.slice.call(a,1));//["second"],呼叫陣列的slice(1);
var a={0:'first',1:'second'};//去掉length屬性,返回一個空陣列
console.log(Array.prototype.slice.call(a,0));//[]
function test(){
console.log(Array.prototype.slice.call(arguments,0));//["a", "b", "c"],slice(0)
console.log(Array.prototype.slice.call(arguments,1));//["b", "c"],slice(1)
}
test("a","b","c");
*筆者注:在驗證7、8行程式碼時,不可用方法呼叫的做法,如:
var a={0:'first',1:'second'};//去掉length屬性
function test(){
console.log(Array.prototype.slice.call(arguments,0));//[Object],slice(0)
console.log(Array.prototype.slice.call(arguments,1));//[],slice(1)
}
test(a);//此時第一行打印出來的是有一個元素的陣列(此元素即為a物件),而不是一個空陣列。
因為使用函式test()將a做為引數呼叫時,此時的arguments是包含a物件的類陣列物件,其預設是有length屬性的,所以在函式 test(a) 內部呼叫Array.prototype.slice.call(arguments,0)這句話時,始終會得到一個有元素的陣列。(此處囉嗦的其實是引數a與裡面的anguments是不等價的,一開始沒注意其中不同,在此提醒自己)。
*
補充:
將函式的實際引數轉換成陣列的方法
方法一:var args = Array.prototype.slice.call(arguments);
方法二:var args = [].slice.call(arguments, 0);
方法三:
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
最後,附個轉成陣列的通用函式
var toArray = function(s){
try{
return Array.prototype.slice.call(s);
} catch(e){
var arr = [];
for(var i = 0,len = s.length; i < len; i++){
//arr.push(s[i]);
arr[i] = s[i]; //據說這樣比push快
}
return arr;
}
}