typeof和instanceof之间的区别是什么?什么时候应该使用其中一个而不是另一个?

在我的特殊情况下:

callback instanceof Function

typeof callback == "function"

这有关系吗,有什么区别?

额外的资源:

javascript花园typeof vs 运算符

201492 次浏览

我认为,当callbackFunction的子类型时,instanceof也可以工作

两者在功能上相似,因为它们都返回类型信息,但我个人更喜欢instanceof,因为它比较的是实际类型而不是字符串。类型比较不容易出现人为错误,而且技术上更快,因为它比较内存中的指针,而不是整个字符串的比较。

我建议使用prototype的callback.isFunction()

他们已经找出了其中的区别,你可以相信他们的理由。

我猜其他JS框架也有这样的东西。

instanceOf不会在其他窗口中定义的函数上工作,我相信。 他们的函数与你的window.Function不同

我从小接受严格的面向对象教育

callback instanceof Function

字符串容易出现我糟糕的拼写或其他拼写错误。而且我觉得读起来更好。

instanceof在Javascript中可能是不可靠的-我相信主要框架试图避免使用它。不同的窗口是它可以打破的方式之一-我相信职业层次结构也可以混淆它。

有更好的方法来测试对象是否为某种内置类型(通常是您想要的类型)。创建实用函数并使用它们:

function isFunction(obj) {
return typeof(obj) == "function";
}
function isArray(obj) {
return typeof(obj) == "object"
&& typeof(obj.length) == "number"
&& isFunction(obj.push);
}

等等。

使用instanceof,因为如果你改变了类的名字,你会得到一个编译器错误。

使用typeof的一个很好的理由是变量可能是未定义的。

alert(typeof undefinedVariable); // alerts the string "undefined"
alert(undefinedVariable instanceof Object); // throws an exception

使用instanceof的一个很好的理由是变量可能为空。

var myNullVar = null;
alert(typeof myNullVar ); // alerts the string "object"
alert(myNullVar  instanceof Object); // alerts "false"

所以在我看来,这取决于你在检查什么类型的数据。

显著的实际差异:

var str = 'hello word';


str instanceof String   // false


typeof str === 'string' // true

别问我为什么。

其他重要的实际差异:

// Boolean


var str3 = true ;


alert(str3);


alert(str3 instanceof Boolean);  // false: expect true


alert(typeof str3 == "boolean" ); // true


// Number


var str4 = 100 ;


alert(str4);


alert(str4 instanceof Number);  // false: expect true


alert(typeof str4 == "number" ); // true

为自定义类型使用instanceof:

var ClassFirst = function () {};
var ClassSecond = function () {};
var instance = new ClassFirst();
typeof instance; // object
typeof instance == 'ClassFirst'; // false
instance instanceof Object; // true
instance instanceof ClassFirst; // true
instance instanceof ClassSecond; // false

对简单的内置类型使用typeof:

'example string' instanceof String; // false
typeof 'example string' == 'string'; // true


'example string' instanceof Object; // false
typeof 'example string' == 'object'; // false


true instanceof Boolean; // false
typeof true == 'boolean'; // true


99.99 instanceof Number; // false
typeof 99.99 == 'number'; // true


function() {} instanceof Function; // true
typeof function() {} == 'function'; // true

对于复杂的内置类型使用instanceof:

/regularexpression/ instanceof RegExp; // true
typeof /regularexpression/; // object


[] instanceof Array; // true
typeof []; //object


{} instanceof Object; // true
typeof {}; // object

最后一个有点棘手:

typeof null; // object

我在Safari 5和Internet Explorer 9中发现了一些非常有趣的(读作“可怕的”)行为。我在Chrome和Firefox中成功地使用了这个功能。

if (typeof this === 'string') {
doStuffWith(this);
}

然后我在IE9中测试,它根本不能工作。大惊喜。但在Safari中,它是间歇性的!所以我开始调试,我发现Internet Explorer是总是返回false。但最奇怪的是,Safari似乎在它的JavaScript VM中做了某种优化,在第一个时间是true,而在false时间是每次你点击重载!< / em >

我的大脑几乎爆炸了。

所以现在我决定这样做:

if (this instanceof String || typeof this === 'string')
doStuffWith(this.toString());
}

现在一切都很好。注意,你可以调用"a string".toString(),它只返回字符串的副本,即。

"a string".toString() === new String("a string").toString(); // true

所以从现在开始我将两者都用。

instanceof将不适用于原语,例如"foo" instanceof String将返回false,而typeof "foo" == "string"将返回true

另一方面,当涉及到自定义对象(或类,无论你想叫它们什么)时,typeof可能不会做你想做的事情。例如:

