JavaScript 类型判断的四种方式与原理详解

作者:半夏时间:2026-10-29 15:36:53浏览:560

本文详细解析 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 实现的。

作者最新文章