Prototype

Prototype是js面向對象中實現多態的核心,或者說是區別于普通面向對象語言如java等的靈魂所在,越是這種玩意,越是坑,因為如果對這些理解不深,反而會被它的一些表面現象所迷惑,導致各種深層bug,不斷入坑。
這里對自己之前所做的項目中所用到的此部分內容做一些總結

思考

開篇我想就我之前用JS的一些體會,簡單說幾個點

  • 人們常說,JS中萬物皆對象,所以很多時候如果我們用Java等語言的(類-對象)邏輯去套JS反而會讓自己無法理解。
    這里,我們需要破而后立,先樹立一個概念,JS萬物皆對象,記住是對象,不是類!即使我們會定義一些function,然后使用new去定義初始化一些對象,但是這里的function仍然是對象,只是剛好能夠用來幫助初始化一類對象而已

JS中的對象

通常來說,javascript中的對象就是一個指向prototype的指針和一個自身的屬性列表。javascript創建對象時采用了寫時復制的理念。
只有 構造器 才具有prototype屬性,原型鏈繼承就是創建一個新的指針,指向構造器的prototype屬性。
prototype屬性之所以特別,是因為javascript時讀取屬性時的遍歷機制決定的。本質上它就是一個普通的指針。
構造器包括以下對象:

  1. Object
  2. Function
  3. Array
  4. Date
  5. String

Prototype

Prototype是啥

這里強烈推薦參考[關于JS中的constructor與prototype][],這應該是全網寫的最直白的,幫助我們小白看懂其中的邏輯
首先,prototype我的理解是一個對象的屬性,根據上文,我們知道只有構造器才有prototype屬性。
prototype屬性又指向了一個prototype對象,注意prototype屬性與prototype對象是兩個不同的東西,要注意區別。在prototype對象中又有一個constructor屬性,這個constructor屬性同樣指向一個constructor對象,而這個constructor對象恰恰就是這個function函數本身。
看圖(本文圖均出自[關于JS中的constructor與prototype][]文章,下文不再說明)


prototype和構造器

Sample Code:

function Person(name)
{
   this.name=name;
   this.showMe=function() {
             alert(this.name);
   }
};
var one=new Person('js');
alert(one.prototype)//undefined
alert(typeof Person.prototype);//object
alert(Person.prototype.constructor);//function Person(name) {...};

分析:
其中one是具體的構造器弄出來的對象,所以沒有prototype屬性。而Function有(Function是構造器),并且prototype中有只想具體構造函數的屬性

JS中對象的定義過程

按照《悟透javascript》書中說的,new形式創建對象的過程實際上可以分為三步:
第一步是建立一個新對象(叫A吧);
第二步將該對象(A)內置的原型對象設置為構造函數(就是Person)prototype 屬性引用的那個原型對象;
第三步就是將該對象(A)作為this 參數調用構造函數(就是Person),完成成員設置等初始化工作。
其中第二步中出現了一個新名詞就是內置的原型對象,注意這個新名詞跟prototype對象不是一回事,為了區別我叫它inobj,inobj就指向了函數Person的prototype對象。在person的prototype對象中出現的任何屬性或者函數都可以在one對象中直接使用,這個就是javascript中的原型繼承了。


對象與Prototype

這樣one對象通過內置的原型對象inobj就可以直接訪問Person的prototype對象中的任何屬性與方法了。這也就解釋了上面的代碼中為什么one可以訪問form函數了。因為prototype對象中有一個constructor屬性,那么one也可以直接訪問constructor屬性。

JS繼承(原型鏈)

繼承的實現很簡單,只需要把子類的prototype設置為父類的一個對象即可。注意這里說的可是對象哦!
那么通過prototype屬性實現繼承的原理是什么呢?還是先看圖形說明,然后編寫代碼進行驗證。


繼承(原型鏈)

Sample Code:

function Person(name) {
   this.name=name;
   this.showMe=function() {
       alert(this.name);
   }
};
Person.prototype.from=function() {
  alert('I come from prototype.');
}
function SubPerson() {
}
SubPerson.prototype=new Person();
var subOne=new SubPerson();
subOne.from();//I come from prototype.
alert(subOne.constructor);//function Person(name) {...};
alert(SubPerson.prototype.constructor);//function Person(name) {...};

這個所謂的原型鏈就是從最下方的子對象開始,往自己的原型上溯。找調用的屬性或者方法,如果找不到就繼續找原型的原型。一直下去。這個比較好理解,和普通的父類回溯是差不多的邏輯。

What's More

至此,prototype的大概應該有所理解了。那么還有幾個非常重要的細節,并且是使用最多的特性需要再強調一下,其實已經隱藏在上面的code里了。

  1. 每一個對象(被構造器創造出來的對象)姑且成為myObj,即new+某個function弄出的對象,可以直接訪問constructor屬性,這玩意指向的就是構造器
    但是同時,這玩意是沒有prototype屬性的,因為prototype只在構造器里才有。不過我們可以這樣找到他的prototype,那就是myObj. constructor. prototype
  2. 函數對象中自身聲明的方法和屬性與prototype聲明的對象有什么差別?
    有下面幾個差別:
  • 自身聲明的方法和屬性是 靜態的, 也就是說你在聲明后,試圖再去增加新的方法或者修改已有的方法,并不會 對由其已經創建的對象產生影響
  • 而prototype可以動態地增加新的方法或者修改已有的方法, 從而是 動態的 ,一旦 父函數對象 聲明了相關 的prototype屬性,由其創建的對象會 自動繼承 這些prototype的屬性.

總結下,結合上文開頭所述,prototype是指針,指向的是一個prototype對象。而具體的調用在在具體使用的時候,才去根據原型鏈一個一個去找的。所以在你update了prototype的屬性和方法后,所有繼承了這個prototype的對象都能動態的自動繼承這些新update的玩意。這個才是JS作為動態的面向對象語言最有價值的玩意

Javascript的方法

JS中有三類方法

a. 類方法
b. 對象方法
c. 原型方法

Sample Code:

function People(name) {
 this.name=name;
 //對象方法
 this.Introduce=function(){
   alert("My name is "+this.name);
  } 
}
//類方法
People.Run=function(){
  alert("I can run");
}
//原型方法
People.prototype.IntroduceChinese=function(){
  alert("我的名字是"+this.name);
}

參考文章
[JS中的prototype][]
[關于JS中的constructor與prototype][]
[javascript必知必會之prototype][]
[JS中的prototype]:http://www.cnblogs.com/yjf512/archive/2011/06/03/2071914.html
[關于JS中的constructor與prototype]:http://blog.csdn.net/niuyongjie/article/details/4810835
[javascript必知必會之prototype]:http://www.cnblogs.com/mindsbook/archive/2009/09/19/javascriptYouMustKnowPrototype.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容