您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)JavaScript中是如何實(shí)現(xiàn)繼承的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
原理: 子類原型指向父類實(shí)例對(duì)象實(shí)現(xiàn)原型共享,即Son.prototype = new Father()。
這里先簡(jiǎn)單介紹下原型
js中每個(gè)對(duì)象都有一個(gè)__proto__屬性,這個(gè)屬性指向的就是該對(duì)象的原型。js中每個(gè)函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性指向該函數(shù)作為構(gòu)造函數(shù)調(diào)用時(shí)創(chuàng)建的實(shí)例的原型。原型對(duì)象上有一個(gè)constructor屬性,指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù),該屬性不可枚舉。
var obj = {}; obj.__proto__ === Object.prototype; //true console.log(Object.prototype.constructor) // ƒ Object
當(dāng)我們?cè)L問一個(gè)對(duì)象的屬性或者方法時(shí),如果找不到,則會(huì)通過原型向上尋找,若原型上也未找到,則會(huì)去原型的原型上面去找。比如我要調(diào)用obj.toString方法時(shí),在自身并未找到toString方法,則會(huì)去原型上去尋找,即在Object.prototype上去尋找,找到后運(yùn)行該方法。
var obj = {}; obj.toString(); obj.__proto__.toString(); //obj.__proto__和Object.prototype指向的是一個(gè)對(duì)象,自然就能訪問Object.prototype上的toString方法啦
注意:原型鏈的終點(diǎn)是null,使用bind方法返回的函數(shù)沒有prototype屬性。
var obj = {}; function fn(){}; fn.bind(obj).prototype; // undefined Object.prototype.__proto__; // null
原型鏈接繼承
function Father(age){ this.age = age; this.color = ['red','pink'] } Father.prototype.sayHello = function(){ console.log('hello') } function Son(sex){ this.sex = sex } console.log(Son.prototype.constructor) // ƒ Son Son.prototype = new Father(15) // 原型鏈繼承關(guān)鍵 var son = new Son('男') son.color.push('black') var son2 = new Son('女') son.sayHello() // hello son.sayHello === son2.sayHello //true console.log(son2.color) // ['red','pink','black'] console.log(Son.prototype.constructor) // ƒ Father
可以看到通過原型鏈實(shí)現(xiàn)繼承,原型上引用類型的值會(huì)被所有實(shí)例共享。子類的constructor指向會(huì)發(fā)生改變,而且在創(chuàng)建子類實(shí)例時(shí)不可以向父類構(gòu)造函數(shù)傳遞參數(shù)??梢允謩?dòng)把子類constructor屬性指回其構(gòu)造函數(shù)。
//寫法一 Son.prototype.constructor = Son // 這種寫法有點(diǎn)缺點(diǎn),它會(huì)讓constructor屬性變的可以枚舉。 //寫法二 Object.defineProperty(Son.prototype,'constructor',{ enumerable:false, // 設(shè)置不可枚舉 value:Son })
原理:在子類構(gòu)造函數(shù)中通過apply或者call調(diào)用父類構(gòu)造函數(shù)來繼承屬性或方法。
function Father(name){ this.color = ['red'] this.sayHello = function(){ console.log('hello') } } Father.prototype.sayName = function(){ console.log('zs') } function Son(num,name){ Father.call(this,name) //實(shí)現(xiàn)繼承的關(guān)鍵代碼 this.num = num } var son = new Son(10,'zs') var son2 = new Son(15,'ls') son.color.push('pink') console.log(son2.color) // ['red'] son.sayName() //報(bào)錯(cuò) son.sayName is not a function console.log(son.sayHello === son2.sayHello) //false
可以看出通過構(gòu)造函數(shù)實(shí)現(xiàn)繼承,解決了原型鏈繼承不能向父類傳參以及引用類型值共享的問題。但這種繼承方法卻不能訪問父類構(gòu)造函數(shù)原型上的方法和屬性,而且定義在父類構(gòu)造函數(shù)中的方法也不能復(fù)用。
組合繼承,有時(shí)候也叫偽經(jīng)典繼承,它是將原型鏈繼承和構(gòu)造函數(shù)繼承結(jié)合到一起的一種繼承模式。實(shí)現(xiàn)思路是通過原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,通過借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。
function Father(name){ this.color = ['red'] } Father.prototype.sayName = function(){ console.log('zs') } function Son(num,name){ Father.call(this,name) //繼承實(shí)例屬性 this.num = num } Son.prototype = new Father() //繼承原型上屬性 Son.prototype.constructor = Son var son = new Son(10,'zs') var son2 = new Son(15,'ls') son.color.push('pink') console.log(son.color,son2.color) //['red','pink'] ['red'] son.sayName() // zs
組合式繼承避免了原型鏈繼承和構(gòu)造函數(shù)繼承的缺點(diǎn),融合了它們的優(yōu)點(diǎn),成為JavaScript中常用的一種繼承模式。
寄生式繼承與工廠模式類似,一般用來繼承對(duì)象。即創(chuàng)建一個(gè)封裝繼承的函數(shù),在函數(shù)內(nèi)部復(fù)制一份該對(duì)象,對(duì)復(fù)制的對(duì)象進(jìn)行處理,返回復(fù)制的對(duì)象。
function createAnother(obj){ var clone = Object.create(obj) clone.name = 'zs' clone.sayHello = function(){ console.log('hello') } return clone } var obj = {age:15} var newObj = createAnother(obj) // 15 console.log(newObj.name) // zs newObj.sayHello() // hello
前面說到過組合式繼承是Javascript中最常用的繼承模式,不過這種模式也有自己的不足,它會(huì)調(diào)用兩次父類構(gòu)造函數(shù)。第一次是在將子類原型指向父類實(shí)例的時(shí)候,第二次是在子類構(gòu)造函數(shù)中調(diào)用的。
function Father(name){ this.name = name } function Son(num,name){ Father.call(this,name) // 第二次調(diào)用 } Son.prototype = new Father('ls') // 第一次調(diào)用 var son = new Son(10,'zs') console.log(son)
在第一次調(diào)用的時(shí)候,Son.prototype會(huì)繼承name這個(gè)屬性,第二次調(diào)用時(shí),實(shí)例對(duì)象會(huì)繼承name。當(dāng)我們獲取實(shí)例對(duì)象的name屬性時(shí)因?yàn)閷?shí)例對(duì)象上有該屬性,所以是不會(huì)去原型上去尋找的,相當(dāng)于實(shí)例對(duì)象上的name屬性把原型上的name屬性給屏蔽掉了,所以原型上的這個(gè)屬性是多余的。
為了解決這個(gè)問題,就有了寄生組合式繼承。主要思路就是創(chuàng)建一個(gè)函數(shù)完成原型鏈繼承和constructor的指向問題,然后通過構(gòu)造函數(shù)繼承屬性。
// 復(fù)制一個(gè)父類的原型指向,將子類的原型指向復(fù)制的父類原型,達(dá)到不用調(diào)用父類構(gòu)造函數(shù)就能繼承其原型上的方法的效果。 function inherit(Sup,Sub){ var prototype = Object.create(Sup.prototype) Sub.prototype = prototype prototype.constructor = Sub } function Father(name){ this.name = name } function Son(name){ Father.call(this,name) } inherit(Father,Son) var son = new Son('zs') console.log(son)
看完上述內(nèi)容,你們對(duì)JavaScript中是如何實(shí)現(xiàn)繼承的有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。