本文深入解析JavaScript中typeof与instanceof运算符的用法与局限。指出typeof无法区分对象、数组和null,推荐通过Object.prototype.toString方法获取对象的内部[[Class]]属性以准确判断类型。同时说明instanceof适用于判断自定义对象原型链,但在跨iframe等复杂场景下存在限制,提供了可靠的类型检测方案。
JavaScript类型检测场景引入
在编写JavaScript代码时,可能会遇到需要动态生成多个名称相同的input复选按钮的场景。
此时需要判断这些元素是否构成数组,便用到了if (typeof(document.MapCheckMgr.checkid)!="undefined")。
由于平时对这类用法使用较少,借此机会顺便梳理一下关于typeof的相关知识。
typeof运算符用于获取变量或表达式的类型,其返回结果通常包括以下几种:
number、boolean、string、function(函数)、object(包含NULL、数组、普通对象)以及undefined。
例如:
alert(typeof (123));//typeof(123)返回"number"
alert(typeof ("123"));//typeof("123")返回"string"
我们可以利用typeof来检查一个变量是否已定义,例如使用if(typeof a!="undefined"){},而不是使用if(a)这种写法,因为如果变量a未声明,直接使用会引发错误。
正因为typeof在处理null、数组和普通对象时均返回object类型,所以当我们需要判断一个对象是否为数组时,
或者判断某个变量是否为特定对象的实例时,应当选择使用另一个关键语法instanceof。
instanceof用于判断变量是否为某个对象的实例,例如var a=new Array();alert(a instanceof Array);会返回true,
同时alert(a instanceof Object)也会返回true;这是因为Array是Object的子类。
再如:function test(){};var a=new test();alert(a instanceof test)会返回true。
<script>
var str = new String();
function show(str1){
if(str1 instanceof String){
alert('1');
}else{
alert('0');
}
}
show(str);
str = "abccddd";
if(typeof str=='string'){alert(str);}
else{alert('0');}
</script>
typeof运算符的局限与用途
关于typeof
typeof是一元运算符,用于返回操作数类型的字符串表示。
typeof往往难以得到开发者预期的结果。它实际上只有一个主要应用场景,即检测一个对象是否已定义或已赋值,而非用于检查对象的具体类型。
| Value | Class | Type |
|---|---|---|
| "foo" | String | string |
| new String("foo") | String | object |
| 1.2 | Number | number |
| new Number(1.2) | Number | object |
| true | Boolean | boolean |
| new Boolean(true) | Boolean | object |
| new Date() | Date | object |
| new Error() | Error | object |
| [1,2,3] | Array | object |
| new Array(1, 2, 3) | Array | object |
| new Function("") | Function | function |
| /abc/g | RegExp | object (function in Nitro/V8) |
| new RegExp("meow") | RegExp | object (function in Nitro/V8) |
| {} | Object | object |
| new Object() | Object | object |
上表中Type列表示typeof操作符的运算结果,可以看到在大多数情况下它返回"object"。
Class列表示对象的内部属性[[Class]]的值。
根据JavaScript标准文档定义,[[Class]]的值只能是以下字符串之一:Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String。
为了获取对象的[[Class]],我们需要使用定义在Object.prototype上的toString方法。
对象的类定义
JavaScript标准文档仅提供了一种获取[[Class]]值的方法,即使用Object.prototype.toString。
function is(type, obj) {
var clas = Object.prototype.toString.call(obj).slice(8, -1);
return obj !== undefined && obj !== null && clas === type;
}
is('String', 'test'); // true
is('String', new String('test')); // true
在上述示例中,Object.prototype.toString 方法被调用,this被设置为需要获取[[Class]]值的对象。
注意:Object.prototype.toString返回标准格式的字符串,因此上述示例可以通过slice截取指定位置的字符串,如下所示:
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(2) // "[object Number]"
注意:这种差异在IE8和Firefox 4中表现不同,如下所示:
// IE8
Object.prototype.toString.call(null) // "[object Object]"
Object.prototype.toString.call(undefined) // "[object Object]"
// Firefox 4
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
测试未定义变量
typeof foo !== 'undefined'
上述代码检测foo是否已定义;若未定义而直接使用,将导致ReferenceError异常。这是typeof唯一具有实际价值的用途。
结论
为了检测对象的类型,强烈推荐使用Object.prototype.toString方法,因为这是唯一可靠的方式。如上表所示,typeof的部分返回值在标准文档中未明确定义,因此不同引擎的实现可能有所差异。
除非为了检测变量是否已定义,否则应尽量避免使用typeof操作符。
| x | typeof x |
|---|---|
| undefined | "undefined" |
| true 或false | "boolean" |
| 任意数字或者NaN | "number" |
| 任意字符串 | "string" |
| 函数对象(在ECMA-262术语中,指的是实现了[[Call]] 的对象) | "function" |
| 任意内置对象(非函数) | "object" |
| 数组 | "obeject" |
| null | "object" |
| 宿主对象(JS引擎内置对象,而不是DOM或者其他提供的) | 由编译器各自实现的字符串,但不是"undefined","number","boolean","number","string"。 |
| 正则表达式 | 各浏览器表现不一 |
若要将null与对象区分开,必须针对特殊值进行显式检测,例如:my_value===null。对于宿主对象,typeof可能不返回'object',而是返回字符串。但实际上,客户端JavaScript中的大多数宿主对象属于'object'类型。对所有内置可执行对象进行typeof运算将返回“function”。
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管NaN是"Not-A-Number"的缩写,意思是"不是一个数字"
typeof Number(1) === 'number'; // 不要这样使用!
// Strings
typeof "" === 'string';
typeof "bla" === 'string';
typeof (typeof 1) === 'string'; // typeof返回的肯定是一个字符串
typeof String("abc") === 'string'; // 不要这样使用!
// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(true) === 'boolean'; // 不要这样使用!
// Undefined
typeof undefined === 'undefined';
typeof blabla === 'undefined'; // 一个未定义的变量,或者一个定义了却未赋初值的变量
// Objects
typeof {a:1} === 'object';
typeof [1, 2, 4] === 'object';
// 使用Array.isArray或者Object.prototype.toString.call方法
//可以分辨出一个数组和真实的对象
typeof new Date() === 'object';
typeof new Boolean(true) === 'object' // 令人困惑.不要这样使用
typeof new Number(1) === 'object' // 令人困惑.不要这样使用
typeof new String("abc") === 'object'; // 令人困惑.不要这样使用
// Functions
typeof function(){} === 'function';
typeof Math.sin === 'function';
instanceof运算符的应用与限制
关于instanceof
instanceof的左操作数是一个类,右操作数是标识对象的类。如果左侧的对象是右侧类的实例,则返回true。JavaScript中对象的类是通过初始化它们的构造函数来定义的,即instanceof的右操作数应当是一个函数。所有对象都是Object的实例。如果左操作数不是对象,则返回false;如果右操作数不是函数,则抛出TypeError。
instanceof运算符用于测试一个对象是否在其原型链中包含原型构造函数的属性。其语法是object instanceof constructor。
instanceof操作符用于比较两个操作数的构造函数。仅在比较自定义对象时才有意义,若用于比较内置类型,其作用与typeof操作符类似,用处不大。
比较自定义对象
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true
// 如果仅仅设置 Bar.prototype 为函数 Foo 本身,而不是 Foo 构造函数的一个实例
Bar.prototype = Foo;
new Bar() instanceof Foo; // false
比较内置类型
new String('foo') instanceof String; // true
new String('foo') instanceof Object; // true
'foo' instanceof String; // false
'foo' instanceof Object; // false
有一点需要注意,instanceof在比较属于不同JavaScript上下文(例如浏览器中不同的文档结构)的对象时会出错,因为它们的构造函数不是同一个对象。
结论:instanceof操作符应仅用于比较来自同一JavaScript上下文的自定义对象。与typeof操作符一样,其他用法均应避免。
function C(){} // defining a constructor
function D(){} // defining another constructor
var o = new C();
o instanceof C; // true, because: Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false, because D.prototype is nowhere in o's prototype chain
o instanceof Object; // true, because:
C.prototype instanceof Object // true
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false, because C.prototype is nowhere in o's prototype chain anymore
D.prototype = new C(); // use inheritance
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true
var myString = new String();
var myDate = new Date();
myString instanceof String; // returns true
myString instanceof Object; // returns true
myString instanceof Date; // returns false
myDate instanceof Date; // returns true
myDate instanceof Object; // returns true
myDate instanceof String; // returns false
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car("Honda", "Accord", 1998);
var a = mycar instanceof Car; // returns true
var b = mycar instanceof Object; // returns true
总结
以上所述是小编给大家介绍的JavaScript中typeof与instanceof用法,希望对大家有所帮助。如果大家有任何疑问请留言,小编会及时回复大家。在此非常感谢大家对本站网站的支持!