对象总结,object总结

469 阅读4分钟

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. 如何判断一个变量是对象

  1. Object.prototype.toString.call(obj).slice(8, -1).toLowerCase() === 'object' // 返回true, 即表示obj为对象
  2. obj instanceof Object // 返回true, 即表示obj为对象

5. 判断对象中是否有某属性

1. in运算符

如果指定的属性在指定的对象或原型链中, 则in运算符返回true 这种方式的局限性就是无法区分自身和原型链上的属性, 在只判断自身属性是否存在时, 这种方式就不适应了。 这时要用hasOwnProperty()

2. hasOwnProperty()

只有存在该属性时, 才会返回true。 适用于判断自身属性的场景。 有时在遍历自身属性的时候, 可以把for... in...hasOwnProperty()结合使用