1. 程式人生 > >Array.prototype.slice.call()詳解及轉換陣列的方法

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;
    }
}