1. 程式人生 > >深入理解js的prototype以及prototype的一些應用

深入理解js的prototype以及prototype的一些應用

上一篇講了js的prototype概念,在這裡回顧一下prototype的定義:

  prototype是函式的一個屬性,並且是函式的原型物件。引用它的必然是函式,這個應該記住。

  但是,很奇怪,各位看官,你有沒有看過類似下面這樣引用prototype的js程式碼:

function func(){
    var args = Array.prototype.slice.call(arguments, 1);
    return args;
 }

  咦???看著上面這行程式碼,你是不是對prototype只是屬於函式產生深深的懷疑呢?

  你明白上面那個函式的意思嗎?我還是解釋一下吧。

  call是函式的一個方法,關於這個方法,它也是隻有····函式····才能夠呼叫的,它的作用是:呼叫引用它的函式。

  就拿這個Array.prototype.slice.call(arguments,1)來講,這裡麵包含太多資訊了,我一個個分析一下。

  slice(start[,end])是js的一個原生陣列函式,作用是獲取陣列中從start下標開始到end下標結束的元素。舉個例子:

var arr1 = [2,3,'l',4,5,'j','i'];
    alert(arr1.slice(1));//結束下標省略表示從開始下標擷取到末尾
   //這裡alert的是3,'l',4,5,'j','i',你自己可以試一下

  arguments是js函式物件的一個屬性,作用是獲取函式的實參,返回的是一個以函式實參為屬性元素的物件。舉個例子:

function args(){
    return this.arguments;//這裡的this可以省略,你自己可以試一下
    //我加上是為了說明,呼叫arguments的只能是物件
}
alert(JSON.stringify(args(1,3,5,6,8)));
//返回的是{"0":"1","1":"3","2":"5","3":"6","4":"8"}

  關於函式的物件屬性arguments這裡就講這麼多了,要詳細理解,可以百度。

  而Array是js中生成陣列的關鍵字。

  這裡為什麼要用Array.prototype來呼叫slice函式呢?而不用Array.slice,原因很簡單,因為陣列的關鍵字Array不能這樣子Array.xx直接呼叫js的陣列函式。但為什麼不能直接這樣呼叫呢?不急,下面我們來做實驗,你就會很清楚了。

alert(Array.slice());
//Uncaught TypeError: Array.slice is not a function

  這裡直接給你報錯了,說明了什麼?呵呵,這說明Array關鍵字確實不能直接呼叫陣列的函式。

alert(JSON.stringify(Array.prototype));
alert(JSON.stringify(Array.prototype.slice()));

  這裡返回都是空陣列···[]···,說明了什麼?說明了Array關鍵字確實是可以呼叫prototype函式的屬性的,同時也說明js是可以這樣子Array.prototype呼叫js的陣列函式的。

  說到這裡,問題就來了,我上面不是說,prototype是js函式的一個屬性,只能被函式呼叫嗎?怎麼這裡Array關鍵字可以呼叫這個屬性prototype呢?那麼,我這不是坑自己對prototype的定義是錯誤的嗎?我這不是給自己打臉嗎?哎,看官,沒錯,你這裡看到的都是正確的。可是,至於Array關鍵字可以呼叫函式的prototype屬性,我有沒有給自己打臉,這裡,我們先別急得下結論。

  轉個彎說,看官是否還記得js生成陣列的幾種方式?應該有多種,但,我這裡就不介紹了。

  不過,你是否看過這樣生成陣列的方式?我們先來看下面的程式碼:

var arr = new Array();

  這個方式生成陣列還記得吧?那麼,我們js的function是不是也可以像下面這樣子生成物件呢?

function func(){

}
var obj = new func();

  上面生成陣列的方式和下面建構函式生成物件的方式是不是很相似?沒錯,js中function和Array都是可以通過new來生成物件這樣的東西,這說明了什麼呢?你看Array()和func()是不是很像?是不是最後面都有一對圓括號?是就對了,呵呵,說了這麼多,我只是想揭露一樣東西,這個東西就是,假如我猜的的沒有錯的話,Array()這個東西其實是js一個·····構造陣列的內建函式····,不然,可以用new Array()的方式生存陣列的方式就說不過去了是吧?

  講到這裡,我們再返回來說js可以這樣子Array.prototype呼叫prototype就很明白不過了是吧?Array()是js的一個內建函式,既然Array是一個函式,那麼Array肯定擁有prototype這個屬性對吧?所以說,Array關鍵字呼叫prototype並沒有違反prototype是函式的一個屬性這個原則,prototype是函式的一個屬性依然是一個不變的結論。

  關於Array生成陣列的方式,類似的我們是否可以這樣子new Object()生成物件,或者new String()這樣子生成字串?既然可以這樣子構造物件和字串,那麼我們下面的程式碼也應該是可行的,對吧?

