摘要:操作符構造步驟有三步構造一個類的實例這個實例是一個空對象,并且他的屬性指向構造函數的原型。不優化原生的或自定義的作為構造函數是及其不高效的。
原文地址:Javascript – How Prototypal Inheritance really works
在網上可以看到各種關于Javascript原型繼承的文章,但Javascript規范中只提供了new操作符這一種實現原型繼承的方法。因此網上大多數的文章是具有迷惑性的,很混亂。這篇文章會讓你清晰的認識到什么是真正的原型繼承?并且怎么樣使用它?
原型繼承的定義:你會經常看到如下關于原型繼承的定義:
訪問一個對象屬性的時候,Javascript會沿著原型鏈向上尋找,直到找到該屬性。
Javascript中大多數實現方式都是使用__proto__來指定原型鏈中下一個被訪問的對象,接下來會揭示__proto__與prototype之間的區別。
注意:不要在你的代碼中使用__proto__,文中使用它僅僅是為了更好的解釋Javascript繼承是如何工作的。
下面的代碼展示了Javascript引擎如何檢索對象的屬性(偽代碼,僅為了方便理解)
function getProperty(obj, prop) { if (obj.hasOwnProperty(prop)){ return obj[prop]; }else if (obj.__proto__ !== null){ return getProperty(obj.__proto__, prop); }else{ return undefined; } }
舉個例子:一個二維的點。擁有x坐標屬性,y坐標屬性和一個print方法。
用書面語言來表示該定義就是:我們定義了一個有三個屬性的對象:x,y和print。為了構造一個新點,我們只需要創建一個對象并將他的__proto__屬性指向Point。
var Point = { x: 0, y: 0, print: function () { console.log(this.x, this.y); } }; var p = {x: 10, y: 20, __proto__: Point}; p.print(); // 10 20奇怪的原型繼承:
怪異之處在于解釋原型繼承的人給出的例子往往與他們的定義不相符合,他們給出的代碼通常如下所示:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype = { print: function () { console.log(this.x, this.y); } }; var p = new Point(10, 20); p.print(); // 10 20
上面所例舉的代碼跟原型繼承完全不相關,Point是構造函數,它有一個prototype屬性,使用了new操作符,但是然后呢?
new是如何工作的:Brendan Eich 想讓javascript像Java,C++這些傳統的面向對象語言一樣,用new操作類直接構造一個實例,所以他給Javascript也添了new操作符。
C++中有構造函數,用來初始化實例的屬性。因此,new操作符操作的對象必須是函數。
我們需要把對象的方法掛載到某個地方,由于我們使用的是原型語言,我們把他放在函數的原型屬性里。
new操作符構造步驟有三步:構造一個類的實例:這個實例是一個空對象,并且他的__proto__屬性指向構造函數的原型。
初始化實例:構造函數被調用,并將this指向這個實例。
返回實例對象。
現在我們了解了new構造的過程,我們在Javascript中實現它:
function New (f) { var n = { "__proto__": f.prototype }; return function () { f.apply(n, arguments); return n; }; }
舉一個小例子:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype = { print: function () { console.log(this.x, this.y); } }; var p1 = new Point(10, 20); p1.print(); // 10 20 console.log(p1 instanceof Point); // true var p2 = New (Point)(10, 20); p2.print(); // 10 20 console.log(p2 instanceof Point); // trueJavascript中真正的原型繼承:
Javascript規范只定義了new操作符的工作流程,Douglas Crockford發現了一種利用new實現原型繼承的新方法,他寫的Object.create函數。
Object.create = function (parent) { function F() {} F.prototype = parent; return new F(); };
看起來很奇怪,但實際上很簡潔。他只創建了一個新的對象,原型你可以隨意設置。如果允許使用__proto__的話,這個例子可以這樣寫:
Object.create = function (parent) { return { "__proto__": parent }; };
下面這個Point例子才是真正的原型繼承。
var Point = { x: 0, y: 0, print: function () { console.log(this.x, this.y); } }; var p = Object.create(Point); p.x = 10; p.y = 20; p.print(); // 10 20結論:
我們了解了原型繼承并用一種具體的方法實現了它。但這樣寫有一些缺點:
不標準:__proto__不是標準并不贊成使用, 并且原生的 Object.create 和 Douglas Crockford的實現不等同。
不優化:Object.create (原生的或自定義的)作為構造函數是及其不高效的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78942.html
摘要:然而,對象字面量不是真正意義上的哈希映射,如果使用不當可能會構成潛在的隱患。空對象創建一個真正的哈希映射的秘訣就是避免原型,及其帶來的包袱。在此之前,甚至之后,你應該使用空對象滿足你所有的基本哈希映射需求。 在JavaScript中存儲鍵值對的一個簡單常見的方法是使用對象字面量。然而,對象字面量不是真正意義上的哈希映射,如果使用不當可能會構成潛在的隱患。雖然目前JavaScrip...
摘要:使用構造函數的原型繼承相比使用原型的原型繼承更加復雜,我們先看看使用原型的原型繼承上面的代碼很容易理解。相反的,使用構造函數的原型繼承像下面這樣當然,構造函數的方式更簡單。 五天之前我寫了一個關于ES6標準中Class的文章。在里面我介紹了如何用現有的Javascript來模擬類并且介紹了ES6中類的用法,其實它只是一個語法糖。感謝Om Shakar以及Javascript Room中...
摘要:避免脆弱的基類問題。紅牌警告沒有提到上述任何問題。單向數據流意味著模型是單一的事實來源。單向數據流是確定性的,而雙向綁定可能導致更難以遵循和理解的副作用。原文地址 1. 你能說出兩種對 JavaScript 應用開發者而言的編程范式嗎? 希望聽到: 2. 什么是函數編程? 希望聽到: 3. 類繼承和原型繼承的不同? 希望聽到 4. 函數式編程和面向對象編程的優缺點? ...
摘要:忍者級別的函數操作對于什么是匿名函數,這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數是一個很重要且具有邏輯性的特性。通常,匿名函數的使用情況是創建一個供以后使用的函數。 JS 中的遞歸 遞歸, 遞歸基礎, 斐波那契數列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執行機制 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果...
摘要:函數式編程前端掘金引言面向對象編程一直以來都是中的主導范式。函數式編程是一種強調減少對程序外部狀態產生改變的方式。 JavaScript 函數式編程 - 前端 - 掘金引言 面向對象編程一直以來都是JavaScript中的主導范式。JavaScript作為一門多范式編程語言,然而,近幾年,函數式編程越來越多得受到開發者的青睞。函數式編程是一種強調減少對程序外部狀態產生改變的方式。因此,...
閱讀 1684·2021-11-23 09:51
閱讀 3174·2021-09-26 10:21
閱讀 798·2021-09-09 09:32
閱讀 881·2019-08-29 16:06
閱讀 3308·2019-08-26 13:36
閱讀 772·2019-08-26 10:56
閱讀 2564·2019-08-26 10:44
閱讀 1143·2019-08-23 14:04