function Dog() {}
var obj = new Dog;
typeof obj == 'Dog' // false, typeof obj is actually "object"
obj instanceof Dog  // true, what we want in this case

函数碰巧既是“函数”原语,又是“函数”的实例,这有点奇怪,因为它不像其他原语类型那样工作,例如。

(typeof function(){} == 'function') == (function(){} instanceof Function)

(typeof 'foo' == 'string') != ('foo' instanceof String)

性能

在两者都适用的情况下,typeofinstanceof快。

根据您的引擎,有利于typeof的性能差异可能在20%左右。(你的里程可能会有所不同)

下面是Array的基准测试:

var subject = new Array();
var iterations = 10000000;


var goBenchmark = function(callback, iterations) {
var start = Date.now();
for (i=0; i < iterations; i++) { var foo = callback(); }
var end = Date.now();
var seconds = parseFloat((end-start)/1000).toFixed(2);
console.log(callback.name+" took: "+ seconds +" seconds.");
return seconds;
}


// Testing instanceof
var iot = goBenchmark(function instanceofTest(){
(subject instanceof Array);
}, iterations);


// Testing typeof
var tot = goBenchmark(function typeofTest(){
(typeof subject == "object");
}, iterations);


var r = new Array(iot,tot).sort();
console.log("Performance ratio is: "+ parseFloat(r[1]/r[0]).toFixed(3));

结果

instanceofTest took: 9.98 seconds.
typeofTest took: 8.33 seconds.
Performance ratio is: 1.198

为了把事情弄清楚,你需要知道两个事实:

  1. 运算符操作符测试构造函数原型属性是否出现在对象的原型链中的任何位置。在大多数情况下,这意味着通过使用该构造函数或其后代,对象是创建。但prototype也可以通过Object.setPrototypeOf()方法(ECMAScript 2015)或__proto__属性显式设置(旧浏览器,已弃用)。但是,由于性能问题,不建议更改对象的原型。

因此,instanceof仅适用于对象。在大多数情况下,你不会使用构造函数来创建字符串或数字。你可以。但你几乎从来没有这样做过。

instanceof也不能检查使用哪个构造函数创建对象,但返回true,即使对象是从被检查的类派生的。在大多数情况下,这是理想的行为,但有时并非如此。所以你要保持这种心态。

另一个问题是不同的作用域具有不同的执行环境。这意味着它们有不同的内置(不同的全局对象,不同的构造函数,等等)。这可能会导致意想不到的结果。

例如,[] instanceof window.frames[0].Array将返回false,因为Array.prototype !== window.frames[0].Array和数组继承自前者 此外,它不能用于未定义的值,因为它没有原型。< / p >
  1. typeof操作符测试值是否属于六种基本类型之一: "数量", "字符串", "布尔", "对象", "函数"或"未定义的"。其中字符串“object”属于所有对象(除了函数,它是对象,但在typeof操作符中有自己的值),还有“null”值和数组(对于“null”是一个bug,但这个bug太老了,所以它已经成为一个标准)。它不依赖于构造函数,即使value未定义也可以使用。但它没有给出物体的任何细节。如果你需要,去instanceof。

现在我们来讨论一个棘手的问题。如果使用构造函数创建基元类型呢?

let num = new Number(5);
console.log(num instanceof Number); // print true
console.log(typeof num); // print object
num++; //num is object right now but still can be handled as number
//and after that:
console.log(num instanceof Number); // print false
console.log(typeof num); // print number

就像魔法一样。但事实并非如此。这就是所谓的装箱(按对象包装原语值)和解装箱(从对象中提取包装的原语值)。这样的代码似乎“有点”脆弱。当然,您可以避免使用构造函数创建原始类型。但还有另一种可能的情况,拳击可能会打到你。在原语类型上使用Function.call()或Function.apply()时。

function test(){
console.log(typeof this);
}
test.apply(5);

为了避免这种情况,你可以使用严格模式:

function test(){
'use strict';
console.log(typeof this);
}
test.apply(5);

< >强乌利希期刊指南: 自ECMAScript 2015以来,又增加了一种名为Symbol的类型,它有自己的typeof == “象征”.

console.log(typeof Symbol());
// expected output: "symbol"

你可以在MDN上读到它:(象征typeof)。

尽管运算符可能比typeof快一点,但我更喜欢第二个,因为这样一个可能的魔法:

function Class() {};
Class.prototype = Function;


var funcWannaBe = new Class;


console.log(funcWannaBe instanceof Function); //true
console.log(typeof funcWannaBe === "function"); //false
funcWannaBe(); //Uncaught TypeError: funcWannaBe is not a function

