1. 程式人生 > 程式設計 >詳解 javascript物件建立模式

詳解 javascript物件建立模式

建立模式

在javascript中,主要有以下幾種建立模式:

工廠模式
建構函式模式
原型模式
組合模式
動態原型模式
寄生建構函式模式
穩妥構造模式

工廠模式

工廠模式是軟體工程領域一種廣為人知的設計模式。javascript實現方式:

function createPerson(name,obj,job) {
    var o = new Object();
    o.name = name;
    o.obj = obj;
    o.job = job;
    o.sayName = function() {
      alert(this.name);
    }
    return o;
  }
  
  var person1 = createPerson("Nicholas",29,"software Enginner");
  var person2 = createPerson("Greg",27,"Doctor");

工廠模式雖然解決了建立多個相似物件的問題,但卻沒有解決物件識別問題

建構函式模式

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

new操作符實現原理請檢視文章附錄

不同於工廠模式,建構函式模式

沒有顯示建立物件

直接將屬性和方法賦值給了this物件

沒有return語句

解決了物件識別問題

但是建構函式模式同樣存在問題,就是每個方法都要在每個例項上重新申明一遍。person1和person2都有一個名為 sayName() 的方法,但那兩個方法不是同一個Function例項。(在javascript中,函式實質上也是物件,因此每定義一個函式,也就是例項化一個物件。)

通過吧函式定義轉移到建構函式外部可以解決這個問題:

function Person(name,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
  }
  function sayName() {
    alert(this.name);
  }
  var person1 = new Person("Nicholas","Doctor");

但這種方式又帶來了一個新的問題,我們在全域性建立了一個全域性函式。

需要注意一點,按照慣例,建構函式始終應該以一個大寫字母開頭,而非建構函式應該以一個小寫字母開頭。這主要用於區別建構函式和非建構函式,因為建構函式本身也是函式。

原型模式

