javascript建立對象的方式有很多,通過object構造函數或對象字面量的方式也可以建立單個對象,顯然這兩種方式會産生大量的重複代碼,并不适合量産。接下來介紹七種非常經典的建立對象的方式,他們也各有優缺點。
工廠模式
function createperson(name, job) {
var o = new object()
o.name = name
o.job = job
o.sayname = function() {
console.log(this.name)
}
return o
}
var person1 = createperson('jiang', 'student')
var person2 = createperson('x', 'doctor')
可以無數次調用這個工廠函數,每次都會傳回一個包含兩個屬性和一個方法的對象
工廠模式雖然解決了建立多個相似對象的問題,但是沒有解決對象識别問題,即不能知道一個對象的類型
構造函數模式
function person(name, job) {
this.name = name
this.job = job
this.sayname = function() {
var person1 = new person('jiang', 'student')
var person2 = new person('x', 'doctor')
沒有顯示的建立對象,使用new來調用這個構造函數,使用new後會自動執行如下操作
建立一個新對象
這個新對象會被執行[[prototype]]連結
這個新對象會綁定到函數調用的this
傳回這個對象
使用這個方式建立對象可以檢測對象類型
person1 instanceof object // true
person1 instanceof person //true
但是使用構造函數建立對象,每個方法都要在每個執行個體上重新建立一次
原型模式
function person() {
person.prototype.name = 'jiang'
person.prototype.job = 'student'
person.prototype.sayname = function() {
console.log(this.name)
var person1 = new person()
将資訊直接添加到原型對象上。使用原型的好處是可以讓所有的執行個體對象共享它所包含的屬性和方法,不必在構造函數中定義對象執行個體資訊。
原型是一個非常重要的概念,在一篇文章看懂proto和prototype的關系及差別中講的非常詳細
更簡單的寫法
person.prototype = {
name: 'jiang',
job: 'student',
sayname: function() {
将person.prototype設定為等于一個以對象字面量形式建立的對象,但是會導緻.constructor不在指向person了。
使用這種方式,完全重寫了預設的person.prototype對象,是以 .constructor也不會存在這裡
person.prototype.constructor === person // false
如果需要這個屬性的話,可以手動添加
constructor:person
}
不過這種方式還是不夠好,應為constructor屬性預設是不可枚舉的,這樣直接設定,它将是可枚舉的。是以可以時候,object.defineproperty方法
object.defineproperty(person.prototype, 'constructor', {
enumerable: false,
value: person
})
缺點
使用原型,所有的屬性都将被共享,這是個很大的優點,同樣會帶來一些缺點
原型中所有屬性執行個體是被很多執行個體共享的,這種共享對于函數非常合适。對于那些包含基本值的屬性也勉強可以,畢竟執行個體屬性可以屏蔽原型屬性。但是引用類型值,就會出現問題了
friends: ['shelby', 'court']
var person1 = new person()
var person2 = new person()
person1.friends.push('van')
console.log(person1.friends) //["shelby", "court", "van"]
console.log(person2.friends) //["shelby", "court", "van"]
console.log(person1.friends === person2.friends) // true
friends存在與原型中,執行個體person1和person2指向同一個原型,person1修改了引用的數組,也會反應到執行個體person2中
組合使用構造函數模式和原型模式
這是使用最為廣泛、認同度最高的一種建立自定義類型的方法。它可以解決上面那些模式的缺點
使用此模式可以讓每個執行個體都會有自己的一份執行個體屬性副本,但同時又共享着對方法的引用
這樣的話,即使執行個體屬性修改引用類型的值,也不會影響其他執行個體的屬性值了
function person(name) {
this.friends = ['shelby', 'court']
console.log(person1.friends) //["shelby", "court", "van"]
console.log(person2.friends) // ["shelby", "court"]
console.log(person1.friends === person2.friends) //false
動态原型模式
動态原型模式将所有資訊都封裝在了構造函數中,初始化的時候,通過檢測某個應該存在的方法時候有效,來決定是否需要初始化原型
// 屬性
// 方法
if(typeof this.sayname !== 'function') {
person.prototype.sayname = function() {
console.log(this.name)
}
person1.sayname()
隻有在sayname方法不存在的時候,才會将它添加到原型中。這段代碼隻會初次調用構造函數的時候才會執行。
此後原型已經完成初始化,不需要在做什麼修改了
這裡對原型所做的修改,能夠立即在所有執行個體中得到反映
其次,if語句檢查的可以是初始化之後應該存在的任何屬性或方法,是以不必用一大堆的if語句檢查每一個屬性和方法,隻要檢查一個就行
寄生構造函數模式
這種模式的基本思想就是建立一個函數,該函數的作用僅僅是封裝建立對象的代碼,然後再傳回建立的對象
這個模式,除了使用new操作符并把使用的包裝函數叫做構造函數之外,和工廠模式幾乎一樣
構造函數如果不傳回對象,預設也會傳回一個新的對象,通過在構造函數的末尾添加一個return語句,可以重寫調用構造函數時傳回的值
穩妥構造函數模式
首先明白穩妥對象指的是沒有公共屬性,而且其方法也不引用this。
穩妥對象最适合在一些安全環境中(這些環境會禁止使用this和new),或防止資料被其他應用程式改動時使用
穩妥構造函數模式和寄生模式類似,有兩點不同:一是建立對象的執行個體方法不引用this,而是不使用new操作符調用構造函數
console.log(name)
var person1 = person('jiang', 'student')
和寄生構造函數模式一樣,這樣建立出來的對象與構造函數之間沒有什麼關系,instanceof操作符對他們沒有意義
作者:xuthus
來源:51cto