理解javascript的原型鏈

做爲已經有了一年多經驗的前端的我,時常仍是不可以理解javascript原型鏈中對象的__proto__和prototype的指向問題,以及一些時候instanceof 檢測問題。因此此次我準備深刻的理解清楚javascript原型鏈的前因後果,並做出本身的總結。
首先畫出我總結的一張原型鏈圖,而後咱們一步步印證圖中每一個指向表明的關係。
1.對象和函數
建立對象的方式有不少種,字面量,構造函數和Object.create(__proto__) (比較特殊,把新對象的__proto__ 指向傳入對象)。建立函數的方式也有不少,函數聲明,函數表達式,構造函數等。javascript中函數也是一種引用類型的數據類型,因此函數也是對象。
// 函數也是對象:對象分爲函數對象和普通對象
// 對象和對象的建立
var o1 = {};
var o2 = new Object();
var o3 = new f1();
var o4 = Object.create({});
 
// 函數和函數的建立
function f1() {}
var f2 = new Function();
var f3 = function() {};
 
console.log("o1", typeof o1); //object
console.log("o2", typeof o2); //object
console.log("o3", typeof o3); //object
console.log("o4", typeof o4); //object
console.log("f1", typeof f1); //function
console.log("f2", typeof f2); //function
console.log("f3", typeof f3); //function
console.log("Function", typeof Function); //function
console.log("Object", typeof Object); //function

 

2.原型鏈上的指針
原型鏈是一種鏈表數據結構,其上的每個節點都有相應的指針來指向對應的節點。
  • 普通對象根本上都是由構造函數建立,
  • 普通對象在建立的時候帶有constructor 和 __proto__ 兩個指針,函數對象在建立的時候帶有constructor , __proto__和prototype三個指針。
  • 普通對象做爲一個實例其constructor指針指向其構造函數,__proto__指向其構造函數的原型
  • 函數對象其constructor指向Function函數(全部函數的構造函數),__proto__指向一個匿名函數,prototype指向帶有其構造函數屬性的一個原型對象(原型對象裏面有一個constructor指針指回向函數對象,即函數對象的原型的構造函數是其本身)
// 普通對象都是由構造函數建立,普通對象在建立的時候帶有constructor 和 __proto__ 屬性
// constructor 是一個指向其構造函數的指針
// __proto__ 是一個指向構造函數原型對象的指針
 
console.log("o1.constructor", o1.constructor); //ƒ Object() { [native code] }
console.log("o2.constructor", o2.constructor); //ƒ Object() { [native code] }
console.log("o3.constructor", o3.constructor); //ƒ f1() {}
 
console.log("o1.prototype", o1.prototype); //undefined
console.log("o2.prototype", o2.prototype); //undefined
console.log("o3.prototype", o3.prototype); //undefined
 
console.log("o1.__proto__", o1.__proto__ === Object.prototype); // true
console.log("o2.__proto__", o2.__proto__ === Object.prototype); //true
console.log("o3.__proto__", o3.__proto__ === f1.prototype); //true
 
// 構造函數在建立的時候也是帶有constructor __proto__ 屬性 ,而且比普通對象多一個 prototype 屬性
// 構造函數的constructor指針都是指向 Function
// 構造函數的__proto__指針都是指向 一個匿名函數
 
console.log("f1.constructor", f1.constructor); //ƒ Function() { [native code] }
console.log("f2.constructor", f2.constructor); //ƒ Function() { [native code] }
console.log("f3.constructor", f3.constructor); //ƒ Function() { [native code] }
 
console.log("f1.prototype", f1.prototype ); //{constructor:f f1(),__proto__:Object}
console.log("f2.prototype", f2.prototype); //{constructor:f anonymous(),__proto__:Object}
console.log("f3.prototype", f3.prototype); //{constructor:f (),__proto__:Object}
 
