對象創(chuàng)建模式

一、工廠模式

工廠模式解決了創(chuàng)建多個相似對象的問題,但卻沒有解決對象識別的問題——即知道一個對象的類型

 function createPerson(name, age, job) {
   var o = new Object();
   o.name = name;
   o.age = age;
   o.job = job;
   o.sayName = function () {
     alert(this.name);
   };
   return o;
 }

 var person1 = createPerson("Nicholas", 29, "Software Engineer");
 var person2 = createPerson("Greg", 27, "Doctor");

二、構造函數(shù)模式

function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function () {
          alert(this.name);
        };
      }
      var person1 = new Person("Nicholas", 29, "Software Engineer");
      var person2 = new Person("Greg", 27, "Doctor");

2.1 和工廠函數(shù)的區(qū)別:

  1. 沒有顯示的創(chuàng)建對象
  2. 直接將屬性和方法賦給了this對象
  3. 沒有return語句
  4. 函數(shù)名首字母大寫

創(chuàng)建Person的新實例,必須用new操作符。以這種方式調用構造函數(shù)實際上會經(jīng)歷以下4個步驟:

  1. 創(chuàng)建一個新對象
  2. 將構造函數(shù)的作用域賦值給新對象(因此this就指向了這個新對象)
  3. 執(zhí)行這個構造函數(shù)中的代碼(為這個新對象添加屬性)
  4. 返回新對象

這里例子中person1person2分別保存著Person的一個不同的實例。這兩個對象都有一個constructor(構造函數(shù))屬性,該屬性指向Person

alert(person1.constructor == Person) // true
alert(person2.constructor == Person) // true

對象的constructor屬性最初是用來標識對象類型的。但是檢測對象類型,還是instanceof更可靠。

創(chuàng)建自定義的構造函數(shù)意味著將來可以將它的實例標識為一種特定的類型。這正是構造函數(shù)勝過工廠模式的地方。

2.2 構造函數(shù)和普通函數(shù)的區(qū)別

構造函數(shù)和其他函數(shù)唯一的區(qū)別,就在于調用它們的方式不同。任何函數(shù),只要通過new操作符調用,那它就可以作為構造函數(shù);而任何函數(shù),如果不通過new操作符來調用,那它和普通函數(shù)也不會有什么兩樣。

// 當做構造函數(shù)使用
var person = new Person("Nicholas",29,"Software Engineer")
person.sayName(); //"Nicholas"

// 作為普通函數(shù)調用
Person("Greg",27,"Doctor") // 添加到window
window.sayName(); //"Greg"

// 在另一個對象的作用域中調用
var o = new Object();
Person.call(o,"Kristen",25,"Nurse")
o.sayName();  //"Kristen"

2.3 構造函數(shù)的問題

每個方法都要在每個實例上重新創(chuàng)建一遍。可以通過在全局作用域中創(chuàng)建函數(shù)解決這一問題,但這樣讓引用類型沒有封裝性。

 function Person(name, age, job) {
   this.name = name;
   this.age = age;
   this.job = job;
   this.sayName = sayName;
 }

 function sayName() {
   alert(this.name);
 }
 var person1 = new Person("Nicholas", 29, "Software Engineer");
 var person2 = new Person("Greg", 27, "Doctor");
 console.log(person1, person2);

三、原型模式

我們創(chuàng)建的每個函數(shù)都有一個`prototype`(原型)屬性,這個屬性是一個指針,這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。使用原型的好處是可以讓所有對象實例共享它包含的屬性和方法。
function Person() {}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
  alert(this.name);
};

var person1 = new Person();
person1.sayName();

var person2 = new Person();
person2.sayName();

alert(person1.sayName == person2.sayName);

3.1 理解原型對象

無論什么時候,只要創(chuàng)建了一個新函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向函數(shù)的原型對象。在默認情況下,所有原型對象都會自動獲得一個constructor(構造函數(shù))屬性,這個屬性包含一個指向prototype屬性所在函數(shù)的指針。

  1. 原型對象的缺點
    原型中所有屬性是被很多實例共享,這種共享對于函數(shù)非常合適。對于包含基本值的屬性也可以,對于包含引用類型的屬性來說,可能會被修改。
function Person() {}

Person.prototype = {
  constructor: Person,
  name: "Nicholas",
  age: 29,
  job: "Software Engineer",
  friends: ["Shelby", "Court"],
  sayName: function () {
    alert(this.name);
  },
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Van");

alert(person1.friends); // "Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court,Van"
alert(person1.friends === person2.friends); // true

四、組合使用構造函數(shù)模式和原型模式

構造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。結果每個實例都會有自己的一份實例屬性的副本,但同時又共享者對方付的引用,最大限度地節(jié)省了內存。

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.friends = ["Shelby", "Court"];
}

Person.prototype = {
  constructor: Person,
  sayName: function () {
    alert(this.name);
  },
};

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court"
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true

五、動態(tài)原型模式

可以將所有信息封裝在構造函數(shù)中。

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;

  if (typeof this.sayName !== "function") {
    Person.prototype.sayName = function () {
      alert(this.name);
    };
  }
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

使用動態(tài)模式時,不能使用對象字面量重寫原型。如果在已經(jīng)創(chuàng)建了實例的情況下重寫原型,那么就會切斷現(xiàn)有實例與新實例之間的聯(lián)系

六、寄生構造函數(shù)模式

通常,在前述幾種模式都不適用的情況下,可以使用寄生構造函數(shù)模式。這種模式的基本思想是創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼,然后再返回新創(chuàng)建的對象;

 function Person(name, age, job) {
   var o = new Object();
   o.name = name;
   o.age = age;
   o.job = job;
   o.sayName = function () {
     alert(this.name);
   };
   return o;
 }

 var friend = new Person("Nicholas", 29, "Software Engineer");
 friend.sayName(); //"Nicholas"

例如:

function SpecialArray() {
  // 創(chuàng)建數(shù)組
  var values = new Array();

  //添加值
  values.push.apply(values, arguments);

  //添加方法
  values.toPipedString = function () {
    return this.join("|");
  };

  //返回數(shù)組
  return values;
}

var colors = new SpecialArray("red", "green", "blue");
alert(colors.toPipedString());

返回的對象與在構造函數(shù)外部創(chuàng)建的對象沒什么不同

七、穩(wěn)妥構造函數(shù)模式

所謂穩(wěn)妥對象,指的是沒有公共屬性,而且其方法也不引用this的對象。

與寄生構造函數(shù)的區(qū)別:

  1. 創(chuàng)建對象的實例方法不引用this
  2. 不使用new操作符調用構造函數(shù)
function Person(name, age, job) {
  // 創(chuàng)建要返回的對象
  var o = new Object();

  //添加方法
  o.sayName = function () {
    alert(name);
  };

  // 返回對象
  return o;
}

var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容