一、如何判断空对象
①最常用 的是 Object.keys(obj).length === 0。它检查对象自身的、可枚举的属性名个数,性能好,能满足绝大多数业务场景。
②如果需要考虑不可枚举属性 (例如通过 Object.defineProperty 创建的属性),可以使用 Object.getOwnPropertyNames(obj).length === 0。
③如果需要考虑最全面的情况(包括 Symbol 类型的属性键),则要使用 Reflect.ownKeys(obj).length === 0。
④JSON.stringify(obj) === '{}' 方法虽然简单,但会忽略函数、undefined 等属性值,存在局限性,一般不推荐在严格的逻辑判断中使用。
1、JSON.stringify()
将对象转换为 JSON 字符串,然后判断其是否等于 '{}'
javascript
const obj = {};
const isEmpty = JSON.stringify(obj) === '{}';
console.log(isEmpty); // true
const obj2 = { name: 'Tom' };
console.log(JSON.stringify(obj2) === '{}'); // false
缺点:
①性能问题:将整个对象序列化为字符串是一个相当耗时的行为
②如果属性值是函数,undefined,Symbol,JSON.stringigy()会忽略它们
javascript
const obj = { a: undefined, b: function() {}, c: Symbol() };
console.log(JSON.stringify(obj) === '{}'); // true (但实际它有属性,所以判断不准确)
2、Object.keys() (最常用,兼顾性能和准确度)
Object.keys(obj) 返回对象自身可枚举属性名组成的数组。判断数组长度是否为 0 即可。
javascript
const obj = {};
const isEmpty = Object.keys(obj).length === 0;
console.log(isEmpty); // true
const obj2 = { name: 'Tom' };
console.log(Object.keys(obj2).length === 0); // false
const obj3 = { a: undefined };
console.log(Object.keys(obj3).length === 0); // false (因为它确实有一个属性 'a')
3、Object.getOwnPropertyNames()可以发现不可枚举属性
返回所有属性名构成的数组,包含不可枚举属性,但是不包含Symbol类型的属性
javascript
const obj = {};
Object.defineProperty(obj, 'hidden', {
value: 'secret',
enumerable: false // 设置为不可枚举
});
console.log(Object.keys(obj).length === 0); // true (keys 无法发现不可枚举属性)
console.log(Object.getOwnPropertyNames(obj).length === 0); // false (发现了不可枚举属性)
4、Reflect.ownKeys()(最彻底最全面的,返回所有属性名构成的数组)
javascript
const obj = {};
const sym = Symbol('key');
obj[sym] = 'symbol value';
Object.defineProperty(obj, 'hidden', {
value: 'secret',
enumerable: false
});
console.log(Reflect.ownKeys(obj).length === 0); // false (包含了 Symbol 和不可枚举属性)
5、for...in(通常不推荐,但需要了解)
for...in 循环会遍历对象的可枚举属性 ,包括继承自原型链的属性。因此用它判断时,通常需要配合 hasOwnProperty 来过滤。
javascript
function isEmpty(obj) {
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
return false
}
return true
}
}
const obj = {}
console.log(isEmpty(obj))
// 但是如果原型链上有可枚举属性
const protoObj = {inherited: 'value'}
// 以protoObj为原型创建新的对象
const childObj = Object.create(protoObj)
// true,(因为 childObj 自身没有属性,虽然原型链上有,但 hasOwnProperty 过滤掉了)
console.log(isEmpty(childObj))
二、如何区分数据类型
javascript的数据类型分为两大类:
基本类型:string, number, boolean, undefined, null
引用类型:object, function, array, date
1、typeof操作符
typeof 适合判断 string, number, boolean, undefined, symbol, bigint 和 function。对于 null 和大部分对象类型,它只能返回 "object",无法进一步区分
javascript
console.log(typeof 'hello'); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol()); // "symbol"
console.log(typeof 123n); // "bigint"
console.log(typeof function(){});// "function" (函数是对象的一种,但 typeof 特殊处理)
// ---------- 著名的坑 ----------
console.log(typeof null); // "object" (这是一个存在了20多年的bug)
console.log(typeof [1, 2, 3]); // "object"
console.log(typeof {}); // "object"
console.log(typeof new Date()); // "object"
2、instanceof 操作符 (用于检查对象的具体类型)
javascript
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true (因为数组的原型链上也有 Object.prototype)
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date);// true
console.log(/regex/ instanceof RegExp); // true
// 基本类型用 instanceof 通常为 false
console.log('hello' instanceof String); // false (除非是 new String('hello') 创建的对象)
3、Object.prototype.toString.call() (终极方案,最准确)
这是最强大、最准确的类型判断方法。它可以返回任何数据类型的内部 [[Class]] 属性。
javascript
const toString = Object.prototype.toString;
console.log(toString.call('hello')); // "[object String]"
console.log(toString.call(42)); // "[object Number]"
console.log(toString.call(true)); // "[object Boolean]"
console.log(toString.call(undefined)); // "[object Undefined]"
console.log(toString.call(null)); // "[object Null]"
console.log(toString.call(Symbol())); // "[object Symbol]"
console.log(toString.call(123n)); // "[object BigInt]"
console.log(toString.call([])); // "[object Array]"
console.log(toString.call({})); // "[object Object]"
console.log(toString.call(function(){})); // "[object Function]"
console.log(toString.call(new Date())); // "[object Date]"
console.log(toString.call(/regex/)); // "[object RegExp]"
console.log(toString.call(new Error())); // "[object Error]"
console.log(toString.call(document)); // "[object HTMLDocument]"
console.log(toString.call(globalThis)); // "[object global]" 或 "[object Window]"