摘要:繼承的是超類型中構造函數中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結果是子類型實例中有兩組超類型的構造函數中定義的屬性,一組在子類型的實例中,一組在子類型實例的原型中。
ECMAScript只支持實現繼承,主要依靠原型鏈來實現。與實現繼承對應的是接口繼承,由于script中函數沒有簽名,所以無法實現接口繼承。
一、原型鏈基本思想:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
構造函數、原型和實例的關系:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個紙箱原型對象的內部指針。
基本用法:
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; } function SubType() { this.subproperty = false; } // 子的原型是父的對象 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; } var instance = new SubType(); console.log(instance.getSuperValue()); // true
關系圖:
但實際上,SuperType也有自己的原型,就是Object,這個原型是默認的。
所有函數的默認原型都是Object的實例,因此默認原型都會包含一個內部指針,指向Object.prototype。
所以完整的關系圖應該是
使用原型能夠做到繼承,但實際中并不多帶帶使用原型鏈來實現繼承,原因如下:
1、對于不需要‘父親’的私有屬性的繼承:我們知道原型來創建對象,使得所有的實例都擁有這些共享的屬性和方法,我們在使用原型鏈來繼承最主要的是SubType的原型變為SuperType的實例對象,那么本來是Super實例私有的屬性property,且處于SubType的原型中成為SubType實例的共享屬性。
2、對于需要‘父親‘私有屬性的繼承:同一,我們知道會繼承父親的私有屬性,但我們無法通過傳入參數到’父親‘的構造函數來實現屬性特有值的目的。
鑒于以上我們開始使用第二種繼承方式。
基本思想:在子類型構造函數的內部調用超類型構造函數
function SuperType() { this.colors = ["red", "yellow", "black"]; } SuperType.prototype.getColor = function () { return this.colors; } function SubType() { // 繼承了SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("pink"); // ["red", "yellow", "black","pink"] console.log(instance1.colors); var instance2 = new SubType(); console.log(instance2.colors); // ["red", "yellow", "black"] console.log(instance2 instanceof SuperType); // false console.log(instance2.getColor()); // instance2.getColor is not a function
此方法是在子類型中調用了超(父)類型的構造函數,使構造函數中的屬性初始化了。
繼承的是超類型中構造函數中的屬性,如上繼承了colors屬性,但沒有繼承SuperType原型中的getcolor方法。
使用此方法,我們還可以傳遞參數對屬性進行初始化
function SuperType(age) { this.age=age; } function SubType() { // 繼承了SuperType SuperType.call(this,18); } var instance1 = new SubType(); console.log(instance1.age); // 18
如果需要確保SuperType構造函數不會重寫子類型的屬性,可以在調用超類型構造函數后,再添加應該在子類型定義的屬性。
缺點:
1、超類型的原型不可見
2、所有屬性方法都必須寫在構造函數中,所有類型都只能使用構造函數模式創建
將原型鏈和借用構造函數的技術組合到一塊。
思想:使用原型鏈實現對原型屬性和方法的繼承,借用構造函數實現對實例屬性的繼承。
function SuperType(age) { this.age = age; } SuperType.prototype.getAge = function () { return this.age; } function SubType(age) { // 繼承了SuperType SuperType.call(this, age); } SubType.prototype = new SuperType(20); var instance1 = new SubType(18); console.log(instance1.age); // 18 console.log(instance1.getAge()); // 18 console.log(instance1.__proto__.age); // 20 var instance2 = new SubType(17); instance2.__proto__.age=55; console.log(instance1.__proto__.age); // 55 console.log(instance2.__proto__.age); // 55
我們可以看到,實際上instance1和instance2的原型上仍然存在屬于SuperType的實例屬性的屬性。只是instance1和instance2有了各自的age屬性,不會再往原型上找。
instanceof和isPrototypeOf()也能夠用于識別基于組合繼承創建的對象。
組合繼承避免了原型鏈和借用構造函數的缺陷并融合了兩者的有點,成為js中最常用的繼承模式。
思想:借助原型可以基于已有的對象創建新的對象,同時還不必因此創建自定義類型。
function object(o) { function F() { }; F.prototype = o; return new F(); } var person = { name: "linda", friends: ["lily", "shirley"] }; var antherPerson = object(person); antherPerson.friends.push("tom"); console.log(antherPerson.name); // linda console.log(antherPerson.friends); // ["lily", "shirley", "tom]
這個方法和原型方法原理一樣,只不過把子類型的原型設置成超類型的實例對象包含在方法object中。
ECMAScript5中新增了object.create()方法來規范原型式繼承(作用與上述object函數作用類似),第一個參數是想要繼承的超類型的實例對象,第二個參數是子類型所具有的屬性。
var person={ name:"lily", age:12 } var anotherPerson=Object.create(person,{name:{value:"linda"}}); console.log(anotherPerson.name); // "linda"
第二個參數的寫法必須如上的格式。
支持Object.create()方法的瀏覽器有ie9+,Firefox4.+、Safari5+、Opera12+和Chrome
思想:與寄生構造函數和工廠模式類似,即創建一個僅用于封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最后再像真的是它做了所有工作一樣返回對象。
代碼:
function object(o){ function F(){} F.prototype=o; return new F(); } var person = { name: "lily", age: 12 } function createAnotherPerson(original){ var clone=object(original); clone.sayHi=function(){ console.log("hi"); } return clone; } var anotherPerson =createAnotherPerson(person); anotherPerson.sayHi(); // "hi" console.log(anotherPerson.name); // "linda"六、寄生組合式繼承
組合繼承是js最常用的繼承模式,可以結合不同的模式的優點,但組合繼承,每次都會調用兩次超類型構造函數:一次是在創建子類型原型的時候,另一次是在子類型構造函數內部。
上述造成的結果是子類型實例中有兩組超類型的構造函數中定義的屬性,一組在子類型的實例中,一組在子類型實例的原型中。寄生組合式繼承可以解決上述缺點。
function Super(name) { this.name = name; } Super.prototype.sayName = function () { console.log(this.name); } function Sub(age) { Super.call(this, "linda"); this.age = age; } Sub.prototype = new Super(); Sub.constructor = Sub; var type=new Sub(); type.sayName(); // "linda" console.log(type.name); // "linda" console.log(type.__proto__.name); // undefined
思想:借用構造函數來繼承屬性,借用原型鏈來繼承方法。即繼承超類型的原型,然后再將結果指定給子類型的原型。
封裝一下即:
function Super(name) { this.name = name; } Super.prototype.sayName = function () { console.log(this.name); } function Sub(age) { Super.call(this, "linda"); this.age = age; } function object(o) { function F() { } F.prototype = o; return new F(); } function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); // 創建對象 prototype.constructor = subType; // 增強對象 subType.prototype = prototype; // 指定對象 } inheritPrototype(Sub, Super); var type = new Sub(); type.sayName(); // "linda" console.log(type.name); // "linda" console.log(type.__proto__.name); // undefined
這個模式的優點體現在
1、只調用了一次Super構造函數,高效率
2、避免了在Sub.prototype上面創建不必要的多余的屬性
3、原型鏈保持不變
開發人員普遍認為寄生組合式繼承是引用類型最理想的繼承范式
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100898.html
摘要:三種使用構造函數創建對象的方法和的作用都是在某個特殊對象的作用域中調用函數。這種方式還支持向構造函數傳遞參數。叫法上把函數叫做構造函數,其他無區別適用情境可以在特殊的情況下用來為對象創建構造函數。 一、工廠模式 工廠模式:使用字面量和object構造函數會有很多重復代碼,在此基礎上改進showImg(https://segmentfault.com/img/bVbmKxb?w=456&...
摘要:創建一個新對象將構造函數的作用域賦給新對象因此就指向了這個新對象執行構造函數中的代碼為這個新對象添加屬性返回新對象。 本章內容 理解對象屬性 理解并創建對象 理解繼承 ECMA-262把對象定義為:無序屬性的集合,其屬性可以包含基本值、對象或者函數 理解對象 創建對象 創建自定義對象的最簡單方式就是創建一個Object的實例,再為它添加屬性和方法。 var person = new...
摘要:高程讀書筆記第六章理解對象創建自定義對象的方式有創建一個實例,然后為它添加屬性和方法。創建了自定義的構造函數之后,其原型對象默認只會取得屬性至于其他方法都是從繼承而來的。 JS高程讀書筆記--第六章 理解對象 創建自定義對象的方式有創建一個Object實例,然后為它添加屬性和方法。還可用創建對象字面量的方式 屬性類型 ECMAScript在定義只有內部采用的特性時,描述了屬性的各種特征...
摘要:分區函數返回一個布爾值,這意味著得到的分組的鍵類型是,于是它最多可以分為兩組是一組,是一組。當遍歷到流中第個元素時,這個函數執行時會有兩個參數保存歸約結果的累加器已收集了流中的前個項目,還有第個元素本身。 一、收集器簡介 把列表中的交易按貨幣分組: Map transactionsByCurrencies = transactions.stream().collect(groupi...
摘要:第六章抽象本章會介紹如何將語句組織成函數。關鍵字參數和默認值目前為止,我們使用的參數都是位置參數,因為它們的位置很重要,事實上比它們的名字更重要。參數前的星號將所有值放置在同一個元祖中。函數內的變量被稱為局部變量。 第六章:抽象 本章會介紹如何將語句組織成函數。還會詳細介紹參數(parameter)和作用域(scope)的概念,以及遞歸的概念及其在程序中的用途。 懶惰即美德 斐波那契數...
閱讀 1837·2021-11-11 16:54
閱讀 2061·2019-08-30 15:56
閱讀 2372·2019-08-30 15:44
閱讀 1297·2019-08-30 15:43
閱讀 1864·2019-08-30 11:07
閱讀 821·2019-08-29 17:11
閱讀 1470·2019-08-29 15:23
閱讀 3011·2019-08-29 13:01