JavaScript的instanceof运算符学习教程
语法
objectinstanceofconstructor
参数
object:
要检测的对象.
constructor:
某个构造函数
描述:
instanceof运算符用来检测constructor.prototype是否存在于参数object的原型链上。
//定义构造函数 functionC(){} functionD(){} varo=newC(); //true,因为Object.getPrototypeOf(o)===C.prototype oinstanceofC; //false,因为D.prototype不在o的原型链上 oinstanceofD; oinstanceofObject;//true,因为Object.prototype.isPrototypeOf(o)返回true C.prototypeinstanceofObject//true,同上 C.prototype={}; varo2=newC(); o2instanceofC;//true oinstanceofC;//false,C.prototype指向了一个空对象,这个空对象不在o的原型链上. D.prototype=newC();//继承 varo3=newD(); o3instanceofD;//true o3instanceofC;//true
需要注意的是,如果表达式objinstanceofFoo返回true,则并不意味着该表达式会永远返回ture,因为Foo.prototype属性的值有可能会改变,改变之后的值很有可能不存在于obj的原型链上,这时原表达式的值就会成为false。另外一种情况下,原表达式的值也会改变,就是改变对象obj的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的__proto__魔法属性,是可以实现的。比如执行obj.__proto__={}之后,objinstanceofFoo就会返回false了。
instanceof和多全局对象(多个frame或多个window之间的交互)
在浏览器中,我们的脚本可能需要在多个窗口之间进行交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。这可能会引发一些问题。比如,表达式[]instanceofwindow.frames[0].Array会返回false,因为Array.prototype!==window.frames[0].Array.prototype,因此你必须使用Array.isArray(myObj)或者Object.prototype.toString.call(myObj)==="[objectArray]"来判断myObj是否是数组。
示例
instanceof的常规用法是判断a是否是b类型:
console.log(trueinstanceofBoolean);//false console.log(newNumber(1)instanceofNumber);//true
instanceof还能判断父类型:
functionFather(){} functionChild(){} Child.prototype=newFather(); vara=newChild(); console.log(ainstanceofChild);//true console.log(ainstanceofFather);//true
Child构造函数继承自Father,实例a是Child构造的无疑,但是为何也是Father的实例呢?其实instanceof运算符的内核可以简单地用以下代码描述:
functioncheck(a,b){ while(a.__proto__){ if(a.__proto__===b.prototype) returntrue; a=a.__proto__; } returnfalse; } functionFoo(){} console.log(ObjectinstanceofObject===check(Object,Object));//true console.log(FunctioninstanceofFunction===check(Function,Function));//true console.log(NumberinstanceofNumber===check(Number,Number));//true console.log(StringinstanceofString===check(String,String));//true console.log(FunctioninstanceofObject===check(Function,Object));//true console.log(FooinstanceofFunction===check(Foo,Function));//true console.log(FooinstanceofFoo===check(Foo,Foo));//true
简单地说,a如果是b的实例,那么a肯定能使用b的prototype中定义的方法和属性,那么用代码表示就是a的原型链中有b.prototype取值相同的对象,于是顺着a的原型链一层层找就行了。
另外值得注意的是,StringNumberBoolean以及Function等都是函数,而函数则是统一由Function构造而来的,so它们和任何单纯的函数一样,能用Function上的原型属性:
Function.prototype.a=10; console.log(String.a);//10
最后来简单讲讲最开始的两道题吧。
//为了方便表述,首先区分左侧表达式和右侧表达式 FunctionL=Function,FunctionR=Function; //下面根据规范逐步推演 O=FunctionR.prototype=Function.prototype L=FunctionL.__proto__=Function.prototype //第一次判断 O==L //返回true //为了方便表述,首先区分左侧表达式和右侧表达式 StringL=String,StringR=String; //下面根据规范逐步推演 O=StringR.prototype=String.prototype L=StringL.__proto__=Function.prototype //第一次判断 O!=L //循环再次查找L是否还有__proto__ L=String.prototype.__proto__=Object.prototype //第二次判断 O!=L //再次循环查找L是否还有__proto__ L=String.prototype.__proto__=null //第三次判断 L==null //返回false