《JavaScript物件導向精要》之六:物件模式

明易發表於2019-02-27

6.1 私有成員和特權成員

JavaScript 物件的所有屬性都是公有的,沒有顯式的方法指定某個屬性不能被外界訪問。

6.1.1 模組模式

模組模式是一種用於建立擁有私有資料的單件物件的模式。
基本做法是使用立即呼叫函式表示式(IIFE)來返回一個物件。原理是利用閉包。

var yourObj = (function(){
  // private data variables

  return {
    // public methods and properties
  }
}());
複製程式碼

模組模式還有一個變種叫暴露模組模式,它將所有的變數和方法都放在 IIFE 的頭部,然後將它們設定到需要被返回的物件上。

//  一般寫法
var yourObj = (function(){
  var age = 25;

  return {
    name: "Ljc",

    getAge: function(){
      return age;
    }
  }
}());


// 暴露模組模式
var yourObj = (function(){
  var age = 25;
  function getAge(){
    return age;
  };
  return {
    name: "Ljc",
    getAge: getAge
  }
}());
複製程式碼

6.1.2 建構函式的私有成員(不能通過物件直接訪問)

模組模式在定義單個物件的私有屬性十分有效,但對於那些同樣需要私有屬性的自定義型別呢?你可以在建構函式中使用類似的模式來建立每個例項的私有資料。

function Person(name){
  // define a variable only accessible inside of the Person constructor
  var age = 22;

  this.name = name;
  this.getAge = function(){
    return age;
  };
  this.growOlder = function(){
    age++;
  }
}

var person = new Person("Ljc");

console.log(person.age); // undefined
person.age = 100;
console.log(person.getAge()); // 22

person.growOlder();
console.log(person.getAge()); // 23
複製程式碼

這裡有個問題:如果你需要物件例項擁有私有資料,就不能將相應方法放在 prototype 上。

如果你需要所有例項共享私有資料。則可結合模組模式和建構函式,如下:

var Person = (function(){
  var age = 22;

  function InnerPerson(name){
    this.name = name;
  }

  InnerPerson.prototype.getAge = function(){
    return age;
  }
  InnerPerson.prototype.growOlder = function(){
    age++;
  };

  return InnerPerson;
}());

var person1 = new Person("Nicholash");
var person2 = new Person("Greg");

console.log(person1.name); // "Nicholash"
console.log(person1.getAge()); // 22

console.log(person2.name); // "Greg"
console.log(person2.getAge()); // 22

person1.growOlder();
console.log(person1.getAge()); // 23
console.log(person2.getAge()); // 23
複製程式碼

6.2 混入

這是一種偽繼承。一個物件在不改變原型物件鏈的情況下得到了另外一個物件的屬性被稱為“混入”。因此,和繼承不同,混入讓你在建立物件後無法檢查屬性來源。
純函式實現:

function mixin(receiver, supplier){
  for(var property in supplier){
    if(supplier.hasOwnProperty(property)){
      receiver[property] = supplier[property];
    }
  }
}
複製程式碼

這是淺拷貝,如果屬性的值是一個引用,那麼兩者將指向同一個物件。

6.3 作用域安全的建構函式

建構函式也是函式,所以不用 new 也能呼叫它們來改變 this 的值。

在非嚴格模式下, this 被強制指向全域性物件。

而在嚴格模式下,建構函式會丟擲一個錯誤(因為嚴格模式下沒有為全域性物件設定 thisthis 保持為 undefined)。

而很多內建建構函式,例如 ArrayRegExp 不需要 new 也能正常工作,這是因為它們被設計為作用域安全的建構函式。

當用 new 呼叫一個函式時,this 指向的新建立的物件是屬於該建構函式所代表的自定義型別。因此,可在函式內用 instanceof 檢查自己是否被 new 呼叫。

function Person(name){
  if(this instanceof Person){
    // called with "new"
  }else{
    // called without "new"
  }
}
複製程式碼

具體案例:

function Person(name){
  if(this instanceof Person){
    this.name = name;
  }else{
    return new Person(name);
  }
}
複製程式碼

相關文章