書接上集,在上集中我們給出了一個需求說明,要求利用現(xiàn)學(xué)的知識實現(xiàn)原型(prototype
)繼承,并且我們給出了三種實現(xiàn)方式。但是這三種方式各自有優(yōu)缺點,都不能很好的滿足要求,那是否還有其他更好的實現(xiàn)方式呢?
在看本文之前,諸位可以自己思考下?帶著問題去學(xué)習(xí)總是能夠?qū)W到更多內(nèi)容。在以后的歲月中,會找一些有意義的句子,與諸君共勉。
在時間的稿紙上,每個人都在寫著自己的歷史。當(dāng)你抓著今天時,你就會前進(jìn)一步,當(dāng)你丟棄今天時,你就會停滯不動。
一、臨時構(gòu)造器方式
上集中,我們利用prototype
方式實現(xiàn)繼承,會帶來一個問題:所有的屬性都指向了一個相同的對象,父對象就會受到子對象屬性的影響。
如何解決這個問題?
這時就必須利用某種中介來打破這種連鎖關(guān)系。我們可以用一個臨時的構(gòu)造器函數(shù)來充當(dāng)中介。即:我們創(chuàng)建一個空函數(shù)f()
,并將其原型設(shè)置為父級構(gòu)造器,然后我們既可以用new F()
來創(chuàng)建一些不包含父對象屬性的對象,同時又可以從父對象prototype
中繼承一切了。
1.1 代碼實現(xiàn)
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {
return this.name;
}
function TwoDShape(){}
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.prototype.name = "2D Shape";
function Triangle(side, height){
this.side = side;
this.height = height;
}
var F = function(){};
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {
return this.side * this.height / 2;
}
var my = new Triangle(5, 10);
console.log(my.getArea()); //25
console.log(my.name); //Triangle
console.log(my.toString()); //Triangle
var supers = new Shape();
console.log(supers.name); //Shape 注意
1.2 代碼分析
從下圖中我們可以看出my
對象的結(jié)構(gòu)
那么這個時候toString()
方法查找分幾步呢?
- 在本類中查找
- 在
my
對象的my.[[Prototype]]
中找,此時該[[Prototype]]
對象為TwoDShape
的實例,里面只有一個name = "Triangele"
屬性。 - 在
my
對象的my.[[Prototype]].[[Prototype]]
中找,為Shape
的實例,同樣里面也只有一個name="2D Shape"
屬性 - 在
my.[[Prototype]].[[Prototype]].[[Prototype]]
中找,為Object
實例,此時,就已經(jīng)找到了toString()
方法。
二. 增加uber屬性,用于子對象訪問父對象
2.1 實現(xiàn)分析
uber
屬性的名字原本應(yīng)該是“superclass”
,但是這樣一來顯得JavaScript
中有了類的概念(在ES6
中引入了class
),或許該叫super
,但是super
是JavaScript
的保留字,因為改成uber
。
- 我們給構(gòu)造函數(shù)增加一個
uber
屬性,使其值為父類的原型Triangle.uber = TwoDShape.prototype;
- 如下圖所示,此時構(gòu)造函數(shù)
Triangle()
中,包含了uber
屬性
2.2 代碼實現(xiàn)
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {
var result = [];
if(this.constructor.uber){
result[result.length] = this.constructor.uber.toString();
}
result[result.length] = this.name;
return result.join(', ');
}
function TwoDShape(){}
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.uber = Shape.prototype; //增加uber屬性,指向父類的原型
TwoDShape.prototype.name = "2D Shape";
function Triangle(side, height){
this.side = side;
this.height = height;
}
var F = function(){};
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.uber = TwoDShape.prototype;
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {
return this.side * this.height / 2;
}
var my = new Triangle(5, 10);
console.log(my.getArea()); //25
console.log(my.name); //Triangle
console.log(my.toString()); //Shape, 2D Shape, Triangle
var supers = new Shape();
console.log(supers.name); //Shape
三. 將繼承封裝成extend()函數(shù)
- 為了代碼的封裝和重用,我們可以將實現(xiàn)繼承的部分抽取出來,定義一個
extend()
函數(shù)。
3.1 代碼實現(xiàn)
與extend()
相比,extend2()
顯得略遜一籌。因為這里執(zhí)行的是子對象原型的逐一拷貝,而非簡單的原型鏈查詢。所以我們必須要記住,這種方式僅適用于只包含基本數(shù)據(jù)類型的對象,所有的對象類型(包括函數(shù)和數(shù)組)都是不可復(fù)制的,因為他們只支持引用傳遞。
這里toString()
方法實際上是同一個函數(shù)對象。這里只是一個函數(shù)引用,函數(shù)本身并沒有再次被創(chuàng)建。
優(yōu)缺點:
-
extend2()
方法的效率要低于extend()
方法,主要是前者對部分原型屬性進(jìn)行了重建。 - 當(dāng)然了,這對于只包含基本數(shù)據(jù)類型的對象來說,是有好處的。因為這樣做能使屬性查找操作更多的停留在對象本身,從而減少了原型鏈上的查找。
3.1.1 臨時構(gòu)造器實現(xiàn)extend()
//定義extend函數(shù)
function extend(Child, Parent){
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
3.1.2 原型復(fù)制實現(xiàn)extend2()
function extend2(Child, Parent){
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p){
c[i] = p[i];
}
c.uber = p; //擴(kuò)展子類原型,增加一個uber屬性
}
3.2 代碼測試
3.2.1 測試extend()函數(shù)
- 利用上述
extend()
函數(shù)實現(xiàn)繼承。
//定義Shape類
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {
return this.name;
}
//定義TwoDShape類
function TwoDShape(){}
extend(TwoDShape, Shape);
TwoDShape.prototype.name = "2D Shape";
//定義Triangle類
function Triangle(side, height){
this.side = side;
this.height = height;
}
extend(Triangle, TwoDShape);
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {
return this.side * this.height / 2;
}
//測試
var my = new Triangle(5, 10);
console.log(my.getArea()); //25
console.log(my.name); //Triangle
console.log(my.toString()); // Triangle
var supers = new Shape();
console.log(supers.name); //Shape
3.2.1 測試extend2() 函數(shù)
//定義Shape類
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {
return this.name;
}
//定義TwoDShape類
function TwoDShape(){}
extend(TwoDShape, Shape);
//extend測試結(jié)果
var td = new TwoDShape();
console.log(td.name); //Shape
console.log(TwoDShape.prototype.name); //Shape
console.log(td.__proto__.name); //Shape
console.log(td.hasOwnProperty("name")); //false
console.log(td.__proto__.hasOwnProperty("name")); //false
//extend2測試結(jié)果
var td = new TwoDShape();
console.log(td.__proto__.hasOwnProperty("name")); //true
console.log(td.__proto__.hasOwnProperty("toString")); //true
console.log(td.__proto__.toString == Shape.prototype.toString); //true
四、實例對象原型繼承:以實例對象為原型創(chuàng)建新對象
4.1 功能分析
基于這種在對象之間直接構(gòu)建繼承關(guān)系的理念,我們可以創(chuàng)建一個object()
函數(shù),接受父對象,并返回一個以該對象為原型的新對象。這和Object.create()
類似。如下代碼所示:文章來源:http://www.zghlxwxcb.cn/news/detail-532800.html
function object(o){
var F = function(){};
F.prototype = o;
return new F();
}
如果需要訪問父類,則可加添加uber屬性。文章來源地址http://www.zghlxwxcb.cn/news/detail-532800.html
function object(o){
var n;
var F = function(){};
F.prototype = o;
n = new F();
n.uber = o;
return n;
}
4.2 功能測試
//定義Shape類
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString2 = function () {
return this.name;
}
var shapeObj = new Shape();
var twoDShape = object(shapeObj);
twoDShape.name = "HHH"
console.log(twoDShape.toString2()); //HHH
-
twoDShape
對象的結(jié)構(gòu)如下所示
到了這里,關(guān)于第4集丨JavaScript 使用原型(prototype)實現(xiàn)繼承——最佳實戰(zhàn)2的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!