console.log("f1.__proto__", f1.__proto__); // ƒ () { [native code] }
console.log("f2.__proto__", f2.__proto__); //ƒ () { [native code] }
console.log("f3.__proto__", f3.__proto__); //ƒ () { [native code] }
 
3.Function函數和Object函數以及匿名函數的關係
  • Function函數最爲函數對象其constructor指針指向本身,__proto__指向匿名函數,prototype也指向匿名函數。
  • Object函數的constructor指向Function函數,__proto__指向匿名函數,prototype指向其本身的原型對象(Object.prototype)。
  • 匿名函數是一種特殊的對象,沒有prototype屬性,其__proto__指向Object.prototype(和普通對象同樣)。
// 因此在Function的原型實際上是一個匿名函數
console.log("Function.prototype", Function.prototype); //ƒ () { [native code] }
// 那麼Function做爲構造函數其 constructor 和 __proto__ 分別指向哪裏呢?
console.log("Function.constructor", Function.constructor); // ƒ Function() { [native code] }
console.log("Function.__proto__", Function.__proto__); //ƒ () { [native code] }
// 能夠看到 Function 的構造函數是本身 ,原型對象是匿名函數
 
// 那麼這個匿名函數有沒有 constructor __proto__ prototype 三個屬性呢?又分別指向哪裏呢
console.log("Function.__proto__.constructor", Function.__proto__.constructor); // ƒ Function() { [native code] }
console.log("Function.__proto__.prototype", Function.__proto__.prototype); // undefined
console.log("Function.__proto__.__proto__", Function.__proto__.__proto__===Object.prototype); // true
// Function.__proto__ 是一個匿名函數 ,其是一個特殊的對象,沒有prototype屬性,
// 和普通對象同樣只有 constructor 和 __proto__ 指針
// 咱們能夠得出這個特殊的匿名函數的__proto__指向是對象的原型
 
// 這個對象的原型是否還存在指針,其指向是哪裏呢?
console.log("Object.prototype.constructor",Object.prototype.constructor); // ƒ Object() { [native code] }
console.log("Object.prototype.__proto__",Object.prototype.__proto__); // null
console.log("Object.prototype.prototype",Object.prototype.prototype); // undefined

 

Object.create()方法對原型鏈的做用
Object.create()方法建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。 
console.log("o4.__proto__", o4.__proto__); // {}
console.log("o4.prototype", o4.prototype); // undefined
console.log("o4.constructor", o4.constructor); // ƒ Object() { [native code] }
 
instanceof的理解
instanceof 運算符用來檢測 constructor.prototype 是否存在於參數 object 的原型鏈上。
返回objectA.__proto__===  ...objectX.__proto__... === objectB.prototype 
// objectA instaceof objectB : instanceof 運算符用來檢測 constructor.prototype 是否存在於參數 object 的原型鏈上。
// return objectA.__proto__ === objectB.prototype
console.log("o1 instanceof Object",o1 instanceof Object) // true
console.log("o1 instanceof Function",o1 instanceof Function) // false
console.log("f1 instanceof Object",o1 instanceof Object) // true
console.log("f1 instanceof Function",o1 instanceof Function) // false
console.log("Object instanceof Function",Object instanceof Function) // true
console.log("Function instanceof Object",Function instanceof Object) // true
須要注意的是:objectA.__proto__ === objectB.prototype 並非永遠成立,當objectB的prototype的指向改變的時候,則不成立。
// 須要注意的是:objectA.__proto__ === objectB.prototype並非永遠成立,當objectB的prototype的指向改變的時候,則不成立。
function C() {}
var c = new C();
var d = {};
console.log("c instanceof C", c instanceof C);
C.prototype = d;
console.log("c instanceof C", c instanceof C);
// console.log("c instanceof d",c instanceof d) // Uncaught TypeError: Right-hand side of 'instanceof' is not callable
var e = new C();
console.log("e.__proto__", e.__proto__); // {}
 
參考:
《javascript高級程序設計》
相關文章
相關標籤/搜索