面向對象三大特性就是封裝繼承和多態,簡單理解,對於貓這種動物,它自己就是一個封裝好的類,你只須要供它吃喝(輸入),它就能表現貓的行爲(輸出),同時它繼承了動物所具備的習性(吃東西等~),而不一樣的貓由於所處環境或者習性的不一樣,可能會有不一樣的表現和行爲,這就是多態。數組
封裝
把客觀事物封裝成抽象的類,隱藏屬性和方法的實現細節,僅對外公開接口。app
① 在ES6以前,沒有class這個概念,藉由原型對象和構造函數來實現函數
function Cat(name, food) { this.name = name // 公有屬性 this.food = food } Cat.prototype.say = function() { // 公有方法 console.log(this.name + " likes eating " + this.food) } Cat.see = function() { console.log('這是靜態方法,無需實例化可調用') } var cat = new Cat("Lazier","mouse") cat.say() // 實例共享原型屬性和方法
② ES6的classthis
class Cat{ constructor(name, food){ this.name = name this.food = food } static see() { console.log('這是靜態方法,無需實例化可調用') } say(){ console.log(this.name+" likes eating " + this.food) } } var cat = new Cat("Lazier","mouse") cat.say()
以上class的基本實現原理以下 ↓prototype
var Cat = function(){ function Cat(name, food){ this.name = name this.food = food } // 執行掛載函數,建立類 createClass(Cat,[{key:"say",value:function(){ console.log(this.name+" likes eating " + this.food) }}],[{key:"see",value:function(){ console.log('這是靜態方法,無需實例化可調用')}]) } // 定義對象屬性 let defineProperties = function(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i] Object.defineProperty(target, descriptor.key, descriptor) } } // 掛載函數,將靜態或動態方法分別掛載到Cat和Cat的prototype上 var createClass = function({ return function(Constructor,protoProps,staticProps){ if(protoProps){ // 原型方法 defineProperties(Constructor.prototype,protoProps) } if(staticProps){ // 靜態方法 defineProperties(Constructor,staticProps) } } })
瞭解面向對象的公有、私有、靜態屬性和方法能夠看下面這篇文章的總結code
js面向對象之公有、私有、靜態屬性和方法詳解對象
繼承
子類可使用父類的全部功能,而且對這些功能進行擴展。繼承的過程,就是從通常到特殊的過程。繼承
js實現繼承有多種方式接口
原型鏈繼承ip
// 將子類的prototype指向父類的實例 function Parent(){} function Son(){} Son.prototype = new Parent() // * 把Son的原型對象的constructor指向Son,解決類型判斷問題 Son.prototype.constructor = Son 藉助構造函數繼承(使用call和apply實現繼承) function Parent(){} function Son(){ // 將父類函數中的this,強行綁定爲子類的this // 可傳參 Parent.call(this, arguments); } 組合繼承 // 原型屬性方法由原型鏈實現繼承,實例屬性方法由借用構造函數實現繼承 // 這樣,在原型上定義方法實現了函數複用,又保證每一個實例都有它本身的屬性 function Parent(name){ this.name = name } function Son(name, age){ // 繼承父類的實例屬性方法,以後再添加本身的實例屬性方法 Parent.call(this, name); this.age = age } Son.prototype = new Parent() // 重寫Son的原型對象 Son.prototype.constructor = Son var demo = new Son("jacksonzhou", 23) 寄生式組合繼承 – 如今最經常使用的繼承方法 // 得到父類原型屬性方法的副本,解決組合繼承的屬性重複問題 function inheritPrototype(son, parent) { var prototype = object(parent.prototype) prototype.constructor = son son.prototype = prototype } function Parent(name){ this.name = name } function Son(name, age){ Parent.call(this, name) this.age = age } Son.prototype = inheritPrototype(Son, Parent) var demo = new Son("jacksonzhou", 23)
多態
同一操做用在不一樣對象上,能夠產生不一樣的解釋和不一樣的執行結果
var makeSound=function(animal){ animal.sound() } // 聲明狗的構造函數 var Dog=function(){} Dog.prototype.sound=function(){ console.log('汪汪汪') } // 聲明貓的構造函數 var Cat=function(){} Cat.prototype.sound=function(){ console.log('喵喵喵') } // 分別調用他們的叫法 makeSound(new Dog()) makeSound(new Cat()) // 非多態寫法 var makeSound=function(animal){ if(animal instanceof Dog){ console.log('汪汪汪') }else if(animal instanceof Cat){ console.log('喵喵喵') } } var Dog=function(){} var Cat=function(){} // 分別調用他們的叫法 makeSound(new Dog()) makeSound(new Cat()) // 很明顯,後續有其餘動物加入都要去修改makeSound函數,很不優雅! 這裏要介紹下方法重載 方法重載是讓類以統一的方式處理不一樣類型數據的一種手段。表現爲多個同名函數同時存在,但具備不一樣的參數個數或類型。調用方法時經過傳遞給它們的不一樣參數個數和參數類型來決定具體使用哪一個方法, 這也是一種多態性。 其實js自己並無這個概念,但咱們能夠經過操做參數的類數組arguments,根據該類數組的長度以及其元素的類型來選擇不一樣的實現,來模擬實現函數重載效果 // js的函數參數至關靈活~可理解成一個動態的類數組 // 不加參數,調用時有傳入參數也不會報錯 function countCat(){ if(arguments.length==1){ console.log(`這是一隻貓,${arguments[0]}`) } else if(arguments.length==2){ console.log(`這是兩隻貓,${arguments[0]}和${arguments[1]}`) } else{ console.log("沒貓了~") } } countCat() countCat("Tom") countCat("Tom","Mary")