alert(JSON.stringify(String.prototype));
alert(JSON.stringify(Object.prototype));

  根據上面的解釋,你應該知道這裡是可以執行的吧?你應該知道這裡的之所以能執行的原理吧?你自己試試。這裡就不再解釋了。

  講到這裡,哎,我既然把行文開始的那個函式給忘了?這裡講解一下本文開始那個func函式的作用:

  func函式的作用就是,從第二個實引數開始獲取func函式的實參。

  我來給你分析一下:

function func(){
var args = Array.prototype.slice.call(arguments, 1);
return args;
}
alert(func(0,1,2,3,4));//給func函式傳實參

  Array.prototype是一個空陣列,

   Array.prototype.slice()的意思是一個空陣列呼叫陣列的函式slice(),

   Array.prototype.slice.call()的意思是call函式呼叫陣列的函式slice(),

   這裡call()怎麼呼叫slice()呢?

   是這樣子的,Arguments獲取func函式實參列表,生成一個物件傳遞給call()函式,call函式又把Arguments生成的物件傳遞給Array.prototype這個空陣列,把第二個引數···1···傳遞給slice函式,然後,call就讓引用它的函式slice執行slice(1),所以slice就從1下標開始獲取陣列的元素了,而這個陣列的元素其實就是Arguments的元素,因此,這個函式func的作用就是獲取下標為1開始的所有實參。不相信,你自己可以執行一下上面的函式。

  下面講講prototype的應用:

  應用1:

  給原型物件增加函式,就是讓物件擁有公用的函式。

  例子:我給陣列原型物件增加一個打亂陣列方法:

//給陣列原型增加一個打亂陣列的函式
Array.prototype.shuffle=function(){
var value = this.valueOf(),len = this.length,temp,key;
while(len--){
//隨機生成陣列的下標
key = Math.floor(Math.random()*len);
//temp為中間交換變數
temp = value[key];
value[key] = value[len];
value[len] = temp;
}
return value;
}
var arr1 = [0,1,2,3,4,5,6,7,8,9];
var arr2 = ['a','b','c','d','e','f'];    
alert(JSON.stringify(arr1.shuffle()));
alert(JSON.stringify(arr2.shuffle()));

  你可以嘗試著再增加一個數組arr3看看它能不能呼叫shuffle函式,因為我這裡是給Array的原型物件增加的函式,所以在這個指令碼內,所有陣列都擁有shuffle這個函式。

  應用2:

  給原型物件增加屬性,也就是給物件增加公用的屬性

  例子:

function fun(){
        
}
fun.prototype.name = '小東';
fun.prototype.arr = [1,2,3,4];//這裡的屬性可以是陣列,也可以是物件
var ob1 = new fun();
var ob2 = new fun();
alert(JSON.stringify(ob1.name));
alert(JSON.stringify(ob2.arr));

  應用3:

  實現原型繼承;

    function P1(){
    
    }
    function P2(){
        
    }
    //原型物件增加屬性和方法
    P2.prototype.name = 'P2"s name';
    P2.prototype.get=function(value){
            return value;
    }
    //例項化P2建構函式的一個物件
    var obp2 = new P2();//這個物件應該包含所有原型物件的屬性和方法
    //給P1的原型物件賦值一個物件,相當於P1繼承了obp2的所有屬性和方法
    P1.prototype = obp2;//這個式子,簡單來講就類似於a = b, b賦值給a這個總該明白吧?
    //呼叫P1從obp2繼承過來的get函式
    alert(P1.prototype.get('out"s name'));
    //展示P1從obp2繼承過來的name屬性
    alert(P1.prototype.name);
    //用建構函式P1例項化一個obp1物件
    var obp1 = new P1();
    //P1的原型物件prototype既然已經繼承了obp2的所有屬性和函式,那麼依據P1所例項化出來的物件也都有obp2的屬性和函數了
    alert(obp1.get('obp1"s name'));

  關於prototype就講到這裡,假如本文有什麼錯誤,還望各位看官指出,我好糾正。

    特別指出:

  Array.prototype是一個數組

  String.prototype是一個字串

  Object.prototype是一個物件