JavaScript中的原型繼承基礎學習教程,javascript原型

來源:互聯網
上載者:User

JavaScript中的原型繼承基礎學習教程,javascript原型

大多數程式設計語言中,都有類和對象,一個類可以繼承其他類。
在JavaScript中,繼承是基於原型的(prototype-based),這意味著JavaScript中沒有類,取而代之的是一個對象繼承另一個對象。:)

1. 繼承, the proto
在JavaScript中,當一個對象rabbit繼承另一了對象animal時,這意味著rabbit對象中將會有一個特殊的屬性:rabbit.__proto__ = animal;
當訪問rabbit對象時,如果解譯器在rabbit中不能找到屬性,那麼它會順著__proto__鏈往上在animal對象中尋找
栗子中的__proto__屬性僅在Chrome和FireFox中可以訪問,請看一個栗子:

var animal = { eats: true }var rabbit = { jumps: true }rabbit.__proto__ = animal // inheritalert(rabbit.eats) // true

eats屬性是從animal對象中訪問的。
如果在rabbit對象中已經發現了屬性,那麼就不會去檢查proto屬性啦。
再來一個栗子,當子類中也有eats屬性時,父類中的就不會訪問了。

var animal = { eats: true }var fedUpRabbit = { eats: false}fedUpRabbit.__proto__ = animal alert(fedUpRabbit.eats) // false

你也可以在animal中添加一個函數,那麼在rabbit中也可以訪問了。

var animal = { eat: function() {  alert( "I'm full" )  this.full = true }}var rabbit = { jump: function() { /* something */ }}rabbit.__proto__ = animal 

(1)rabbit.eat():
rabbit.eat()函數以如下兩步執行:
首先,解譯器尋找rabbit.eat,rabbit中沒有eat函數,那麼它就順著rabbit.__proto__往上找,在animal中找到了。
函數以this = rabbit運行。this值與__proto__屬性完全無關。
因此,this.full = true在rabbit中:
看看這裡我們有什麼新發現,一個對象調用了父類函數,但是this還是指向對象本身,這就是繼承。
被__proto__引用的對象稱作是原型(prototype),animal是rabbit的原型(譯者註:這就是rabbit的__proto__屬性引用了animal 的prototype屬性)
(2)讀時尋找,不是寫時
當讀取一個對象時,比如this.prop,解譯器會在它的原型中尋找屬性。
當設定一個屬性值時,比如this.prop = value,那麼就沒有理由去尋找了,這個屬性(prop)會被直接添加到這個對象中(這裡是this)。delete obj.prop也類似,它只刪除對象本身的屬性,原型中的屬性保持原封不動。
(3)關於proto
如果你在閱讀指南,這裡我們叫的__proto__,在指南中表示為[[Prototype]]。雙方括弧是很重要的,因為有另一個屬性叫做prototype。

2. Object.create, Object.getPrototypeOf
__proto__是一個非標準的屬性,由Chrome/FireFox提供訪問,在其他的瀏覽器中保持不可見。
所有的現代瀏覽器除了Opera(IE > 9)支援兩個標準的函數來處理原型問題:

Object.ceate(prop[,props])

用給定了proto建立一個Null 物件:

var animal = { eats: true }rabbit = Object.create(animal)alert(rabbit.eats) // true

上面代碼建立了一個空rabbit對象,並且原型設定為animal
rabbit對象建立好以後,我們可以往裡添加屬性了:

var animal = { eats: true }rabbit = Object.create(animal)rabbit.jumps = true

Object.creat函數的第二個參數props是可選的,它允許像新對象設定屬性。這裡就省略了,因為我們關係的繼承。
(1)Object.getPrototypeOf(obj)
返回obj.__proto__的值。這個函數是標準的,可以在不能直接存取__proto__屬性的瀏覽器中使用了。

var animal = { eats: true}rabbit = Object.create(animal)alert( Object.getPrototypeOf(rabbit) === animal ) // true

現代瀏覽器允許讀取__proto__屬性值,但是不能設定。

3. The prototype
有一些好的跨瀏覽器的方式設定__proto__屬性,這將會使用構造器函數(constructor functions)。記住!任何函數建立一個對象都是通過new關鍵字的。
一個栗子:

function Rabbit(name) { this.name = name}var rabbit = new Rabbit('John')alert(rabbit.name) // John

new操作將原型的屬性設定到rabbit對象的的__proto__屬性中了。
讓我們來看看它的原理,例如,new Rabbit 對象,而Rabbit是繼承animal 的。

