javascript 对象及原型继承有关的属性:constructor、prototype、isPrototypeOf、instanceof、in 、hasOwnProperty 等等
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