什么是Class 類芭析?
MDN上說(shuō):類定義對(duì)象的特征。它是對(duì)象的屬性和方法的模板定義吞瞪。
簡(jiǎn)單說(shuō)馁启,“類”是生產(chǎn)對(duì)象的模板,通過(guò)類這個(gè)模板芍秆,可以毫不費(fèi)勁地生產(chǎn)出無(wú)數(shù)個(gè)一樣的對(duì)象惯疙,而不用通過(guò)一次次的定義去聲明對(duì)象。而這些對(duì)象妖啥,因?yàn)榫哂幸粯拥膶傩悦沟摺⒁粯拥姆椒ǎ詫⑦@些對(duì)象歸為一個(gè)“類”荆虱,就像將人類歸入人這一類一樣蒿偎。
JavaScript 的類
在es 6 出現(xiàn)之前,ECMAScript 標(biāo)準(zhǔn)中都是沒(méi)有類的官方規(guī)范的克伊,JavaScript 的類都是通過(guò)其他的方法來(lái)模擬定義酥郭。直到ES 6 標(biāo)準(zhǔn)的到來(lái),JavaScript 才擁有官方的定義類的方法愿吹。
定義類的方法
1. 構(gòu)造函數(shù)法
構(gòu)造函數(shù)法使用構(gòu)造函數(shù)來(lái)模擬“類”不从,使用 this 在構(gòu)造函數(shù)內(nèi)部指代實(shí)例對(duì)象。
function Person(){
this.species = 'human'
}
定義一個(gè)構(gòu)造函數(shù)之后犁跪,使用 new 關(guān)鍵字來(lái)生成實(shí)例對(duì)象
let xxx = new Person
console.log(xxx) // {species: 'human'}
像上面定義的構(gòu)造函數(shù) Person椿息,可以使用 new 關(guān)鍵字來(lái)生成無(wú)數(shù)個(gè) 擁有屬性 species = 'human' 的對(duì)象。
然而坷衍,使用這種方法構(gòu)造對(duì)象寝优,當(dāng)構(gòu)造對(duì)象的數(shù)量太多時(shí),會(huì)極大地消耗內(nèi)存枫耳,所以JavaScript提供了函數(shù)的prototype
屬性來(lái)節(jié)約內(nèi)存乏矾。
function Person(){
}
Person.prototype.species = 'human'
let xxx = new Person()
console.log(xxx) // {}
可以看到,使用 Person 的prototype
屬性定義對(duì)象的公共屬性 species
迁杨,依然可以生成一個(gè)對(duì)象钻心。然而生成的卻是一個(gè)空對(duì)象。那么species
屬性去哪了铅协?
species
屬性跑到了實(shí)例對(duì)象 xxx
的原型上了:
console.log(xxx.species) // human
console.log(xxx.__proto__) // {species:human,constructor:function}
通過(guò)構(gòu)造函數(shù)的prototype
屬性捷沸,可以將實(shí)例對(duì)象的公共屬性集成到一個(gè)原型對(duì)象上面,節(jié)約內(nèi)存
構(gòu)造函數(shù)法同時(shí)還可以實(shí)現(xiàn)對(duì)象的自有屬性和自有方法
function Person(){
}
Person.prototype.species = 'human'
let xxx = new Person()
xxx.abc = "abc"
console.log(xxx) // {abc:abc}
構(gòu)造函數(shù)通過(guò)將值以參數(shù)的形式傳入函數(shù)內(nèi)部狐史,使構(gòu)造出的實(shí)例對(duì)象具有不同的屬性值痒给。
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.species = 'human'
let xxx = new Person(‘xiao’,18)
console.log(xxx) // {name:xiao,age:18}
那么说墨,使用構(gòu)造函數(shù)法模擬類的流程是:
function 構(gòu)造函數(shù)名(自有屬性值1,自有屬性值2,...){
this.自有屬性1 = 自有屬性值1
this.自有屬性2 = 自有屬性值2
}
構(gòu)造函數(shù)名.prototype.xxx = xxx // 設(shè)置構(gòu)造函數(shù)的原型屬性
// 還可以直接往實(shí)例對(duì)象上添加自己的屬性
2. Object.create() 實(shí)現(xiàn)類
Object.create()語(yǔ)法
Object.create(proto[, propertiesObject])
參數(shù):
- proto:新創(chuàng)建對(duì)象的原型對(duì)象
- propertiesObject:新創(chuàng)建對(duì)象的屬性配置。(如:是否可枚舉苍柏、是否只寫(xiě)等)
返回值: - 返回新創(chuàng)建的對(duì)象尼斧。
使用Object.create()模擬類,是將一個(gè)對(duì)象直接作為新創(chuàng)建對(duì)象的原型序仙,直接將原型植入新對(duì)象突颊。
在這種方法中,“類”就是一個(gè)對(duì)象潘悼,而不是函數(shù)律秃。
let Person = {
species: 'human',
walk: function(){},
speak: function(){},
}
let xxx = Object.create(Person)
console.log(xxx)
上面這段代碼,以 Person 這個(gè)對(duì)象作為原型治唤,生成一個(gè)新的空對(duì)象 xxx棒动,xxx 的原型指向 Person。換言之宾添,對(duì)象 Person 被當(dāng)做了一個(gè)類船惨,創(chuàng)建新的對(duì)象。
Object.create()模擬類的缺陷:
- 實(shí)例對(duì)象的屬性全部在同一個(gè)”類“對(duì)象上面缕陕,只能
實(shí)例對(duì)象名.屬性名 = 屬性值
手動(dòng)添加自有屬性和自有方法 - 由于Object.create() 只是將創(chuàng)建的實(shí)例對(duì)象的原型綁定到一個(gè)”類“對(duì)象上面粱锐。一旦”類“對(duì)象發(fā)生改變,所有的實(shí)例對(duì)象的值都會(huì)改變扛邑。
- 實(shí)例對(duì)象的共享數(shù)據(jù)全部綁定在”類“對(duì)象上面怜浅。
3. 極簡(jiǎn)主義法
極簡(jiǎn)主義法同樣使用一個(gè)對(duì)象作為”類“,在對(duì)象里面蔬崩,定義一個(gè)createNew
方法來(lái)生成實(shí)例
let Person = {
createNew: function(){},
}
在createNew
方法里面恶座,定義一個(gè)實(shí)例對(duì)象作為返回值
let Person = {
createNew: function(){
let person = {}
person.species = "human"
person.walk = function(){}
person.speak = function(){}
return person
},
}
調(diào)用createNew
方法,就可以得到一個(gè)新的對(duì)象
let xxx = Person.createNew()
console.log(xxx) // {species: "human", walk: function, speak: function}
極簡(jiǎn)主義法的原理:使用一個(gè)對(duì)象作為原本沥阳,去復(fù)制完成另一個(gè)對(duì)象
事實(shí)上跨琳,極簡(jiǎn)主義法的原理概念與Object.create()
極為類似,兩個(gè)的唯一區(qū)別是:極簡(jiǎn)主義法不會(huì)修改實(shí)例對(duì)象的原型桐罕,而Object.create()
涉及到原型脉让。兩者之間的公共屬性共享全部是通過(guò)操作“原本”來(lái)實(shí)現(xiàn)。
4. ES 6 的 class 聲明
ES 6 的 class 不是一個(gè)全新的類繼承模型功炮,而是一個(gè)原有模型的語(yǔ)法糖溅潜。
ECMAScript2015 將 第一種:構(gòu)造函數(shù)法 給官方化,定義一個(gè) api 直接使用“類”死宣。本質(zhì)上伟恶, class 定義的“類”還是一個(gè)函數(shù)
class Person {
constructor(name, age){
this.name = name
this.age = age
}
walk(){}
speak(){}
}
let xxx = new Person('xiao',18)
typeof Person // "function",Person 本質(zhì)上還是一個(gè)函數(shù)
console.log(xxx) // {name: "xiao", age: 18}
用函數(shù)模擬一個(gè)類的過(guò)程(舉例)
假設(shè)現(xiàn)在在設(shè)計(jì)一款游戲碴开,需要生成許多小兵毅该,就需要一個(gè)生成小兵的類博秫。
使用函數(shù)來(lái)生成小兵
function createBing(id,hp){
let bing = {} // 創(chuàng)建一個(gè)空對(duì)象存儲(chǔ)小兵的屬性
bing.id = id
bing.hp = hp
bing.attack = 5
bing.walk= function(){console.log('walk')}
return bing
}
此時(shí),調(diào)用函數(shù) createBing 就能生成一個(gè)具有4個(gè)屬性的小兵對(duì)象眶掌。
此時(shí)挡育,生成數(shù)量多的小兵時(shí),會(huì)重復(fù)創(chuàng)建 hp 和 walk 這兩個(gè)屬性朴爬,浪費(fèi)內(nèi)存即寒。JS 中有原型,可以將公共屬性綁定到原型上面召噩。
// 首先需要一個(gè)原型對(duì)象母赵,將公共屬性放到原型對(duì)象上面
bingPrototype = {
attack: 5,
walk: function(){console.log('walk')}
}
function creareBing(id, hp){
let bing = {}
bing.__proto__ = bingPrototype // 將原型屬性綁定到生成的對(duì)象上面
bing.id = id
bing.hp = hp
return bing
}
此時(shí),調(diào)用 createBing 函數(shù)可以生成一個(gè)具有 id 和 hp 兩個(gè)屬性的小兵對(duì)象具滴,attack 和 walk 被綁定到原型上面凹嘲,所有小兵對(duì)象共享。
由于__proto__
不是標(biāo)準(zhǔn)規(guī)范构韵,所以使用另一個(gè)符合規(guī)范的方法周蹭,使用函數(shù)的 prototype
屬性和new
關(guān)鍵字。
將實(shí)例對(duì)象的全部共有屬性綁定到生成實(shí)例對(duì)象的函數(shù)的prototype
屬性上面疲恢,再用new
關(guān)鍵字生成實(shí)例凶朗,可以直接將原型綁定到實(shí)例對(duì)象上。
function createBing(id, hp){
this.id = id
this.hp = hp
}
createBing.prototype = {
construcotr: createBing, // constrctor 是 prototype 的默認(rèn)屬性显拳,此寫(xiě)法會(huì)覆蓋棚愤,所以要重新賦值
attack: 5,
walk: function(){console.log('walk')}
}
至此,利用函數(shù)的prototype
和new
關(guān)鍵字萎攒,實(shí)現(xiàn)了用函數(shù)模擬類的目的遇八。