JavaScript 之 原型及原型鏈

對象【回顧】

經過字面量建立對象

//經過字面量建立對象
var obj1 = {
        name:'Jack',
        age: 18
      }

經過系統自帶的構造函數構造對象

// 經過系統自帶的構造函數構造對象
// 其餘構造函數有:new Array(),new Number,new Date(),new Boolean()
 var obj2 = new Object();
 obj2.name = 'Jack';
 obj2.age = 18;

經過自定義函數構造對象

// 經過自定義函數構造對象
// 爲了和普通函數區別,構造函數命名首字母要大寫(帕斯卡命名法)
    function Obj3(name,age) {
      this.name = name;
      this.age = age;
      this.sayHi = function () {
        alert(this.name)
      };
    }
    var dx1 = new Obj3('Jack', 18);
    var dx2 = new Obj3('Brush', 18);

擴展之new關鍵字

  首先,我先說下自定義構造函數建立對象的基本流程,使用new和不適用new差異很大,不用new的話,則Obj3(name)就做爲一個普通的函數執行(只是函數名的首字母大寫了),沒有返回值,那麼默認返回的是undefined,而用new關鍵字後,JS就會把該函數做爲一個構造函數看待,通過一系列的隱式操做,返回值是一個對象了。函數

  new關鍵字的「隱式操做」:this

    ①在內存中建立一個空的Object類型的對象----看不到spa

    ②讓this關鍵字指向這個空的對象(將構造函數的做用域賦給新對象)----看不到prototype

// ① ② 的執行過程
var this = Object.creat(Obj3.prototype);
// this 指向 Obj3.prototype(後面講到)
// 能夠看出構造函數建立對象的最根本原理是借用Object.create()方法來實現的,只不過被封裝功能化了

    在前面的例子中,dx1和dx2分別保存着Obj3的一個不一樣的實例。這倆個對象都有有一個constructor(構造函數)的屬性,該屬性指向Obj3,以下:設計

console.log(dx1.constructor  == Obj3) // true
console.log(dx2.constructor  == Obj3) // true  

    對象的constructor屬性最初用來表示對象類型的。可是,檢測對象類型,仍是instanceof操做符要更可靠一些-----用來檢測一個對象在其原型鏈中,是否存在一個構造函數的prototype屬性。指針

    咱們在這個例子中建立的全部對象即便Object的實例,同時也是Obj3的實例,這一點能夠經過instanceof操做符能夠獲得驗證。code

    console.log(dx1 instanceof Obj3);  // true
    console.log(dx1 instanceof Object);  // true
    console.log(dx2 instanceof Obj3);  // true
    console.log(dx2 instanceof Object);  // true
 // dx1 和 dx2 之因此同時是Object的實例,是由於全部的對象均繼承自Object

  ③經過this給這個對象添加屬性和方法---看得見對象

// this.name = 'Jack';
   this.age = 18;

  ④將這個對象返回給用new關鍵字調用的構造函數Obj3(name)的調用者(dx1)blog

return this;

注:Object在JS中,全部不一樣類型的對象,都直接或間接的集成於它。繼承

原型對象和原型鏈

原型

  咱們所建立的每個函數都有一個 prototype (原型)的屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途包含能夠由特定類型的全部實例共享的屬性和方法。

  那麼prototype就能夠經過調用構造函數而建立的那個對象實例的原型對象。使用原型對象的好處是可讓全部對象實例共享它所包含的屬性和方法。換句話說,沒必要在構造函數中第一對象實例的信息,而是能夠將這些信息直接添加到原型對象中去,那麼上面的Obj3()函數就能夠寫成這樣:

 

    function Obj3(name,age) {
      this.name = name;
      this.age = age;
    }
    Obj3.prototype.sayHi = function () {
      alert(this.name)
    }
    var dx1 = new Obj3('Jack', 18);
    var dx2 = new Obj3('Brush', 18); 

  在默認狀況下,全部原型對象都會自動得到一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針。就拿上面的例子來講, Obj3.prototype.constructor指向Obj3。而經過這個構造函數,咱們還能夠繼續爲原型對象添加其餘屬性和方法。

  當構造函數建立一個新實例後,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型方法。在每個對象都支持一個屬性:"_ proto _"。

  咱們能夠經過isPrototype()方法來肯定對象之間是否存在這個關係。從本質上講,若是[Prototype]指向調用isPrototype()方法的對象(Obj3.prototype),那麼這個方法就返回true。(參考《JavaScript高級程序設計(第3版)》)

  使用hasOwnProperty()能夠檢測一個屬性是否存在在實例中,仍是存在在原型中。這個方法(從Object繼承來的)只是給定屬性存在在對象實例中,纔會返回true;存在在原型中,返回false。

原型對象的原型

  全部的原型應用類型(Object、Array、String)都在其構造函數的原型上定義了方法。在Array.prototype中能夠找到sort()方法;在String.prototype中能夠找到substring()方法,以下:

 console.log(Array.prototype.sort); // ƒ sort() { [native code] }
 console.log(String.prototype.substring); // ƒ substring() { [native code] }

原型鏈

  原型和實例的關係:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。若是,咱們讓這個原型對象等於另外一個類型的實例,顯然,此時的原型對象包含一個指向另外一個原型的指針,相應的,另外一個原型中野包含着指向另另外一個類型的實例,那麼,層層遞進,就構成了實例與原型的鏈條,這就是原型鏈的基本概念。

  咱們知道,全部引用類型默認都繼承了Object,而這個繼承也是經過原型鏈實現的。全部函數的默認原型都是Object的實例,所以默認原型都包含一個內部指針,指向Object.prototype。這正是全部自定義類型都會繼承toString()、valueOf()等默認方式的根本緣由。

相關文章
相關標籤/搜索