JavaScript沒有類別的概念,但幾乎所有的東西又是基於物件的,同時也能實作繼承,這就是js 跟其他OOP語言最大的不同之處,這也是js最難理解的一塊。下面我來說說我個人的理解。
先從建立物件說起,一般會有下面幾種方法:
var person() = new Object(); person.name = 'mikej'; person.sayName = function(){ alert(this.name); }
2.也可以這樣寫:
var parson = { name : 'mikej', sayName : function(){ alert(this.name); } }
3.這兩種建立物件的方法很簡單,但都有缺陷,使用同一個模式建立物件的時候,會產生大量重複程式碼。於是就有了工廠模式:
function createPerson(name){ var p = new Object(); p.name=name; p.sayName = function(){ alert(this.name); }; return p; } var p1 = createPerson('mikej'); var p2 = createPerson('tom');
這樣就可以無限創建物件了。
4.還有一種方法,跟工廠模式異曲同工,叫做建構子模式:
function Person(name){ this.name=name this.sayName = function(){ alert(this.name); } this.say = function(){ alert('hi'); } } var p1 = new Person('mikej'); var p2 = new Person('tom');
這裡有幾個值得關注的地方:沒有顯示的創建對象、函數名Person使用的是大寫字母P(這是必須的)、p1和p2中都有一個constructor(建構子)屬性,指向Person。同時p1和p2既是Object的實例,也是Person的實例。
alert(p1.constructor == Person); //true alert(p1 instanceof Object); //true alert(p1 instanceof Person); //true
//5.11更新:以一個phper的角度看的話,之前很容易將創建物件的流程想成這樣,Person就是一個“類別”,然後用 new Person('mikej')
實例化了這個類,並且傳入參數。但實際上並不是這樣的,創建的流程應該是這樣:首先,建立一個空對象,然後用apply方法,第一個參數是這個空對象,第二個參數是上下文的參數,這樣Person中的this就會指向這個對象,也就是p1。
var p1 = new Person('mikej'); //上面代码就相当于 var p1 = {}; Person.apply(p1, ['mikej']);
建構函數模式看起來很好,但是它有一個弊端就是浪費內存,接上例
alert(p1.say == p2.say) //false
.為了避免這個缺陷,可是使用原型模式來創建對象,js中的每個對像都有一個JavaScript原型與原型鏈的詳細分析屬性用來指向另一個對象,這個對象的所有屬性和方法都會被構造函數的實例繼承,是共享的,這就意味著,我們可以把那些不變的屬性和方法,定義到JavaScript原型與原型鏈的詳細分析物件上。
function Person(name){ this.name = name; } //Person的原型对象 Person.JavaScript原型與原型鏈的詳細分析 = { say: function(){ alert('hi'); }, sayName: function(){ alert(this.name); } }; var p1 = new Person("mikej"); var p2 = new Person("tom"); p1.sayName(); p2.sayName(); //下面就可以看出方法实现了共享 alert(P1.say == P2.say) //true alert(P1.sayName == P2.sayName) //true
再來擴充一下上面的例子,使用原型來實作繼承。
function Person(name){ this.name = name; } Person.JavaScript原型與原型鏈的詳細分析 = { say: function(){ alert('hi'); }, sayName: function(){ alert(this.name); } }; function Programmer(){ this.say = function(){ alert('im Programmer, my name is ' + this.name); } } Programmer.JavaScript原型與原型鏈的詳細分析 = new Person('mikej'); //手动修正构造函数 Programmer.JavaScript原型與原型鏈的詳細分析.constructor = Programmer; var p1 = new Programmer(); console.dir(Programmer.JavaScript原型與原型鏈的詳細分析.constructor);//Programmer console.dir(p1.constructor);//Programmer console.dir(p1);
Programmer的原型指向了Person的一個實例,那麼所有的Programmer的實例都能繼承Person和Person的原型了。
這裡會有一個問題。
預設原型物件裡有一個constructor屬性,指向它的建構子。而每一個實例也有一個constructor屬性,會預設呼叫JavaScript原型與原型鏈的詳細分析物件的constructor屬性。
假設沒有Programmer.JavaScript原型與原型鏈的詳細分析 = new Person('mikej');
Programmer.JavaScript原型與原型鏈的詳細分析.constructor是指向Programmer的。 p1的構造也指向Programmer
alert(Programmer.JavaScript原型與原型鏈的詳細分析.constructor == Programmer) //true alert(p1.constructor == Programmer) //true
但有了這句Programmer.JavaScript原型與原型鏈的詳細分析 = new Person('mikej');
之後,
Programmer.JavaScript原型與原型鏈的詳細分析.constructor就指向了Object,也就是Person.JavaScript原型與原型鏈的詳細分析所指向的物件的建構。 p1.constructor也指向了Object。但p1明明是建構函數Programmer產生的,這就造成了繼承的混亂,所以我們必須手動修正建構函數,也就是下面這程式碼。
Programmer.JavaScript原型與原型鏈的詳細分析.constructor = Programmer;
好了,現在我們再來看看原型鏈:
console.dir(p1);
這句程式碼的結果是
可以看出, p1是Programmer的實例,Programmer的原型是Person,Person的原型是Object,再網路上就是JS的物件了。這就是原型鏈,也就是說,JavaScript的繼承是基於原型鏈來實現的。
#以上是JavaScript原型與原型鏈的詳細分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!