創建對象 第一種:基於Object對象 第二種:對象字面量方式(比較清楚的查找對象包含的屬性及方法) 使用Object構造函數或對象字面量都可以創建對象,但缺點是創建多個對象時,會產生大量的重覆代碼,因此下麵介紹可解決這個問題的創建對象的方法 1、工廠模式 缺點:創建對象交給一個工廠方法來實現,可以 ...
創建對象
第一種:基於Object對象
var person = new Object(); person.name = 'My Name'; person.age = 18; person.getName = function(){ return this.name; }
第二種:對象字面量方式(比較清楚的查找對象包含的屬性及方法)
var person = { name : 'My name', age : 18, getName : function(){ return this.name; } }
使用Object構造函數或對象字面量都可以創建對象,但缺點是創建多個對象時,會產生大量的重覆代碼,因此下麵介紹可解決這個問題的創建對象的方法
1、工廠模式
function createPerson(name, age) { var o = new Object(); o.name = name; o.age = age; o.getAge = function () { return this.age; }; return o; } var person = createPerson('張三', 23); console.log(person.name); //'張三' console.log(person.age); //23 console.log(person.getAge()); //23
缺點:創建對象交給一個工廠方法來實現,可以傳遞參數,但主要缺點是無法識別對象類型,因為創建對象都是使用Object的原生構造函數來完成的。
2、構造函數模式
function Person(name, age) { this.name = name; this.age = age; this.getAge = function () { return this.age; }; } var person = new Person('張三', 23); console.log(person.name); //'張三' console.log(person.age); //23 console.log(person.getAge()); //23
alert(person instanceof Person); //true;
alert(person instanceof Object); //true;
2.1 使用自定義的構造函數(與普通函數一樣,只是用它來創建對象),定義對象類型(如:Person)的屬性和方法。它與工廠方法區別在於:
- 沒有顯式地創建對象
- 直接將屬性和方法賦值給this對象;
- 沒有return語句;
此外,要創建Person的實例,必須使用new關鍵字,以Person函數為構造函數,傳遞參數完成對象創建;
2.2 要創建 Person 的新實例,必須使用 new 操作符。以這種方式調用構造函數實際上會經歷以下 4個步驟:
(1) 創建一個新對象;
(2) 將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象);
(3) 執行構造函數中的代碼(為這個新對象添加屬性);
(4) 返回新對象。
在前面例子的最後, person1 和 person2 分別保存著 Person 的一個不同的實例。這兩個對象都有一個 constructor (構造函數)屬性,該屬性指向 Person ,如下所示。
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
對象的 constructor 屬性最初是用來標識對象類型的。
創建自定義的構造函數意味著將來可以將它的實例標識為一種特定的類型;而這正是構造函數模式勝過工廠模式的地方。
function Person(name, age) { this.name = name; this.age = age; this.getAge = new Function ("return this.age"); //和上面是一樣的,會重覆創建多個函數 }
缺點:上述代碼,創建多個實例時,會重覆調用new Function();創建多個函數實例,這些函數實例還不是一個作用域中,當然這一般不會有錯,但這會造成記憶體浪費。
3、原型模式
function Person(name) { this.name = name; } Person.prototype.age = 23; Person.prototype.getAge = function () { return this.age; }; var person = new Person('張三'); console.log(person.name); //'張三' console.log(person.age); //23 console.log(person.getAge()); //23
JS每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,它是所有通過new操作符使用函數創建的實例的原型對象。
原型對象最大特點是,所有對象實例共用它所包含的屬性和方法,也就是說,所有在原型對象中創建的屬性或方法都直接被所有對象實例共用。
實例屬性或方法的訪問過程是一次搜索過程:
- 首先從對象實例本身開始,如果找到屬性就直接返回該屬性值;
- 如果實例本身不存在要查找屬性,就繼續搜索指針指向的原型對象,在其中查找給定名字的屬性,如果有就返回;
基於以上分析,原型模式創建的對象實例,其屬性是共用原型對象的;但也可以自己實例中再進行定義,在查找時,就不從原型對象獲取,而是根據搜索原則,得到本實例的返回;簡單來說,就是實例中屬性會屏蔽原型對象中的屬性;
可以通過使用hasOwnProperty()方法來判斷,屬性是實例本身的,還是原型上的。
person.hasOwnProperty("name"); //true person.hasOwnProperty("age"); //false
缺點:最主要是當對象的屬性是引用類型時,它的值是不變的,總是引用同一個外部對象,所有實例對該對象任何一個地方產生的改動會引起其他實例的變化。
function Person(name) { this.name = name; } Person.prototype.age = 23; Person.prototype.color = ['red', 'yellow']; var person1 = new Person('張三'); console.log(person1.name); //'張三' console.log(person1.color); //["red", "yellow"] person1.color.push('black'); var person2 = new Person('李四'); console.log(person2.name); //'李四'
console.log(person2.color); //["red", "yellow", "black"] //person1的修改影響了person2
4、組合使用構造函數模式及原型模式
目前最為常用的定義類型方式,是組合使用構造函數模式與原型模式。
構造函數模式用於定義實例的屬性,而原型模式用於定義方法和共用的屬性。結果,每個實例都會有自己的一份實例屬性的副本,但同時又共用著對方方法的引用,最大限度的節約記憶體。
此外,組合模式還支持向構造函數傳遞參數,可謂是集兩家之所長。
function Person(name, age) { this.name = name; this.age = age; this.color = ['red', 'yellow']; } Person.prototype = { constructor : Person, //原型字面量形式會將對象的constructor變Object,此外強制指回Person; getAge : function () { return this.age; } }; var person1 = new Person('張三', 23); person1.color.push('black'); console.log(person1.name); //張三 console.log(person1.color); //["red", "yellow", "black"] console.log(person1.getAge()); //23 var person2 = new Person('李四', 24); console.log(person2.name); //李四 console.log(person2.color); //['red','yellow'] console.log(person2.getAge()); //24
5、動態原型模式
組合模式中實例屬性與共用方法(由原型定義)是分離的,這與純面向對象語言不太一致;動態原型模式將所有構造信息都封裝在構造函數中,又保持了組合的優點。其原理就是通過判斷構造函數的原型中是否已經定義了共用的方法或屬性,如果沒有則定義,否則不再執行定義過程。該方式只原型上方法或屬性只定義一次,且將所有構造過程都封裝在構造函數中,對原型所做的修改能立即體現所有實例中:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.lessons = ['Math', 'Physics']; } if (typeof this.getName != 'function') {//通過判斷實例封裝 Person.prototype = { constructor: Person,//原型字面量方式會將對象的constructor變為Object,此外強制指回Person getName: function () { return this.name; } } } var person1 = new Person('Jack', 19, 'SoftWare Engneer'); person1.lessons.push('Biology'); var person2 = new Person('Lily', 39, 'Mechanical Engneer'); alert(person1.lessons);//Math,Physics,Biology alert(person2.lessons);//Math,Physics alert(person1.getName === person2.getName);//true,//共用原型中定義方法