一、建立物件的幾種方式
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是互相獨立的,不會混淆。
但是這樣子每聲明一個結構相同的對象,都得加一段程式碼,特別不方便,所以有了下面的工廠模式。
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出來的物件在本質做一些區分的時候,就不行了,此時就有了更簡便的建構子模式。
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了。
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出來的所有屬性都是一樣的
(原型模式的另一個模式,大體上差別不大,都屬於原型模式)
function Box() {}; Box.prototype = { //使用字面量的方式 constructor : Box, name : 'Lee', age : 100, run : function () { return this.name + this.age + '运行中...'; } };
此模式繼承了原型模式的所有優缺點,但是也有自己特有的優缺點
優點: 更好的體現了封裝性
缺點: constructor沒有指向自身,必須手動強制指向
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
優點: 這種模式完美解決了共享屬性與不共享屬性的問題,而且可以精確控制,是非常不錯的模式。
缺點: 但是此種辦法,原型與建構子相分離,讓人感覺很怪異,體現不了封裝。
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
優點:這個模式繼承了上一個模式(建構+原型)的所有優點,同時也完美解決了封裝性問題
注意:此種模式書寫原型時不可用字面量模式,會切斷實例和新原型之間的聯繫的。 ? ? ?
基本上到這裡,以上兩種模式已經能處理大多數問題,而且是比較好的模式了。
但是,還是有一些比較特定的需求,需要用到以下兩種比較另類的模式。
function Box(name, age) { var obj = new Object(); obj.name = name; obj.age = age; obj.run = function () { return this.name + this.age + '运行中...'; }; return obj; }
function Box(name , age) { var obj = new Object(); obj.run = function () { return name + age + '运行中...'; //直接打印参数即可 }; return obj; }
穩做建構函式模式,是當建構函式裡不允許使用this,外部實例化時不準用new時,才需要用到的模式。
我也不知道為啥會出現這樣那麼奇葩的需求。不過這麼多的創建物件模式,確實比Java眼花撩亂多了。
以上是理解JavaScript創建對象與透過原型創建對象的詳細內容。更多資訊請關注PHP中文網其他相關文章!