JavaScript經常使用繼承方式主要分爲(7種):原型鏈繼承、構造函數繼承、組合繼承、原型式繼承、寄生式繼承、寄生組合繼承以及繼承多個對象。函數
基本概念:重寫原型對象,賦予一個新的對象的實例。基本思想就是讓一個原型對象指向另外一個父類的實例。this
function Super() { //基本數據類型 this.text = 'Hello'; } Super.prototype.getSuperText = function() { return this.text; } function Sub() { this.subText = 'Word'; } Sub.prototype = new Super(); const instance = new Sub(); console.log(instance);
特色:很是純粹的繼承關係,實例是子類的實例,也是父類的實例。父類新增原型方法或屬性,子類都能訪問到。prototype
優勢:簡單易於操做code
缺點:對引用類型數據操做會互相(多個實例之間)影響對象
function Super() { //複雜對象,也就是引用類型 this.value = [1, 2, 3, 4]; } Super.prototype.getSuperValue = function() { return this.value; } function Sub() { this.subText = 'Word'; } Sub.prototype = new Super(); const instance1 = new Sub(); const instance2 = new Sub(); instance1.value.push(5); console.log(instance2.value); // (5) [1, 2, 3, 4, 5]
//定義構造函數 function Super(){ this.value = [1, 2, 3, 4]; } //新增屬性getSuperValue Super.prototype.getSuperValue = function() { return this.value; } //sub每次執行都要從新調用 function Sub(){ Super.call(this); } const instance1 = new Sub(); instance1.value.push(5); console.log(instance1.value); // (5) [1, 2, 3, 4, 5] const instance2 = new Sub(); console.log(instance2.value); // (4) [1, 2, 3, 4]
構造函數的特色:(對引用數據類型沒有影響)上面的代碼輸出instance1是1,2,3,4,5,instance2是1,2,3,4。這是由於sub每次在執行時都是從新調用了一個super.call(),並且構造函數在構建對象的過程當中,每次都是建立了一個新的object,所以每次調用sub都會執行一遍super,每次執行時都會有申請一個新的內存空間,因此獲得的兩個value值是不同互不影響的。繼承
缺點:在整個構造函數的基礎過程當中,上面的代碼並無使用proto和prototype的屬性,沒有使用的話那麼原型鏈就沒有接上。因此說,構造函數基礎只能繼承父類的實例屬性和方法,不能繼承原型鏈上的屬性和方法。ip
經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用。內存
保留了構造函數繼承與原型鏈繼承的優勢。可是執行了兩次Person,屬性重複了。原型鏈
function Person(name) { this.name = name; this.value = ["head", "body", "legs"]; } Person.prototype.getName = function() { return this.name; }; // 構造函數繼承 function Teacher(name, school){ // 執行又一次Person Person.call(this, name); this.school = school; } // 原型鏈繼承 // 執行一次Person Teacher.prototype = new Person(); const Eric = new Teacher("Eric",27); Eric.getName(); // 輸出:Eric // prototype構造器指回本身 Teacher.prototype.constructor = Teacher; Teacher.prototype.getSchool = function() { return this.school; };
特色:既能夠繼承實例屬性和方法,也能夠繼承原型屬性和方法。既是子類的實例也是父類的實例,不存在引用屬性共享的問題。能夠傳參,函數可複用。get
缺點:調用兩次父類構造函數,生成了兩份實例。
藉助原型能夠基於已有的對象建立新的對象,同時還沒必要所以建立自定義類型。
原理:(本質)利用一個空對象做爲一箇中介。
const lakers = { name: "lakers", value: ["Micheal", "Wade", "Kobe"] }; const lakers1 = Object.create(lakers); const lakers2 = Object.create(lakers); lakers1.value.push('Fish'); console.log(lakers);
模擬Object.create()
object.create()原理:用一個函數包裝一個對象,而後返回這個函數的調用,這個函數就變成了一個能夠隨意添增屬性的實例或對象。
Object.prototype.create = function(obj) { function Fun() {} Fun.prototype = obj; return new Fun(); }
缺點有兩點:第一點是沒法傳遞參數,第二點是引用類型存在變量的污染。****
寄生式繼承的思路與寄生構造函數和工廠模式相似,即建立一個僅用於封裝繼承過程的函數。
目的:在原型式繼承的基礎上,寄生增長了一些新的方法和屬性。
它的特色同原型式繼承同樣,也是沒法傳遞參數,並且引用的數據類型也容易存在樣式污染。
Object.createNew()
Object.prototype.createNew = function(obj){ var newObj = Object.create(obj); //獲取長度等於一個function newObj.getLength = function(){ ... }; return newObj; }
目的:爲了解決數據重複拷貝兩遍的問題。
Super只執行一次。
//定義Super構造函數 function Super(name) { this.name = name; this.value = ["Hello", "Word"]; } //在super的原型鏈添加一個getName Super.prototype.getName = function() { return this.name; }; //定義Sub function Sub(name, age) { //調用構造函數繼承 Super.call(this, name); this.age = age; } let prototype = Object.create(Super.prototype); prototype.constructor = Sub; Sub.prototype = prototype; Sub.prototype.getAge = function(){ return this.age; } const instance1 = new Sub("Eric", 23); const instance2 = new Sub("Vico", 23); instance1.value.push("!"); instance2.value.push("!!");
藉助原型式繼承Object.create拿到SuperClass,也就是父類,拿到父類的prototype以後把它賦給ClassOne,
再而後咱們將ClassTwo的prototype使用一個Object.assign,一個對象的拷貝,把它拷貝到ClassOne裏面來,
而後最後ClassOne.prototype.constructor等於ClassOne
也就是使用一個Class.assign把全部咱們想要繼承的父類的prototype所有組合到一塊兒完成一個拷貝,以後再賦給對象。
function ClassOne.prototype = Object.create(SuperClass.prototype); Object.assign(ClassOne.prototype, ClassTwo.prototype); ClassOne.prototype.constructor = ClassOne;