好程序員分享JavaScript建立對象的方式!JavaScript建立對象的方式有不少,經過Object構造函數或對象字面量的方式也能夠建立單個對象,顯然這兩種方式會產生大量的重複代碼,並不適合量產。接下來介紹七種很是經典的建立對象的方式,他們也各有優缺點。程序員
function createPerson(name, job) { var o = new Object()數組
o.name = name安全
o.job = job函數
o.sayName = function() { console.log(this.name)this
} return ospa
}var person1 = createPerson('Jiang', 'student')var person2 = createPerson('X', 'Doctor')prototype
能夠無數次調用這個工廠函數,每次都會返回一個包含兩個屬性和一個方法的對象對象
工廠模式雖然解決了建立多個類似對象的問題,可是沒有解決對象識別問題,即不能知道一個對象的類型ip
function Person(name, job) { this.name = name this.job = job this.sayName = function() { console.log(this.name)原型
}
}var person1 = new Person('Jiang', 'student')var person2 = new Person('X', 'Doctor')
沒有顯示的建立對象,使用new來調用這個構造函數,使用new後會自動執行以下操做
使用這個方式建立對象能夠檢測對象類型
person1 instanceof Object // trueperson1 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的關係及區別中講的很是詳細
更簡單的寫法
function Person() {
}
Person.prototype = {
name: 'jiang',
job: 'student',
sayName: function() { console.log(this.name)
}
}var person1 = new Person()
將Person.prototype設置爲等於一個以對象字面量形式建立的對象,可是會致使.constructor不在指向Person了。
使用這種方式,徹底重寫了默認的Person.prototype對象,所以 .constructor也不會存在這裏
Person.prototype.constructor === Person // false
若是須要這個屬性的話,能夠手動添加
function Person() {
}
Person.prototype = {
constructor:Person
name: 'jiang',
job: 'student',
sayName: function() {
console.log(this.name)
}
}
不過這種方式仍是不夠好,應爲constructor屬性默認是不可枚舉的,這樣直接設置,它將是可枚舉的。因此能夠時候,Object.defineProperty方法
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value: Person
})
缺點
使用原型,全部的屬性都將被共享,這是個很大的優勢,一樣會帶來一些缺點
原型中全部屬性實例是被不少實例共享的,這種共享對於函數很是合適。對於那些包含基本值的屬性也勉強能夠,畢竟實例屬性能夠屏蔽原型屬性。可是引用類型值,就會出現問題了
function Person() {
}
Person.prototype = {
name: 'jiang',
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.name = name this.friends = ['Shelby', 'Court']
}
Person.prototype.sayName = function() { console.log(this.name)
}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"]console.log(person1.friends === person2.friends) //false
動態原型模式將全部信息都封裝在了構造函數中,初始化的時候,經過檢測某個應該存在的方法時候有效,來決定是否須要初始化原型
function Person(name, job) { // 屬性
this.name = name this.job = job // 方法
if(typeof this.sayName !== 'function') {
Person.prototype.sayName = function() { console.log(this.name)
}
}
}var person1 = new Person('Jiang', 'Student')
person1.sayName()
只有在sayName方法不存在的時候,纔會將它添加到原型中。這段代碼只會初次調用構造函數的時候纔會執行。
此後原型已經完成初始化,不須要在作什麼修改了
這裏對原型所作的修改,可以當即在全部實例中獲得反映
其次,if語句檢查的能夠是初始化以後應該存在的任何屬性或方法,因此沒必要用一大堆的if語句檢查每個屬性和方法,只要檢查一個就行
這種模式的基本思想就是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建的對象
function Person(name, job) { var o = new Object()
o.name = name
o.job = job
o.sayName = function() { console.log(this.name)
} return o
}var person1 = new Person('Jiang', 'student')
person1.sayName()
這個模式,除了使用new操做符並把使用的包裝函數叫作構造函數以外,和工廠模式幾乎同樣
構造函數若是不返回對象,默認也會返回一個新的對象,經過在構造函數的末尾添加一個return語句,能夠重寫調用構造函數時返回的值
首先明白穩妥對象指的是沒有公共屬性,並且其方法也不引用this。
穩妥對象最適合在一些安全環境中(這些環境會禁止使用this和new),或防止數據被其餘應用程序改動時使用
穩妥構造函數模式和寄生模式相似,有兩點不一樣:一是建立對象的實例方法不引用this,而是不使用new操做符調用構造函數
function Person(name, job) { var o = new Object()
o.name = name
o.job = job
o.sayName = function() { console.log(name)
} return o
}var person1 = Person('Jiang', 'student')
person1.sayName()
和寄生構造函數模式同樣,這樣建立出來的對象與構造函數之間沒有什麼關係,instanceof操做符對他們沒有意義。