constructor:對象的構造器,是一個函數。
prototype:構造器的原型,只有函數才具有這個屬性,一般來說這個屬性值應該是一個 "純粹的" Object 類型對象("[object Object]"),如果過設置為其他類型,可能會有一些意外。
var s ="abcd"; //試試下幾種情形 //var s =new String("abcd"); //var s = function(){} //var s = {} var f = function (){} f.prototype = s; var o = new f() console.log(f.prototype, typeof f.prototype); console.log(o.__proto__, typeof o.__proto__); console.log(f.prototype.isPrototypeOf(o)); console.log(o instanceof f); //if s ="abcd", thrown error; console.log(o.toString===Object.prototype.toString); console.log(o.toString());
如果 s 為標量, 對象 o 將繼承 new Object() ,即 o.__proto__ 為 {} , f.prototype.isPrototypeOf(o) 也就為 false, 並且 o instanceof f 將拋出錯誤 TypeError: 'prototype' property of f is not an object
如 果 s 為 Function、String、Number、Boolean 的對象, o.toString() 將拋出錯誤.
因為 o 為 object 對象不是 Function、String、Number、Boolean 對象, 但是 o 對象卻繼承了這些對象的 toString 方法, 這些對象的 toString 方法被其他類型的對象調用會導致報錯,跟 Object.prototype.toString 不同,
試試下面代碼
Function.prototype.toString.call(function(){}) //"function (){}" Function.prototype.toString.call({}) //TypeError: Function.prototype.toString is not generic Object.prototype.toString.call(function(){}) //"[object Function]"
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); //Y console.log(obj.hasOwnProperty('constructor')); //false console.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); //X console.log(obj.hasOwnProperty('constructor'));//false console.log(obj.__proto__.hasOwnProperty('constructor')); //false console.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 的調試器里面執行代碼,會發現全局變量被刪除了,實際上這在頁面上的代碼中是不會發生的事。 另外,數組的 length 屬性是不能刪除的。
var f = function(){}; f.prototype = { x:99 }; var o = new f; console.log(o.hasOwnProperty('x')); //false console.log(o.x); //99 delete o.x ; console.log(o.x); //99 var x = 1; window.hasOwnProperty('x'); //true delete window.x; console.log(x); // error,x is not defined in firebug , 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 , 即 fun.prototype.isPrototypeOf(obj) === true , 則 obj instcaneof fun 返回 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 true k.prototype = f.prototype; console.log(obj instanceof k); //true var o = {}; o.__proto__ = obj; console.log(o instanceof f); //true function P(){} P.prototype.constructor = obj.__proto__.constructor; console.log(obj instanceof P.prototype.constructor, obj instanceof P); //true false obj.__proto__ = {}; console.log(obj instanceof f); //false g.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__); //true console.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(prototype,[PropertyDescriptors])
新版本的 ECMAScript 為 Object 對象擴展了一些方法,其中 Object.create(pro)可以基於 pro 為原型創建一個對象
可選的 PropertyDescriptors 可以在穿件新對象的同事以屬性描述的方式添加為創建的對象添加屬性,具體參考 Object.defineProperties 方法。
Object.create(pro)效果類似於
var f = function(){}; f.prototype = pro; var obj = new f();
測試例子
function f(){} var o = new f() var obj = Object.create(o); console.log(obj .__proto__===o) //true console.log(o instanceof f) //true console.log(obj instanceof f) //true console.log(f.prototype.isPrototypeOf(o)) //true console.log(f.prototype.isPrototypeOf(obj)) //true console.log(o.constructor) //function f(){} console.log(obj.constructor) //function f(){} f.prototype.k=123 console.log(o.k) //123 console.log(obj.k) //123 o.s = 567; console.log(obj.s) //567
//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
使用對象直接量創建對象:
var o = {a:123} var obj = Object.create(o); console.log(obj.a); //123 console.log(obj.hasOwnProperty('a')); //false console.log(obj === o); //false console.log(obj.__proto__===o); //true console.log(Object.getPrototypeOf(obj) === obj.__proto__); //true
5、Object 與 Function:
console.log(Function instanceof Object); //true console.log(Object instanceof Function); //true
先有 Function 還是先有 Object ?下面這個現象或許能解釋,Object 是 Function 的一個實例, 但 Function 構造器原型的原型才是“最頂級”的對象,這個對象是 Object.prototype
console.log(Object.__proto__==Function.prototype); //true console.log(Object.__proto__.__proto__===Function.prototype) ; // false console.log(Function.prototype.__proto__ === Object.prototype); //true console.log(Function.__proto__.__proto__ === Object.prototype); // true console.log(Object.__proto__.__proto__ == Object.prototype); //true console.log(Function.__proto__.__proto__ === Object.__proto__.__proto__); //true console.log(Object.prototype.__proto__); // null ,Object 的對象原型已經是女媧級的了(還是說null 是女媧? ......) console.log(null instanceof Object); //false console.log(Object.prototype.hasOwnProperty('__proto__')); //true
另一個比較特別的不存在 __proto__ 的對象,而 Object.create() 也不接受 undefined 參數
var obj = Object.create(null); //創建沒有原型的對象 console.log(obj.__proto__); //undefined , 而不是 null console.log('__proto__' in obj); //false ,他是不存在 __proto__ 屬性的 console.log(obj instanceof Object); //false console.log(obj.toString); //undefined , toString、 valueOf 等方法也不存在 obj.toString(); //error 但是 Object.prototype.toString.call(obj); //[object Object]
與下面代碼區別一下:
var obj = Object(null); console.log(obj.__proto__); //Object {} console.log(obj instanceof Object); //true