Prototype是js面向對象中實現多態的核心,或者說是區別于普通面向對象語言如java等的靈魂所在,越是這種玩意,越是坑,因為如果對這些理解不深,反而會被它的一些表面現象所迷惑,導致各種深層bug,不斷入坑。
這里對自己之前所做的項目中所用到的此部分內容做一些總結
思考
開篇我想就我之前用JS的一些體會,簡單說幾個點
- 人們常說,JS中萬物皆對象,所以很多時候如果我們用Java等語言的(類-對象)邏輯去套JS反而會讓自己無法理解。
這里,我們需要破而后立,先樹立一個概念,JS萬物皆對象,記住是對象,不是類!即使我們會定義一些function,然后使用new去定義初始化一些對象,但是這里的function仍然是對象,只是剛好能夠用來幫助初始化一類對象而已
JS中的對象
通常來說,javascript中的對象就是一個指向prototype的指針和一個自身的屬性列表。javascript創建對象時采用了寫時復制的理念。
只有 構造器 才具有prototype屬性,原型鏈繼承就是創建一個新的指針,指向構造器的prototype屬性。
prototype屬性之所以特別,是因為javascript時讀取屬性時的遍歷機制決定的。本質上它就是一個普通的指針。
構造器包括以下對象:
- Object
- Function
- Array
- Date
- String
Prototype
Prototype是啥
這里強烈推薦參考[關于JS中的constructor與prototype][],這應該是全網寫的最直白的,幫助我們小白看懂其中的邏輯
首先,prototype我的理解是一個對象的屬性,根據上文,我們知道只有構造器才有prototype屬性。
prototype屬性又指向了一個prototype對象,注意prototype屬性與prototype對象是兩個不同的東西,要注意區別。在prototype對象中又有一個constructor屬性,這個constructor屬性同樣指向一個constructor對象,而這個constructor對象恰恰就是這個function函數本身。
看圖(本文圖均出自[關于JS中的constructor與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中的原型繼承了。
這樣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里了。
- 每一個對象(被構造器創造出來的對象)姑且成為myObj,即new+某個function弄出的對象,可以直接訪問constructor屬性,這玩意指向的就是構造器
但是同時,這玩意是沒有prototype屬性的,因為prototype只在構造器里才有。不過我們可以這樣找到他的prototype,那就是myObj. constructor. prototype - 函數對象中自身聲明的方法和屬性與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