JavaScript 中的 instanceof
关键字在第一次遇到时可能会非常混乱,因为人们倾向于认为 JavaScript 不是一种面向对象的编程语言。
它是什么?
它解决了哪些问题?
什么时候合适,什么时候不合适?
"foo" instanceof String
=>假,1 instanceof Number
=>假,{} instanceof Object
=>错误的。说什么?!
"foo" instanceof String => false
是正确的,因为 typeof "foo" == 'string'
。 new String("foo") instanceof String => true
,因为 typeof String == 'function'
- 您应该将函数视为类(类的定义)。当您将其分配为 var v = new AnythingWhatTypeofEqualsFunction()
时,变量变为 instanceof
一些 function
(类)。这同样适用于 1
。 typeof 1 == 'number'
- 'number' 不是 'function' :) 下一个 - {} instanceof Object
在节点和现代浏览器中是 TRUE
({}) instanceof Object
将返回 true
。事实上,你写的代码会给你一个错误。
实例
左侧 (LHS) 操作数是被测试到右侧 (RHS) 操作数的实际对象,右侧 (RHS) 操作数是类的实际构造函数。基本定义是:
检查当前对象,如果对象属于指定对象类型,则返回 true。
以下是一些 good examples,下面是直接取自 Mozilla's developer site 的示例:
var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral"; //no type specified
color2 instanceof String; // returns false (color2 is not a String object)
值得一提的是,如果对象继承自类的原型,则 instanceof
的计算结果为 true:
var p = new Person("Jon");
p instanceof Person
即 p instanceof Person
为真,因为 p
继承自 Person.prototype
。
根据 OP 的要求
当你声明一个变量时,你给它一个特定的类型。
例如:
int i;
float f;
Customer c;
上面显示了一些变量,即 i
、f
和 c
。类型为 integer
、float
和用户定义的 Customer
数据类型。上述类型可以用于任何语言,而不仅仅是 JavaScript。但是,在 JavaScript 中,当您声明一个变量时,您没有明确定义类型 var x
,x 可以是数字/字符串/用户定义的数据类型。所以 instanceof
所做的是检查对象是否属于指定的类型,因此从上面获取 Customer
对象,我们可以这样做:
var c = new Customer();
c instanceof Customer; //Returns true as c is just a customer
c instanceof String; //Returns false as c is not a string, it's a customer silly!
上面我们已经看到 c
是用类型 Customer
声明的。我们已经新建了它并检查了它是否属于 Customer
类型。当然,它返回 true。然后仍然使用 Customer
对象,我们检查它是否是 String
。不,绝对不是 String
,我们新建了 Customer
对象而不是 String
对象。在这种情况下,它返回 false。
真的就是这么简单!
instanceof 有一个重要方面,到目前为止似乎没有在任何评论中涉及:继承。由于原型继承,使用 instanceof 评估的变量可能会为多个“类型”返回 true。
例如,让我们定义一个类型和一个子类型:
function Foo(){ //a Foo constructor
//assign some props
return this;
}
function SubFoo(){ //a SubFoo constructor
Foo.call( this ); //inherit static props
//assign some new props
return this;
}
SubFoo.prototype = Object.create(Foo.prototype); // Inherit prototype
SubFoo.prototype.constructor = SubFoo;
现在我们有了几个“类”,让我们创建一些实例,并找出它们的实例:
var
foo = new Foo()
, subfoo = new SubFoo()
;
alert(
"Q: Is foo an instance of Foo? "
+ "A: " + ( foo instanceof Foo )
); // -> true
alert(
"Q: Is foo an instance of SubFoo? "
+ "A: " + ( foo instanceof SubFoo )
); // -> false
alert(
"Q: Is subfoo an instance of Foo? "
+ "A: " + ( subfoo instanceof Foo )
); // -> true
alert(
"Q: Is subfoo an instance of SubFoo? "
+ "A: " + ( subfoo instanceof SubFoo )
); // -> true
alert(
"Q: Is subfoo an instance of Object? "
+ "A: " + ( subfoo instanceof Object )
); // -> true
看到最后一行了吗?对函数的所有“新”调用都返回一个继承自 Object 的对象。即使在使用对象创建速记时也是如此:
alert(
"Q: Is {} an instance of Object? "
+ "A: " + ( {} instanceof Object )
); // -> true
那么“类”定义本身呢?它们是什么实例?
alert(
"Q: Is Foo an instance of Object? "
+ "A:" + ( Foo instanceof Object)
); // -> true
alert(
"Q: Is Foo an instance of Function? "
+ "A:" + ( Foo instanceof Function)
); // -> true
我觉得理解任何对象都可以是 MULTIPLE 类型的实例很重要,因为您(错误地)假设您可以通过使用 instanceof
来区分对象和函数。正如最后一个示例清楚地显示了一个函数是一个对象。
如果您使用任何继承模式并希望通过鸭子类型以外的方法确认对象的后代,这也很重要。
希望对探索 instanceof
的人有所帮助。
SubFoo.prototype = new Foo();
从原型继承之后,您可以向其添加更多方法,并且 subfoo instanceof Foo
检查仍然会与 subfoo instanceof SubFoo
一样通过
这里的其他答案是正确的,但他们没有深入了解 instanceof
的实际工作原理,这可能会引起一些语言律师的兴趣。
JavaScript 中的每个对象都有一个原型,可通过 __proto__
属性访问。函数还有一个 prototype
属性,它是它们创建的任何对象的初始 __proto__
。创建函数时,会为 prototype
赋予一个唯一的对象。 instanceof
运算符使用此唯一性为您提供答案。如果您将 instanceof
编写为一个函数,这就是它的样子。
function instance_of(V, F) {
var O = F.prototype;
V = V.__proto__;
while (true) {
if (V === null)
return false;
if (O === V)
return true;
V = V.__proto__;
}
}
这基本上是对 ECMA-262 5.1 版(也称为 ES5)第 15.3.5.3 节的解释。
请注意,您可以将任何对象重新分配给函数的 prototype
属性,并且可以在构造对象后重新分配对象的 __proto__
属性。这会给你一些有趣的结果:
function F() { }
function G() { }
var p = {};
F.prototype = p;
G.prototype = p;
var f = new F();
var g = new G();
f instanceof F; // returns true
f instanceof G; // returns true
g instanceof F; // returns true
g instanceof G; // returns true
F.prototype = {};
f instanceof F; // returns false
g.__proto__ = {};
g instanceof G; // returns false
__proto__
”属性。如果我没记错的话,ECMA 规范中也不包含对该属性的直接操作,因此将其用于除学术追求之外的作业可能是个坏主意。
Object.getPrototypeOf(o)
,这将与您描述的 __proto__
相同,但符合 ECMAScript。
我认为值得注意的是,instanceof 是通过在声明对象时使用“new”关键字来定义的。在 JonH 的例子中;
var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral";
color2 instanceof String; // returns false (color2 is not a String object)
他没有提到的是这个;
var color1 = String("green");
color1 instanceof String; // returns false
指定“new”实际上将 String 构造函数的结束状态复制到 color1 var 中,而不仅仅是将其设置为返回值。我认为这更好地显示了 new 关键字的作用;
function Test(name){
this.test = function(){
return 'This will only work through the "new" keyword.';
}
return name;
}
var test = new Test('test');
test.test(); // returns 'This will only work through the "new" keyword.'
test // returns the instance object of the Test() function.
var test = Test('test');
test.test(); // throws TypeError: Object #<Test> has no method 'test'
test // returns 'test'
使用“new”将函数内部的“this”的值分配给声明的var,而不使用它则分配返回值。
new
与任何 JavaScript 类型一起使用是没有意义的,这会使接受的答案对初学者更加困惑。 text = String('test')
和 options = {}
不会由 instanceof
测试,而是由 typeof
代替。
您可以将其用于错误处理和调试,如下所示:
try{
somefunction();
}
catch(error){
if (error instanceof TypeError) {
// Handle type Error
} else if (error instanceof ReferenceError) {
// Handle ReferenceError
} else {
// Handle all other error types
}
}
它是什么?
Javascript 是一种原型语言,这意味着它使用原型进行“继承”。 instanceof
运算符测试构造函数的 prototype
属性是否存在于对象的 __proto__
链中。这意味着它将执行以下操作(假设 testObj 是一个函数对象):
obj instanceof testObj;
检查对象的原型是否等于构造函数的原型: obj.__proto__ === testObj.prototype >> 如果为真,instanceof 将返回真。将爬上原型链。例如: obj.__proto__.__proto__ === testObj.prototype >> 如果这是真的 instanceof 将返回真。将重复步骤 2,直到检查对象的完整原型。如果对象的原型链上没有任何地方与 testObj.prototype 匹配,则 instanceof 运算符将返回 false。
例子:
函数人(姓名){ this.name = name; } var me = new Person('Willem'); console.log(me instanceof Person); // true // 因为:me.__proto__ === Person.prototype // 计算结果为 true console.log(me instanceof Object); // true // 因为:me.__proto__.__proto__ === Object.prototype // 计算结果为 true console.log(me instanceof Array); // false // 因为:数组不在原型链上
它解决了哪些问题?
它解决了方便检查对象是否源自某个原型的问题。例如,当一个函数接收到一个可以有各种原型的对象时。然后,在使用原型链中的方法之前,我们可以使用 instanceof
运算符来检查这些方法是否在对象上。
例子:
函数 Person1 (name) { this.name = name; } function Person2 (name) { this.name = name; } Person1.prototype.talkP1 = function () { console.log('Person 1 talk'); } Person2.prototype.talkP2 = function () { console.log('Person 2 talk'); } function talk (person) { if (person instanceof Person1) { person.talkP1(); } if (person instanceof Person2) { person.talkP2(); } } const pers1 = new Person1 ('p1'); const pers2 = new Person2 ('p2');谈话(pers1);谈话(pers2);
这里的 talk()
函数首先检查原型是否位于对象上。在此之后,选择适当的方法来执行。不执行此检查可能会导致执行不存在的方法,从而导致引用错误。
什么时候合适,什么时候不合适?
我们已经讨论过了。当您需要在使用对象之前检查对象的原型时使用它。
PersonX.prototype.talk
会更合适,这样 talk
函数就可以简单地执行 person.talk()
。
__proto__
,它已被弃用 - 改为使用 Object.getPrototype()
//Vehicle is a function. But by naming conventions
//(first letter is uppercase), it is also an object
//constructor function ("class").
function Vehicle(numWheels) {
this.numWheels = numWheels;
}
//We can create new instances and check their types.
myRoadster = new Vehicle(4);
alert(myRoadster instanceof Vehicle);
关于“什么时候合适,什么时候不合适?”这个问题,我的 2 美分:
instanceof
在生产代码中很少有用,但在您想要断言代码返回/创建正确类型的对象的测试中很有用。通过明确您的代码返回/创建的对象类型,您的测试作为理解和记录代码的工具变得更加强大。
我刚刚找到了一个真实的应用程序,我想现在会更频繁地使用它。
如果您使用 jQuery 事件,有时您想编写一个更通用的函数,该函数也可以直接调用(不带事件)。您可以使用 instanceof
检查函数的第一个参数是否是 jQuery.Event
的实例并做出适当的反应。
var myFunction = function (el) {
if (el instanceof $.Event)
// event specific code
else
// generic code
};
$('button').click(recalc); // Will execute event specific code
recalc('myParameter'); // Will execute generic code
就我而言,该函数需要为所有(通过按钮上的单击事件)或仅一个特定元素计算某些东西。我使用的代码:
var recalc = function (el) {
el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
// calculate...
};
instanceof
只是 isPrototypeOf
的语法糖:
function Ctor() {}
var o = new Ctor();
o instanceof Ctor; // true
Ctor.prototype.isPrototypeOf(o); // true
o instanceof Ctor === Ctor.prototype.isPrototypeOf(o); // equivalent
instanceof
仅依赖于对象构造函数的原型。
构造函数只是一个普通函数。严格来说它是一个函数对象,因为在 Javascript 中一切都是对象。而这个函数对象有一个原型,因为每个函数都有一个原型。
原型只是一个普通对象,它位于另一个对象的原型链中。这意味着在另一个对象的原型链中使一个对象成为原型:
function f() {} // ordinary function
var o = {}, // ordinary object
p;
f.prototype = o; // oops, o is a prototype now
p = new f(); // oops, f is a constructor now
o.isPrototypeOf(p); // true
p instanceof f; // true
应避免使用 instanceof
运算符,因为它会伪造 Javascript 中不存在的类。尽管 class
关键字在 ES2015 中也没有,因为 class
再次只是语法糖......但这是另一个故事。
instanceof
仍然是从 isPrototypeOf
派生的。我称之为语法糖。而且你的 MDN 来源是个笑话,不是吗?
"" instanceof object
是 false
,但 new String('') instanceof object
是 true
。 Javascript 中的一切都不是对象
@SebastianSimon 我的最后一个答案是 8 岁(当我跛脚时),我可能写了一些公牛*命中 :)
简而言之 - 目前我使用 instanceof
的唯一情况是我使用 class
实例并且行为取决于我将收到的类,例如。我想区分 404 是 ErrorA(resource not exist) 还是 ErrorB(service not found) - 库响应代码令人困惑,但幸运的是它使用不同的错误类抛出。
绝对(目前)我不会使用它来检查反映原语的类型 - 您无法确定库返回的是 'msg'
还是 new String('msg')
。
它们都有属于 String
类的方法,因为 'msg'
原语在内部被包装到字符串对象中。内部意味着解释器。它们都是 Strings
但 instanceof
运算符在这里似乎不够 - 检查是否某事。是原始的或类我会使用 typeof
&& 的组合instanceof
- 但仅适用于从外部 JS 库返回的内容。
Currenlty TypeScript 正在解决这个问题,您不再需要对 typeof
和 instanceof
进行如此繁琐的检查。
color1 instanceof String;
时,这将返回 true,因为 color1 是一个字符串。person p = new person()
p 现在是 person 类型而不是 string 类型。var zero = 0; alert(zero); zero = "0"; alert(zero)
,我们从原始int
到原始string
没有任何问题。int
更改为原始string
非常容易。