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點:
- 沒有返回物件,而是直接把引數賦值給this作用域下的同名變數,因為new的時候, 會把this指向呼叫new出來的那個例項物件,所以就完成了賦值操作
- 呼叫建構函式的時候,在建構函式前面加一個new( 如果沒加new,就當做普通函式使用,作用域會在當前程式碼塊的環境下面,函式裡面的值會賦給當前作用域)
通過建構函式new出來的物件,我們是能檢測到它的型別的
studentA instanceof Student // true
studentA instanceof Object // true
複製程式碼
事實上,當我們使用 new 例項化一個建構函式的時候,js其實偷偷的在背後做了4件事,這個也是個比較經典的面試題:
- 建立一個新物件(prototype 指向建構函式的prototype)
- 把作用域(this)指給這個物件
- 執行建構函式的程式碼
- 返回這個物件
然而,建構函式也不是沒有缺點,使用建構函式建立的物件裡面都是資料是沒啥問題, 但是如果物件裡面有函式(方法)呢? 還是上面那個程式碼,我們拿來稍微修改一下,需要給學生增加一個學習的技能:
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上還有其他一些關於前端的教程和元件,有興趣的童鞋可以看看,你們的支援就是我最大的動力。