Check if a value is an object in JavaScript
技术背景
在 JavaScript 中,准确判断一个值是否为对象并非易事。因为 JavaScript 的数据类型系统较为复杂,typeof
操作符在判断对象时存在一些问题,例如它会将 null
也判定为 object
,同时对于函数和数组的判定也不符合某些场景的需求。因此,我们需要掌握多种方法来准确判断一个值是否为对象。
实现步骤
1. 定义“对象”和“原始值”
在 JavaScript 中,每个值要么是对象,要么是原始值。原始值包括:string
、number
、bigint
、boolean
、undefined
、symbol
和 null
。而对象则是除了这些原始值之外的其他值,例如 Object.prototype
、数组、函数等。
2. 各种判断方法
使用 typeof
和 Array.isArray
1 2 3
| function isObject(o) { return o !== null && typeof o === 'object' && Array.isArray(o) === false; }
|
使用 instanceof
和 constructor
1 2 3
| function isObject(o) { return o instanceof Object && o.constructor === Object; }
|
使用 Object.prototype.toString.call
1 2 3
| function isObject(obj) { return Object.prototype.toString.call(obj) === '[object Object]'; }
|
使用 Object
构造函数
1 2 3
| function isObject(value) { return Object(value) === value; }
|
使用 constructor.name
1 2 3
| function isObject(obj) { return obj != null && obj.constructor.name === "Object"; }
|
使用可选链操作符
1
| const isObj = o => o?.constructor === Object;
|
3. 不同方法的适用场景
- 排除
null
、数组和函数:可以使用 typeof x === 'object' && !Array.isArray(x) && x !== null
。 - 判断普通对象:可以使用
Object.prototype.toString.call(obj) === '[object Object]'
或者 obj.constructor.toString().indexOf("Object") > -1
。 - 判断是否为任何非原始值对象:可以使用
value != null && (typeof value === 'object' || typeof value === 'function')
。
核心代码
综合判断函数
1 2 3 4 5 6 7 8 9 10 11 12
| function isObject(o) { return null != o && typeof o === 'object' && Object.prototype.toString.call(o) === '[object Object]'; }
function isDerivedObject(o) { return !isObject(o) && null != o && (typeof o === 'object' || typeof o === 'function') && /^\[object /.test(Object.prototype.toString.call(o)); }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
console.log( 'is null an object?', isObject(null) );
console.log( 'is null a derived object?', isDerivedObject(null) );
console.log( 'is 1234 an object?', isObject(1234) );
console.log( 'is 1234 a derived object?', isDerivedObject(1234) );
console.log( 'is new Number(1234) an object?', isObject(new Number(1234)) );
console.log( 'is new Number(1234) a derived object?', isDerivedObject(1234) );
console.log( 'is (new (function (){})) an object?', isObject((new (function (){}))) );
console.log( 'is (new (function (){})) a derived object?', isObject((new (function (){}))) );
console.log( 'is {} an object?', isObject({}) );
console.log( 'is {} a derived object?', isDerivedObject({}) );
console.log( 'is Array an object?', isObject([]) );
console.log( 'is Array a derived object?', isDerivedObject([]) );
console.log( 'is Date an object?', isObject(new Date()) );
console.log( 'is Date a derived object?', isDerivedObject(new Date()) );
console.log( 'is function an object?', isObject(function(){}) );
console.log( 'is function a derived object?', isDerivedObject(function(){}) );
|
最佳实践
- 在实际开发中,根据具体需求选择合适的判断方法。如果需要排除数组和
null
,可以使用 typeof x === 'object' && !Array.isArray(x) && x !== null
。 - 对于需要判断普通对象的场景,
Object.prototype.toString.call(obj) === '[object Object]'
是一个比较可靠的方法。 - 如果使用框架,可以优先使用框架提供的判断函数,例如 jQuery 的
jQuery.type(obj)
、Angular 的 angular.isObject(obj)
、Underscore 和 Lodash 的 _.isObject(obj)
。
常见问题
typeof
操作符的局限性
typeof
操作符会将 null
判定为 object
,同时对于函数和数组的判定也不符合某些场景的需求。例如:
1 2 3
| typeof null; typeof []; typeof function() {};
|
instanceof
的局限性
instanceof
无法正确判断 Object.prototype
和 Object.create(null)
是否为对象。例如:
1 2 3 4 5
| function isObject(val) { return val instanceof Object; } isObject(Object.prototype); isObject(Object.create(null));
|
Object.prototype.toString.call
的局限性
Object.prototype.toString.call
会对所有原始值返回 [object 类型名]
,可能会导致误判。例如:
1 2
| Object.prototype.toString.call(3); Object.prototype.toString.call(new Number(3));
|