js基礎篇——原型與原型鏈的詳細理解

  js中的對象分爲兩種:普通對象object和函數對象function。html

    function fn1(){};
    var fn2 = function(){};
    var fn3 = new Function();
    var fn4 = Function();

    var obj1 = new fn1();
    var obj2 = {};
    var obj3 = new Object();
    var obj4 = Object();

    console.log(typeof fn1);//function
    console.log(typeof fn2);//function
    console.log(typeof fn3);//function
    console.log(typeof fn4);//function

    console.log(typeof obj1);//object
    console.log(typeof obj2);//object
    console.log(typeof obj3);//object
    console.log(typeof obj4);//object

  還有就是全部的構建函數好比Function、Object、Number等等都是函數對象,這個共知的。chrome

    //全部的構建函數都是function類型的
    console.log(typeof Object);//function
    console.log(typeof Function);//function
    console.log(typeof Number);//function

  全部的這類構建函數使用new或者直接調用方式都能構建出一個新的數據類型。可是構建出來的數據有區別的函數

    var o = new Object(),
    o1 = Object();
    console.log(o == o1);//false
    console.log(o === o1);//false

    var f = new Function(),
    f1 = Function();
    console.log(f == f1);//false
    console.log(f === f1);//false

    
    var a = new Array(),
    a1 = Array();
    console.log(a == a1);//false
    console.log(a === a1);//false

    var n = new Number(),
    n1 = Number();
    console.log(n == n1);//true
    console.log(n === n1);//false

    var s = new String(),
    s1 = String();
    console.log(s == s1);//true
    console.log(s === s1);//false

    var b = new Boolean(),
    b1 = Boolean();
    console.log(b == b1);//true
    console.log(b === b1);//false


    //數據類型null/undefined是沒有構造函數的

  上面的例子中Object/Function/Array的對比結果都好說,由於他們構建出來的都是新的對象,對象比較是要比較根源(數據是不是同一個)。不管是使用new仍是直接調用生成一個新的對象是要開闢新的空間存儲的,不會和任何一個對象相等。spa

  可是對於數值類型,比較符「==」只是比較值相等,比較符"==="除了比較值之外還要比較數據類型。prototype

  那麼構建數值類型比較爲何呈現上面的樣子?指針

  咱們以Number爲例。實際上new Number()構建出來的變量n是一個特殊的對象,chrome上的展現以下code

  

  只不過這個對象和數值類型比較的時候被當作數值類型來比較。當使用「===」的時候比較數值相等時再比較數據類型的時候是有別於其餘數值類型的。htm

  上面提到了和數值類型比較的時候才成立,若是這個對象和其餘對象比較則使用對象比較的規則。對象

  好比下面的例子blog

var num = new Number(0);
var str = new String(0);
var str1 = String(0);
console.log(num == str);//false
console.log(num == str1);//true
console.log(num === str1);//false

  num和str都是比較特殊的對象,str1爲數值類型。num和str比較實用對象比較的規則來,num和str1比較實用數值比較的規則來。

 

  上面分析了那麼多,如今進入正題。

  普通對象是沒有prototype屬性的,只有隱藏屬性__proto__(IE上也有該隱藏屬性,可是使用obj.__proto__不能輸出東西,因此建議不要使用__proto__屬性)。而函數對象則二者兼有。prototype屬性指向的是函數對象的原型對象,對象的__proto__屬性是建立實例對象的時候對應的函數對象的原型對象

a.函數對象的原型對象(fn.prototype)


  這裏咱們須要理解原型對象的值是怎麼來的。原型對象的值實際上就是在函數建立的時候,建立了一個它的實例對象並賦值給它的prototype。過程以下(以Function爲例)

