JavaScript中建立對象的5種模式

構造函數模式

實現方式:
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        return this.name
    }
}

// 測試
var person = new Person('Nicholas', 29, 'Engineer');

// 檢測對象類型
console.log(person.constructor === Person);  // true
console.log(person instanceof Person);  // true
console.log(Person instanceof Object);  // true
注意:
  • 構造函數名使用首字母大寫。
  • 使用new操做符建立實例。
  • 優勢:
    • 能夠使用instanceof操做符檢測對象類型。
  • 缺點:
    • 每一個方法都要在每一個實例上從新建立一遍。不一樣實例的同名函數不相等。
var person1 = new Person('Nicholas', 29, 'Engineer');
var person2 = new Person('Greg', 30, 'Doctor');
console.log(person1.sayName === person2.sayName);  // false

原型模式

實現方式:
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function() {
    return this.name;
}

// 測試
var person = new Person();
console.log(person.sayName());  // Nicholas

// 檢測對象類型
console.log(person.constructor === Person);  // true
console.log(person instanceof Person);  // true
console.log(Person instanceof Object);  // true
原型鏈:

關於原型:
  • 構造函數與原型對象
    • 構造函數得到一個prototype屬性,指向函數的原型。
    • 原型得到一個constructor屬性,指向構造函數。
  • 實例對象與原型對象
    • 建立實例後,實例得到內部屬性[[Prototype]]_proto_,指向構造函數的原型。
    • isPrototypeOf()方法會返回一個布爾值,能夠肯定實例和原型的關係。
    • Object.getPrototypeOf()方法會返回實例對應的原型。
// isPrototypeOf()方法
console.log(Person.prototype.isPrototypeOf(person));  // true
// Object.getPrototypeOf()方法
console.log(Object.getPrototypeOf(person) === Person.prototype); // true
  • 訪問原型鏈
    • 讀取實例的屬性:若是在實例中找到了該屬性,則返回屬性的值;若是沒有找到,則繼續搜索原型的同名屬性,若是找到則返回該屬性。
    • 添加實例的屬性:若是在實例中添加一個屬性,而該屬性與原型中的一個屬性同名。則在實例中建立該屬性,並屏蔽原型中的同名屬性。
    • 刪除實例的屬性:若是刪除了實例的屬性,則會使原型中的同名屬性暴露出來。
// 添加屬性
person.name = "Greg";
console.log(person.name);  // Greg
// 刪除屬性
delete person.name;
console.log(person.name);  // Nicholas
  • 使用對象字面量定義原型
    • 須要手動設置原型的constructor屬性
    • 默認的construnctor屬性是不可枚舉的。能夠使用Object.defineProperty()方法定義。
function Person() {};
Person.prototype = {
    // constructor: Person,
    name: 'Nicholas',
    age: 29,
    job: 'Engineer',
    sayName: function() {
        return this.name;
    }
};
Object.defineProperty(Person.prototype, 'constructor', {
    enumerable: false,
    value: Person
});

// 測試
var person = new Person();
console.log(person.sayName());  // Nicholas

// 檢測對象類型
console.log(person.constructor === Person);  // true
console.log(person instanceof Person);  // true
console.log(Person instanceof Object);  // true
注意:
  • 使用new操做符建立實例。
  • 優勢:
    • 能夠使用instanceof操做符檢測對象類型。
    • 全部對象實例共享原型對象所包含的屬性和方法。
  • 缺點:
    • 對於引用類型值,會存在實例意外修改原型的風險。
function Student() {}
Student.prototype.friends = ['Shelby', 'Court'];

// 測試
var stu1 = new Student();
var stu2 = new Student();
stu1.friends.push('Van');   // 實例stu1修改了原型的friends屬性,並影響到了stu2
console.log(stu1.friends);  // ["Shelby", "Court", "Van"] 
console.log(stu2.friends);  // ["Shelby", "Court", "Van"]

組合模式:構造函數模式&原型模式

實現方式:
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}
Person.prototype.sayName = function() {
    return this.name;
}

// 測試
var person = new Person('Nicholas', 29, 'Engineer');
console.log(person.sayName());  // Nicholas

// 檢測對象類型
console.log(person.constructor === Person);  // true
console.log(person instanceof Person);  // true
console.log(Person instanceof Object);  // true
注意:
  • 使用new操做符建立實例。
  • 構造函數用於自定義實例屬性,原型中定義方法和共享屬性。
  • 優勢:
    • 能夠使用instanceof操做符檢測對象類型。
    • 每一個實例都會有本身的一份實例屬性的副本,同時又共享着對方法的引用,最大限度地節省了內存。
    • 支持向構造函數傳遞參數。

動態原型模式

實現方式:
function Person(name, age, job) {
    // 屬性
    this.name = name;
    this.age = age;
    this.job = job;
    
    // 方法
    if (typeof this.sayName !== 'function') {
        Person.prototype.sayName = function() {
            return this.name;
        }
    }
}

// 測試
var person = new Person('Nicholas', 29, 'Engineer');
console.log(person.sayName());  // Nicholas

// 檢測對象類型
console.log(person.constructor === Person);  // true
console.log(person instanceof Person);  // true
console.log(Person instanceof Object);  // true
注意:
  • 使用new操做符建立實例。
  • 原型初始化只在初次調用構造函數時才執行。
  • 不能使用對象字面量方式重寫原型。不然就會切斷現有實例與新原型之間的關係。
  • 優勢:
    • 能夠使用instanceof操做符檢測對象類型。
    • 把全部信息都封裝在構造函數中,在構造函數中初始化原型,保持了同時使用構造函數和原型的優勢。

寄生構造函數模式

實現方式:
function Person(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job
    o.sayName = function() {
        return o.name;
    }
    return o;
}

// 測試
var person = new Person('Nicholas', 29, 'Engineer');
console.log(person.sayName());  // Nicholas

// 檢測對象類型
console.log(person.constructor === Person);  // false
console.log(person instanceof Person);  // false
console.log(Person instanceof Object);  // true
注意:
  • 能夠不使用new操做符建立實例。在不使用new操做符時,又被稱爲工廠模式
  • 在構造函數末尾添加return語句,能夠重寫調用構造函數時的返回值。
  • 沒法使用instanceof操做符檢測對象類型。由於返回的對象與構造函數及原型之間沒有關係。

參考:《JavaScript高級程序設計》javascript

相關文章
相關標籤/搜索