【JS系列】一起理解物件的7種建立方式(全)

一燈發表於2019-04-25

前言

起初接觸JS時常見的簡單地建立單個物件的方式有下面三種:

  • 字面量,即var obj = {}
  • new Object()
  • Object.create()

以上三種方式在需要建立很多物件時會產生大量重複程式碼。顯然需要更高效的方式,下面將介紹7種建立物件的方式

1. 工廠模式

1.1介紹

工廠模式正是源自設計模式中的工廠模式,其基本思想:

  • 既然建立物件數量多時產生了大量重複程式碼
  • 那就建一座工廠,通過工廠來建立加工
  • 再返回建立好的物件
function createPerson(name, age) {
  var obj = new Object()
  obj.name = name
  obj.age = age
  obj.getName = function() {
    console.log(this.name)
  }
  return obj
}

var person1 = createPerson("AAA", 23)
person1.getName()   //AAA

var person2 = createPerson("BBB", 30)
person2.getName()   //BBB
複製程式碼

1.2 優劣分析

  • 解決了複用性的問題
  • 但無法判斷物件的型別(只知道是Objcet型別)

2. 建構函式模式

2.1 介紹

建構函式可建立特定型別的物件,類似Object和Array(實際上Object和Array本身就是建構函式)

function Person(name, age) {
  this.name = name
  this.age = age
  this.getName = function() {
    console.log(this.name)
  }
}

var person1 = new Person("AAA", 23)
person1.getName()  //AAA

var person2 = new Person("BBB", 30)
person2.getName()  //BBB
複製程式碼

通過上面程式碼我們可以看到建構函式模式:

  • 沒有通過new Object()顯示建立物件
  • 屬性和方法賦值給了this
  • 無需return

2.2 建構函式也是函式

建構函式其實也是個函式,只是可以通過new呼叫來建立物件,當然也可作為普通函式使用

//作為普通函式使用
Person("CCC", 24)
Person.getName() //CCC

//在一個物件內呼叫
var obj = {}
Person.call(obj, "DDD", 25)
obj.getName()    //DDD
複製程式碼

2.3 優劣分析

  • 建構函式模式可以建立具體型別的物件
console.log(person1 instanceof Object)  //true
console.log(person1 instanceof Person)  //true
複製程式碼
  • 然而,建構函式中的方法是通用的,但每個例項都建立了一份屬於自己的副本,造成記憶體浪費
console.log(person1.getName === person2.getName)  //false
複製程式碼

3. 原型模式

3.1 介紹

原型模式即通過原型物件來實現屬性和方法的共享,例項物件不會建立各自的副本

要理解原型模式,建議先理解透徹JS原型的概念——推薦閱讀一張圖徹底KO原型鏈(prototype,__proto__)

function Person() {
}

Person.prototype.name = "AAA"
Person.prototype.age = 23
Person.prototype.getName = function() {
  console.log(this.name)
}

var person1 = new Person()
person1.getName()    //AAA

var person2 = new Person()
person2.name = "BBB"
person2.getName()    //BBB

console.log(person1.getName === person2.getName)  //true
複製程式碼

然而原型物件也存在缺陷——對於引用型別的屬性,各例項物件間指向同一個地址,某個物件修改了屬性,所有物件都會受到影響

function Person() {
}

Person.prototype.arr = [1,2,3]
Person.prototype.getArr = function() {
  console.log(this.arr)
}

var person1 = new Person()
person1.arr.push(4)
person1.getArr()    //1,2,3,4

var person2 = new Person()
person2.getArr()    //1,2,3,4

person1.arr.push(5)
person1.getArr()    //1,2,3,4,5
person2.getArr()    //1,2,3,4,5
複製程式碼

3.2 優劣分析

  • 原型模式解決了屬性和方法的共享問題
  • 但對於引用型別的屬性,各例項物件會相互影響

4. 組合模式

4.1介紹