我們建立的每個函式都有一個 prototype (原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途可以由特定型別的所有例項共享的屬性和方法。

函式原型物件請檢視附錄

通過原型模式建立物件,我們不必在建構函式中定義物件例項的資訊,同時例項化多個物件,每個物件不會再申明一個新的函式。

可以看到, person1.sayName 和 person2.sayName 都指向了同一個函式。

但是原型模式的缺點也是顯而易見的。

首先原型模式省略了建構函式模式傳遞引數這一環節,結果導致所有例項的初始值在預設情況下都是相同的屬性值。

更重要的是,因為將屬性和方法都放置在原型物件上,實質上原型上的屬性是 被所有例項所共享的 。對於包含基本值的屬性還表現正常,改變屬性值,只是在例項上新增一個同名屬性。但對於引用型別值的屬性來說,這可能是個災難。

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");
  
  person1.friends; // ["shelby","Court","Van"]
  person2.friends; // ["shelby","Van"]

組合模式

建立自定義型別最常見的方式,就是組合使用建構函式模式和原型模式。構造模式用於定義例項屬性,而原型模式用於定義方法和共享屬性。

這樣,每個例項都會有自己的一份例項屬性副本,但同時又共享方法的引用。

function Person(name,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby","Court"];
  }
  
  Person.prototype.sayName = function() {
    alert(this.name);
  }
  
  var person1 = new Person("Nicholas","Software Enginner");
  var person2 = new Person("Greg","Doctor");
  
  person1.friends.push("Van");
  
  person1.firends; // ["Shelby","Van"];
  person2.firends; // ["Shelby","Court"]
  
  person1.firends === person2.firends; // false
  person1.sayName === person2.sayName; // true

動態原型模式

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

寄生建構函式模式

寄生模式的基本概念就是建立一個函式,該函式的作用僅僅是封裝建立物件的程式碼,然後再返回新建立的物件。

function Person(name,job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job.
    
    o.sayName = function() {
      alert(this.name);
    }
  }
  
  var person1 = new Person("Nicholas","Software Engineer");
  person1.sayName(); // "Nicholas"

看起來,除了使用new操作符之外,這個模式和工廠模式其實是一模一樣的。
這個模式可以在特殊的情況下用來作為物件建立建構函式。
假設我們需要一個具有額外方法的特殊陣列型別。由於不能直接修改Array建構函式,因此可以使用這個模式。

  function SpecialArray() {
    var values = [];
    
    values.push.push(values,arguments);
    values.toPipedString = function() {
      return this.join("|");
    }
    
    return values;
  }
  
  var colors = new SpecialArray("red","blue","green");
  colors.toPipedString(); // "red|blue|green"

該模式主要缺點:
返回的物件和建構函式或建構函式的原型屬性間沒有關係,不·能依賴instanceof來確定物件型別。
在其他模式能夠使用的情況下,儘量不要使用這種模式。

穩妥建構函式模式

  function Person(name,job) {
    var o = new Object();
    var name = name;
    var age = age;
    var job = jbo;
    
    o.sayName = function() {
      alert(name);
    }
  }
  
  var person1 = Person("Nicholas","Software Enginner");
  firend.sayName(); // "Nicholas"
  1. 附錄
  2. new 操作符

new操作符實際上會經歷4個步驟:

  1. 建立一個空的簡單JavaScript物件(即**{}**);
  2. 連結該物件(設定該物件的constructor)到另一個物件 ;
  3. 將步驟1新建立的物件作為**this**的上下文 ;
  4. 如果該函式沒有返回物件,則返回**this**。
  function new(func) {
    var o = {};
    o.__proto__ = func.prototype;
    var result = func.apply(o,arguments);
    return typeof result === "object" ? object : o;
  }

函式原型物件

理解原型物件

無論什麼時候,只要建立一個新函式,就會根據一組特定的規則為該函式建立一個prototype屬性,這個屬性指向函式的原型物件。在預設情況下,所有原型物件都會自動獲得一個construtor(建構函式)屬性,這個屬性包含一個指向prototype屬性所在函式的指標。

在建立了一個自定義的建構函式之後,其原型物件只會取得construtoe屬性,至於其他屬性,則都是從Object繼承而來。當呼叫建構函式建立一個新例項時,該例項的內部將包含一個指標(內部屬性),指向建構函式的原型物件,這個指標叫[[Prototype]]。在多數瀏覽器中,每個物件都支援一個屬性__proto__來呼叫[[Prototype]]。

詳解 javascript物件建立模式

雖然所有實現都無法直接訪問[[Prototype]],但可以通過isPrototype方法來確定物件之間是否存在關係。

我們測試了person1和person2,都返回了true。因為他們內部都有一個指向Person.prototype的指標。

Object.getPrototype()可以返回物件的原型物件。

每當程式碼讀取某個物件的屬性時,都會執行一次搜尋,目標是具有給定名字的屬性。搜尋首先會從物件本身開始,如果在例項中找到了對應的屬性,則返回該屬性的值。如果沒找到,則繼續搜尋指標指向的原型物件。這也是為什麼我們在person1和person2兩個例項中,並沒有定義sayName這個屬性,但仍能夠正常使用。
我們在呼叫person1.sayName()是,會執行兩次搜尋。首先,解析器會問:“例項person1有sayName屬性嗎?”,答:“沒有”。然後他繼續搜尋,再問:“person1的原型有sayName屬性嗎?”,答:“有”。於是,它就讀取儲存在原型中的函式。

雖然我們能夠通過例項訪問原型的屬性,但卻不能重新原型的屬性。
如果我們在例項上新增屬性名,而這個屬性名又與原型中的屬性名相同,即我們希望在例項中重寫屬性。

  function Person() {}
  Person.prototype.name = 'Nicholas';
  
  var person1 = new Person();
  var person2 = new Person();
  
  person1.name === person2.name; // true
  person1.name = 'Greg';
  
  person1.name === person2.name; // false
  person1.name; // 'Greg'
  person2.name; // 'Nicholas'
  
  person1.__proto__.name; // 'Nicholas'

事實上,當我們重寫原型屬性時,只是在例項上添加了一個新屬性。當我們把例項上的屬性刪除後,又會暴露出原型屬性。

  delete person1.name;
  person1.name; // 'Nicholas'

使用hasOwnProperty()函式能判斷屬性是否在例項上。

  person1.hasOwnProperty('name'); // false
  person1.name = 'Greg';
  person1.hasOwnProperty('name'); // true

以上就是詳解 javascript物件建立模式的詳細內容,更多關於Java 建立模式的資料請關注我們其它相關文章!