什麼是Class 類?
MDN上說:類定義物件的特徵。它是物件的屬性和方法的模板定義。
簡單說,“類”是生產物件的模板,通過類這個模板,可以毫不費勁地生產出無數個一樣的物件,而不用通過一次次的定義去宣告物件。而這些物件,因為具有一樣的屬性、一樣的方法,所以將這些物件歸為一個“類”,就像將人類歸入人這一類一樣。
JavaScript 的類
在es 6 出現之前,ECMAScript 標準中都是沒有類的官方規範的,JavaScript 的類都是通過其他的方法來模擬定義。直到ES 6 標準的到來,JavaScript 才擁有官方的定義類的方法。
定義類的方法
1. 建構函式法
建構函式法使用建構函式來模擬“類”,使用 this 在建構函式內部指代例項物件。
function Person(){
this.species = 'human'
}
複製程式碼
定義一個建構函式之後,使用 new 關鍵字來生成例項物件
let xxx = new Person
console.log(xxx) // {species: 'human'}
複製程式碼
像上面定義的建構函式 Person,可以使用 new 關鍵字來生成無數個 擁有屬性 species = 'human' 的物件。
然而,使用這種方法構造物件,當構造物件的數量太多時,會極大地消耗記憶體,所以JavaScript提供了函式的prototype
屬性來節約記憶體。
function Person(){
}
Person.prototype.species = 'human'
let xxx = new Person()
console.log(xxx) // {}
複製程式碼
可以看到,使用 Person 的prototype
屬性定義物件的公共屬性 species
,依然可以生成一個物件。然而生成的卻是一個空物件。那麼species
屬性去哪了?
species
屬性跑到了例項物件 xxx
的原型上了:
console.log(xxx.species) // human
console.log(xxx.__proto__) // {species:human,constructor:function}
複製程式碼
通過建構函式的prototype
屬性,可以將例項物件的公共屬性整合到一個原型物件上面,節約記憶體
建構函式法同時還可以實現物件的自有屬性和自有方法
function Person(){
}
Person.prototype.species = 'human'
let xxx = new Person()
xxx.abc = "abc"
console.log(xxx) // {abc:abc}
複製程式碼
建構函式通過將值以引數的形式傳入函式內部,使構造出的例項物件具有不同的屬性值。
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}
複製程式碼
那麼,使用建構函式法模擬類的流程是:
function 建構函式名(自有屬性值1,自有屬性值2,...){
this.自有屬性1 = 自有屬性值1
this.自有屬性2 = 自有屬性值2
}
建構函式名.prototype.xxx = xxx // 設定建構函式的原型屬性
// 還可以直接往例項物件上新增自己的屬性
複製程式碼
2. Object.create() 實現類
Object.create()語法
Object.create(proto[, propertiesObject]) 引數:
- proto:新建立物件的原型物件
- propertiesObject:新建立物件的屬性配置。(如:是否可列舉、是否只寫等)
返回值: - 返回新建立的物件。
使用Object.create()模擬類,是將一個物件直接作為新建立物件的原型,直接將原型植入新物件。
在這種方法中,“類”就是一個物件,而不是函式。
let Person = {
species: 'human',
walk: function(){},
speak: function(){},
}
let xxx = Object.create(Person)
console.log(xxx)
複製程式碼
上面這段程式碼,以 Person 這個物件作為原型,生成一個新的空物件 xxx,xxx 的原型指向 Person。換言之,物件 Person 被當做了一個類,建立新的物件。
Object.create()模擬類的缺陷:
- 例項物件的屬性全部在同一個”類“物件上面,只能
例項物件名.屬性名 = 屬性值
手動新增自有屬性和自有方法 - 由於Object.create() 只是將建立的例項物件的原型繫結到一個”類“物件上面。一旦”類“物件發生改變,所有的例項物件的值都會改變。
- 例項物件的共享資料全部繫結在”類“物件上面。
3. 極簡主義法
極簡主義法同樣使用一個物件作為”類“,在物件裡面,定義一個createNew
方法來生成例項
let Person = {
createNew: function(){},
}
複製程式碼
在createNew
方法裡面,定義一個例項物件作為返回值
let Person = {
createNew: function(){
let person = {}
person.species = "human"
person.walk = function(){}
person.speak = function(){}
return person
},
}
複製程式碼
呼叫createNew
方法,就可以得到一個新的物件
let xxx = Person.createNew()
console.log(xxx) // {species: "human", walk: function, speak: function}
複製程式碼
極簡主義法的原理:使用一個物件作為原本,去複製完成另一個物件
事實上,極簡主義法的原理概念與Object.create()
極為類似,兩個的唯一區別是:極簡主義法不會修改例項物件的原型,而Object.create()
涉及到原型。兩者之間的公共屬性共享全部是通過操作“原本”來實現。
4. ES 6 的 class 宣告
ES 6 的 class 不是一個全新的類繼承模型,而是一個原有模型的語法糖。
ECMAScript2015 將 第一種:建構函式法 給官方化,定義一個 api 直接使用“類”。本質上, class 定義的“類”還是一個函式
class Person {
constructor(name, age){
this.name = name
this.age = age
}
walk(){}
speak(){}
}
let xxx = new Person('xiao',18)
typeof Person // "function",Person 本質上還是一個函式
console.log(xxx) // {name: "xiao", age: 18}
複製程式碼
用函式模擬一個類的過程(舉例)
假設現在在設計一款遊戲,需要生成許多小兵,就需要一個生成小兵的類。
使用函式來生成小兵
function createBing(id,hp){
let bing = {} // 建立一個空物件儲存小兵的屬性
bing.id = id
bing.hp = hp
bing.attack = 5
bing.walk= function(){console.log('walk')}
return bing
}
複製程式碼
此時,呼叫函式 createBing 就能生成一個具有4個屬性的小兵物件。
此時,生成數量多的小兵時,會重複建立 hp 和 walk 這兩個屬性,浪費記憶體。JS 中有原型,可以將公共屬性繫結到原型上面。
// 首先需要一個原型物件,將公共屬性放到原型物件上面
bingPrototype = {
attack: 5,
walk: function(){console.log('walk')}
}
function creareBing(id, hp){
let bing = {}
bing.__proto__ = bingPrototype // 將原型屬性繫結到生成的物件上面
bing.id = id
bing.hp = hp
return bing
}
複製程式碼
此時,呼叫 createBing 函式可以生成一個具有 id 和 hp 兩個屬性的小兵物件,attack 和 walk 被繫結到原型上面,所有小兵物件共享。
由於__proto__
不是標準規範,所以使用另一個符合規範的方法,使用函式的 prototype
屬性和new
關鍵字。
將例項物件的全部共有屬性繫結到生成例項物件的函式的prototype
屬性上面,再用new
關鍵字生成例項,可以直接將原型繫結到例項物件上。
function createBing(id, hp){
this.id = id
this.hp = hp
}
createBing.prototype = {
construcotr: createBing, // constrctor 是 prototype 的預設屬性,此寫法會覆蓋,所以要重新賦值
attack: 5,
walk: function(){console.log('walk')}
}
複製程式碼
至此,利用函式的prototype
和new
關鍵字,實現了用函式模擬類的目的。