1. 如何判断一个对象是空对象
首先要先判断是否是对象, 方法是 Object.prototype.toString.call(obj) === "[object Object]"返回true。 然后在判断是否是空对象
- 方法1: 使用JSON.stringify去判断转成字符串是否等于"{}"
Object.prototype.toString.call(obj) === "[object Object]" && JSON.stringify(obj) === "{}"
- 方法2: 使用es6的Object.keys(obj)去判断返回的对象长度是否等于0。如果是0,则证明是空对象
Object.prototype.toString.call(obj) === "[object Object]" && Object.keys(obj).length === 0
Object.keys
它会先列举对象的所有可枚举属性键名到数组中,再判断数组的长度 Object.keys(obj).length === 0 对不可枚举的属性无效
JSON.stringify
JSON.stringify(obj) === {} 其无法转化函数键值对,同时对不可枚举的属性束手无策 Object.getOwnPropertyNames 可以获取到不可枚举的属性键
还要注意一种情况对象属性为 Symbol 的时候,Object.getOwnPropertyNames 无法检测出来,需要单独处理
当一个 symbol 类型的值在属性赋值语句中被用作标识符,该属性(像这个 symbol 一样)是匿名的;并且是不可枚举的。因为这个属性是不可枚举的,它不会在循环结构 “for( ... in ...)” 中作为成员出现,也因为这个属性是匿名的,它同样不会出现在 “Object.getOwnPropertyNames()” 的返回数组里。这个属性可以通过创建时的原始 symbol 值访问到,或者通过遍历 “Object.getOwnPropertySymbols()” 返回的数组。
Object.getOwnPropertySymbols(obj).length === 0
Reflect.ownKeys
Reflect.ownKeys 既可以解决非枚举属性也可以解决Symbol属性
2. 终极版判断一个变量是不是空对象封装成函数如下
function isEmptyObj(obj) {
return obj !== null
&& typeof obj === 'object'
&& !Array.isArray(obj)
&& (Object.getOwnPropertyNames(obj).length === 0)
&& (Object.getOwnPropertySymbols(obj).length === 0)
}
// or
function isEmptyObj(obj) {
return (Object.prototype.toString.call(obj) === '[object Object]')
&& (Object.getOwnPropertyNames(obj).length === 0)
&& (Object.getOwnPropertySymbols(obj).length === 0)
}
// or
function isEmptyObj(obj) {
return (Object.prototype.toString.call(obj) === '[object Object]') && (Reflect.ownKeys(obj).length === 0)
}
3. 如何判断两个对象相等
最终的eq函数。 出自文章JavaScript 专题之如何判断两个对象相等
var toString = Object.prototype.toString;
function isFunction(obj) {
return toString.call(obj) === '[object Function]'
}
function eq(a, b, aStack, bStack) {
// === 结果为 true 的区别出 +0 和 -0
if (a === b) return a !== 0 || 1 / a === 1 / b;
// typeof null 的结果为 object ,这里做判断,是为了让有 null 的情况尽早退出函数
if (a == null || b == null) return false;
// 判断 NaN
if (a !== a) return b !== b;
// 判断参数 a 类型,如果是基本类型,在这里可以直接返回 false
var type = typeof a;
if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
// 更复杂的对象使用 deepEq 函数进行深度比较
return deepEq(a, b, aStack, bStack);
};
function deepEq(a, b, aStack, bStack) {
// a 和 b 的内部属性 [[class]] 相同时 返回 true
var className = toString.call(a);
if (className !== toString.call(b)) return false;
switch (className) {
case '[object RegExp]':
case '[object String]':
return '' + a === '' + b;
case '[object Number]':
if (+a !== +a) return +b !== +b;
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
return +a === +b;
}
var areArrays = className === '[object Array]';
// 不是数组
if (!areArrays) {
// 过滤掉两个函数的情况
if (typeof a != 'object' || typeof b != 'object') return false;
var aCtor = a.constructor,
bCtor = b.constructor;
// aCtor 和 bCtor 必须都存在并且都不是 Object 构造函数的情况下,aCtor 不等于 bCtor, 那这两个对象就真的不相等啦
if (aCtor == bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && isFunction(bCtor) && bCtor instanceof bCtor) && ('constructor' in a && 'constructor' in b)) {
return false;
}
}
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
// 检查是否有循环引用的部分
while (length--) {
if (aStack[length] === a) {
return bStack[length] === b;
}
}
aStack.push(a);
bStack.push(b);
// 数组判断
if (areArrays) {
length = a.length;
if (length !== b.length) return false;
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
}
// 对象判断
else {
var keys = Object.keys(a),
key;
length = keys.length;
if (Object.keys(b).length !== length) return false;
while (length--) {
key = keys[length];
if (!(b.hasOwnProperty(key) && eq(a[key], b[key], aStack, bStack))) return false;
}
}
aStack.pop();
bStack.pop();
return true;
}
console.log(eq(0, 0)) // true
console.log(eq(0, -0)) // false
console.log(eq(NaN, NaN)); // true
console.log(eq(Number(NaN), Number(NaN))); // true
console.log(eq('Curly', new String('Curly'))); // true
console.log(eq([1], [1])); // true
console.log(eq({ value: 1 }, { value: 1 })); // true
var a, b;
a = { foo: { b: { foo: { c: { foo: null } } } } };
b = { foo: { b: { foo: { c: { foo: null } } } } };
a.foo.b.foo.c.foo = a;
b.foo.b.foo.c.foo = b;
console.log(eq(a, b)) // true
4. 如何判断一个变量是对象
Object.prototype.toString.call(obj).slice(8, -1).toLowerCase() === 'object'// 返回true, 即表示obj为对象obj instanceof Object// 返回true, 即表示obj为对象
5. 判断对象中是否有某属性
1. in运算符
如果指定的属性在指定的对象或原型链中, 则in运算符返回true
这种方式的局限性就是无法区分自身和原型链上的属性, 在只判断自身属性是否存在时, 这种方式就不适应了。 这时要用hasOwnProperty()
2. hasOwnProperty()
只有存在该属性时, 才会返回true。 适用于判断自身属性的场景。 有时在遍历自身属性的时候, 可以把for... in... 和 hasOwnProperty()结合使用