一篇筆記帶你快速掌握面向對象的Javascript(純手打)

/**
 * 建立對象
 */

//1.工廠模式
//特色:
//建立的對象沒法識別其對象類型,不支持instanceof等判斷方法,沒法標識不一樣的對象
function createObj(name,sex,age){
	var obj = new Object();
	obj.name = name;
	obj.sex = sex;
	obj.age = age;
	obj.show = function(){
		alert(this.name);
	}
	return obj;
}
var obj = createObj("jackie","male",29);
console.log(obj);



//2.構造函數模式
//特色:
//使用new建立對象,所以可使用instanceof,constructor等來識別對象類型
//new建立的對象,內部會有一個__proto__指針指向構造函數的prototype
//若是漏寫new,會致使構造函數內部this指針指向window,致使嚴重錯誤
//缺點是沒法複用公用的方法,建立多個對象時會重複建立屢次公用方法
function CreateObj(name,sex,age){
	this.name = name;
	this.sex = sex;
	this.age = age;
	this.show = function(){
		alert(this.name);
	}
}

var obj = new CreateObj("jackie","male",29);
console.log(obj);
console.log(obj.constructor); //Function CreateObj
console.log(obj instanceof CreateObj); //true
console.log(obj instanceof Object); //true
console.log(obj.__proto__ === CreateObj.prototype); //true



//3.原型模式
//全部屬性方法均綁在原型上,這樣生成的對象打印是空,可是屬性和方法能夠經過原型鏈查找獲取到
//在實例上重寫原型鏈已有屬性不會改變原型鏈上的對應屬性,就算設置爲null也不行,只能屏蔽掉
//屏蔽掉原型屬性後想恢復須要使用delete操做符移除對應實例屬性
//多個實例共享綁在原型鏈上的屬性和方法,且原型只須要初始化的時候建立一次而已
//可使用instanceof,constructor,isPrototypeOf等方法來判斷對象所屬
//缺點在於屬性沒法傳參數,致使建立的對象屬性都相同,方法也是共享,沒法體現出對象的獨一性
//引用類型的屬性多個對象會同時改變,例如數組
function CreateObj(){

}
CreateObj.prototype.name = "jackie";
CreateObj.prototype.sex = "male";
CreateObj.prototype.age = 29;
CreateObj.prototype.show = function(){
	alert(this.name);
}

var obj = new CreateObj();
var obj2 = new CreateObj();
console.log(obj); // {}
console.log(obj.name); // jackie
console.log(obj2.name); //jackie
obj2.name = "tommy";
console.log(obj2.name); //tommy
delete obj2.name;
console.log(obj2.name); //jackie
console.log(obj.show === obj2.show); //true
console.log(obj.constructor); //Function CreateObj
console.log(obj2 instanceof CreateObj); //true
console.log(CreateObj.prototype.isPrototypeOf(obj)); //true
console.log(CreateObj.prototype.constructor === CreateObj); //true
CreateObj.prototype.arr = [1,2,3];
obj.arr.push(4);
console.log(obj2.arr); //[1,2,3,4]



//4.組合構造函數原型模式
//屬性使用構造函數傳參來構造,公用方法綁定在prototype上實現複用
//可使用instanceof,constructor,hasOwnProperty,in等方法來判斷對象所屬
//此時引用類型數據存在構造函數內,不存在不一樣對象相互影響的問題
//結合了構造函數和原型鏈的優點,解決了二者存在的問題,是目前最爲經常使用的一種方式
function CreateObj(name,sex,age){
	this.name = name;
	this.sex = sex;
	this.age = age;
	this.arr = [1,2,3];
}
CreateObj.prototype.show = function(){
	alert(this.name);
}

var obj = new CreateObj('jackie','male',29);
console.log(obj);
console.log(obj.constructor); //Function CreateObj
console.log(obj instanceof CreateObj); //true
console.log(obj.hasOwnProperty('sex')); //true
console.log('show' in obj); //true
var obj2 = new CreateObj('jackie','male',29);
obj.arr.push(4);
console.log(obj.arr); //[1,2,3,4]
console.log(obj2.arr); //[1,2,3]


//5.單例模式
//只能建立一次,經常使用於單頁面,實現某個頁面的具體功能
//相對於面向過程的散亂寫法,這種寫法可維護性更好
var CreateObj = {
	name: 'jackie',
	sex: 'male',
	age: 29,
	init: function(){
		console.log(this.name,this.sex,this.age);
	}
}

CreateObj.init(); //jackie male 29


/**
 * 繼承
 */

//1.原型鏈繼承
//將父類實例賦值給子類的prototype,這樣父類的全部方法和屬性(不管是構造函數仍是原型上定義的)所有都繼承給了子類
//使用instanceof發現子類是子類,父類,Object類的實例,這是因爲原型鏈的關係,一層層往內找,最後終點是Object類
//繼承後子類實例的constructor會被指向父類,所以若是須要,能夠手動修正將constructor指回子類,雖然指回後的是可枚舉的constructor
//因爲是綁定在原型鏈上的繼承,所以引用類型數據在多個實例內是共享的,會相互影響
//一個嚴重缺陷,子類構造函數沒法傳參數,由於繼承來的屬性所有在原型鏈上,和構造函數沒有聯繫,所以繼承後子類全部對象的屬性都相同
function Super(age){
	this.name = 'yinshawn';
	this.age = age;
	this.arr = [1,2,3];
}
Super.prototype.show = function(){
	console.log(this.name);
}
function Sub(){

}
Sub.prototype = new Super(29);