var animal = { eats: true }function Rabbit(name) { this.name = name}Rabbit.prototype = animalvar rabbit = new Rabbit('John')alert( rabbit.eats ) // true, because rabbit.__proto__ == animal

Rabbit.prototype = animal 字面量意味著:對所有由new Rabbit建立的對象設__proto__ = animal

4. 跨瀏覽器 Object.create(proto)
Object.create(prop)函數功能的強大的,因為它允許從給定的對象直接繼承。它可以由如下代碼類比:

function inherit(proto) { function F() {} F.prototype = proto return new F}

inherit(animal) 與Object.create(animal)是完全等同的,返回一個空的對象,並且object.__proto__ = animal。
一個栗子:

var animal = { eats: true }var rabbit = inherit(animal)alert(rabbit.eats) // truealert(rabbit.hasOwnProperty('eats')) // false, from prototype

來看一下它的原理是什麼:

function inherit(proto) { function F() {}   // (1) F.prototype = proto // (2) return new F()   // (3)}

(1) 建立了一個新函數,函數沒有向this設定任何屬性,以此`new F` 會建立一個Null 物件。
(2) `F.prototype`被設定為proto
(3) `new` F建立了一個Null 物件,對象的`__proto__ = F.prototype`
(4) Bingo! 我們得到了一個繼承`proto`的Null 物件
這個函數廣泛適用於各種庫和架構之中。
你的函數接受了一個帶有options 的對象

/* options contains menu settings: width, height etc */function Menu(options) { // ...}你想設定某些optionsfunction Menu(options) { options.width = options.width || 300 // set default value // ...}

。。。但是改變參數值可能會產生一些錯誤的結果,因為options可能會在外部代碼中使用。一個解決辦法就是複製options對象,複製所有的屬性到一個新的對象中,在新對象中修改,
怎樣用繼承來解決這個問題呢? P.S. options可以添加設設定,但是不能被刪除。
Solution
你可以繼承options,並且在它的子類的中修改或者添加新的屬性。

function inherit(proto) { function F() {} F.prototype = proto return new F}function Menu(options) { var opts = inherit(options) opts.width = opts.width || 300 // ...}

所有的操作只在子物件中有效,當Menu方法結束時,外部代碼仍然可以使用沒有修改的過的options對象。delete操作在這裡非常重要,如果width是一個prototype中的屬性,delete opts.width不會產生任何作用

5. hasOwnProperty
所有的對象都有hasOwnProperty函數,它可以用來檢測一個屬性是否對象自身還是屬於原型
一個栗子:

function Rabbit(name) { this.name = name}Rabbit.prototype = { eats: true }var rabbit = new Rabbit('John')alert( rabbit.hasOwnProperty('eats') ) // false, in prototypealert( rabbit.hasOwnProperty('name') ) // true, in object

6. Looping with/without inherited properties
for..in迴圈輸出一個對象的所有屬性,包括自身的和原型的。

function Rabbit(name) { this.name = name}Rabbit.prototype = { eats: true }var rabbit = new Rabbit('John')for(var p in rabbit) { alert (p + " = " + rabbit[p]) // outputs both "name" and "eats"}

用hasOwnProperty可以過濾得到屬於對象自己的屬性:

function Rabbit(name) { this.name = name}Rabbit.prototype = { eats: true }var rabbit = new Rabbit('John')for(var p in rabbit) { if (!rabbit.hasOwnProperty(p)) continue // filter out "eats" alert (p + " = " + rabbit[p]) // outputs only "name"}

7. Summary
JavaScript是通過一個特殊的屬性proto來實現繼承的
當訪問一個對象的屬性時,如果解譯器不能在對象中找到,它就會去對象的原型中繼續尋找 對函數屬性來說,this指向這個對象,而不是它的原型。
賦值obj.prop = value, 刪除delete obj.prop
管理proto:
Chrome和FireFox可以直接存取對象的__proto__屬性,大多數現代瀏覽器支援用Object.getPrototypeOf(obj)唯讀訪問。
Object.create(proto) 可以用給定的proto產生空的子物件,或者通過如下代碼達到相同的功能:

function inherit(proto) {   function F() {}      F.prototype = proto   return new F()    }

其他方法:
for..in迴圈輸出一個對象的所有屬性(包括自身的和原型的)和對象的原型鏈。
如果一個屬性prop屬於對象obj那麼obj.hasOwnProperty(prop)返回true,否則返回false。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.