还有一种情况是你只能用instanceof进行排序——它返回true或false。使用typeof,你可以获得提供的某物类型

在检查函数时,必须始终使用typeof

区别在于:

var f = Object.create(Function);


console.log(f instanceof Function); //=> true
console.log(typeof f === 'function'); //=> false


f(); // throws TypeError: f is not a function

这就是为什么永远不能使用instanceof来检查函数。

var newObj =  new Object;//instance of Object
var newProp = "I'm xgqfrms!" //define property
var newFunc = function(name){//define function
var hello ="hello, "+ name +"!";
return hello;
}
newObj.info = newProp;// add property
newObj.func = newFunc;// add function


console.log(newObj.info);// call function
// I'm xgqfrms!
console.log(newObj.func("ET"));// call function
// hello, ET!


console.log(newObj instanceof Object);
//true
console.log(typeof(newObj));
//"object"

考虑到性能,最好使用typeof 对于典型的硬件,如果您创建一个具有1000万次迭代循环的脚本 指令:typeof STR == 'string'将花费9毫秒 而'string' instanceof string将花费19ms

当然重要........! < / em >

让我们用例子来解释一下。在我们的例子中,我们将以两种不同的方式声明函数。

我们将同时使用< em > function declaration < / em >函数构造函数。我们将观察typeofinstanceof在这两种不同情况下的表现。

使用函数声明创建函数

function MyFunc(){  }


typeof Myfunc == 'function' // true


MyFunc instanceof Function // false

对这种不同结果的可能解释是,因为我们声明了一个函数,typeof可以理解它是一个函数。因为typeof检查typeof运算的表达式是否在其上,在我们的例子中是MyFunc implemented 调用方法是否。如果它实现了Call方法,则它是一个函数。否则不是。为了澄清检查ecmascript typeof规范

使用函数构造函数:

var MyFunc2 = new Function('a','b','return a+b') // A function constructor is used


typeof MyFunc2 == 'function' // true


MyFunc2 instanceof Function // true

这里MyFunc24断言MyFunc25是一个函数,也是MyFunc26操作符。我们已经知道MyFunc24检查MyFunc25是否实现了MyFunc29方法。由于MyFunc25是一个函数,并且它实现了instanceof1方法,这就是MyFunc24如何知道它是一个函数。另一方面,我们使用instanceof3创建了MyFunc25,它变成了instanceof5的实例。这就是为什么MyFunc26也解析为instanceof7。

哪种使用更安全

正如我们所看到的,在这两种情况下,typeof操作符都可以成功地断言我们正在处理的是一个函数,它比instanceof更安全。在function declaration的情况下,instanceof将失败,因为instanceof0不是instanceof1的实例。

最佳实践:

正如Gary Rafferty 所建议的,最好的方法应该是同时使用typeof和instanceof。

  function isFunction(functionItem) {


return typeof(functionItem) == 'function' || functionItem instanceof Function;


}


isFunction(MyFunc) // invoke it by passing our test function as parameter

这只是对这里所有其他解释的补充知识——我建议在任何地方都使用.constructor

TL;博士:typeof不可选且当你知道你不关心原型链不可选的情况下,Object.prototype.constructor可能是一个可行的甚至比instanceof更好的选择:

x instanceof Y
x.constructor === Y

它从1.1开始就在标准中了,所以不用担心向后兼容性。

Muhammad Umer也在这里的一个评论中简要地提到了这一点。它适用于所有有原型的东西——所以所有不是nullundefined的东西:

// (null).constructor;      // TypeError: null has no properties
// (undefined).constructor; // TypeError: undefined has no properties


(1).constructor;                 // function Number
''.constructor;                  // function String
([]).constructor;                // function Array
(new Uint8Array(0)).constructor; // function Uint8Array
false.constructor;               // function Boolean()
true.constructor;                // function Boolean()


(Symbol('foo')).constructor;     // function Symbol()
// Symbols work, just remember that this is not an actual constructor:
// new Symbol('foo'); //TypeError: Symbol is not a constructor


Array.prototype === window.frames.Array;               // false
Array.constructor === window.frames.Array.constructor; // true

此外,根据你的用例,它可以比instanceof很多(原因可能是它不需要检查整个原型链)。在我的情况下,我需要一个快速的方法来检查一个值是否是一个类型数组:

function isTypedArrayConstructor(obj) {
switch (obj && obj.constructor){
case Uint8Array:
case Float32Array:
case Uint16Array:
case Uint32Array:
case Int32Array:
case Float64Array:
case Int8Array:
case Uint8ClampedArray:
case Int16Array:
return true;
default:
return false;
}
}