var sub = new Sub();
console.log(sub.name); //yinshawn
console.log(sub.age); //29
sub.show(); //yinshawn
console.log(sub.constructor); //Function Super
Sub.prototype.constructor = Sub;
console.log(sub.constructor); //Function Sub
console.log(sub instanceof Sub); //true
console.log(sub instanceof Super); //true
console.log(sub instanceof Object); //true
var sub2 = new Sub();
sub.arr.push(4);
console.log(sub2.arr); //[1,2,3,4]



//2.構造函數繼承
//經過call或apply在子類構造函數裏調用父類構造函數,call(this,param1,param2,param3)或apply(this,arguments)都可
//此類繼承能夠在子類的構造函數內傳參,使生成的對象真正擁有本身的屬性
//因爲是在構造函數內,所以引用類數據不會有多個對象相互影響的問題
//依然可使用constructor和instanceof來判斷對象所屬
//一個嚴重缺陷,全部須要繼承的屬性和方法都須要寫在構造函數內,原型鏈上的方法屬性沒法繼承,若是所有寫在構造函數內,沒法實現方法複用
function Super(age,sex){
	this.name = "jackie";
	this.age = age;
	this.sex = sex;
	this.arr = [1,2,3];
}
Super.prototype.show = function(){
	console.log(this.name);
}
function Sub(age,sex){
	Super.call(this,age,sex);
	Super.apply(this,arguments);
}

var sub = new Sub(29);
console.log(sub.name); //jackie
console.log(sub.age); //29
console.log(sub.show); //undefined
var sub2 = new Sub(29,'female');
console.log(sub2.sex); //female
sub2.arr.push(4);
console.log(sub.arr); //[1,2,3]
console.log(sub2.arr); //[1,2,3,4]
console.log(sub instanceof Super); //true
console.log(sub.constructor); //Function Sub



//3. 組合構造函數原型繼承
//即結合構造函數繼承和原型繼承,二者合二爲一
//此時子類構造函數能夠傳參,不只能夠繼承構造函數內的屬性方法,也能夠繼承原型鏈上的屬性方法,本身能夠靈活分配
//惟一美中不足的是會執行兩次父類構造函數,並且會生成兩組相同的屬性,同時存在於原型鏈和構造函數內
function Super(age,sex){
	this.name = "jackie";
	this.age = age;
	this.sex = sex;
	this.arr = [1,2,3];
}
Super.prototype.show = function(){
	console.log(this.name);
}
function Sub(age,sex){
	Super.call(this,age,sex);
	Super.apply(this,arguments);
}
Sub.prototype = new Super();
Object.defineProperty(Sub.prototype,"constructor",{
	value: 'Sub',
	enumerable : false
});

var sub = new Sub(29,'male');
console.log(sub.age); //29
console.log(sub.show); //Function
console.log(sub instanceof Super); //true
console.log(sub.constructor); //Sub



//4.單對象淺複製繼承
//不使用構造函數,直接實現兩個對象之間的繼承,使用ES5的create方法來繼承父類,並本身添加新屬性或重載父類屬性
//若考慮兼容性問題,則可自定義函數,實現相似效果
//使用非標準的__proto__也可實現簡單繼承
var Super = {
	name: 'jackie',
	age: 29
}

//方法1
var Sub = Object.create(Super,{
	sex : {
		value: 'male'
	},
	age: {
		value: 92
	}
});
 
console.log(Sub.name); //jackie
console.log(Sub.sex); //male
console.log(Sub.age); //92

//方法2
function create2(obj){
	var F = function(){};
	F.prototype = obj;
	return new F();
}

var Sub = create2(Super);
Sub.age = 92;
console.log(Sub.name); //jackie
console.log(Sub.age); //92

//方法3
var Sub = {
	__proto__: Super,
	age: 92
}

console.log(Sub.name); //jackie
console.log(Sub.age); //92



//5.寄生組合式完美繼承
//目前最完美的繼承方式,主要在構造函數原型組合繼承基礎上改動
//改動了其中原型繼承裏將父類實例賦值給子類的prototype這一段,這裏產生的缺陷也就是多調用了一次父類構造函數,並且將本來構造函數內的屬性記錄在了原型裏
//改動方向是避免調用構造函數,而是使用別的方法來獲得父類實例,且這個實例上沒有跟構造函數相關的屬性,例如Object,Object.create,自定義淺複製函數等
//主要思路就是將父類prototype經過Object方法或create方法生成一個只帶有父類原型屬性的副本,將這個副本的constructor指正後賦值給子類prototype
//這種方法是目前最完美的方法,無明顯缺陷
function Super(age,sex){
	this.name = "jackie";
	this.age = age;
	this.sex = sex;
	this.arr = [1,2,3];
}
Super.prototype.show = function(){
	console.log(this.name);
}
function Sub(age,sex){
	Super.call(this,age,sex);
	Super.apply(this,arguments);
}
var prototype = Object(Super.prototype); //獲得一個父類實例
prototype.constructor = Sub;
Sub.prototype = prototype;

var sub = new Sub(28,'male');
console.log(sub.show); //Function
console.log(sub.age); //28
console.log(sub instanceof Super); //true













相關文章
相關標籤/搜索