constructor:對象的構造器,是一個函數。
prototype:構造器的原型,只有函數才具有這個屬性。
isPrototypeOf:如果對象 A 存在於 對象obj的原形鏈中,則 A.isPrototypeOf(obj)返回true,而obj必定繼承了A 的屬性。
__proto__:訪問對象的原型鏈在當前對象的上一級對象,即對象的父級對象,非W3C 或 ECMAscript 標準,是瀏覽器對原型繼承 的一種實現手段,存在於firefox 和 chrome ,IE下不存在這個屬性。js 對象的原形鏈應當是由 js 引擎維護的,屬於“引擎級”的資料,__proto__ 屬性的出現使原形鏈可以被修改,這使得在 js 文法範圍內討論 js 對象的原型鏈稱為可能。
在對象的繼承關係中,對象obj的構造器 constructor 其實是存在於原型鏈中的,即 obj.constructor 實際上 是 obj.__proto__.constructor, obj.hasOwnProperty('constructor'); 為 false
function Y() {this.y=99;}var obj = new Y(); console.log(obj.constructor); //Yconsole.log(obj.hasOwnProperty('constructor')); //falseconsole.log(obj.__proto__.hasOwnProperty('constructor')); //true //=====function X(){this.x=88; }function Y() {this.y=99;}Y.prototype = new X();var obj = new Y(); console.log(obj.constructor); //Xconsole.log(obj.hasOwnProperty('constructor'));//falseconsole.log(obj.__proto__.hasOwnProperty('constructor')); //falseconsole.log(obj.__proto__.__proto__.hasOwnProperty('constructor')); //true
//============
//訪問對象時,對於 obj.__proto__.x=value 則必定有 obj.x=value,//obj.constructor.prototype 上的屬性將被儲存在obj.__proto__中。 function Y() {this.y=99;} Y.prototype = {a:11}; var obj = new Y(); obj.__proto__.hasOwnProperty("a");//true Y.prototype.a=77; console.log([obj.a,obj.__proto__.a,obj.y,obj.__proto__.y]); //77,77,99,undefined,y 屬性是由物件建構器直接產生的而不是從原形鏈繼承來的*/
//new 運算構造obj對象之後,obj訪問繼承來的屬性時,是通過__proto__ 訪問的,而不是通過obj.constructor.prototype 訪問,obj.__proto.__ 並非簡單指向 obj.constructor.prototype,而是與它指向同一個對象
//因此,如果修改obj.constructor.prototype指向另一個對象,並不會影響obj繼承原有的屬性。
Y.prototype = {b:22};
console.log(obj.b); //undefined
console.log(obj.a); //77
//另一例子,可能會帶來一點迷惑function ClassA(){ var prop = 'hello'; arguments.callee.prototype={ getProp:function(){ return prop; } }}var obj1 = new ClassA();obj1.getProp(); // error,Object #<ClassA> has no method 'getProp'var obj2 = new ClassA();obj2.getProp(); // hello因為初始化 obj1 時,ClassA.prototype 被修改,而修改前,obj1 已經執行個體化了
in:如果對象 obj 有屬性 property(包括繼承來的和不可列舉屬性,不同於 for in 迴圈中的 in,for in 忽略了 不可列舉屬性), 則'property' in obj 返回 true,這個運算不存在於初期版本的javascript。
propertyIsEnumerable:如果對象obj上的屬性property可以被列舉出來(可被 for in 迴圈遍曆), 則 obj.propertyIsEnumerable('property') 返回true,值得注意的 是,propertyIsEnumerable對繼承來的屬性一律判斷為false,這一般被認為是ECMA Script 規範的一個設計上的錯誤。
hasOwnProperty:如果對象obj 上的屬性 property 不是繼承的來的,則 obj.hasOwnProperty('property') 返回true。
delete:刪除對象自身上的屬性,不能刪除繼承來的屬性,不能刪除使用 var 聲明的變數 ,不能刪除函式宣告,但是在如果在 firebug 和 IE9 的調試器裡面執行代碼,會發現全域變數被刪除了,實際上這在頁面上的代碼中是不會發生的事。
var f = function(){};f.prototype = { x:99 };var o = new f;console.log(o.hasOwnProperty('x')); //falseconsole.log(o.x); //99delete o.x ;console.log(o.x); //99 var x = 1;window.hasOwnProperty('x'); //truedelete window.x;console.log(x); // error,x is not defined in firefox bug , it will be 1 in chrome
instanceof:如果obj對象是建構函式Fun的一個執行個體,則 obj instanceof Fun 返回 true,
值得注意的是,instanceof 並不檢查 Fun 函數,其檢測是基於"原形鏈"的,如果 fun.prototype == obj.__proto__ 為 true, 或者 fun.prototype.isPrototypeOf(obj.__proto__) 為 true , 則 obj instcaneof fun 返回 true,即 fun.prototype.isPrototypeOf(obj) 為 true . (之前這裡寫 instanceof 以 fun.prototype.constructor==obj.__proto__.constructor 是否為 true 作為 依據是錯誤的 )
因此,即使 obj instanceof Fun 返回 true,obj 也可能不具有 Fun 構造器中定義的屬性,因為 Fun 不一定是 obj 的構造器。
試看下面代碼:
function f(){};function g(){};function k(){}f.prototype = new g();var obj = new f();console.log(obj instanceof f,obj instanceof g,f.prototype==obj.__proto__,g.prototype.isPrototypeOf(obj.__proto__)) ; //true true true truek.prototype = f.prototype;console.log(obj instanceof k); //truevar o = {};o.__proto__ = obj;console.log(o instanceof f); //truefunction P(){}P.prototype.constructor = obj.__proto__.constructor;console.log(obj instanceof P.prototype.constructor, obj instanceof P); //true falseobj.__proto__ = {};console.log(obj instanceof f); //falseg.prototype = {};console.log(obj instanceof g); //false
//另一個例子function A(){ this.a='a';}function B(){ this.b = 'b';}B.prototype.constructor = A;var obj = new B();console.log(obj instanceof B, obj instanceof A); //true ,false console.log(B.prototype==obj.__proto__); //trueconsole.log(obj.constructor === A,obj.__proto__.constructor ===A, obj instanceof A); //true true false , 這說明 instanceof 不以構造器為判斷依據
關於instanceof,試考察下面代碼:
function P(){this.p=11;}; var pro = new P(); function X() {this.x=88;} function Y() {this.y=99;} Y.prototype =pro; var obj = new Y();
1、物件建構器在哪
console.log(obj.hasOwnProperty('constructor')); //false console.log(obj.constructor); //P console.log(obj.__proto__.constructor);//P console.log(obj.__proto__.constructor === Y.prototype.constructor); //true
這說明執行new時,obj.constructor 即 obj.__proto__.constructor ,obj.__proto__ == Y.prototype;
因此 obj.constructor 實際是 Y.prototype.constructor 而不是 Y,
因此,“物件建構器” 實質上應是 “對象原型的構造器”, 即, 當 obj 對象的建構函式的 prototype.constructor 不是指向自身時, obj.constructor 不是指向 obj 的構造器,而是 obj 對像的原型的構造器。
2、物件建構器修複
但是,有一點小問題,考察一下 y 屬性:
console.log(obj.y); // 99 console.log(obj.__proto__.y); //undefined
y 屬性既然不是來自於“原型鏈”,那自然是來自於物件建構器,但是 P 函數中並沒有定義 y 屬性,
從“類式繼承” 形成的“繼承鏈” 看來,P 只是“繼承鏈”的源頭,也就是最頂級的 “基類”, obj 對象執行個體的的構造來源於“子類” y 函數,
這是 js 對象繼承系統中 “類比類式繼承” new 與“原型繼承” prototype 之間的一點裂縫,
很多人執著於修複這個裂縫,於是有了構造器修複的做法.
預設狀態下聲明一個函數fun,有 fun.prototype.constructor===fun,於是:
obj.constructor = Y ; //修複構造器,賦一個constructor 屬性來覆蓋掉繼承鏈上的constructor console.log(obj.hasOwnProperty('constructor')); //true console.log(obj.constructor); //Y console.log(obj.__proto__.constructor); //P
//或者向下面這個構造器處理方式
var fun = (function(){
var f = function(){
this.a =1;
};
f.prototype = {
constructor:f, //修複構造器為函數 f,因為直接將一個對象直接量賦給 f.prototype,fun構造的對象的constructor 將是對象直接量的constructor,即 Object函數
method1:function(){
return this.a;
}
};
return f;
})();
var obj = new fun();
3、obj instancof Fun 為 true 並不意味著 Fun 一定是 obj 對象的構造器,Fun 可能並不存在於 obj 的繼承鏈中:
X.prototype = pro; console.log(obj instanceof X); //true console.log(obj.x);//undefined , X 不是 obj 的構造器 console.log(obj instanceof Y); //true console.log(obj.y);//99
4、Object.create()
新版本的 ECMAScript 為 Object 對象擴充了一些方法,
Object.create(pro)可以基於 pro 為原型建立一個對象,其效果相當於
var f = function(){}; f.prototype = pro; var obj = new f();
//try: function P(){this.p=11;} function K(){this.k=22;} function F(){this.f=33;} var pro = new K(); P.prototype = pro; var o= new P(); var obj = Object.create(o); console.log(o,obj); // 都是 {p:11,k:22} console.log(obj.constructor); // K console.log(obj.__proto__.constructor); //k console.log(obj instanceof P); //true console.log(obj instanceof K); //true console.log(obj instanceof F); //false F.prototype = pro; console.log(obj instanceof F); //true
5、Object 與 Function:
console.log(Function instanceof Object); //trueconsole.log(Object instanceof Function); //true
先有 Function 還是先有 Object ?下面這個現象或許能解釋,Object 才是“最頂級”的對象
console.log(Object.__proto__.__proto__===Function.prototype) ; // false console.log(Function.__proto__.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null ,Object 的對象原型已經是女媧級的了