談JavaScript的繼承

最近在忙前端的工做,由於以前作.net和php的開發比較多,前端開發喜歡把庫拿來就用,幾回事實證實,不懂原理,連改代碼也改很差,因此仍是下定決心研究下JavaScript的幾個技術難點。javascript

0x1.JavaScript的對象和構造函數php

定義一個JavaScript對象能夠這麼定義前端

var a = {
	x : 1,
	y : 2,
	add : function () {
		return this.x + this.y;
	},
	mul : function () {
		return this.x * this.y;
	}
}

這樣,你就定義了一個變量a,這個變量除了有x和y兩個公有成員外,還有兩個add和mul兩個函數(公有方法)。可是這樣的定義方法的缺點有2條:java

1.批量生成對象很不方便,若是你var b=a;那麼你每次修改b的成員,都會同時改掉a的成員,由於JavaScript的引用機制app

2.若是每次生成對象須要自定義一些成員,都要寫出相應的賦值操做,增長代碼行數。函數

因此,在定義一個JavaScript對象以前,咱們能夠先定義一個構造函數。this

function A(x, y) {
	this.x = x;
	this.y = y;
	this.add = function () {
		return this.x + this.y;
	}
	this.mul = function () {
		return this.x * this.y;
	}
}

而後,定義一個對象.net

a = new A(1, 2);

上面這句代碼看起來簡單,可是要和C++等面向對象的語言作個區分,A並非嚴格意義上「類」的概念,由於JavaScript是沒有類的,只是調用了構造函數而已。prototype

如今問題來了,咱們怎麼實現繼承?C++把封裝,繼承,多態這三個面向對象的特徵實現得清清楚楚。可是對於JavaScript這樣一個比較浪的語言,沒有一個很嚴格的繼承機制,而是採用如下幾種方式來模擬。設計

0x2.JavaScript的prototype

爲了可以講清後面的apply或call函數,這裏先引入prototype。prototype是隻有Function纔有的。

要用好繼承,首先要明白爲何要設計繼承這個東西?無非就是「把公共的部分」提取出來,實現代碼複用。

因此在JavaScript裏,也是把公共部分放在Function的prototype裏。

咱們來比較兩個用prototype來實現繼承的例子

function A(x, y) {
	this.x = x;
	this.y = y;
	this.add = function () {
		return this.x + this.y;
	}
	this.mul = function () {
		return this.x * this.y;
	}
}

function B(x,y){
	
}

B.prototype=new A(1,2);

console.log(new B(3,4).add());  //3

這個例子中,子類的prototype指向一個A類對象

咱們再實現一個B繼承A的例子:

function A() {
	
}

A.prototype = {
	x : 1,
	y : 2,
	add : function () {
		return this.x + this.y;
	},
	mul : function () {
		return this.x * this.y;
	}
}
A.prototype.constructor=A;

function B(){
	
}

B.prototype=A.prototype;
B.prototype.constructor=B;

B的prototype對象引用了A的prototype對象,由於是引用,因此若是修改了B的prototype對象,A的prototype對象也隨之修改,由於本質上他們都指向一塊內存。因此每次改動B類型的prototype都要手動將constructor改回,防止錯亂。相比兩個例子,上一個例子由於沒有引用,因此不會發生這個問題。

建立一個B類型的對象

b=new B();

b對象具備A類型的一切成員

console.log(b.add());    //3

由於每一個prototype對象都有兩個重要成員:constructor和_proto_,constructor本質上是一個函數指針,因此B.prototype=A.prototype執行後,覆蓋掉了constructor,因此後面要讓constructor從新指向B類型的構造函數。

0x3.JavaScript的構造函數綁定

在定義完一個A類型的構造函數後,再定義一個B類型,而後在B類型構造函數內部,「嵌入執行」A類型的構造函數。

function A(x, y) {
	this.x = x;
	this.y = y;
	this.add = function () {
		return this.x + this.y;
	}
	this.mul = function () {
		return this.x * this.y;
	}
}

function B(x, y, z) {
	A.apply(this, arguments);
	this.z = z;
}

console.log(new B(1,2,3));

apply函數和call函數基本同樣,能夠在B類型構造函數內部執行A類型構造函數。同時也能夠繼承A的全部成員。

顯示結果:

這裏給個公式:在B構造函數裏寫A.apply(this),可讓B構造出來的對象能夠擁有A構造函數裏的全部成員。

談到apply和call,還能夠實現多重繼承

function IA(){
	this.walk=function(){
		console.log("walk");
	}
}

function IB(){
	this.run=function(){
		console.log("run");
	}
}

function Person(){
	IA.apply(this);
	IB.apply(this);
}

var p=new Person();
p.walk();  //walk        
p.run();  //run
相關文章
相關標籤/搜索