JavaScript 类型判断的四种方式与原理详解
本文详细解析 JavaScript 中判断数据类型的四种常用方法:typeof、instanceof、constructor 属性以及 Object.prototype.toString。通过对比分析,阐述了各方法的适用场景、局限性及底层原理,特别是针对跨窗口对象检测、原型链修改等特殊情况提供了准确的判断建议。
JavaScript 类型判断概述
在 JavaScript 开发中,准确判断数据类型的场景非常常见,主要存在以下几种判断方式。
第一种是使用一元运算符 typeof。
第二种是使用关系运算符 instanceof。
第三种是利用对象的 constructor 属性。
第四种是通过 prototype 属性进行判断。
typeof 运算符的原理与局限
一、typeof 运算符详解
typeof 运算符返回的结果包含以下几种可能的值。
| 类型 | 结构 |
|---|---|
| Undefined | "undefined" |
| Null | "object" (见下方) |
| 布尔值 | "boolean" |
| 数值 | "number" |
| 字符串 | "string" |
| Symbol (ECMAScript 6 新增) | "symbol" |
| 宿主对象(JS环境提供的,比如浏览器) | Implementation-dependent |
| 函数对象 (implements [[Call]] in ECMA-262 terms) | "function" |
| 任何其他对象 | "object" |
这是一种直观且直接的方法,具体表现可通过以下代码示例观察。
// 以下代码在版本 Google Chrome 45.0.2454.101 m 中测试通过
// Numbers
console.log(typeof 37 === 'number');
console.log(typeof 3.14 === 'number');
console.log(typeof Math.LN2 === 'number');
console.log(typeof Infinity === 'number');
console.log(typeof NaN === 'number'); // 尽管NaN是"Not-A-Number"的缩写,意思是"不是一个数字"
console.log(typeof Number(1) === 'number'); // 不要这样使用!
// Strings
console.log(typeof "" === 'string');
console.log(typeof "bla" === 'string');
console.log(typeof (typeof 1) === 'string'); // console.log(typeof返回的肯定是一个字符串
console.log(typeof String("abc") === 'string'); // 不要这样使用!
// Booleans
console.log(typeof true === 'boolean');
console.log(typeof false === 'boolean');
console.log(typeof Boolean(true) === 'boolean'); // 不要这样使用!
// Symbols
console.log(typeof Symbol() === 'symbol');
console.log(typeof Symbol('foo') === 'symbol');
console.log(typeof Symbol.iterator === 'symbol');
// Undefined
console.log(typeof undefined === 'undefined');
console.log(typeof blabla === 'undefined'); // 一个未定义的变量,或者一个定义了却未赋初值的变量
// Objects 使用Array.isArray或者Object.prototype.toString.call方法可以从基本的对象中区分出数组类型
console.log(typeof {a:1} === 'object');
console.log(typeof [1, 2, 4] === 'object');
console.log(typeof /^[a-zA-Z]{5,20}$/ === 'object');
console.log(typeof {name:'wenzi', age:25} === 'object');
console.log(typeof null === 'object');//true
// 下面的容易令人迷惑,不要这样使用!
console.log(typeof new Boolean(true) === 'object');
console.log(typeof new Number(1) === 'object');
console.log(typeof new Date() === 'object');
console.log(typeof new String("abc") === 'object');
console.log(typeof new Error() === 'object');
// 函数
console.log(typeof function(){} === 'function');
console.log(typeof Math.sin === 'function');
typeof 仅能识别上述 7 种类型
instanceof 运算符与原型链
二、instanceof 运算符详解
instanceof 运算符主要用于识别当前处理对象的类型,它要求开发者明确地确认对象是否属于某个特定的构造函数类型。
首先,需要明确的是,instanceof 的判断结果与 constructor 属性没有直接关系。
var A = function() {};
A.prototype = {};
var B = {};
console.log(A.constructor);//function Function() { [native code] }
console.log(B.constructor);//function Object() { [native code] }
var a = new A();
A.prototype = {};
var b = new A();
b.constructor = A.constructor;
console.log(a.constructor === A);//false
console.log(a.constructor);//function Object() { [native code] }
console.log(typeof A);//function Object() { [native code] }
console.log(a.constructor === b.constructor);//false
console.log(b.constructor);//function Function() { [native code] }
console.log(a instanceof A);//false
console.log(b instanceof A);//true
其次,instanceof 本质上是一种关系运算符,它的核心作用在于判断某个构造函数的 prototype 属性是否存在于另一个要检测对象的原型链上。
var str = new String("hello world");
console.log(str instanceof String);//true
console.log(String instanceof Function);//true
console.log(str instanceof Function);//false
关于为何某些情况下会返回 false,可以参考原文地址:Javascript中一个关于instanceof的问题。
//表达式一的指向
console.log(str.__proto__ === String.prototype);//true
console.log(str instanceof String); //true
//表达式二的指向
console.log(String .__proto__ === Function.prototype);//true
console.log(String instanceof Function);//true
//表达式三的指向
console.log(str .__proto__ === String.prototype);//true
console.log(str .__proto__.__proto__ === String.prototype.__proto__);//true
console.log(str .__proto__.__proto__ === Object.prototype);//true
console.log(str .__proto__.__proto__.__proto__ === null);//true
console.log(str instanceof Object);//true
console.log(str instanceof Function);//false
接下来,我们再看一个较为复杂的 usage 场景。
console.log(Object instanceof Object);//true
console.log(Function instanceof Function);//true
console.log(Number instanceof Number);//false
console.log(String instanceof String);//false
console.log(Function instanceof Object);//true
console.log(Foo instanceof Function);//true
console.log(Foo instanceof Foo);//false
为了理解其背后的原因,我们需要深入探讨以下关键含义。
首先,语言规范中对这一运算符的定义是什么?
其次,JavaScript 的原型继承机制是如何影响这一判断的?
以 Object instanceof Object 为例进行分析。
// 为了方便表述,首先区分左侧表达式和右侧表达式
ObjectL = Object, ObjectR = Object;
console.log(ObjectL instanceof ObjectR);//true
// 下面根据规范逐步推演
console.log(ObjectL.__proto__ === Function.prototype); //true
console.log(ObjectL.__proto__.__proto__ === Object.prototype);//true
同样地,Function instanceof Function 的结果也值得探讨。
FunctionL = Function, FunctionR = Function;
console.log(FunctionL instanceof FunctionR);//true
console.log(FunctionL.__proto__ === Function.prototype); //true
Foo instanceof Foo
function Foo(){}
var foo = new Foo();
FooL = Foo, FooR = Foo;
console.log(FooL instanceof FooR);//false
console.log(FooL.__proto__ === Function.prototype );//true
console.log(FooL.__proto__.__proto__ === Object.prototype );//true
console.log(FooL.__proto__.__proto__.__proto__ === null );//true
此外,instanceof 在 Dojo 库的继承机制中也有具体应用。
在 JavaScript 语言标准中,并不支持多重继承的概念,这与 Java 类似。然而,在 Dojo 框架中使用 declare 声明类时,允许一个类继承自多个父类。
dojo.declare("Aoo",null,{});
dojo.declare("Boo",null,{});
dojo.declare("Foo",[Aoo,Boo],{});
var foo = new Foo();
console.log(foo instanceof Aoo);//true
console.log(foo instanceof Boo);//false
console.log(foo.isInstanceOf(Aoo));//true
console.log(foo.isInstanceOf(Boo));//true
instanceof 在处理多全局对象(例如多个 frame 或多个 window 之间的交互)时也会遇到特殊情况。
在浏览器环境中,脚本可能需要在多个窗口之间进行交互。多个窗口意味着存在多个全局执行环境,每个环境拥有独立的全局对象,进而拥有各自内置类型的构造函数。这可能导致一些问题,例如表达式 [] instanceof window.frames[0].Array 会返回 false,因为 Array.prototype !== window.frames[0].Array.prototype。因此,在这种情况下,你必须使用 Array.isArray(myObj) 或者 Object.prototype.toString.call(myObj) === "[object Array]" 来判断 myObj 是否是数组。
// 以下代码在版本 Google Chrome 45.0.2454.101 m 中测试通过
// Numbers
console.log(37 instanceof Number);//false
console.log( 3.14 instanceof Number);.//false
console.log( Math.LN2 instanceof Number);//false
console.log( Infinity instanceof Number);//false
console.log( NaN instanceof Number); // false尽管NaN是"Not-A-Number"的缩写,意思是"不是一个数字"
console.log( Number(1) instanceof Number); // false不要这样使用!
// Strings
console.log( "" instanceof String);// false
console.log( "bla" instanceof String);// false
console.log( ( 1) instanceof String); // falseconsole.log(返回的肯定是一个字符串
console.log( String("abc") instanceof String); // false 不要这样使用!
// Booleans
console.log( true instanceof Boolean);// false
console.log( false instanceof Boolean);// false
console.log( Boolean(true) instanceof Boolean); //false 不要这样使用!
// Symbols
console.log( Symbol() instanceof Symbol);// false
console.log( Symbol("foo") instanceof Symbol);// false
console.log( Symbol.iterator instanceof Symbol);// false
// Undefined
var blabla;
//console.log( undefined instanceof Undefined);// Uncaught ReferenceError: Undefined is not defined
//console.log( blabla instanceof Undefined); // Uncaught ReferenceError: Undefined is not defined
console.log( undefined instanceof Object);// false
console.log( blabla instanceof Object);// false
// Objects 使用Array.isArray或者Object.prototype.toString.call方法可以从基本的对象中区分出数组类型
console.log( {a:1} instanceof Object);//true
console.log( [1, 2, 4] instanceof Object);//true
console.log( /^[a-zA-Z]{5,20}$/ instanceof Object);//true
console.log( {name:'wenzi', age:25} instanceof Object);//true
console.log( null === Object);//false
// 下面的容易令人迷惑,不要这样使用!
console.log( new Boolean(true) instanceof Object);//true
console.log( new Number(1) instanceof Object);//true
console.log( new Date() instanceof Object);//true
console.log( new String("abc") instanceof Object);//true
console.log( new Error() instanceof Object);//true
// 函数
console.log( function(){} instanceof Function );//true
console.log( Math.sin instanceof Function);//true
需要注意:undefined 和 null 在使用 typeof 检测时返回 "object",这是因为 JavaScript 中没有 Undefined 和 Null 这种全局类型;而 number、string 和 boolean 等基本类型无法通过 instanceof 检测出它们的类型。
constructor 属性的使用与风险
三、constructor 属性详解
在使用 instanceof 检测变量类型时,我们无法直接检测 number、'string'、bool 等基本类型的实例。因此,我们需要换一种方式来解决这个问题。
Object.prototype.constructor 返回一个指向创建了该对象原型的函数引用。需要特别注意的是,该属性的值是那个函数本身,而不是一个包含函数名称的字符串。对于原始值(如 1,true 或 "test"),该属性为只读,所有对象都会从它的原型上继承一个 constructor 属性。
constructor 本来是原型对象上的属性,指向构造函数。但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用 constructor 属性的。
function Person(){
}
var Tom = new Person();
console.log(Tom.constructor === Person);//true
不过要注意,constructor 属性是可以被修改的,这会导致检测出的结果不正确。
function Person(){
}
function Student(){
}
Student.prototype = new Person();
var John = new Student();
console.log(John.constructor==Student); // false
console.log(John.constructor==Person); // true
例如,改变这个对象的 constructor 属性的值。
function Type() { };
var types = [
new Array,
[],
new Boolean,
true, // remains unchanged
new Date,
new Error,
new Function,
function(){},
Math,
new Number,
1, // remains unchanged
new Object,
{},
new RegExp,
/(?:)/,
new String,
"test" // remains unchanged
];
for(var i = 0; i < types.length; i++) {
types[i].constructor = Type;
types[i] = [ types[i].constructor, types[i] instanceof Type, types[i].toString() ];
};
console.log( types.join("\n") );
除了 undefined 和 null,其他类型的变量均能使用 constructor 判断出类型。
Object.prototype.toString 的通用性
四、万能的 Object.prototype.toString.call 方法
使用 toString() 方法来检测对象类型是一种更为可靠的手段。
function Type() { };
var toString = Object.prototype.toString;
console.log(toString.call(new Date) === '[object Date]');//true
console.log(toString.call(new String) ==='[object String]');//true
console.log(toString.call(new Function) ==='[object Function]');//true
console.log(toString.call(Type) ==='[object Function]');//true
console.log(toString.call('str') ==='[object String]');//true
console.log(toString.call(Math) === '[object Math]');//true
console.log(toString.call(true) ==='[object Boolean]');//true
console.log(toString.call(/^[a-zA-Z]{5,20}$/) ==='[object RegExp]');//true
console.log(toString.call({name:'wenzi', age:25}) ==='[object Object]');//true
console.log(toString.call([1, 2, 3, 4]) ==='[object Array]');//true
//Since JavaScript 1.8.5
console.log(toString.call(undefined) === '[object Undefined]');//true
console.log(toString.call(null) === '[object Null]');//true
附上判断函数 Javascript中的数据类型知多少,供参考。
五、jquery 的实现 jquery: "1.8.2"。
jquery 中提供了一个 $.type 的接口,看看代码实现。
var m = Object.prototype.toString //501行
E = {};//512行
isFunction: function(a) { //645行
return p.type(a) === "function"
},
isArray: Array.isArray || function(a) {
return p.type(a) === "array"
}
,
isWindow: function(a) {
return a != null && a == a.window
},
isNumeric: function(a) {
return !isNaN(parseFloat(a)) && isFinite(a)
},
type: function(a) {
return a == null ? String(a) : E[m.call(a)] || "object"
},
isPlainObject: function(a) {
if (!a || p.type(a) !== "object" || a.nodeType || p.isWindow(a))
return !1;
try {
if (a.constructor && !n.call(a, "constructor") && !n.call(a.constructor.prototype, "isPrototypeOf"))
return !1
} catch (c) {
return !1
}
var d;
for (d in a)
;
return d === b || n.call(a, d)
},
isEmptyObject: function(a) {
var b;
for (b in a)
return !1;
return !0
},
可以看出来,jquery 中就是用 Object.prototype.toString.call 实现的。
JavaScript闭包与类(Class)的区别及适用场景对比
2026-11-05 15:48:57
JavaScript入门要学ES6吗?let与箭头函数等新特性解析
2026-11-05 14:49:16
JavaScript 8种数据类型详解:基本类型与引用类型的区别
2026-11-05 14:25:15
怎么在JavaScript中使用async/await优化Ajax异步流程
2026-11-05 13:56:15
JavaScript replace回调函数实现动态正则替换逻辑
2026-11-05 13:52:13