var temp = new Function();
Function.prototype = temp;

  因此咱們看一下熟知的函數的原型對象吧

    //chrome下的顯示效果
    Function.prototype;//function() {}
    Object.prototype;//Object {}
    Number.prototype;//Number {[[PrimitiveValue]]: 0}
    Boolean.prototype;//Boolean {[[PrimitiveValue]]: false}
    Array.prototype;//[]
    String.prototype;//String {length: 0, [[PrimitiveValue]]: ""}

   說道這裏,必須提的是全部函數對象的原型對象都繼承製原始對象,即fn.prototype.__proto__爲原始對象(原始對象在繼承屬性__proto__中有定義)。這其中比較特別的是Object函數,他的原型對象就是原始對象,即Object.prototype。

    var f1 = new Function();
    var f2 = Function();
    var fn3 = function(){}

    console.log(f1.prototype.__proto__ === Object.prototype);//true
    console.log(f2.prototype.__proto__ === Object.prototype);//true
    console.log(f2.prototype.__proto__ === Object.prototype);//true

    console.log(Number.prototype.__proto__ === Object.prototype);//true
    console.log(Boolean.prototype.__proto__ === Object.prototype);//true

 

b.繼承屬性__proto__


  實際上js沒有繼承這個東東,可是__proto__卻起到了相似繼承的做用。咱們所知的全部的對象起源都是一個空對象,咱們把這個空對象叫作原始對象。全部的對象經過__proto__回溯最終都會指向(所謂的指向相似C中的指針,這個原始對象是惟一的,整個內存中只會存在一個原始對象)這個原始對象。用下面的例子佐證

    var o = new Object();
    o.__proto__;//Object {}
    o.prototype;//undefined
    Object.prototype;//Object {}
    Object.__proto__;//function(){}
    Object.__proto__.__proto__;//Object {}

    var f = new Function();
    f.__proto__;//function(){}
    f.prototype;//Object {}
    Function.prototype;//function(){}
    Function.__proto__;//function(){}
    Function.__proto__.__proto__;//Object {}

  原始對象的__proto__屬性爲null,而且沒有原型對象。

  全部的對象都繼承自原始對象;Object比較特殊,他的原型對象也就是原始對象;因此咱們每每用Object.prototype表示原始對象。

    //全部的對象都繼承自原始對象
    //Object比較特殊,他的原型對象也就是原始對象
    //因此咱們每每用Object.prototype表示原始對象
    Object.prototype === o.__proto__;//true
    Object.prototype === Object.__proto__.__proto__;//true
    Object.prototype === Function.__proto__.__proto__;//true

  f.prototype的的值貌似也是原始對象?其實不是,咱們在函數對象的原型對象這一段中不是說過嗎函數對象f的原型對象其實是函數對象的一個實例。每個實例都是一個新的單獨的對象。

new f();//Object {}

  

  全部的函數對象都繼承製原始函數對象;Function比較特殊,他的原型對象也就是原始函數對象;因此咱們每每用Function.prototype表示原始函數對象;而原始函數對象又繼承自原始對象

    //全部的函數對象都繼承製原始函數對象,
    //Function比較特殊,他的原型對象也就是原始函數對象
    Function.prototype === f.__proto__
    Function.prototype === Object.__proto__ ;//true
    Function.prototype === Function.__proto__;//true
    //因此咱們每每用Function.prototype表示原始函數對象

    //而原始函數對象又繼承自原始對象
    Function.prototype.__proto__ === Object.prototype;

  因此對象之間的繼承和原型對象結構以下圖(引用的別人的js object猜測圖)

  看了上面的圖咱們還知道函數對象的原型對象的構造函數就是函數對象自己。不難理解函數對象的原型對象就是函數對象的實例了吧。

 

c. 原型鏈


  在使用New方法初始化函數的時候(詳細點擊查看new的深度理解)獲得的新對象的__proto__屬性會指向函數對象的原型對象,而函數對象的原型對象又繼承至原始對象。因此呈現如下結構

    function fn(){};
    var test = new fn();

  

  把這個有__proto__串起來的直到Object.prototype.__proto__爲null的鏈叫作原型鏈。原型鏈實際上就是js中數據繼承的繼承鏈。

 

  若是以爲本文不錯,請點擊右下方【推薦】!

相關文章
相關標籤/搜索