JavaScript系列之對象的繼承

這篇文章的重點講的是關於JavaScript中對象的繼承,這是面向對象編程很重要的一個方面。A 對象繼承自B 對象,就能直接擁有 B 對象的全部屬性和方法,做用是避免了代碼的複用,節省代碼量。javascript

而大部分面向對象的編程語言,都是經過「類」(class)來實現對象的繼承。傳統上,JavaScript 語言的繼承不經過 class(ES6 引入了 class 語法,基於 class 的繼承不在這裏介紹),而是經過「原型對象」(prototype)實現,所以,這裏把「繼承」着重拿出來說,就爲了體現這個不一樣。java

複習原型/原型鏈

JavaScript的繼承得靠原型/原型鏈來實現,固然原型/原型鏈不是這篇文章的重點,以前的文章也已經介紹過了,因此這裏咱們來複習一下便可。git

其實原型的概念很簡單,我以爲用如下簡短的幾句話就能歸納徹底了:github

  • 全部對象都有一個屬性 __proto__ 指向一個對象,也就是原型
  • 每一個對象的原型均可以經過 constructor 找到構造函數,構造函數也能夠經過 prototype 找到原型
  • 全部函數均可以經過 __proto__ 找到 Function 對象
  • 全部對象均可以經過 __proto__ 找到 Object 對象
  • 對象之間經過 __proto__ 鏈接起來,這樣稱之爲原型鏈。當前對象上不存在的屬性能夠經過原型鏈一層層往上查找,直到頂層 Object 對象,頂層 Object 對象最終指向null

我以爲原型中最重要的內容其實就這些了,不必看太多關於原型的文章,到頭來只是會愈來愈糊塗,若是硬要推薦原型參閱資料的話,《JavaScript高級程序設計》這本書,當之無愧!編程

繼承

經過上面的介紹,咱們知道JavaScript中的繼承是經過原型/原型鏈來體現的,先看幾行代碼:app

function Foo() { }
var f1 = new Foo();

f1.a = 10;

Foo.prototype.a = 100;
Foo.prototype.b = 200;

console.log(f1.a);  // 10
console.log(f1.b);  // 200
console.log(f1.c);  // undefined
複製代碼

以上代碼中,f1Foo函數經過new構造出來的對象,f1.af1對象的基本屬性,而f1.b是從Foo.prototype繼承獲得的,由於f1.__proto__指向的是Foo.prototype編程語言

這裏有一個重要的規則:當訪問一個對象的屬性時,首先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈往上找,看是否在鏈上,有的話就能繼承這一屬性,若是沒有,就返回undefined,這就是原型鏈,又複習一遍咯。函數

看圖直觀一些,這裏仍是採用反覆用的原型/原型鏈經典圖:ui

上圖中,訪問f1.a時,f1的基本屬性中有a,則不會繼續沿着__proto__找,直接讀出基本屬性a的值;而訪問f1.b時,f1的基本屬性中沒有b,因而沿着__proto__找到了Foo.prototype.bspa

那咱們如何在實際應用中區分一個屬性究竟是基本屬性仍是在原型鏈中的公有屬性呢?這裏能夠好好利用一下這個屬性——hasOwnProperty,一下就能測出誰是基本屬性,當在for…in…循環中,須要額外注意。

hasOwnProperty() 方法會返回一個布爾值,指示對象自身屬性中是否具備指定的屬性,因此上圖中,右邊的只打印出a一個值,由於b是在Foo.prototype上的,不屬於自身屬性。

f1的這個hasOwnProperty()方法,f1自身沒有,Foo.prototype中也沒有,又是從何而來呢?

仍是引用那張原型/原型鏈經典圖,從圖上來看,hasOwnProperty()方法是從Object.prototype中繼承來的:

因此對象的原型鏈是沿着__proto__這條線走的,所以在查找f1.hasOwnProperty屬性時,由於自身沒有這一屬性,就會沿着原型鏈一直查找到Object.prototype上有這一屬性,若是沒找到則返回undefined

因爲全部的對象的原型鏈都會最終找到Object.prototype,所以全部的對象都會有Object.prototype中的方法,好比toStringvalueOf等這些公有屬性,這就是所謂的「繼承」。

固然這只是一個例子,你能夠自定義函數和對象來實現本身的繼承,這一點後續文章會有專門介紹。

這裏再說一個函數的例子來加深理解吧。

咱們都知道每一個函數都有applycall方法,都有lengthargumentscaller等屬性。爲何每一個函數都有?這確定是「繼承」來的。在介紹instanceof這篇文章中也提到,函數是由Function函數建立,都繼承自Function.prototype中的方法。不信能夠在Chrome中打印出:

直接能夠看到了吧,有calllength等這些屬性。

那怎麼還有hasOwnProperty呢?上圖中hasOwnProperty右邊顯示Object,表明Function.prototype繼承自Object.prototype。有疑問能夠再看看這張原型/原型鏈經典大圖,Function.prototype.__proto__會指向Object.prototype

最後仍是那句話,當你徹底搞懂上面這張圖的時候,就是你掌握原型、原型鏈的時候了。

若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!

相關文章
相關標籤/搜索