"javaScript 沒有提供傳統面嚮對象語言中的類式繼承,而是經過原型委託的方式來實現對象與對象之間的繼承",這句話是摘自《javaScript設計模式與開發實踐》書中第一章的一句話,這句話開門見山的說明了js的編程模式「原型」,而這樣也直接說明了javaScript的面向對象是基於原型克隆的方式來建立對象,並以原型鏈的方式實現對象與對象之間的關係。java
那咱們先來了解一下這js這種基於原型編程語言的基本規則:es6
上面這幾句話,我一樣是摘自《javaScript設計模式與開發實踐》裏面的,原模原樣抄錄下來分享給你們的。編程
1. 原型鏈繼承設計模式
實現方式:新對象的實例的原型等於父對象類的實例。 `閉包
let Animals = function (type, name) { // 定義父類
this.type = type;
this.name = name;
}
Animals.prototype.animal = '動物';
Animals.prototype.eat = function () {
console.log('這是一隻小'+ this.type + ': 它會吃東西')
}
let Cat = function (sex) { // 定義子類
this.sex = sex
}
Cat.prototype = new Animals('貓', '小花'); // 原型鏈繼承
let cat = new Cat('公') // 生成子類實例
cat.eat() // 執行繼承來的方法
複製代碼
` 原型鏈繼承的特色是:可繼承父類的私有屬性和原型上的屬性和方法,可是,父類私有上的屬性方法它們之間的繼承是屬於引用同一個內存地址,所以修改其中的一個,也會影響到另外一個對象app
2. 構造函數繼承編程語言
實現方式:在子類的函數體裏執行父類,經過call和apply來改變this的指向。函數
`性能
let Dog = function (sex) {
Animals.call(this, '狗', '旺財'); // 構造繼承:執行父類,經過call改變this指向
this.sex = sex;
}
let dog = new Dog('母');
dog.eat(); // 報錯:Uncaught TypeError: dog.eat is not a function;
複製代碼
`this
構造函數繼承: 可繼承父類裏的私有屬性和方法,可是不能繼承父類原型上的方法和屬性,此類繼承修改一個對象的屬性不會影響到另外一個對象。
3. 組合式繼承
實現方式:該方式的繼承其實就是上面的原型繼承和構造函數繼承的混合方式。
`
let Dog = function (sex) {
Animals.call(this, '狗', '旺財'); // 構造繼承: 執行父類,經過call改變this指向
this.sex = sex;
}
Dog.prototype = new Animals('貓', '小花'); // 原型鏈繼承
let dog = new Dog('母');
dog.eat(); // 執行父類原型上的方法
複製代碼
` 組合式繼承:可繼承父類私有的屬性和方法,繼承的私有屬性和方法都是子類私有的,能夠繼承父類原型上的屬性,能夠傳參,可複用。可是組合式繼承調用了兩次父類方法,所以在性能上有必定的損耗
4. 包裝式繼承
實現方式: 經過一個包裝函數,把父類實例做爲參數傳遞進去,子類實例在包裝函數體裏生成。
`
function context (obj) { // 定義包裝函數,並傳遞父類實例
function Pig () {
this.sex = '公'
}
Pig.prototype = obj; // 繼承父類實例
return new Pig() // 返回子類實例
}
let animals = new Animals('豬', '小胖')
let pig = context(animals);
pig.type = '羊'; //這樣寫等於給子類添加一個新的type屬性
pig.eat() // 輸出:這是一隻小羊: 它會吃東西
animals.eat() // 輸出: 這是一隻小豬: 它會吃東西
複製代碼
` 包裝式繼承:相似於函數閉包的用法,語義上不夠明顯。
5. 組合寄生式繼承
實現方式: 經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點。
`
function context (obj) {
function Fn () {
this.sex = '公'
}
Fn.prototype = obj; // 函數的原型等於另外一個實例
return new Fn()
}
let objA = context(Animals.prototype);
function Cattle () {
Animals.call(this, '牛', '小蠻'); //在函數中用apply或者call引入另外一個構造函數
}
Cattle.prototype = objA;
objA.constructor = Cattle; // 修復實例
let cattle = new Cattle();
console.log(cattle)
複製代碼
`
組合寄生式繼承:一、函數的原型等於另外一個實例。二、在函數中用apply或者call引入另外一個構造函數。繼承方式太過複雜。
6. es6繼承 (使用最多)
實現方式:使用class關鍵字聲明類,經過extends關鍵字實現繼承。
`
class Animals {
constructor(type, name) {
this.type = type;
this.name = name;
}
eat () {
console.log('這是一隻小'+ this.type + ': 它會吃東西')
}
}
let animal = new Animals('狗', '旺財');
class Car extends Animals {
constructor(sex) {
super('貓', '小花');
// 此處是重點在子類的構造函數中,只有調用super以後,才能夠使用this關鍵字,不然
// 會報錯。這是由於子類實例的構建,基於父類實例,只有super方法才能調用父類實例
this.sex = sex;
}
}
let car = new Car('公')
car.eat()
複製代碼
` es6繼承:使用extends關鍵字實現繼承,語法上更加清晰明瞭,子類繼承父類後,子類的構造函數,必須執行super方法
在上面的繼承演示中,無論是es5仍是es6,js建立一個類都必須使用new
關鍵字去執行。 `
let Animals = function (type, name) { // 定義父類
this.type = type;
this.name = name;
}
Animals.prototype.animal = '動物';
Animals.prototype.eat = function () {
console.log('這是一隻小'+ this.type + ': 它會吃東西')
}
複製代碼
`
在這裏面,new
關鍵字主要作了以下工做:
javaScript的面向對象與繼承,繼承的核心思想是複用,不須要在去編寫多餘代碼,還有就是代碼的管理。喜歡的朋友給個贊吧,謝謝。