類 Class
1.通過一系列特性、行為對事物的特征進行描述。
2.在類定義中般此,通過 屬性(數(shù)據(jù))描述事物的特性唠亚,通過 方法(操作)描述事物的 行為撬槽。
const Animal= function(name, type) {
/* private members */
var type = type // 私有屬性
var isAnimal = function() { return type === 'animal' } // 私有方法
/* protect members */
this.color = 'blue'
Object.defineProperty(this, 'color', {
get: function(){ if (this instanceof Animal) return color },
set: function(v){ if (this instanceof Animal) color = v }
})
/* public members */
this.name = name // 實例屬性
this.animalName = function(){ // 實例方法
// 實例方法可訪問私有屬性和方法,原型方法不可以
isAnimal() && console.log(this.name)
}
}
Animal.prototype = {
constructor: Animal,
type: ' animal ', // 原型屬性
action: function() { console.log(' eating, drinking ') } // 原型方法
}
Animal.number = 0 // 類靜態(tài)屬性
Animal.count = function(){ return Animal.number } // 類靜態(tài)方法
抽象 Abstract
1.沒有足夠信息描述一個具體的事物趾撵,而僅對事物進行概括性的描述侄柔。
2.無法展現(xiàn)出事物對象,必須要有事物的完整描述才能展現(xiàn)出對象占调。
3.抽象出的類 就像一張藍圖暂题,只是告訴我們寶藏的位置,但想得到寶藏就必須我們自己去尋寶究珊。
const Animal = function() {
this.initialize() // 抽象類不可實例化薪者,在校驗抽象方法時異常
}
Animal.prototype.initialize = function() {
this.walk() // 抽象方法在初始化時進行校驗是否被派生類所實現(xiàn)
this.yell()
}
}
繼承 Inheritance
1.若多個事物具有很多相似的特性和行為,可以通過對基礎(chǔ)的描述進行擴充剿涮,而不需要重新描述言津。
2.派生出的類 就像繼承者,它不但獲得被繼承者的所有財產(chǎn)取试,而且也擁有自己的財產(chǎn)悬槽。
- 原型鏈繼承(實例化時無法向父類構(gòu)造函數(shù)傳參、原型對象的引用 屬性 被所有實例共享)
function Cat(name, type) { this.type = type } // 無法向父類傳遞name屬性
Cat.prototype = new Animal() // 原型指向?qū)ο蟊凰袑嵗蚕?Cat.prototype.name = name // 實例化后方可為父類初始化屬性值
- 構(gòu)造繼承(無法繼承原型的屬性和方法瞬浓,每個子類均包含父類方法的副本)
function Cat(name, type) {
Animal.call(this, name) // 通過父類的構(gòu)造函數(shù)對子類實例對象進行初始化
Animal.prototype.contructor.call(this, name) // 可多繼承 (原型的方法不會被直接繼承)
this.type = type // 子類構(gòu)造函數(shù)初始化對象屬性(覆蓋父類)
this.yell = function() { console.log('喵') } // 子類構(gòu)造函數(shù)初始化對象方法(覆蓋父類)
}
- 實例繼承(擴展后的父類實例初婆,不支持多繼承)
function Cat(name, type) {
var surperClass = new Animal(name)
surperClass.type = type
return surperClass
}
- 拷貝繼承(內(nèi)存占用高,無法獲取父類不可枚舉的屬性和方法)
function Cat(name) {
var animal = new Animal()
for(var p in animal) { Cat.prototype[p] = animal[p] }
Cat.prototype.name = name
}
- 組合繼承(即可繼承實例屬性和方法猿棉,也可繼承原型屬性和方法)
function Cat(name, type) {
Animal.call(this, name) // 繼承父類實例的屬性和方法磅叛,并可傳參
this.type = type // 子類實例屬性和方法
}
Cat.prototype = new Animal() // 原型鏈屬性和方法繼承,并支持 instanceof 指向
Cat.prototype.constructor = Cat // 構(gòu)造函數(shù)指向萨赁,用于子類訪問父類實例屬性和方法
- 寄生組合繼承(剔除父類實例屬性和方法)
function Cat(name, type) {
Animal.call(this, name)
this.type = type
}
(function() { // 閉包的方式弊琴,避免污染全局
var Super = function() {} // 空的構(gòu)造函數(shù),剔除實例屬性和方法
Super.prototype = Animal.prototype // 保存原型屬性和方法
Cat.prototype = new Super()
Cat.prototype.constructor = Cat
})()
封裝 Encapsulation
1.對事物描述的具體細節(jié)(隱私)被隱藏起來杖爽,而僅僅透露出事物的表面信息敲董。
- 封裝出的類 就像事物對象的說明書紫皇,僅告訴我們?nèi)绾稳ナ褂眠@個對象而已。
const Animal= function(name, secret) {
this.name = name // 表面信息(公共屬性)
let secret = secret // 被隱藏的信息(私有變量)
let collect = function() { secret = 'collect something' } // 被隱藏的行為(私有方法)
/* 由于閉包的特性臣缀,原型鏈中的方法無法訪問到私有變量 */
this.exposure = function() { // 暴露出來的表面行為(共有方法)可訪問被隱藏的信息
this.collect()
console.log(secret)
}
}
多態(tài) Polymorphism
事物衍生出來的坝橡,具有相似特性和行為的其它事物,在針對這些事物進行描述時精置,都具有不同的特征计寇。
- 多態(tài)屬于繼承的一種特點,如從形狀繼承而來的 圓形 與 方形脂倦,他們繪制的結(jié)果卻完全不同番宁。
const Cat = function(name, type) {
Animal.call(this, name)
this.type = type
}
(function() {
Super = function(){}
Super.prototype = Animal.prototype
Cat.prototype = new Super()
Cat.prototype.constructor = Cat
Cat.prototype.polyFunc = function() { console.log('it's cat') } // 由派生類重寫,從而具有不同的行為
})()
ES6 中 “類” 的定義與繼承
- 類的定義
const privateProperty = Symbol('privateProperty') // closure unique property identifer
const privateMethod = Symbol('privateMethod') // closure unique method identifer
class Person {
constructor(name) { // constructor initial properties
this._name = name // initial public property
this[privateProperty ] = 'private information' // initial private property
}
get name() { return this._name } // validate get property (use for protect members)
set name(value) { this._name = value } // validate set property (use for protect members)
say() { console.log(this.name); this[privateMethod]() } // public method, call the private method
[privateMethod]() { console.log(this[privateProperty ]) } // private method
static getInformation(){ console.log('parent static method') } // static method
}
- 類的繼承
class RacePerson extends Person { // inherit the Person class
constructor(name, from) {
super(name) // must call parent constructor
this.from = from // create and initial it's own property
}
say() {
super.say() // call it's parent method
console.log('from: ' + this.from) // handle it's own logic
}
static getInformation() {
super.getInformation() // call it's parent static method
console.log('child static method') // handle it's own logic
}
}
Vue 中 “類” 的定義與繼承
- 類的定義
const App = new Vue.extend({
el: '#app',
template: '<div>Hello world! {{name}}</div>',
props: { name: '' },
data() { return {} },
created() { console.log('app created') }
})
- extends 單繼承(生命周期和watch鉤子優(yōu)先級最高赖阻,屬性和方法繼承優(yōu)先級則最低)
const ExApp = new Vue.extend({
extends: App, // extends 繼承中的生命周期鉤子優(yōu)先級最高蝶押,最先被調(diào)用
data() { return { } }, // 所有派生類的同名的共有屬性將覆蓋父類的
methods: {}, // 所有派生類的同名的共有方法將覆蓋父類的
created() { console.log('ExApp created') }, // 派生類的生命周期鉤子優(yōu)先級最低,最后被調(diào)用
})
- mixins 多繼承 (生命周期和watch鉤子優(yōu)先級次于extends火欧,屬性和方法繼承優(yōu)先級次于派生類)
const mixin1 = {
data() { return {} },
methods: {},
created() { console.log('mixin1 created') }
}
const mixin2 = {
data() { return {} },
methods: {},
created() { console.log('mixin2 created') }
}
const MixinApp = Vue.extend({
mixins: [ mixin1, mixin2 ], // 生命周期鉤子前者優(yōu)先于后者棋电,而屬性和方法繼承后者優(yōu)先于前者
created() { console.log('MixinApp created') }
})
- inheritAttrs, $attrs, $listeners 組件式繼承(模板擴展,父類作為模板的一個模塊)
const CompApp = Vue.extend({
/* 父類將獲取派生類實例化時的所有沒有被繼承的屬性值和相應(yīng)事件處理方法*/
template: '<div>{{component}}<app v-bind="$attrs" v-on="$listeners"></app></div>',
props: { component: '' }, // 派生類繼承或擴展的屬性
data() { return {} },
created() { console.log('componentApp created') },
components: { app: App },
inheritAttrs: false //設(shè)置 $attrs 僅包含沒有被繼承的屬性對象(不包含派生類props定義的屬性)
})
問題
- 函數(shù)內(nèi)部定義方法 與 proto原型鏈中定義方法的區(qū)別
// 函數(shù)內(nèi)部定義的 實例方法 可獲取到閉包的作用域的 私有變量 和 私有方法苇侵。
const Person = function(name) {
var name = name
this.call = function() { console.log(name) }
}
// prototype 原型鏈定義的 原型方法 指向其它內(nèi)存空間獨立的函數(shù)體赶盔,無法獲取閉包的作用域。
Person.prototype = {
constructor: Person,
say: function() { console.log(name) }
}
-
instanceof 操作符(遞歸判斷實例原型鏈上的原型對象指向)
JS原型鏈.jpg
function instance_of(instance, constructor) {
var prototype = constructor.prototype // prototype由定義時直接掛載在構(gòu)造函數(shù)上(顯式原型)
var __proto__ = instance.__proto__ // __proto__ 指向?qū)?yīng)原型對象 prototype (隱式原型)
while(true) {
if ( __proto__ === null ) return false // 匹配失敗榆浓,僅Object.prototype.__proto__為null
if ( __proto__ === prototype ) return true // 當(dāng)前實例原型指向與指定構(gòu)造函數(shù)匹配
__proto__ = __proto__.__proto__ // 實現(xiàn)遞歸檢測
}
}