JavaScript中六種面試常考繼承方式總結(jié)
js的幾種繼承方式在我們面試的時候經(jīng)常會被問到,所以深入理解js幾種繼承方式以及它們的優(yōu)缺點(diǎn)是非常有必要的。
原型鏈繼承
之前我們介紹過原型和實例的關(guān)系:每一個構(gòu)造函數(shù)都有一個原型prototype
,原型對象中的constructor
又指回構(gòu)造函數(shù),實例中有一個內(nèi)部指針__proto__
指向構(gòu)造函數(shù)的prototype
。不清楚的可以看這篇 下面看個代碼
function Parent() { this.name = "mick"; } Parent.prototype.getName = function () { console.log(this.name); }; function Child() {} Child.prototype = new Parent(); var child1 = new Child(); console.log(child1.getName());
我們將構(gòu)造函數(shù)Parent
的實例賦值給了構(gòu)造函數(shù)Child
的原型,實現(xiàn)了Child
能夠繼承Parent
的屬性和方法。
優(yōu)點(diǎn)
1.父類的方法可以被復(fù)用
缺點(diǎn)
1.父類的所有屬性都會被子類共享,只要修改了一個子類的引用類型的屬性,其他的子類也會受影響
function Parent() { this.names = ["mick", "randy"]; } function Child() {} Child.prototype = new Parent(); var child1 = new Child(); child1.names.push("qr"); console.log(child1.names); // ["mick", "randy", "qr"] var child2 = new Child(); console.log(child2.names); // ["mick", "randy", "qr"]
2.子類實例不能給父類構(gòu)造函數(shù)傳參
盜用構(gòu)造函數(shù)
盜用構(gòu)造函數(shù)的思路其實就是在子類構(gòu)造函數(shù)中通過call
或者apply
方法調(diào)用父類構(gòu)造函數(shù)
function Parent() { this.names = ["mick", "randy"]; } function Child() { Parent.call(this); } var child1 = new Child(); child1.names.push("qr"); console.log(child1.names); // ["mick", "randy", "qr"] var child2 = new Child(); console.log(child2.names); // ["mick", "randy"]
這里我們通過在構(gòu)造函數(shù)Child
中通過call
調(diào)用Parent
,此時this
就是構(gòu)造函數(shù)Child
,其實就是Child
的實例被創(chuàng)建的時候都會對Parent
進(jìn)行初始化,相當(dāng)于每一個實例都擁有了names
屬性。
我們也可以給父構(gòu)造函數(shù)傳參了
function Parent(name) { this.name = name; } function Child(name, age) { Parent.call(this, name); this.age = age; } var child1 = new Child("randy", 18); console.log(child1.name); // randy
優(yōu)點(diǎn)
- 可以在子類構(gòu)造函數(shù)向父類構(gòu)造函數(shù)傳參
- 父類的實例的引用屬性不會被共享
缺點(diǎn)
子類不能訪問父類原型上的方法,所以所有方法和屬性都寫在構(gòu)造函數(shù)中,每次實例創(chuàng)建都會被初始化。
組合繼承
組合繼承就是綜合原型鏈繼承和盜用構(gòu)造函數(shù)繼承的優(yōu)點(diǎn),從何又對這兩種方法的缺點(diǎn)互補(bǔ)。使用原型鏈繼承可以訪問父類原型上的屬性和方法,通過構(gòu)造函數(shù)繼承可以訪父類實例的屬性和方法。
function Parent(name) { this.name = name; this.colors = ["red", "blue", "green"]; } Parent.prototype.getName = function () { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); // 第一次 this.age = age; } Child.prototype = new Parent(); // 第二次 Child.prototype.constructor = Child; var child1 = new Child("mick", "18"); child1.colors.push("black"); console.log(child1.name); // mick console.log(child1.age); // 18 console.log(child1.colors); // ["red", "blue", "green", "black"] var child2 = new Child("randy", "20"); console.log(child2.name); // randy console.log(child2.age); // 20 console.log(child2.colors); // ["red", "blue", "green"]
優(yōu)點(diǎn)
- 父類的方法可以復(fù)用
- 可以在子類構(gòu)造函數(shù)向父類傳參
- 父類構(gòu)造函數(shù)中的引用屬性不會共享
缺點(diǎn)
父類構(gòu)造函數(shù)被調(diào)用了兩次(文章中的注釋已經(jīng)標(biāo)出)
原型式繼承
創(chuàng)建一個臨時的構(gòu)造函數(shù),將傳入的對象賦值給這個構(gòu)造函數(shù)的原型,然后返回這個臨時類型的一個實例。其實就是對傳入對應(yīng)進(jìn)行一次淺復(fù)制。
function createObj(o) { function F() {} F.prototype = o; return new F(); }
其實就是Object.create的模擬實現(xiàn),將傳入的對象作為創(chuàng)建的對象的原型 缺點(diǎn)
1.引用類型的屬性值始終都會共享相應(yīng)的值,這點(diǎn)跟原型鏈繼承一樣
function createObj(o) { function F() {} F.prototype = o; return new F(); } let person = { name: "mick", colors: ["red", "blue", "green"], }; let anotherPerson = createObj(person); anotherPerson.name = "randy"; anotherPerson.colors.push("black"); console.log(anotherPerson.colors); // ['red', 'blue', 'green', 'black'] let yetAnotherPerson = createObj(person); yetAnotherPerson.colors.push("yellow"); console.log(yetAnotherPerson.name); // mick console.log(yetAnotherPerson.colors); // ['red', 'blue', 'green', 'black', 'yellow']
修改了anotherPerson.name
的值,yetAnotherPerson.name
沒有發(fā)生變化,這是因為anotherPerson.name
給anotherPerson
添加了name
的值,并不是修改了原型上的值。
寄生式繼承
寄生式繼承背后類似于寄生構(gòu)造函數(shù)和工廠模式:創(chuàng)建一個實現(xiàn)繼承的函數(shù),以某種方式增強(qiáng)對象,然后返回這個對象。我們繼續(xù)使用原型式繼承創(chuàng)建的方法
function createObj(o) { function F() {} F.prototype = o; return new F(); } function createAnother(original) { let clone = createObj(original); clone.sayHi = function () { console.log("hi"); }; return clone; }
缺點(diǎn)
跟盜用構(gòu)造函數(shù)一樣的,方法在每次創(chuàng)建對象都會重新創(chuàng)建一遍
寄生式組合繼承
我們首先回看下組合繼承有個缺點(diǎn)就是父類構(gòu)造函數(shù)會調(diào)用兩次,那如何優(yōu)化這個缺點(diǎn)呢?
寄生式組合繼承通過盜用構(gòu)造函數(shù)繼承屬性,但使用混合式原型鏈繼承方法?;舅悸肥遣煌ㄟ^調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是取得父類原型的一個副本。也就是使用寄生式繼承來繼承父類原型,然后將返回的新對象賦值給子類原型
function Parent(name) { this.name = name; this.colors = ["red", "blue", "green"]; } Parent.prototype.getName = function () { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); this.age = age; } var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); var child1 = new Child("mick", "18"); console.log(child1);
封裝一下
function createObj(o) { function F() {} F.prototype = o; return new F(); } function prototype(child, Parent) { var prototype = createObj(parent.prototype); prototype.constructor = child; child.prototype = prototype; } function Parent(name) { this.name = name; this.colors = ["red", "blue", "green"]; } Parent.prototype.getName = function () { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); this.age = age; } prototype(Child, Parent) var child1 = new Child("mick", "18"); console.log(child1);
這種方式的高效率體現(xiàn)它只調(diào)用了一次Parent
構(gòu)造函數(shù),并且因此避免了在Parent.prototype
上面創(chuàng)建不必要的、多余的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof
和isPrototypeOf
。開發(fā)人員普遍認(rèn)為寄生組合式繼承是引用類型最理想的繼承范式。
以上就是JavaScript中六種面試常考繼承方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript繼承方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript結(jié)合html5 canvas實現(xiàn)(可調(diào)畫筆顏色/粗細(xì)/橡皮)的涂鴉板
js+html5 canvas實現(xiàn)的涂鴉畫板特效,可調(diào)畫筆顏色|粗細(xì)|橡皮,可以保存涂鴉效果為圖片編碼,測試了下還不錯,感興趣的朋友可以參考下2013-04-04WebRTC媒體權(quán)限申請getUserMedia實例詳解
這篇文章主要為大家介紹了WebRTC媒體權(quán)限申請getUserMedia實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11