在ES5繼承的實(shí)現(xiàn)非常有趣的,由于沒(méi)有傳統(tǒng)面向?qū)ο箢惖母拍睿?a href="http://www.companysz.com/article.asp?typeid=160">javascript利用原型鏈的特性來(lái)實(shí)現(xiàn)繼承,這其中有很多的屬性指向和需要注意的地方。
原型鏈的特點(diǎn)和實(shí)現(xiàn)已經(jīng)在之前的一篇整理說(shuō)過(guò)了,就是通過(guò)將子類構(gòu)造函數(shù)的原型作為父類構(gòu)造函數(shù)的實(shí)例(sub.PRototype=new super),這樣就連通了子類-子類原型-父類,原型鏈的特點(diǎn)就是逐層查找,從子類開(kāi)始一直往上直到所有對(duì)象的原型Object.prototype,找到屬性方法之后就會(huì)停止查找,所以下層的屬性方法會(huì)覆蓋上層。
一個(gè)基本的基于原型鏈的繼承過(guò)程大概是這樣的:
//先來(lái)個(gè)父類,帶些屬性function Super(){ this.flag = true;}//為了提高復(fù)用性,方法綁定在父類原型屬性上Super.prototype.getFlag = function(){ return this.flag;}//來(lái)個(gè)子類function Sub(){ this.subFlag = false;}//實(shí)現(xiàn)繼承Sub.prototype = new Super;//給子類添加子類特有的方法,注意順序要在繼承之后Sub.prototype.getSubFlag = function(){ return this.subFlag;}//構(gòu)造實(shí)例var es5 = new Sub;原型鏈實(shí)現(xiàn)的繼承主要有幾個(gè)問(wèn)題:1、本來(lái)我們?yōu)榱藰?gòu)造函數(shù)屬性的封裝私有性,方法的復(fù)用性,提倡將屬性聲明在構(gòu)造函數(shù)內(nèi),而將方法綁定在原型對(duì)象上,但是現(xiàn)在子類的原型是父類的一個(gè)實(shí)例,自然父類的屬性就變成子類原型的屬性了;這就會(huì)帶來(lái)一個(gè)問(wèn)題,我們知道構(gòu)造函數(shù)的原型屬性在所有構(gòu)造的實(shí)例中是共享的,所以原型中屬性的改變會(huì)反應(yīng)到所有的實(shí)例上,這就違背了我們想要屬性私有化的初衷;2、創(chuàng)建子類的實(shí)例時(shí),不能向父類的構(gòu)造函數(shù)傳遞參數(shù)function Super(){ this.flag = true;}function Sub(){ this.subFlag = false;}Sub.prototype = new Super;var obj = new Sub();obj.flag = flase; //修改之后,由于是原型上的屬性,之后創(chuàng)建的所有實(shí)例都會(huì)受到影響var obj_2 = new Sub();console.log(obj.flag) //false;為了解決以上兩個(gè)問(wèn)題,有一個(gè)叫借用構(gòu)造函數(shù)的方法只需要在子類構(gòu)造函數(shù)內(nèi)部使用apply或者call來(lái)調(diào)用父類的函數(shù)即可在實(shí)現(xiàn)屬性繼承的同時(shí),又能傳遞參數(shù),又能讓實(shí)例不互相影響
function Super(){ this.flag = true;}function Sub(){ Super.call(this) //如果父類可以需要接收參數(shù),這里也可以直接傳遞}var obj = new Sub();obj.flag = flase;var obj_2 = new Sub();console.log(obj.flag) //依然是true,不會(huì)相互影響結(jié)合借用構(gòu)造函數(shù)和原型鏈的方法,可以實(shí)現(xiàn)比較完美的繼承方法,可以稱為組合繼承:function Super(){ this.flag = true;}Super.prototype.getFlag = function(){ return this.flag; //繼承方法}function Sub(){ this.subFlag = flase Super.call(this) //繼承屬性}Sub.prototype = new Super;var obj = new Sub();Sub.prototype.constructor = Sub;Super.prototype.getSubFlag = function(){ return this.flag;}function Box(age) {this.name = ['Lee', 'Jack', 'Hello']this.age = age;}Box.prototype.run = function () {return this.name + this.age;};function Desk(age) {Box.call(this, age); //對(duì)象冒充}Desk.prototype = new Box(); //原型鏈繼承var desk = new Desk(100);alert(desk.run());這里還有個(gè)小問(wèn)題,Sub.prototype = new Super; 會(huì)導(dǎo)致Sub.prototype的constructor指向Super;然而constructor的定義是要指向原型屬性對(duì)應(yīng)的構(gòu)造函數(shù)的,Sub.prototype是Sub構(gòu)造函數(shù)的原型,所以應(yīng)該添加一句糾正:Sub.prototype.constructor = Sub;
看完ES5的實(shí)現(xiàn),再來(lái)看看ES6的繼承實(shí)現(xiàn)方法,其內(nèi)部其實(shí)也是ES5組合繼承的方式,通過(guò)call借用構(gòu)造函數(shù),在A類構(gòu)造函數(shù)中調(diào)用相關(guān)屬性,再用原型鏈的連接實(shí)現(xiàn)方法的繼承
class B extends A { constructor() { return A.call(this); //繼承屬性 }}A.prototype = new B; //繼承方法ES6封裝了class,extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承,內(nèi)部的實(shí)現(xiàn)原理其實(shí)依然是基于上面所講的原型鏈,不過(guò)進(jìn)過(guò)一層封裝后,Javascript的繼承得以更加簡(jiǎn)潔優(yōu)雅地實(shí)現(xiàn)class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 等同于parent.constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 等同于parent.toString() }}通過(guò)constructor來(lái)定義構(gòu)造函數(shù),用super調(diào)用父類的屬性方法轉(zhuǎn)至:http://www.jianshu.com/p/342966fdf816
新聞熱點(diǎn)
疑難解答
圖片精選