JS中建立物件的方法

noahlam發表於2018-04-03

JS中建立物件的方法

最近手頭一個專案剛完成,下一個顯目還在準備中,趁這個空檔期,拿起塵封多年的JS書, 重溫一遍JS物件導向程式設計,然後就得出下文,算是一個總結吧。

也許,你會說 “建立物件不就是一對花括號的事嗎?”,是的,目前我們最常用, 也是最便捷的方式就是所謂的一對花括號的事,也就是我們常說的JSON物件(嚴格意義上,這其實不算JSON物件,具體我們這裡不做深入),如下:

let obj = {
    name:'xiaohong',
    age: 17,
    gender: 'female'
}
複製程式碼

這是就是我們常說的物件字面量(Literal)的方法建立物件,應該是目前使用最廣泛的方法了。 這總方法基本上等同與下面(建立Object例項)的方法

let obj = new Object()
obj.name = 'xiaohong'
obj.age = 17
obj.gender: 'female'
複製程式碼

但是由於 物件字面量的方法,比較簡潔直觀,足以滿足大部分場景下的需求,所以被開發著們廣泛採用, 而Object例項的方式就很少有人問津了。 不過字面量方法也是有缺點的,缺點就是完全沒有複用性可言,如果需要建立多個物件, 就會有很多重複的程式碼,比如:

var studentA = {
    name: 'xiaohong'
    age: 17,
    gender: 'female'
}
var studentB = {
    name: 'xiaoming'
    age: 18,
    gender: 'male'
}
var studentC = {
    name: 'lili'
    age: 17,
    gender: 'female'
}
複製程式碼

不難看出,三個物件只有冒號後面的內容不一樣,其他的程式碼都是冗餘的,那有什麼辦法可以避免這個冗餘呢? 平常開發中,如果我們碰到一個要重複用到的功能的時候,我們會怎麼做?對的, 就是把這個功能封裝成一個函式,然後重複呼叫:

function Student(name,age,gender){
    // 以下程式碼還可以用es6這樣寫 return {name,age,gender}
    // 詳情請參考es6 屬性簡寫
    return {
        name:name,
        age:age,
        gender:gender
    }
}
// 然後在需要的時候,呼叫一下這個函式,傳進一些引數即可
var studentA = Student('xiaohong',17,'f')
var studentB = Student('xiaoming',18,'m')
var studentC = Student('lili',17,'f')
複製程式碼

這樣是不是簡潔很多,每次只要呼叫一下Student這個函式,然後傳進名字,年齡,性別, 就能得到一個你想要的物件了。 這種方法叫 工廠模式 ,是不是真的很像一個加工廠呢? 這種方法對建立多個物件的時候很有用, 不過這種方法也是有缺點的,就是無法知道物件的型別,比如, 我想通過條件語句判斷studentA是不是一個Student物件,就做不到

typeof studentA === 'Student'   //false
studentA instanceof Student     // false
複製程式碼

由於工廠模式在物件識別的問題上不堪重任,所以我們通常用 建構函式 模式來解決物件識別的問題

function Student(name,age,gender){
    this.name = name
    this.age = age
    this.gender = gender
}
// 通過呼叫建構函式,new一個物件(這個估計是有其他面嚮物件語言基礎的童鞋對容易接受的一種方式)
var studentA = new Student('xiaohong',17,'f')
var studentB = new Student('xiaoming',18,'m')
var studentC = new Student('lili',17,'f')
複製程式碼

建構函式跟工廠模式的很相似,區別主要在以下2點:

  1. 沒有返回物件,而是直接把引數賦值給this作用域下的同名變數,因為new的時候, 會把this指向呼叫new出來的那個例項物件,所以就完成了賦值操作
  2. 呼叫建構函式的時候,在建構函式前面加一個new( 如果沒加new,就當做普通函式使用,作用域會在當前程式碼塊的環境下面,函式裡面的值會賦給當前作用域)

通過建構函式new出來的物件,我們是能檢測到它的型別的

studentA instanceof Student // true
studentA instanceof Object  // true
複製程式碼

事實上,當我們使用 new 例項化一個建構函式的時候,js其實偷偷的在背後做了4件事,這個也是個比較經典的面試題:

  1. 建立一個新物件(prototype 指向建構函式的prototype)
  2. 把作用域(this)指給這個物件
  3. 執行建構函式的程式碼
  4. 返回這個物件

然而,建構函式也不是沒有缺點,使用建構函式建立的物件裡面都是資料是沒啥問題, 但是如果物件裡面有函式(方法)呢? 還是上面那個程式碼,我們拿來稍微修改一下,需要給學生增加一個學習的技能:

function Student(name,age,gender){
    this.name = name
    this.age = age
    this.gender = gender
    this.study = fucntion() { console.log('我在學習...')}
 }
複製程式碼

這樣咋看起來,也沒啥毛病,呼叫一些例項的study,也可以列印出“我在學習...”

var studentA = new Student('xiaohong',17,'f')
studentA.study()   // 我在學習...
複製程式碼

但是,如果我們這樣

var studentA = new Student('xiaohong',17,'f')
var studentB = new Student('xiaoming',18,'m')
studentA.study == studentB.study  // false
複製程式碼

我們發現,2個例項的study不是指向同一個函式,而是2個不同的函式,但是他們的功能一模一樣 相當於這樣

studentA.study = fucntion() { console.log('我在學習...')}
studentB.study = fucntion() { console.log('我在學習...')}
studentC.study = fucntion() { console.log('我在學習...')}
複製程式碼

這讓強迫症怎麼接受? 事實證明,寫程式碼的,大部分都有強迫症,於是,就有了原型模式 原型模式的原理,就是我不把方法和屬性新增到建構函式裡面去,我直接新增到建構函式的原型裡面去, 由於原型的共享的,所以我們在這邊就解決了冗餘:

function Student(){}   // 宣告一個空函式
Student.prototype.name = 'xiaohong'
Student.prototype.age = 17
Student.prototype.gender = 'f'
Student.prototype.study = fucntion() { console.log('我在學習...')}

var studentA = new Student()
var studentB = new Student()
studentA.study == studentA.study  // true
複製程式碼

這樣,就能解決函式重複宣告的問題,所有的例項,都共享原型上的函式study.然而,函式是共享了沒錯, 不過其他資料也共享了,所有例項上都會有相同的name,age,gender,這也不是我們想要的效,這時, 聰明的你肯定會想,把資料放在建構函式裡面,只把方法放在原型裡面:

function Student(name,age,gender){
    this.name = name
    this.age = age
    this.gender = gender
 }
 Student.prototype.study = fucntion() { console.log('我在學習...')}
複製程式碼

這樣把普通的資料放在建構函式裡面宣告,把函式(方法)放在原型上宣告的模式, 我們稱之為組合模式(即組合使用建構函式模式和原型模式),組合模式,既有資料的獨立,又有方法的共享 可以說是比較完美的一種物件的建立方式了。ES6的class語法糖實現的原理大體上也是利用組合模式。

以上就是ES5裡面建立物件的一些常用模式,當然還有一些不常用的奇葩的模式,比如動態原型模式, 寄生建構函式模式,穩妥建構函式模式...等等,,這裡就不一一列舉了,感興趣的童鞋自行百度一下

好了,關於建立物件的話題,就到這裡了,感謝收看!

如果覺得對您有用,請給本文的github加個star,萬分感謝,另外,github上還有其他一些關於前端的教程和元件,有興趣的童鞋可以看看,你們的支援就是我最大的動力。

相關文章