function Person(name,age){ this.name=name; this.age=age; } Person.prototype.sayHello=function(){ alert("使用原型獲得Name:"+this.name); } var per=new Person("alin",21); per.sayHello(); //輸出:使用原型獲得Name:alin
在函數Person裏面自定義了屬性name和age,而prototype是咱們的屬性集合,也就是說,我要添加sayHello這個屬性到Person,則要這樣寫:Person.prototype.sayHello,就能添加Person的屬性。javascript
(咱們能夠簡單的把prototype看作是一個模板,新建立的自定義對象都是這個模板prototype的一個拷貝,其實準確來講,不該該說是一個拷貝,而是一個鏈接,只不過這種連接是不可見,新實例化的對象內部有一個看不見的_Proto_指針,指向原型對象)。java
使用原型來優化代碼:函數
function add(x,y){ return x+y; } function subtract(x,y){ return x-y; } console.log(add(1,3));
var Calculator = function(){ }; Calculator.prototype = { add:function(x,y){ return x+y; }, subtract:function(x,y){ return x-y; } }; console.log((new Calculator()).add(1,3));
var Calculator = function () {}; Calculator.prototype = function(){ add = function(x,y){ return x+y; }, subtract = function(x,y){ return x-y; } return{ add:add, subtract:subtract } }(); console.log((new Calculator()).add(1,3));
var foo = { x: 10, y: 20 };
解析:當你定義一個函數對象的時候,其內部就有這樣一個鏈表關係。聲明foo對象,自帶了_proto_的屬性,而這個屬性指向了prototype,從而實現對象的擴展(例如繼承等操做)。優化
再看一個栗子:this
var a = { x: 10, calculate: function (z) { return this.x + this.y + z } }; var b = { y: 20, __proto__: a }; var c = { y: 30, __proto__: a }; b.calculate(30); // 60
附上另外說明:spa
一、一個沒有繼承操做的函數的_proto_都會指向Object.prototype,而Object.prototype都會指向null。prototype
二、因此,也能夠很明顯知道,爲什麼null是原型鏈的終端。設計
理解了__proto__這個屬性連接指針的本質。。再來理解constructor。指針
prototype默認的有一個叫作constructor的屬性,指向這個函數自己。
通常construtor就是咱們平時對函數設置的實例化對象code
如上圖:SuperType是一個函數,下面包括他的原型對象prototype,原型對象指向了構造函數的指針,而構造函數指回像了原型對象的內部指針,這樣就造成了鏈式關係了。
就是說,當一個函數對象被建立時候,Function構造器產生的函數對象會運行相似這樣的一行代碼:
this.prototype = {constructor:this};
這個prototype對象是存放繼承特徵的地方。由於js沒有提供一個方法去肯定哪一個函數是打算用來作構造器,因此每一個函數都會獲得一個prototype對象。constructor屬性沒有什麼用,重要的是prototype對象。
實現原型鏈有一種基本模式,其代碼大體以下:
function A(){ this.Aproperty = "111"; } A.prototype.getA = function(){ return this.Aproperty; }; function B(){ this.Bproperty = "222"; } B.prototype = new A();//B繼承A B.prototype.getB = function(){ return this.Bproperty; }; var C = new B(); console.log(C.getA());//111
以上定義了兩個類型A和B。每一個類型分別有一個屬性和一個方法。它們的主要區別是B繼承了A,而繼承是經過建立A的實例,並將實例賦給B.prototype實現的。實現的本質是重寫原型的對象,代之以一個新的類型的實例。換句話說,原來存在於A的實例中的全部屬性和方法,如今也存在於B.prototype中了。在確立了繼承關係以後,咱們給B.prototype添加了一個方法,這樣就繼承A的屬性和方法的基礎上又添加了一個新方法。
如圖:
另一個很重要的鏈式繼承模式:
function A(x){ this.x = x; } A.prototype.a = "a"; function B(x,y){ this.y = y; A.call(this,x); } B.prototype.b1 = function(){ alert("b1"); } B.prototype = new A(); B.prototype.b2 = function(){ alert("b2"); } B.prototype.constructor = B; var obj = new B(1,3);
就是說把B的原型指向了A的1個實例對象,這個實例對象具備x屬性,爲undefined,還具備a屬性,值爲"a"。因此B原型也具備了這2個屬性(或者說,B和A創建了原型鏈,B是A的下級)。而由於方纔的類繼承,B的實例對象也具備了x屬性,也就是說obj對象有2個同名的x屬性,此時原型屬性x要讓位於實例對象屬性x,因此obj.x是1,而非undefined。第13行又定義了原型方法b2,因此B原型也具備了b2。雖然第9~11行設置了原型方法b1,可是你會發現第12行執行後,B原型再也不具備b1方法,也就是obj.b1是undefined。由於第12行使得B原型指向改變,原來具備b1的原型對象被拋棄,天然就沒有b1了。
第12行執行完後,B原型(B.prototype)指向了A的實例對象,而A的實例對象的構造器是構造函數A,因此B.prototype.constructor就是構造對象A了(換句話說,A構造了B的原型)。
alert(B.prototype.constructor)出來後就是"function A(x){...}" 。一樣地,obj.constructor也是A構造對象,alert(obj.constructor)出來後就是"function A(x){...}" ,也就是說B.prototype.constructor===obj.constructor(true),可是B.prototype===obj.constructor.prototype(false),由於前者是B的原型,具備成員:x,a,b2,後者是A的原型,具備成員:a。如何修正這個問題呢,就在第16行,將B原型的構造器從新指向了B構造函數,那麼B.prototype===obj.constructor.prototype(true),都具備成員:x,a,b2。
若是沒有第16行,那是否是obj = new B(1,3)會去調用A構造函數實例化呢?答案是否認的,你會發現obj.y=3,因此仍然是調用的B構造函數實例化的。雖然obj.constructor===A(true),可是對於new B()的行爲來講,執行了上面所說的經過構造函數建立實例對象的3個步驟,第一步,建立空對象;第二步,obj.__proto__ === B.prototype,B.prototype是具備x,a,b2成員的,obj.constructor指向了B.prototype.constructor,即構造函數A;第三步,調用的構造函數B去設置和初始化成員,具備了屬性x,y。雖然不加16行不影響obj的屬性,但如上一段說,卻影響obj.constructor和obj.constructor.prototype。因此在使用了原型繼承後,要進行修正的操做。
關於第十二、16行,總言之,第12行使得B原型繼承了A的原型對象的全部成員,可是也使得B的實例對象的構造器的原型指向了A原型,因此要經過第16行修正這個缺陷。
文章說明:我的查看各類資料,原創所得,觀點不必定準確,歡迎各路大牛勘誤,小女子感激涕零。