JavaScript實現繼承的6種方式
**1 、原型鏈繼承**
將構造函數的原型設置為另一個構造函數的實例對象,這樣就可以繼承另一個原型對象的所有屬性和方法,可以繼續往上,最終形成原型鏈。
第一個問題是,當實現繼承后,另一個原型的實例屬性,變成了現在這個原型的原型屬性,然后該原型的引用類型屬性會被所有的實例共享,這樣繼承原型引用類型屬性的實例之間不再具有自己的獨特性了。
第二個問題是,在創建子類型的實例時,沒有辦法在不影響所有對象實例的情況下給超類型的構造函數中傳遞參數。
**2、借用構造函數繼承**
為了解決原型中包含引用類型值的問題,開始使用借用構造函數,也叫偽造對象或經典繼承
```
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
//繼承SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
```
將SuperType函數在SubType構造函數中調用,在每個實例中執行,這樣每個實例中都會有一份SuperType中的屬性方法的副本,也就實現了繼承SuperType。
這種模式的優勢就是可以在子類型構造函數中向超類型構造函數傳遞參數。
存在的問題就是,所有的類型都只能使用構造函數模式(因為超類型的原型中定義的方法對于子類型不可見),因此方法都在構造函數中定義,函數復用就無從談起了。
**3、組合繼承**
也叫偽經典繼承,將原型鏈和借用構造函數的技術組合到一塊。使用原型鏈實現對原型屬性和方法的繼承,而通過構造函數來實現對實例屬性的繼承。
```
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
alert(this.name);
}
function SubType(name, age) {
// 繼承屬性
SuperType.call(this, name);
this.age = age;
}
// 繼承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
```
將SubType的原型指定為SuperType的一個實例,大致步驟和原型鏈繼承類似,只是多了在SubType中借調SuperType的過程。
實例屬性定義在構造函數中,而方法則定義在構造函數的新原型中,同時將新原型的constructor指向構造函數。
可以通過instanceof和isPrototypeOf()來識別基于組合繼承創建的對象。
避免了原型鏈和借用構造函數的缺陷,融合了它們的優點,成為JS中最常用的繼承模式。
實際上是借用了構造函數,以覆蓋的方式,解決了在原型鏈繼承中原型的引用類型屬性共享在所有實例中的問題。
因為在子類型中借調構造函數(SuperType.call(this))時,會在自己的所有實例中執行一遍SuperType中的代碼,由于每個實例this都是不同的,因此SuperType中定義的屬性會在每個實例中有一份副本,也就避免了原型鏈繼承中,原型屬性共享的問題(覆蓋了原型屬性)。
**4.、原型式繼承**
不自定義類型的情況下,臨時創建一個構造函數,借助已有的對象作為臨時構造函數的原型,然后在此基礎實例化對象,并返回。
```
function object(o){
function F(){}
F.prototype = o;
return new F();
```
本質上是object()對傳入其中的對象執行了一次淺復制
```
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
```
原型的引用類型屬性會在各實例之間共享。
當只想單純地讓一個對象與另一個對象保持類似的情況下,原型式繼承是完全可以勝任的。
注意:
ES5 通過新增 Object.create()方法規范化了原型式繼承。這個方法接收兩個參數:一個用作新對象原型的對象和(可選的)一個為新對象定義額外屬性的對象。在傳入一個參數的情況下,Object.create()與這里的object()方法的行為相同。第二個參數與Object.defineProperties()方法的第二個參數格式相同:每個屬性都是通過自己的描述符定義的。
**5、寄生式繼承**
其實就是在原型式繼承得到對象的基礎上,在內部再以某種方式來增強對象,然后返回。
```
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert("hi");
};
return clone;
}
```
思路與寄生構造函數和工廠模式類似。
新的對象中不僅具有original的所有屬性和方法,而且還有自己的sayHi()方法。
寄生式繼承在主要考慮對象而不是自定義類型和構造函數的情況下非常有用。
由于寄生式繼承為對象添加函數不能做到函數復用,因此效率降低。
**6、寄生組合式繼承**
組合繼承是JS中最常用的繼承模式,但其實它也有不足,組合繼承無論什么情況下都會調用兩次超類型的構造函數,并且創建的每個實例中都要屏蔽超類型對象的所有實例屬性。
寄生組合式繼承就解決了上述問題,被認為是最理想的繼承范式。
```
function object(o) {
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(superType, subType) {
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
alert(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SuperType, SubType); // 這一句,替代了組合繼承中的SubType.prototype = new SuperType()
SubType.prototype.sayAge = function() {
alert(this.age);
};
```
既然在組合模式中我們通過借調構造函數來為每個實例定義實例屬性,從而覆蓋原型屬性,影響了效率,那么是否可以把原型改變一下呢,不讓它作為SuperType的實例,這樣就不會有一些無用的原型屬性了。
不必為了指定子類型的原型而調用超類型的構造函數,我們需要的只不過是超類型原型的一個副本。
在inheritPrototype()函數中所做的事:
在inheritPrototype函數中用到了原型式繼承中的object()方法,將超類型的原型指定為一個臨時的空構造函數的原型,并返回構造函數的實例。
此時由于構造函數內部為空(不像SuperType里面有實例屬性),所以返回的實例也不會自帶實例屬性,這很重要!因為后面用它作為SubType的原型時,就不會產生無用的原型屬性了,借調構造函數也就不用進行所謂的“重寫”了。
然后為這個對象重新指定constructor為SubType,并將其賦值給SubType的原型。這樣,就達到了將超類型構造函數的實例作為子類型原型的目的,同時沒有一些從SuperType繼承過來的無用原型屬性。
- 前端綜合問題
- 【01】工作中你是如何檢測瀏覽器兼容問題的?
- 【02】手機端怎么適配?
- 【03】你對手機平臺安裝包后綴的理解
- 【04】你所了解的Web攻擊技術
- 【05】如何防止XSS攻擊?
- 【06】項目開發過程中都用過什么框架?
- 【07】一般情況下,端口號的取值范圍是多少?
- 【08】簡述http常見的狀態碼及含義
- 【09】CommonJS,AMD,CMD區別
- 【10】Svn與Git的區別
- 【11】http請求方式有幾種?分別是什么?
- 【12】請你談談cookie的弊端?
- 【13】window對象常用的屬性和方法有那些
- 【14】你有哪些性能優化的方法?
- 【15】web頁面重構怎么操作?
- 【16】線程與進程的區別是什么?
- 【17】什么是WEB標準?什么是W3C標準?
- 【18】行內元素有哪些? 塊級元素有哪些? CSS的盒模型?
- 【19】你如何優化自己的代碼?
- 【20】如何實現瀏覽器內多個標簽頁之間的通信?
- 【21】請談一下你對網頁標準和標準制定機構重要性的理解
- 【22】瀏覽器是如何渲染頁面的
- 【23】從輸入url到顯示頁面,都經歷了什么
- 【24】談談垃圾回收機制方式及內存管理
- 【25】請解釋JSONP的工作原理,以及它為什么不是真正的AJAX
- 【26】http 和 https 有何區別?如何靈活使用?
- 【27】瀏覽器緩存有哪些,通常緩存有哪幾種
- 【28】xml和json的區別
- 手寫代碼及閱讀題
- 【01】計算1-10000中出現的0 的次數
- 【02】寫一個兼容的function,清除字符串前后的空格
- 【03】請對一個數組的數組從小到大進行排序
- 【04】以下代碼輸出什么[空函數返回值]
- 【05】下面代碼輸出什么?[各種類型拼接]
- 【06】常用的兩種函數聲明有什么區別?
- 【07】請計算一個對象的長度
- 【08】看下面代碼,給出輸出結果[計時器]
- 【10】看下列代碼,輸出什么? [null相關]
- 【11】寫一個正則,字母開頭,后面可以是數字,下劃線,字母,長度6-30
- 【12】以下代碼的alert的值分別是多少?
- 【13】如何獲取三個數中的最大值和最小值?
- 【14】數組去重幾種方法是什么?
- 【15】求數組中的最大值
- 【16】求數組中的最小值
- 【17】獲得每個月1號是星期幾?
- 【18】將字符串轉化為駝峰表示法
- 邏輯題目
- 【01】1元1瓶汽水,2個空瓶再換1瓶汽水,30元最多能喝幾瓶?
- 【02】有8個球體,外觀一樣,但有1個球比其他7個球重,給你一個天平,幾次能找出那個較重的球?
- CSS3
- 【01】把一個div變成圓左邊藍色 右邊綠色
- 【02】css盒模型有哪些及區別
- 【03】什么是css盒模型
- 【04】如何讓英文小寫轉換為大寫
- 【05】 去掉inline-block元素間隙的幾種方法
- 【06】less和sass的特點和區別
- 【07】什么叫優雅降級和漸進增強?
- 【08】rgba和opacity的透明何不同?
- 【09】css選擇器有那些?CSS3新增了哪些
- 【10】CSS3有哪些新特性?
- 【11】display屬性有哪些?
- 【12】box-sizing的屬性和作用
- 【13】CSS樣式覆蓋規則
- HTML5
- 【01】請列舉5個html5輸入類型?并簡要描述其用途?
- 【02】em和rem的區別?
- 【03】精靈圖和base64 如何選擇,或者字體圖標什么時候使用?
- 【04】position定位有幾種?有什么區別?哪幾個是脫離文檔流的?
- 【05】垂直水平居中的方法有幾種?
- 【06】清除浮動的方法有哪些?
- 【07】link和import的區別
- 【08】opacity/visibility/display 隱藏對比
- 【09】XHTML和HTML有什么區別
- 【10】什么是語義化的HTML?
- 【11】常見的瀏覽器內核有哪些?
- 【12】HTML5有哪些新特性,移除了哪些元素?
- 【13】如何處理HTML5新標簽的瀏覽器兼容問題?
- 【14】HTML5為什么只需要寫!DOCTYPE HTML?
- 【15】介紹以下你對瀏覽器內核的理解?
- 【16】行內元素有哪些?塊級元素有哪些?
- 【17】標簽上title與alt屬性的區別是什么?
- 【18】前端頁面有哪三層構成是什么?作用是什么?
- 【19】H5特點有哪些?
- 【20】HTML5里的video標簽支持哪些視頻格式?
- 【21】margin-top父元素塌陷及解決辦法
- 【22】透明度opacity的兼容寫法
- 【23】讓一行文字在固定寬度div里顯示省略號的方法
- 【24】如何解決input和select的寬高不一致問題
- 【25】如何解決div與內部img間隙問題
- 【26】這個要改一下
- 【27】input設置為readonly和disable有什么區別?
- 【28】談談對BFC的理解
- 【29】簡述src與href的區別
- 【30】什么是外邊距重疊?以及防止方法
- 【31】HTML5 Input 表單新增輸入類型
- 【32】HTML5語義化標簽有哪些
- 【33】html常見兼容性問題
- 【34】Doctype的作用?嚴格模式與混雜模式的區別?
- 【35】HTML與XHTML的區別?
- 【36】input有那些類型?
- 【37】 img的alt與title有何異同?strong與em的異同?
- 【38】px和em的區別是什么?你還了解那些單位?
- 【39】把 Script 標簽 放在頁面的最底部的body封閉之前 和封閉之后有什么區別?瀏覽器會如何解析它們?
- 【40】什么是雪碧圖?它作用是什么?
- Javascript
- 【01】簡述ajax請求過程,以及注意事項。
- 【02】post和get的區別?
- 【03】簡述jsonp跨域原理,以及你知道的其他跨域方法
- 【04】JSONP與JSON的區別
- 【05】談談對http和https的理解
- 【06】var let const 的區別是什么?
- 【07】簡述“==”與“===”的區別?
- 【08】javascript各類型在轉換為boolean式的規則?
- 【09】Object.creat()和new對象有什么區別?
- 【10】for in和Object.keys來遍歷對象有什么區別?
- 【11】Object.assign()函數的作用及用法
- 【12】數組的常用方法有那些?
- 【13】js中內置 對象有哪些有哪些?
- 【14】控制臺undefined和is not defined異常區別?
- 【15】JS中面向對象和面向過程的區別
- 【16】什么是腳本?
- 【17】cookies、sessionStorage和localStorage區別?
- 【18】談一下你對面向對象的理解?
- 【19】什么是函數?
- 【20】正則驗證,match()與test()函數的區別?
- 【21】什么是閉包?簡述的優缺點
- 【22】Es6新特性有哪些?
- 【23】怎么判斷一個object是否是數組(array)?
- 【24】函數聲明和函數表達式的區別?
- 【25】this一般的指向,以及如何改變指向
- 【26】javascript的typeof返回哪些數據類型(和37重復了)
- 【27】列舉IE和標準下有哪些兼容性的寫法
- 【28】new操作符具體干了什么呢?
- 【29】什么是Json?
- 【30】js中的3種彈出式命令什么
- 【31】節點的種類有幾種,分別是什么?
- 【32】Void(0)的作用是什么?
- 【33】箭頭函數和普通函數的區別
- 【34】怎么獲取對象的key值
- 【35】談談你對原型鏈的理解
- 【36】Javascript如何實現繼承?
- 【37】typeof返回哪些數據類型
- 【38】創建函數的幾種方式?
- 【39】js中同步和異步的區別
- 【40】事件綁定和普通事件有什么區別?
- 【41】documen.write和 innerHTML的區別?
- 【42】JS實現繼承的6種方式
- 【43】什么是事件委派以及優缺點
- 【44】JS創建對象的幾種方式?
- 【45】解釋js垃圾回收機制和引起內存泄漏?
- 【46】DOM怎樣添加、移除、移動、復制、創建和查找節點
- 【47】null和undefined的區別?
- 【48】正則常用符號代表什么?
- 【49】callee和caller的作用?
- 【50】如何阻止事件冒泡和默認事件?
- 【51】例舉3種強制類型轉換和2種隱式類型轉換?
- 【52】JavaScript是一門什么樣的語言,它有哪些特點?
- 【53】js延遲加載的方式有哪些?
- 【54】js作用域有那些
- 【55】同步和異步的區別?
- 【56】已知ID的Input輸入框,希望獲取這個輸入框的輸入值
- 【57】ajax請求的時候get 和post方式的區別
- 【58】eval()的作用
- 【59】如何用原生js給一個按鈕綁定兩個onclick事件?
- 【60】Javascript中的定時器有哪些?他們的區別及用法是什么?
- Node
- 【01】對nodejs的理解?
- 【02】import和require的區別
- 【03】Node.js的優點和缺點
- 框架及JS庫
- 【01】jQuery如何實現事件委托?
- 【02】$(document).ready和window.onload的區別?
- 【03】v-if與v-show區別
- 【04】mvc和mvvm模式區別
- 【05】React早期創建組件的方式
- 【06】export與export default 的區別
- 【07】簡述一下 Handlebars 的基本用法?
- React
- 【01】對React生命周期的理解
- 【02】React創建組件的三種方式及其區別是什么?
- 【03】react和vue有哪些不同,說說你對這兩個框架的看法