【Javascript】Javascript原型與繼承

一切都是對象!javascript

  如下的四種(undefined, number, string, boolean)屬於簡單的值類型,不是對象。剩下的幾種狀況——函數、數組、對象、null、new Number(10)都是對象。他們都是引用類型。java

  判斷一個變量是否是對象很是簡單。值類型的類型判斷用typeof,引用類型的類型判斷用instanceof。數組

var fn = function () { };
console.log(fn instanceof Object);  // true

  java中的對象都是new一個class出來的,並且裏面有字段、屬性、方法,規定的很是嚴格。可是javascript就比較隨意了,數組是對象,函數是對象,對象仍是對象。對象裏面的一切都是屬性,只有屬性,沒有方法。那麼這樣方法如何表示呢?方法也是一種屬性。由於它的屬性表示爲鍵值對的形式。並且javascript中的對象能夠任意的擴展屬性,沒有class的約束。函數

  在javascript中,一切(引用類型)都是對象,對象是屬性的集合。spa


一切對象都是經過函數建立的!prototype

譬如:對象

var obj = {a: 1, b: 2}, 
var mm = {12, 'X', false} 

實質上的本質爲:blog

var obj = new Object();
  obj.a = 1;
  obj.b = 2;

var mm = new Array();
  mm[0] = 12;
  mm[1] = X;
  mm[2] = false;

其中的Object和Array都是函數。繼承

上面的寫法實際上是下面寫法的一種"便捷方式"。 ip


 原型 prototype

  每個函數都有一個默認屬性「prototype」。

  前面說過,每個函數都是一種對象,都是屬性的集合,你能夠對函數進行自定義屬性。Javascrpit默認給每個函數一個默認屬性---prototype。

  這個prototype的屬性值是一個對象(屬性的集合),默認的只有一個叫作constructor的屬性,指向這個函數自己。

  

  固然,prototype做爲一個對象,屬性的集合,也能夠自定義的增長許多屬性。

function Fn() { }
Fn.prototype.name = 'Tom';
Fn.prototype.getYear = function () {
       return 1988;
 };

隱式原型 _proto_  

  每個對象都有一個隱藏的屬性「_proto_」,這個屬性引用了建立這個對象的函數的prototype。即:fn.__proto__ === Fn.prototype.

  這裏的"__proto__"稱之爲「隱式原型」。

  

  在上面的這張圖中能夠看出來,自定義函數Foo.__proto__指向Function.prototype,Object.__proto__指向Function.prototype。Function也是一個函數,函數是一種對象,也有__proto__屬性。既然是函數,那麼它必定是被Function建立。因此——Function是被自身建立的。因此它的__proto__指向了自身的Prototype。

  每一個對象都有一個__proto__屬性,指向建立該對象的函數的prototype。而每一個函數的prototype也是一個對象,他一樣有一個_proto_屬性,其本質上與var obj = {} 是同樣的,都是被Object建立,因此它的__proto__指向的就是Object.prototype。

  而Object.prototype的_protype屬性比較特殊,它指向的是一個null值。

typeof & instanceof

  對於值類型,你能夠經過typeof判斷,string/number/boolean都很清楚,可是typeof在判斷到引用類型的時候,返回值只有object/function,你不知道它究竟是一個object對象,仍是數組,仍是new Number等等,這個時候就須要用到instanceof。

  

  Instanceof運算符的第一個變量是一個對象,暫時稱爲A;第二個變量通常是一個函數,暫時稱爲B。

  Instanceof的判斷隊則是:沿着A的__proto__這條線來找,同時沿着B的prototype這條線來找,若是兩條線能找到同一個引用,即同一個對象,那麼就返回true。若是找到終點還未重合,則返回false。

  好比:

console.log(Object instanceof Function);
console.log(Function instanceof Object);
console.log(Function instanceof Function);

  結果都爲true。

  而如何理解instanceof的做用呢?

  其實instanceof表示的就是一種繼承關係,或者原型鏈的結構。


 繼承 & 原型鏈

  「繼承」是經常使用面嚮對象語言中最基本的概念,可是java中的繼承與javascript中的繼承又徹底是兩回事兒。

  javascript中的繼承是經過原型鏈來體現的。

  以下面的代碼:

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

f.name = 'Cat'; Foo.prototype.name = 'Tom'; Foo.prototype.sex = 'Man' console.log(f.name); console.log(f.sex);

  結果:

  訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找,這就是原型鏈。

  在上例中,訪問f.sex時,f的基本屬性中沒有sex,因而沿着__proto__找到了Foo.prototype.sex。

  咱們在實際應用中如何區分一個屬性究竟是基本的仍是從原型中找到的呢?答案是——hasOwnProperty。譬如: if (f.hasOwnProperty(X)){}。

  而問題又出來了,在f中是沒有hasOwnProperty這個方法的,那這個方法是從哪裏來的呢?

  其實他是從Object.prototype中獲得的。對象的原型鏈是沿着__proto__這條線走的,所以在查找f.hasOwnProperty屬性時,就會順着原型鏈一直查找到Object.prototype。

  因爲全部的對象的原型鏈都會找到Object.prototype,所以全部的對象都會有Object.prototype的方法。這就是所謂的「繼承」。


ps:

  若是繼承獲得的方法感受不合適,你能夠本身進行修改。

  若是繼承沒法知足須要,還能夠本身添加方法。不過若是你要添加內置方法的原型屬性,最好作一步判斷,若是該屬性不存在,則添加。若是原本就存在,就不必再添加了。 

相關文章
相關標籤/搜索