原文連接javascript
面向對象的語言有一個標誌,即擁有類的概念,抽象實例對象的公共屬性與方法,基於類能夠建立任意多個實例對象,通常具備封裝、繼承、多態的特性!html
但JS中對象與純面嚮對象語言中的對象是不一樣的,ECMA標準定義JS中對象:無序屬性的集合,其屬性能夠包含基本值、對象或者函數。java
能夠簡單理解爲JS的對象是一組無序的值,其中的屬性或方法都有一個名字,根據這個名字能夠訪問相映射的值(值能夠是基本值/對象/方法)。less
建立對象交給一個工廠方法來實現,能夠傳遞參數,但主要缺點是沒法識別對象類型,由於建立對象都是使用Object的原生構造函數來完成的。函數
function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.getName = function () { return this.name; } return o; // 使用return返回生成的對象實例 } var person = createPerson('Jack', 19, 'SoftWare Engineer');
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getName = function () { return this.name; } } var person1 = new Person('Jack', 19, 'SoftWare Engineer'); var person2 = new Person('Liye', 23, 'Mechanical Engineer');
構造函數模式與工廠方法區別在於:this
沒有顯式地建立對象prototype
直接將屬性和方法賦值給this對象指針
沒有return語句code
上述由Person構造函數生成的兩個對象person1與person2都是Person的實例,所以可使用instanceof判斷,而且由於全部對象都繼承Object,所以person1 instanceof Object也返回真:htm
console.log(person1 instanceof Person); // true; console.log(person2 instanceof Person); // true; console.log(person1 instanceof Object); // true; console.log(person2 instanceof Object); // true; console.log(person1.constructor === person2.constructor); // ture;
雖然構造函數方式比較不錯,但也存在缺點,那就是在建立對象時,特別針對對象的屬性指向函數時,會重複的建立函數實例,以上述代碼爲基礎,能夠改寫爲:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getName = getName; } function getName() { return this.name; }
JS中每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,它是全部經過new操做符使用函數建立的實例的原型對象。
原型對象最大特色是,全部對象實例共享它所包含的屬性和方法,也就是說,全部在原型對象中建立的屬性或方法都直接被全部對象實例共享。
function Person() {} Person.prototype.name = "Jack"; Person.prototype.age = 29; Person.prototype.getName = function () { return this.name; } var person1 = new Person(); var person2 = new Person(); console.log(person1.getName === person2.getName); // ture;
目前最爲經常使用的定義類型方式,是組合構造函數模式與原型模式。
構造函數模式用於定義實例的屬性,而原型模式用於定義方法和共享的屬性。
這樣,每一個實例都會有本身的一份實例屬性的副本,但同時又共享着對方方法的引用,最大限度的節約內存。此外,組合模式還支持向構造函數傳遞參數,可謂是集兩家之所長。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.lessons = ['Math', 'Physics']; } 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'); console.log(person1.lessons); // ["Math", "Physics", "Biology"] console.log(person2.lessons); // ["Math", "Physics"] console.log(person1.getName === person2.getName); // true
組合模式中實例屬性與共享方法(由原型定義)是分離的,這與純面嚮對象語言不太一致。
動態原型模式將全部構造信息都封裝在構造函數中,同時又保持了組合的優勢。
其原理就是經過判斷構造函數的原型中是否已經定義了共享的方法或屬性,若是沒有則定義,不然再也不執行定義過程。
該方式只定義一次原型上方法或屬性,且將全部構造過程都封裝在構造函數中,對原型所作的修改能當即體現全部實例中:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.lessons = ['Math', 'Physics']; } if (typeof this.getName) { 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'); console.log(person1.lessons); // ["Math", "Physics", "Biology"] console.log(person2.lessons); // ["Math", "Physics"] console.log(person1.getName === person2.getName); // true