摘要:構造函數繼承在子類的構造函數中,通過或的形式,調用父類構造函數,以實現繼承。所以,其實多帶帶使用原型鏈繼承或者借用構造函數繼承都有自己很大的缺點,最好的辦法是,將兩者結合一起使用,發揮各自的優勢。使指向自己而不是指向構造函數
原型鏈繼承
子類的所有實例都共享著原型上的所有屬性和方法。通過子類實例,可以訪問原型上的屬性,但是,不能重寫原型上的屬性。
//定義一個學生類 function Student(stuID, schoolName) { this.stuID = stuID; this.schoolName = schoolName; } //所有學生都有這樣一個特征 Student.prototype.characteristic = "年輕有朝氣"; var stu1 = new Student(1001,"第一小學"); console.log(stu1.stuID); //1001 console.log(stu1.characteristic); //"年輕有朝氣" //重寫characteristic stu1.characteristic = "活潑可愛"; console.log(stu1.characteristic); //"活潑可愛" var stu2 = new Student(1002,"第一小學"); console.log(stu2.characteristic); //"年輕有朝氣" console.log(Student.prototype); //{characteristic: "年輕有朝氣"}
上面這段代碼表明:
通過stu1.characteristic = "活潑可愛";并沒有改變原型上的屬性值。
當實例中,存在和原型中同名的屬性時,會自動屏蔽原型上的同名屬性。stu1.characteristic = = "活潑可愛" 實際上是給實例stu1添加了一個本地屬性 characteristic,所以當我們再次訪問stu1.characteristic 時,訪問的是實例的本地屬性,而不是原型上的characteristic屬性(它因和本地屬性名同名已經被屏蔽了)。
原型上的 characteristic 值是一個基本類型的值,如果是一個引用類型呢?這其中又會有一堆小九九。
其實原型上任何類型的值,都不會被實例所重寫。在實例上設置與原型上同名屬性的值,只會在實例上創建一個同名的本地屬性。但是,原型上引用類型的值可以通過實例進行修改,而且所有的實例訪問到的該引用類型的值也會隨之改變。(不是很明白,既然引用類型的值都能被修改了,那么為什么還說不會被實例重寫??難道修改!= 重寫)
//定義一個學生類 function Student(stuID, schoolName) { this.stuID = stuID; this.schoolName = schoolName; } //所有學生都有這樣一個特征 Student.prototype.characteristic = "年輕有朝氣"; Student.prototype.examItems = ["語文","數學","英語"]; //考試項目 var stu1 = new Student(1001, "第一小學"); console.log(stu1.examItems); //["語文","數學","英語"] //修改examItems stu1.examItems.push("科學"); console.log(stu1.examItems); //["語文","數學","英語","科學"] var stu2 = new Student(1002, "第一小學"); console.log(stu2.examItems); //["語文","數學","英語","科學"]
原型上任何類型的屬性值都不會通過實例被重寫,但是引用類型的屬性值會受到實例的影響而修改。
構造函數繼承在子類的構造函數中,通過 apply( ) 或 call( )的形式,調用父類構造函數,以實現繼承。
//定義一個超類/父類: 人 function Person (name, age) { //人都有姓名,年齡,會吃飯,會睡覺 //傳入出生年份 year,自動計算年齡 this.name = name; this.age = age; this.eat = function () { alert("吃飯"); } this.sleep = function () { alert("睡覺"); } } //定義一個子類: 學生 //學生Student也是人,自然要繼承超類 Person 的所有屬性和方法 //學生都應當有姓名、年齡、會吃飯、會睡覺 //當然學生也有自己的一些屬性:學號,學校名稱等,和方法,比如都要去做一件事:寫作業 function Student (stuID, schoolName, name, age) { this.stuID = stuID; this.schoolName = schoolName; //用call調用 Person,以實現繼承 Person.call(this, name, age); } Student.prototype.doHomework = function () { alert("做作業"); } //實例化一個學生 var stu1 = new Student(1001, "第一小學", "王寶寶",20); console.log(stu1.stuID); //1001 console.log(stu1.schoolName); //"第一小學" console.log(stu1.name); //"王寶寶" console.log(stu1.age); //20 stu1.eat(); //"吃飯" stu1.sleep(); //"睡覺" stu1.doHomework(); //"做作業"
在子類構造函數中,我們通過 call 的方式調用了父類構造函數 Person實現了繼承。別忘了,函數只不過是一段可以在特定作用域執行代碼的特殊對象,我們可以通過 call 方法指定函數的作用域。
在 stu1 = new Student() 構造函數時,Student 內部 this 的值指向的是 stu1, 所以 this.stuID =stu1.stuID, 所以 Person.call(this, name, age) 就相當于Person.call(stu1, "王寶寶", 20),就相當于 stu1.Person("王寶寶",20)。最后,stu1 去調用 Person 方法時,Person 內部的 this 指向就指向了 stu1。那么Person 內部this 上的所有屬性和方法,都被拷貝到了stu1上。
總之,在子類函數中,通過call() 方法調用父類函數后,子類實例 stu1, 可以訪問到 Student 構造函數和 Person 構造函數里的所有屬性和方法。這樣就實現了子類向父類的繼承。
缺點這種形式的繼承,每個子類實例都會拷貝一份父類構造函數中的方法,作為實例自己的方法,比如 eat()。這樣做,有幾個缺點:
1. 每個實例都拷貝一份,占用內存大,尤其是方法過多的時候。
2. 方法都作為了實例自己的方法,當需求改變,要改動其中的一個方法時,之前所有的實例,他們的該方法都不能及時作出更新。只有后面的實例才能訪問到新方法。
所以,其實多帶帶使用原型鏈繼承或者借用構造函數繼承都有自己很大的缺點,最好的辦法是,將兩者結合一起使用,發揮各自的優勢。
例如:
function OSTAccountDC() { OSTBaseDC.call(this); } OSTAccountDC.prototype = new OSTBaseDC(); //通過OSTBaseDC創建一個實例,避免改變父類構造函數的屬性,而只是在本地創建一個屬性。 OSTAccountDC.prototype.constructor = OSTAccountDC; //使this指向自己OSTAccountDC,而不是指向構造函數OSTBaseDC OSTAccountDC.prototype.accountRequest = function(callback){ // do something }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93754.html
摘要:綜上所述有原型鏈繼承,構造函數繼承經典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優點于一身是實現基于類型繼承的最有效方法。 一、前言 繼承是面向對象(OOP)語言中的一個最為人津津樂道的概念。許多面對對象(OOP)語言都支持兩種繼承方式::接口繼承 和 實現繼承 。 接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。由于js中方法沒有簽名...
摘要:想要解決這樣的問題的話,可以借助構造函數也可以叫做偽造對象或經典繼承。通過借助構造函數實現對實例對象的屬性和繼承。 原型鏈 原型鏈是什么 構造函數或構造器具有prototype屬性 對象具有__proto__屬性 這就是之前學習的原型如果構造函數或對象A A的原型指向構造函數或對象B B的原型在指向構造函數或對象C 以此類推 最終的構造函數或對象的原型指向Object的原型 由此形成一...
摘要:在節中,我們學習到了通過構造函數創建對象的三個重要步驟,其中的一步是把構造函數的對象設置為創建對象的原型。利用而不是直接用創建一個實例對象的目的是,減少一次調用父構造函數的執行。 JavaScript語言不像面向對象的編程語言中有類的概念,所以也就沒有類之間直接的繼承,JavaScript中只有對象,使用函數模擬類,基于對象之間的原型鏈來實現繼承關系,ES6的語法中新增了class關鍵...
摘要:在使用原型鏈實現繼承時有一些需要我們注意的地方注意繼承后的變化。在了解原型鏈時,不要忽略掉在末端還有默認的對象,這也是我們能在所有對象中使用等對象內置方法的原因。 在上一篇post中,介紹了原型的概念,了解到在javascript中構造函數、原型對象、實例三個好基友之間的關系:每一個構造函數都有一個守護神——原型對象,原型對象心里面也存著一個構造函數的位置,兩情相悅,而實例呢卻又...
摘要:這正是我們想要的太棒了毫不意外的,這種繼承的方式被稱為構造函數繼承,在中是一種關鍵的實現的繼承方法,相信你已經很好的掌握了。 你應該知道,JavaScript是一門基于原型鏈的語言,而我們今天的主題 -- 繼承就和原型鏈這一概念息息相關。甚至可以說,所謂的原型鏈就是一條繼承鏈。有些困惑了嗎?接著看下去吧。 一、構造函數,原型屬性與實例對象 要搞清楚如何在JavaScript中實現繼承,...
摘要:因為這造成了繼承鏈的紊亂,因為的實例是由構造函數創建的,現在其屬性卻指向了為了避免這一現象,就必須在替換對象之后,為新的對象加上屬性,使其指向原來的構造函數。這個函數接收兩個參數子類型構造函數和超類型構造函數。 最近一直在研究js面向對象,原型鏈繼承是一個難點,下面是我對繼承的理解以下文章借鑒自CSDN季詩筱的博客 原型鏈繼承的基本概念: ES中描述了原型鏈的概念,并將原型鏈作為實現...
閱讀 3672·2021-09-22 15:28
閱讀 1296·2021-09-03 10:35
閱讀 878·2021-09-02 15:21
閱讀 3474·2019-08-30 15:53
閱讀 3496·2019-08-29 17:25
閱讀 569·2019-08-29 13:22
閱讀 1555·2019-08-28 18:15
閱讀 2287·2019-08-26 13:57