function isTypedArrayInstanceOf(obj) {
return obj instanceof Uint8Array ||
obj instanceof Float32Array ||
obj instanceof Uint16Array ||
obj instanceof Uint32Array ||
obj instanceof Int32Array ||
obj instanceof Float64Array ||
obj instanceof Int8Array ||
obj instanceof Uint8ClampedArray ||
obj instanceof Int16Array;
}

< a href = " https://run.perf。zone/view/isTypedArray-constructor-vs-instanceof-1519140393812" rel="nofollow noreferrer">https://run.perf.zone/view/isTypedArray-constructor-vs-instanceof-1519140393812

结果是:

Chrome 64.0.3282.167(64位,Windows)

类化数组实例vs构造函数- 1.5倍更快在Chrome 64.0.3282.167(64位,Windows) < / >

Firefox 59.0b10(64位,Windows)

 typing Array instanceof vs constructor - 30x faster in Firefox 59.0b10(64位,Windows) < / >

出于好奇,我对typeof做了一个快速的玩具基准测试;令人惊讶的是,它的表现并没有差到哪里去,而且在Chrome中似乎还要快一点:

let s = 0,
n = 0;


function typeofSwitch(t) {
switch (typeof t) {
case "string":
return ++s;
case "number":
return ++n;
default:
return 0;
}
}


// note: no test for null or undefined here
function constructorSwitch(t) {
switch (t.constructor) {
case String:
return ++s;
case Number:
return ++n;
default:
return 0;
}
}


let vals = [];
for (let i = 0; i < 1000000; i++) {
vals.push(Math.random() <= 0.5 ? 0 : 'A');
}

< a href = " https://run.perf。Zone /view/ typef -vs-construct -string-or-number " rel="nofollow noreferrer">https://run.perf.zone/view/typeof-vs-constructor-string-or-number-1519142623570 .

注意:函数列表的顺序在图像之间切换!

Chrome 64.0.3282.167(64位,Windows)

String/Number typeof vs constructor - 1.26x faster in Chrome 64.0.3282.167(64位,Windows) < / >

Firefox 59.0b10(64位,Windows)

注意:函数列表的顺序在图像之间切换!

String/Number typeof vs constructor - 0.78x慢于Firefox 59.0b10(64位,Windows) < / >

准确地说 应该在通过构造函数(通常是自定义类型)创建值的地方使用Instanceof

var d = new String("abc")

typeof检查仅由赋值创建的值,例如

var d = "abc"

没有必要用上面的大量例子来压倒你,只需要记住两个观点:

  1. typeof var;是一个一元操作符,将返回变量的原始类型或根类型。因此它将返回原语类型(stringnumberbigintbooleanundefinedsymbol)或object类型。

  2. 对于更高级别的对象,如内置对象(String, Number, Boolean, Array..)或复杂或自定义对象,它们都是object根类型,但基于它们构建的实例类型是不同的(如OOP类继承概念),这里a instanceof A -一个二进制操作符-将帮助你,它将遍历原型链以检查右操作数(a)的构造函数是否出现。

所以当你想检查“根类型”或使用基元变量时,使用“typeof”,否则使用“instanceof”。

null是一个特殊情况,它看起来很原始,但实际上是object的特殊情况。使用a === null检查null。

另一方面,function也是一个特殊情况,它是内置对象,但typeof返回function

正如你所看到的,instanceof必须遍历原型链,而typeof只检查一次根类型,所以很容易理解为什么typeofinstanceof

根据关于typeof的MDN文档,用"new"关键字实例化的对象类型为'object':

typeof 'bla' === 'string';


// The following are confusing, dangerous, and wasteful. Avoid them.
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';

关于instanceof的文档指出:

const objectString = new String('String created with constructor');
objectString instanceOf String; // returns true
objectString instanceOf Object; // returns true

因此,如果有人想检查,例如,无论某物是如何创建的,它都是字符串,最安全的方法是使用instanceof

typeof:根据MDN文档,typeof是一个一元操作符,返回指示未求值操作数类型的字符串。

在字符串原语和字符串对象的情况下,typeof返回如下:

const a = "I'm a string primitive";
const b = new String("I'm a String Object");


typeof a; --> returns 'string'
typeof b; --> returns 'object'

运算符:是一个二进制操作符,接受一个对象和一个构造函数。它返回一个布尔值,指示对象的原型链中是否有给定的构造函数

当应用于上面的string实例时,并与string进行比较,它的行为如下:

const a = "I'm a string primitive";
const b = new String("I'm a String Object");


a instanceof String; --> returns false
b instanceof String; --> returns true

参考:https://bambielli.com/til/2017-06-18-typeof-vs-instanceof/