js"面向對象"和"繼承"的碎碎念

1、prototype屬性的特色
java

一、定義在prototype中的方法是"實例方法",必須是new出來的實例,才能調用prototype中的方法,相同的方法能夠被不一樣的實例調用,互不干擾json

function Human (name) {
	this.name = name;
}
Human.prototype = {
	sayHi:function () {
		console.log('Hi! I\'m '+this.name+'.');
	}
}

var tom = new Human('Tom');
tom.sayHi();//  Hi! I'm Tom.
var perter = new Human('Perter');
perter.sayHi();// Hi! I'm Perter.

能夠作個小調整,將打招呼的的方法sayHi()移到構造函數中,一實例化就執行,沒必要單獨調用:函數

function Human (name) {
	this.name = name;
	this.sayHi()
}
Human.prototype = {
	sayHi:function () {
		console.log('Hi! I\'m '+this.name+'.');
	}
}

var tom = new Human('Tom');//  Hi! I'm Tom.
var perter = new Human('Perter');// Hi! I'm Perter.

自問:可是,這樣跟在window全局做用域寫個sayHi方法,傳不一樣值調用有何區別?:this

function sayHi (name) {
	console.log("Hi! I'm "+name+'.');
}
sayHi('Tom');//  Hi! I'm Tom.
sayHi('Perter');// Hi! I'm Perter.

自答:全局的function對象適合封裝簡單的小塊邏輯,若是是較爲複雜的邏輯,都寫在一個function對象中會使邏輯更趨複雜,難以維護,通常會拆分紅多個function,以便於管理和維護:spa

function Human (name) {
	this.name = name;
	this.sayHi()
}
Human.prototype = {
	sayHi:function () {
		console.log('Hi! I\'m '+this.name+'.');
	},
	work:function () {
		console.log("For live,I must work.");
	},
	eat:function () {
		console.log("For live,I must eat.");
	}
	sex:function () {
		console.log("Sex is an instinct");
	}
}

上例的Human類比開始多定義了3個方法,prototype實現的是,對已經拆分的邏輯模塊(sayHi,work,eat,sex),再封裝後的複用,這是一個簡單的function對象難以作到的。prototype

二、其餘屬性定義的方法都是"靜態方法",只能在統一不變的"類"的層級調用。code

三、獨立的json對象也是封裝邏輯的很好容器,但由於是引用類型,在相同的文檔上下文中,複用會出現後者參數覆蓋前者的狀況。orm




2、使用prototype的注意事項對象

一、若是使用xx.prototype={}的方式封裝邏輯,會影響"constructor"值,因此使用後注意重置"constructor"爲當前的類。方便之後判斷一個實例的構造類是誰。繼承

function Human (name) {
	this.name = name;
}
var a = new Human('Peter');
console.log(a.constructor);

上面代碼的輸出結果是:

咱們獲得了實例a的構造類是Human!

可是使用xx.prototype={}的方式封裝邏輯後:

function Human (name) {
	this.name = name;
}
Human.prototype = {
	sayHi:function () {
		console.log("Hi! I'm "+this.name+'.');
	}
}
var a = new Human('Peter');
console.log(a.constructor);

輸出結果是:

咱們已經再也不知道實例a的構造類是誰?因此,應該重置"constructor"爲當前的類:

function Human (name) {
	this.name = name;
}
Human.prototype = {
	sayHi:function () {
		console.log("Hi! I'm "+this.name+'.');
	}
}
Human.prototype.constructor = Human;
var a = new Human('Peter');
console.log(a.constructor);

輸出結果又是:

二、在構建繼承父類的子類時子類會調用父類的構造函數,生成實例,再賦值給子類的prototype。也就是子類還沒調用,子類繼承自父類的構造方法已經執行了。

// 定義"人"類
function Human () {
	alert("hello world!");
}
Human.prototype = {
	sayHi:function () {
		console.log("hello world!");
	}
}
Human.prototype.constructor = Human;


// 定義"女人"類
function Woman (name) {}
// "女人"類繼承"人"類
Woman.prototype = new Human();
// 定義"女人"類的特有方法
Woman.prototype.bear = function () {
	console.log("I can bear baies.");
}
Woman.prototype.constructor = Woman;

上述代碼只是定義了一個Human類,而後Woman子類繼承Human父類,尚未實例化,就執行了

alert("hello world!");

不只不合理,當父類構造函數含有破壞性代碼,或者要依賴特定狀態時,還會引起其餘錯誤。

因此,一般咱們須要單獨封裝一個實現繼承的方法,而不是僅僅把子類的prototype賦值爲父類的實例。


3、繼承方法的封裝步驟

3.1.構造一個新類,該類具備一個空的構造函數,將該類的prototype賦值爲父類的prototype,而後將該類的實例賦值給子類的prototype:

function inheritance(){};
inheritance.prototype = superClass.prototype;
subClass.prototype = new inheritance();

構造函數爲空了,子類再來繼承時,也就不會出現還沒實例化,代碼已經開始執行的狀況了。

3.2.重置恢復子類的constructor:

subClass.prototype.constructor = subClass;

便於對實例追根溯源。

3.3.子類自定義屬性(baseConstructor)保存對父類的構造函數的引用:

subClass.baseConstructor = superClass;

構造函數只是在過分的「inheritance」類置空了,它的引用仍然會經過子類的自定義屬性(baseConstructor)保留,方便在須要的時候調用。

3.4.若是父類自定義屬性(__super__)包含對祖父類的引用,那麼這個屬性要賦值給父類prototype下的同名屬性:

if(superClass.__super__){
	superClass.prototype.__super__ = superClass.__super__;
}

只要保存在prototype屬性中的方法才能被後代繼承,這樣作方便子類對祖先類的方法的重寫。

3.5.子類自定義屬性(__super__)保存對父類的prototype的引用:

subClass.__super__ = superClass.prototype;

方便子類對父類的方法的重寫。


完整的代碼:

// 實現繼承的方法
function extend (subClass,superClass) {
	// 建立一個新的類,該類具備一個空的構造函數,並具備父類的成員
	function inheritance(){};
	inheritance.prototype = superClass.prototype;

	// 將子類的prototype屬性設置爲不帶構造函數的父類新實例
	subClass.prototype = new inheritance();
	subClass.prototype.constructor = subClass;// 重置恢復constructor屬性
	subClass.baseConstructor = superClass;// 用自定義屬性保存父級的構造函數

	// 容許多重繼承
	if(superClass.__super__){
		superClass.prototype.__super__ = superClass.__super__;
	}
	subClass.__super__ = superClass.prototype;// 保持對父類原型的引用,方便子類重寫
}

相關文章
相關標籤/搜索