JS中“創建對象”及“通過原型創建對象”淺析

這兩天重新看着李炎恢的JavaScript視頻學習了一遍,在看到第十五章 面向對象及原型 的時候,感覺必須要把所講的知識點總結一下,以便今後查看:

(備註:查看該篇博客時,請結合視頻及視頻課件)

一、創建對象的幾種方式


1、各自獨立聲明模式

var box1 = new Object();	//聲明第一個對象並給各屬性賦值
box1.name = 'Lee';
box1.age = 100;
box1.run = function () {
	return this.name + this.age + '運行中...';
};
alert(box.run());

var box2 = new Object();	//聲明第二個對象並給屬性賦值
box2.name = 'Jack';
box2.age = 200;
box2.run = function () {
	return this.name + this.age + '運行中...';
};
alert(box2.run());
此時box2和box1是相互獨立的,不會混淆。
但是這樣子每聲明一個結構相同的對象,都得加一段代碼,特別不方便,所以有了下面的工廠模式。

2、工廠模式

function createObject(name, age) { //集中實例化的函數
	var obj = new Object();
	obj.name = name;
	obj.age = age;
	obj.run = function () {
		return this.name + this.age + '運行中...';
	};
	return obj;
}
這樣子每次new對象時只需調用createObject()方法即可,同時,還可以傳參初始化對象。
但由於new出來的對象都從屬於Object,當我要使new出來的對象在本質做一些區分的時候,就不行了,此時就有了更簡便的構造函數模式。

3、構造函數模式

function Box(name, age) {
	this.name = name;
	this.age = age;
	this.run = function () {
		return this.name + this.age + '運行中...';
	};
}
var box1 = new Box('Lee', 100);
var box2 = new Box('Jack', 200);
alert(box1.run());
alert(box1 instanceof Box); //很清晰的識別他從屬於Box
構造函數模式很大程度上解決了對象複用及傳參初始化問題,是最常用的new對象的模式。
注意: 
此處box1.run != box2.run, 因爲他們判斷的是引用地址,由此可知,兩個對象的方法分別存儲在不同的地方,是兩個不同的方法。
box1.run() == box2.run(),因爲它們返回值一樣。
當有了對象以後,自然而然就會涉及到對象屬性及方法的共享問題(比如,在java中子類們共享父類的成員變量及方法)。
構造函數模式創建出的對象說白了還是相互獨立的(比如上面的box1.run != box2.run),如果我要在兩個對象之間共享某些屬性的話,就必須要採用原型prototype了。

二、通過原型創建對象


1、原型模式

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.arr = ['aaa','bbb'];
Box.prototype.run = function () { //在原型裏添加方法
	return this.name + this.age + '運行中...';
};
var box1 = new Box();
var box2 = new Box();

//修改普通屬性,看下會不會影響prototype
alert(box1.name);	//Lee
box1.name = 'Rinima';
alert(box1.name);	//Rinima
alert(box2.name);	//Lee,修改box1的普通屬性,相當於直接在box1中添加一個屬性,是不會牽扯到原型中的屬性的。
					//這一點非常好。但是如果是引用對象(數組)的話,就有問題了。

//修改引用屬性,看下會不會影響prototype
alert(box1.arr);	//aaa,bbb
box1.arr.push('ccc');
alert(box1.arr);	//aaa,bbb,ccc
alert(box2.arr);	//aaa,bbb,ccc,修改box1的引用屬性,卻牽扯到了原型中的屬性。
			//這是因爲arr只是個引用地址,指向數組真實存儲的位置,修改box1中的引用屬性就是修改引用所指向的數組,所以原型也被修改了
			//這是我們不希望看到的!
該模式的優點: 
1、所new出來的對象在prototype中的屬性是共享的,此時box1.run == box2.run
2、能保證所new的對象實例的屬性完全統一(和缺點2相對)
該模式的缺點: 
1、當含有引用類型的屬性時,引用類型所指向的屬性是存在prototype中,直接在對象中修改引用屬性就把所有對象的引用屬性都修改了
2、該種模式省略了初始化傳參,使得new出來的所有屬性都是一樣的

1.1、原型字面量模式

(原型模式的另外一種模式,大體上區別不大,都屬於原型模式)
function Box() {};
Box.prototype = { //使用字面量的方式
	constructor : Box,
	name : 'Lee',
	age : 100,
	run : function () {
		return this.name + this.age + '運行中...';
	}
};
該模式繼承了原型模式的所有優缺點,但是也有自己特有的優缺點
優點: 更好的體現了封裝性
缺點: constructor沒有指向自身,必須手動強制指向

2、構造函數+原型模式

function Desk(name, age) { //不共享的使用構造函數
	this.name = name;
	this.age = age;
	this.family = ['aaa', 'bbb', 'ccc'];
};
Desk.prototype = { //共享的使用原型模式
	constructor : Desk,
	run : function () {
		return this.name + this.age + this.family;
	}
};

var desk1 = new Desk('Lee',100);
var desk2 = new Desk('Jack',200);
alert(desk1.family);	//aaa,bbb,ccc
desk1.family.push('ddd');
alert(desk1.family);	//aaa,bbb,ccc,ddd
alert(desk2.family);	//aaa,bbb,ccc
優點: 這種模式完美解決了共享屬性與不共享屬性的問題,而且可以精確控制,是非常不錯的模式。
缺點: 但是此種辦法,原型與構造函數相分離,讓人感覺很怪異,體現不了封裝。

3、動態原型模式

function Box(name ,age) { //將所有信息封裝到函數體內
	this.name = name;		//不共享的屬性
	this.age = age;
	
	if (typeof this.arr != 'object') {		//共享的屬性
		alert('在第一次調用的時候...arr');
		Box.prototype.arr = ['aaa','bbb'];
	}
	if (typeof this.run != 'function') {
		alert('在第一次調用的時候...run');
		Box.prototype.run = function () {
			return this.name + this.age + '運行中...';
		};
	}
}
var box1 = new Box();
var box2 = new Box();
alert( box1.arr );		//aaa,bbb
box1.arr.push('ccc');
alert( box1.arr );		//aaa,bbb,ccc
alert( box2.arr );		//aaa,bbb

優點:這個模式繼承了上一個模式(構造+原型)的所有優點,同時也完美解決了封裝性問題
注意:此種模式書寫原型時不可用字面量模式,會切斷實例和新原型之間的聯繫的。???

基本到這裏,以上兩種模式已經能處理大多數問題,而且是比較好的模式了。
但是,還是有一些比較特定的需求,需要用到以下兩種比較另類的模式。


4、寄生構造函數模式(工廠模式+構造函數模式)

function Box(name, age) {
	var obj = new Object();
	obj.name = name;
	obj.age = age;
	obj.run = function () {
		return this.name + this.age + '運行中...';
	};
	return obj;
}


5、穩妥構造函數模式

function Box(name , age) {
	var obj = new Object();
	obj.run = function () {
		return name + age + '運行中...'; //直接打印參數即可
	};
	return obj;
}
穩妥構造函數模式,是當構造函數裏不允許使用this,外部實例化時不準用new時,才需要用到的模式。
我也不知道爲啥會出現這樣那麼奇葩的需求。不過這麼多的創建對象模式,確實比Java眼花繚亂多了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章