javascript
JavaScript面向对象——深入理解默认的继承方式原型链
描述:
正如我們所了解,JavaScript中的每個函數(shù)中都有一個指向某一對象的prototype屬性。該函數(shù)被new操作符調用時會創(chuàng)建并返回一個對象,并且該對象中會有一個指向其原型對象的秘密鏈接,通過該秘密鏈接(__proto__),我們就可以在新建的對象中調用相關原型對象的方法和屬性。
而原型對象自身也具有對象固有的普遍特征,因此本身也包含了指向其原型的鏈接,由此就形成了一條鏈,即我們所說的原型鏈。
如下圖所示:在對象A的一系列屬性中,有一個叫__proto__的隱藏屬性,它指向了另外一個對象B。而B的__proto__屬性又指向了對象C,以此類推,直至鏈條末端的Object對象,該對象是JavaScript的最高級對象,語言中所有對象都必須繼承自它。
意義:
在某個屬性不存在對象A中,而存在對象B中時,依然可以將它當做A的屬性來訪問,進而實現(xiàn)代碼的重用。
舉例說明:
function Shape() {this.name = 'Shape';this.toString = function() {return this.name;};}function TwoDShape() {this.name = '2D shape';}function Triangle(side, height) {this.name = 'Triangle';this.side = side;this.height = height;this.getArea = function() {return this.side * this.height/2;}}// 繼承實現(xiàn)TwoDShape.prototype = new Shape();Triangle.prototype = new TwoDShape();// 當我們對對象的prototype屬性進行完全替換時,需要重置這些對象的constructor屬性。TwoDShape.prototype.constructor = TwoDShape;Triangle.prototype.constructor = Triangle;// 驗證是否已經實現(xiàn)繼承var my = new Triangle(5, 10);console.log(my.toString());// 運行結果為:Triangle 解釋:
當我們在用Triangle()構造器創(chuàng)建的實例調用構造器不存在的toString()方法時,JavaScript引擎究竟做了什么?
? ? ? ? ①:遍歷my對象中的所有屬性,但沒有找到一個叫做toString()的方法。
②:查看my.__proto__所指向的對象,該對象是繼承關系構建過程中由new? TwoDShape()所創(chuàng)建的實體。
? ? ? ? ③:JavaScript引擎在遍歷TwoShape實體的過程中依然不會找到toString方法,這時候,__proto__屬性所指向的實體是new Shape()所創(chuàng)建的實體。
④:在new Shape()創(chuàng)建的實體對象中找到toString()方法。
⑤:toString()方法在my對象中調用,并且其this指向my。
繼承后的一些驗證:
①:my的構造函數(shù)是:my.constructor === Triangle; 運行結果:ture.
②:my對象同時是上述三個構造器的實例,以下運行結果均為true,即:my同時是三個構造器的實例
my instanceof Shape;? my instanceOf TwoDShape; my instanceOf Triangle;?
擴展:將共享屬性遷移到原型中去
背景描述:當我們用某一個構造器創(chuàng)建對象時,其屬性就會被添加到this中去。并且當被添加的屬性實際上不會隨著實體改變時,這種做法就會顯得很沒有效率。比如在上面的示例中,Shape()構造器是這樣定義的:
function Shape(){this.name = 'Shape'; } 這種實現(xiàn)即意味著我們用new Shape()創(chuàng)建的每個實體都會擁有一個全新的name屬性,并在內存中擁有自己獨立的存儲空間。
解決辦法:將name屬性移到原型上去,這樣一來,所有實體就可以共享這個屬性:
function Shape(){}; Shape.prototype.name = 'Shape'; 將name移到prototype上后,用new Shape()創(chuàng)建對象時,name屬性就不再是新對象的私有屬性了,而是被添加到了該對象的原型中,這樣做的前提是該屬性是不可變的,對象的共享方法就非常適合這種共享形式。
下面對以上的例子進行改造,把符合條件的方法和屬性移到原型中去。
// constructorfunction Shape() {}// argument prototypeShape.prototype.name = 'Shape';Shape.prototype.toString = function() {return this.name;};// another constructorfunction TwoDShape() {}// take care of inheritanceTwoDShape.prototype = new Shape();TwoDShape.prototype.constructor = TwoDShape;//argument prototypeTwoDShape.prototype.name = '2D shape';// another constructorfunction Triangle(side, height) {this.side = side;this.height = height;}// take care of inheritanceTriangle.prototype = new TwoDShape();Triangle.prototype.constructor = Triangle;// argument prototypeTriangle.prototype.name = 'Triangle';Triangle.prototype.getArea = function() {return this.side * this.height/2;}// 驗證是否已經實現(xiàn)繼承var my = new Triangle(5, 10);console.log(my.toString());// 運行結果為:Triangle
在調用my.toString()時,主要區(qū)別是查找操作將更多地發(fā)生在Shape.prototype中,而不再需要像前面示例中那樣,到由new Shape()所創(chuàng)建的實體對象中查找。
總結
以上是生活随笔為你收集整理的JavaScript面向对象——深入理解默认的继承方式原型链的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国务院发展研究中心原副主任王一鸣:202
- 下一篇: office专业增强版2021激活密钥2