構造函數、原型對象、實例、隱式原型的理解
(歡迎一起探討,如果有什麽地方寫的不準確或是不正確也歡迎大家指點,最後留了一個疑問,歡迎各位大神來探討~)
PS:
內容中的
__proto__
可能會被markdown語法導致顯示為proto。建議將構造函數中的方法都定義到構造函數的原型中,由該構造函數創建的實例的方法都會指向同一個方法。(在內部聲明的話,每創建一個實例都會重新實例化函數,每個實例中的函數的指向是不同的;如果定義在全局中,且有很多方法,這樣毫無封裝性可言。)
一、屬性和方法
構造函數可以定義三種屬性和方法:
對象屬性/對象方法:在構造函數中定義的屬性或方法
靜態屬性/靜態方法:構造函數的靜態屬性或方法
原型屬性/原型方法:構造函數的原型的屬性或方法
(以下,構造函數的對象屬性和對象方法 簡稱 對象屬性和對象方法,構造函數的靜態屬性和方法 簡稱 靜態屬性和靜態方法,構造函數的原型的屬性和方法 簡稱 原型屬性和原型方法)
實例:
function Shape() { // 對象屬性和對象方法 this.color = ‘red‘; this.show = function(){ console.log(‘show‘) } } // 靜態屬性和靜態方法 Shape.type = ‘auto‘ Shape.printing = function(){ console.log(‘Shape‘) } // 原型屬性和原型方法 Shape.prototype.area = ‘unknow‘ Shape.prototype.move = function() { console.info(‘move‘); }; // 實例 var rect = new Shape(); console.log(rect.color) // red console.log(rect.show()) // show console.log(rect.area) // unknow console.log(rect.move()) // move console.log(Shape.type) //auto console.log(Shape.printing()) //Shape console.log(rect.type) // undefined console.log(rect.printing()) //undefined
構造函數的對象屬性和對象方法、原型屬性和原型方法,對應的實例都可以調用,構造函數本身調用失敗,會提示undefined;
構造函數的靜態屬性和靜態方法,對應的實例調用失敗,會提示undefined,但是可由構造函數本身調用。
調用屬性、方法 | 對象屬性和對象方法 | 原型屬性和原型方法 | 靜態屬性和靜態方法 |
---|---|---|---|
構造函數 | undefined | undefined | 可以得到 |
實例 | 可以得到 | 可以得到 | undefined |
二、prototype、constructor、proto
在以上的基礎上,再實例化一個新的對象和普通函數。
var circle = new Shape(); function test(){ console.log(‘test‘) } console.log(Shape.prototype,‘\n‘,Shape.constructor,‘\n‘,Shape.__proto__) console.log(Shape.prototype.constructor,‘\n‘,Shape.prototype.constructor ===Shape) console.log(rect.prototype,‘\n‘,rect.constructor,‘\n‘,rect.__proto__) console.log(rect.prototype.constructor,‘\n‘,rect.prototype.constructor ===rect) console.log(circle.prototype,‘\n‘,circle.constructor,‘\n‘,circle.__proto__) console.log(circle.prototype.constructor,‘\n‘,circle.prototype.constructor ===circle) console.log(test.prototype,‘\n‘,test.constructor,‘\n‘,test.__proto__) console.log(test.prototype.constructor,‘\n‘,test.prototype.constructor ===test)
Shape的constructor(構造器)指向Function,proto(隱式原型)指向 ? () { [native code] }。
實例的原型均為undefined,constructor(構造器)指向Shape構造函數(代表是Shape的實例),proto(隱式原型)指向Shape的原型對象。
test的原型指向一個包含construct和__proto__的對象,它的constructor(構造器)指向Function,proto(隱式原型)指向 ? () { [native code] }。
指向 | prototype | constructor | proto | prototype.constructor(如果有值,與本身嚴格比較) |
---|---|---|---|---|
構造函數 | 構造函數的原型 | Function | ? () { [native code] } | 其本身 |
實例 | undefined | 構造其本身的構造函數 | 構造其本身的構造函數的原型 | Cannot read property ‘constructor‘ of undefined |
普通函數 | 一個包含construct和__proto__的對象 | Function | ? () { [native code] } | 其本身 |
綜上可以知道
? () { [native code] } 是Function的原型。
實例的constructor(構造器)都指向構造其本身的構造函數。
而__proto__,均指向構造其本身的構造函數的原型。
驗證一下。
console.log(Shape.__proto__ === Shape.constructor.prototype) // true
console.log(Shape.__proto__ === Function.prototype) // true
console.log(rect.__proto__ === rect.constructor.prototype) // true
console.log(rect.__proto__ === Shape.prototype) // true
console.log(circle.__proto__ === circle.constructor.prototype) // true
console.log(circle.__proto__ === Shape.prototype) // true
console.log(test.__proto__ === test.constructor.prototype) // true
console.log(test.__proto__ === Function.prototype) // true
每個函數都有prototype屬性。
實例都有一個constructor(構造函數)屬性,該屬性指向構造它的構造函數。
調用構造函數創建一個新實例後,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象。
即__proto__屬性。
默認情況下,所有原型對象都會自動獲得一個constructor屬性,這個屬性包含一個指向prototype屬性所在函數的指針。
如:Shape.prototype.constructor === Shape
prototype就是通過調用構造函數而創建的那個對象實例的原型對象。使用原型對象的好處是可以讓所有對象實例共享它所包含的屬性和方法。
(重點在 共享 )
PS:
可以通過hasOwnProperty判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性。
如果是通過原型添加的屬性和方法,實例中雖然不包含屬性和方法,但是卻可以調用構造函數的原型中定義的方法。這是通過查找對象屬性(proto)的過程來實現的。
實例通過__proto__屬性去該構造函數的原型中去找屬性/方法,如果沒有,該構造函數的原型通過__proto__屬性去找構造它的構造函數的原型中去找,以此類推。這個通過__proto__屬性連接起來的鏈條就是原型鏈。原型鏈最終得到的值是null。
// 此處代表原型鏈
console.log(circle.__proto__,circle.__proto__ === Shape.prototype) // true
console.log(circle.__proto__.__proto__,circle.__proto__.__proto__ === Object.prototype) // true
console.log(circle.__proto__.__proto__.__proto__) // null
/*
以上代碼可以理解為
circle.__proto__ === Shape.prototype
Shape.prototype.__proto__ === Object.prototype
*/
// 已經知道constructor得到的是“構造”關系
// 那麽__proto__形成的是什麽關系?
// 或許會認為出現以下情況,註意:__proto__查找的是原型
console.log(circle.__proto__,circle.__proto__ === Function.prototype) // false
console.log(Shape.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Object.prototype) // false
console.log(Function.__proto__ === Function.prototype) // true
console.log(circle.__proto__.__proto__,circle.__proto__.__proto__ === Function.prototype) // false
下面來驗證一下~
function Shape() {
this.color = ‘red‘;
this.show = function(){
console.log(‘show‘);
}
}
Shape.prototype.area = ‘unknow‘
Shape.prototype.move = function() {
console.info(‘move‘);
};
function Rect(){
Shape.call(this);
this.color = ‘pink‘;
}
Rect.prototype = new Shape();
Rect.prototype.constructor = Rect;
Rect.prototype.name = ‘Rect‘;
var rect = new Rect();
console.log(rect); // Rect {color: "pink", show: ?}
console.log(rect.__proto__) // Shape {color: "red", show: ?, constructor: ?, name: "Rect"}
console.log(rect.__proto__.__proto__) // {area: "unknow", move: ?, constructor: ?}
由以上代碼得出,__proto__得到的是繼承關系~
額外:
那麽Function呢?
console.log(Function.prototype) // ? () { [native code] }
console.log(Function.prototype.constructor === Function) // true
console.log(Function.constructor) // ? Function() { [native code] } 會不會是其本身?
console.log(Function.constructor === Function) // true
// 為什麽???
// 根據以上輸出,可以認為它的__proto__就是它的prototype
console.log(Function.__proto__ === Function.constructor.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
// 檢測
console.log(Function instanceof Object) // true
// 疑問!?為什麽都指向Function呢?
console.log(Function.constructor) // ? Function() { [native code] }
console.log(Object.constructor) // ? Function() { [native code] }
構造函數、原型對象、實例、隱式原型的理解