這兩天重新看着李炎恢的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(),因爲它們返回值一樣。
當有了對象以後,自然而然就會涉及到對象屬性及方法的共享問題(比如,在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中的引用屬性就是修改引用所指向的數組,所以原型也被修改了
//這是我們不希望看到的!
該模式的優點: 2、能保證所new的對象實例的屬性完全統一(和缺點2相對)
該模式的缺點:
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眼花繚亂多了。