組合模式即組合建構函式模式和原型模式,取二者之長,建構函式模式用於定義例項的屬性,原型模式用於定義方法和共享的屬性

function Person(name, age) {
  this.name = name
  this.age = age
  this.arr = [1,2,3]
}

Person.prototype = {
  getName: function() {
    console.log(this.name)
  }
}

var person1 = new Person("AAA", 23)
person1.arr.push(4)
console.log(person1.arr)  //1,2,3,4
person1.getName()         //AAA

var person2 = new Person("BBB", 30)
console.log(person2.arr)  //1,2,3
person2.getName()         //BBB
複製程式碼

4.2 優劣分析

  • 組合模式是使用最廣泛的一種模式,尤其在需要定義引用型別時

5. 動態原型模式

5.1 介紹

剛說組合模式是認可度最高的一種模式,然而也有些美中不足——每建立一個例項物件,原型方法都被重複定義一次

動態原型模式正是解決這個問題,使用if語句,使得原型方法只初始化一次

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

  //這裡只需要使用任何一個方式或屬性,不一定是getName,getAge也可以
  //只要保證if裡面的程式碼只執行一次就行
  if(typeof this.getName !== 'function') {
    Person.prototype.getName = function() {
      console.log(this.name)
    }

    Person.prototype.getAge = function() {
      console.log(this.age)
    }
  }
}

var person = new Person("AAA", 23)
person.getName()    //AAA
複製程式碼

5.2 優劣分析

  • 動態原型模式在組合模式的基礎上做了優化,可謂更加完美

6. 寄生建構函式模式

6.1 介紹

長得和工廠模式一樣,但呼叫方式和建構函式模式一樣的模式,通過new呼叫建構函式,本該返回一個例項物件,但return語句重寫了建構函式的返回值

function Person(name, age) {
  var obj = new Object()
  obj.name = name
  obj.age = age
  obj.getName = function() {
    console.log(this.name)
  }
  return obj
}

var person = new Person("AAA", 23)
person.getName()   //AAA
複製程式碼

6.2 優劣分析

  • 這種方式和工廠模式沒什麼不同,要說不同的就是通過new來呼叫,仍然存在工廠模式的無法判別物件型別的問題

  • 個人認為這是一種多餘的方式,複雜而不好理解,也沒有什麼應用場景

7. 穩妥建構函式模式

7.1 介紹

先介紹一個概念——穩妥物件:

  • 沒有公共屬性
  • 不使用this
  • 不使用new呼叫

穩妥建構函式模式和工廠模式類似,但遵循穩妥物件的原則

function Person(name, age) {
  var obj = new Object()

  //getName是唯一讀取name的方式
  obj.getName = function() {
    console.log(name)
  }
  return obj
}

var person = Person("AAA", 23)
person.name = "BBB" //無效
person.getName()    //AAA
複製程式碼

7.2 優劣分析

  • 穩妥建構函式模式的特點就是安全性,適用於某些需要安全執行的環境
  • 類似工廠模式,無法判別物件型別

8. 總結

  1. 工廠模式:解決了大量重複程式碼的問題,但無法判別物件型別

  2. 建構函式模式:例項物件又具體的型別,但每個例項物件都有方法的副本,造成記憶體浪費

  3. 原型模式:可共享方法和屬性,但存在引用型別相互影響的問題

  4. 組合模式:取建構函式模式和原型模式之長 (最常用)

  5. 動態原型模式:優化了組合模式

  6. 寄生建構函式模式:類似工廠模式,只是通過new呼叫

  7. 穩妥建構函式模式:類似工廠模式,只是提升了安全性

後記

希望本文對你有幫助,更多文章將持續更新……

感謝你的點贊和鼓勵

與本文相關的文章有:

【JS系列】一張圖徹底KO原型鏈(prototype,__proto__)

【JS系列】物件詳解

【JS系列】繼承的這6種方式!(上)

【JS系列】繼承的這6種方式!(下)

相關文章