Object

js操作object

Object 里有什么

!Object.png

1
2
3
console.log(Object.getOwnPropertyNames(Object))
console.log(Object.getOwnPropertyNames(Object.prototype))
console.log(Object.getOwnPropertyNames(new Object()))

Object 对象自身的属性

1
(26) ['length', 'name', 'prototype', 'assign', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'hasOwn', 'is', 'preventExtensions', 'seal', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getPrototypeOf', 'setPrototypeOf', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'entries', 'fromEntries', 'values', 'groupBy']

Object.prototype

1
(12) ['constructor', '__defineGetter__', '__defineSetter__', 'hasOwnProperty', '__lookupGetter__', '__lookupSetter__', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'valueOf', '__proto__', 'toLocaleString']

在 JavaScript 中,Object 是所有对象的祖先,它本身也具有一些属性和方法。这些属性和方法可以用于操作对象,包括创建对象、获取对象属性、修改对象属性等。以下是一些 Object 对象自身的常用属性和方法:

一、属性

(一)Object.prototype
  • 描述:所有对象都会继承 Object.prototype 上的属性和方法。这是因为 Object 是 JavaScript 中所有对象的原型。例如,hasOwnProperty 方法就是定义在 Object.prototype 上的,所有对象都可以使用这个方法来判断自身是否具有某个特定的属性。
  • 示例
    1
    2
    3
    4
    let obj = { name: "Kimi" };
    console.log(obj.hasOwnProperty("name")); // 输出 true
    console.log(obj.hasOwnProperty("toString")); // 输出 false
    // 因为 toString 是继承自 Object.prototype 的,不是 obj 自身的属性
(二)Object.length
  • 描述Object 对象的 length 属性值为 1。这个属性在实际开发中很少用到,它主要是用于一些 JavaScript 内部的机制,比如函数调用时参数的处理等。
  • 示例
    1
    console.log(Object.length); // 输出 1

二、方法

(一)创建对象相关的方法
  1. Object.create(proto[, propertiesObject])
  • 功能:创建一个新对象,使用现有的对象来提供新创建对象的 __proto__(原型)。
  • 参数
    • proto:新创建对象的原型对象。
    • propertiesObject(可选):一个对象,其自身属性将被定义为新创建对象的属性。
  • 返回值:新创建的对象。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    let protoObj = {
    greet: function() {
    console.log("Hello");
    }
    };
    let newObj = Object.create(protoObj);
    newObj.greet(); // 控制台输出 Hello
  1. Object.assign(target, ...sources)
  • 功能:将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
  • 参数
    • target:目标对象。
    • sources:源对象。
  • 返回值:目标对象。
  • 示例
    1
    2
    3
    4
    5
    let target = { a: 1 };
    let source1 = { b: 2 };
    let source2 = { c: 3 };
    Object.assign(target, source1, source2);
    console.log(target); // 输出 { a: 1, b: 2, c: 3 }
(二)获取对象属性相关的方法
  1. Object.getOwnPropertyDescriptor(obj, prop)
  • 功能:返回指定对象上一个自有属性(不是继承属性)的属性描述符。
  • 参数
    • obj:需要检索其属性描述符的对象。
    • prop:需要检索其属性描述符的属性的名称。
  • 返回值:一个对象,描述了给定属性的配置。如果指定的属性不存在于对象上,则返回 undefined
  • 示例
    1
    2
    3
    4
    5
    6
    7
    let obj = {
    name: "Kimi",
    age: 20
    };
    let descriptor = Object.getOwnPropertyDescriptor(obj, "name");
    console.log(descriptor);
    // 输出 { value: "Kimi", writable: true, enumerable: true, configurable: true }
  1. Object.getOwnPropertyDescriptors(obj)
  • 功能:返回一个对象,它包含了指定对象自身的所有属性的属性描述符。
  • 参数obj - 需要检索其属性描述符的对象。
  • 返回值:一个对象,其属性是给定对象的属性名,属性值是对应属性的属性描述符。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let obj = {
    name: "Kimi",
    age: 20
    };
    let descriptors = Object.getOwnPropertyDescriptors(obj);
    console.log(descriptors);
    // 输出 {
    // name: { value: "Kimi", writable: true, enumerable: true, configurable: true },
    // age: { value: 20, writable: true, enumerable: true, configurable: true }
    // }
  1. Object.keys(obj)
  • 功能:返回一个由给定对象的自身可枚举属性组成的数组。
  • 参数obj - 需要返回其键名的对象。
  • 返回值:一个数组,其元素是字符串,表示给定对象的键名。
  • 示例
    1
    2
    3
    4
    5
    let obj = {
    name: "Kimi",
    age: 20
    };
    console.log(Object.keys(obj)); // 输出 [ 'name', 'age' ]
  1. Object.values(obj)
  • 功能:返回一个给定对象自身可枚举属性的值的数组。
  • 参数obj - 需要返回其键值的对象。
  • 返回值:一个数组,其元素是给定对象的键值。
  • 示例
    1
    2
    3
    4
    5
    let obj = {
    name: "Kimi",
    age: 20
    };
    console.log(Object.values(obj)); // 输出 [ 'Kimi', 20 ]
  1. Object.entries(obj)
  • 功能:返回一个给定对象自身可枚举属性的键值对的数组。
  • 参数obj - 需要返回其键值对的对象。
  • 返回值:一个数组,其元素是 [key, value] 形式的数组,表示给定对象的键值对。
  • 示例
    1
    2
    3
    4
    5
    let obj = {
    name: "Kimi",
    age: 20
    };
    console.log(Object.entries(obj)); // 输出 [ [ 'name', 'Kimi' ], [ 'age', 20 ] ]
(三)修改对象属性相关的方法
  1. Object.defineProperty(obj, prop, descriptor)
  • 功能:在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
  • 参数
    • obj:要在其上定义属性的对象。
    • prop:要定义或修改的属性的名称。
    • descriptor:将被定义或修改的属性的描述符。
  • 返回值:被定义或修改属性的对象。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let obj = {};
    Object.defineProperty(obj, "name", {
    value: "Kimi",
    writable: false, // 设置为只读
    enumerable: true,
    configurable: true
    });
    console.log(obj.name); // 输出 Kimi
    obj.name = "Moonshot"; // 由于 writable 为 false,这行代码不会改变 obj.name 的值
    console.log(obj.name); // 仍然输出 Kimi
  1. Object.defineProperties(obj, props)
  • 功能:在一个对象上定义多个新属性,或者修改多个现有属性,并返回该对象。
  • 参数
    • obj:要在其上定义属性的对象。
    • props:一个对象,其自身属性指定了要在 obj 上定义或修改的属性。
  • 返回值:被定义或修改属性的对象。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let obj = {};
    Object.defineProperties(obj, {
    name: {
    value: "Kimi",
    writable: true
    },
    age: {
    value: 20,
    writable: false
    }
    });
    console.log(obj.name); // 输出 Kimi
    console.log(obj.age); // 输出 20
    obj.age = 21; // 由于 age 的 writable 为 false,这行代码不会改变 obj.age 的值
    console.log(obj.age); // 仍然输出 20
(四)其他方法
  1. Object.is(value1, value2)
  • 功能:判断两个值是否相同。这个方法比严格相等运算符(===)更严格,能够区分 -0+0,并且认为 NaNNaN 是相等的。
  • 参数
    • value1:第一个要比较的值。
    • value2:第二个要比较的值。
  • 返回值:如果两个值相同,返回 true;否则返回 false
  • 示例
    1
    2
    3
    console.log(Object.is(0, -0)); // 输出 false
    console.log(Object.is(NaN, NaN)); // 输出 true
    console.log(Object.is(1, 1)); // 输出 true
  1. Object.seal(obj)
  • 功能:封闭一个对象,阻止添加新的属性并将所有现有属性标记为不可配置。被封闭的对象仍然可以更改其属性的值。
  • 参数obj - 要封闭的对象。
  • 返回值:被封闭的对象。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    let obj = {
    name: "Kimi"
    };
    Object.seal(obj);
    obj.age = 20; // 这行代码不会添加 age 属性
    console.log(obj); // 输出 { name: "Kimi" }
    obj.name = "Moonshot"; // 可以修改属性值
    console.log(obj); // 输出 { name: "Moonshot" }
    1. Object.freeze(obj)
  • 功能:冻结一个对象。冻结指的是不能向这个对象添加新的属性,不能删除已有属性,不能修改已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。也就是说,这个对象永远不变。
  • 参数obj - 要冻结的对象。
  • 返回值:被冻结的对象。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    let obj = {
    name: "Kimi"
    };
    Object.freeze(obj);
    obj.age = 20; // 这行代码不会添加 age 属性
    console.log(obj); // 输出 { name: "Kimi" }
    obj.name = "Moonshot"; // 由于对象被冻结,这行代码不会改变 name 的值
    console.log(obj); // 仍然输出 { name: "Kimi" }

Object.prototype 的属性

Object.prototype 是 JavaScript 中所有对象的原型,它提供了一些基本的方法和属性,这些方法和属性会被所有对象继承。以下是一些 Object.prototype 上的常用属性和方法:

一、属性

(一)constructor
  • 描述:指向创建该对象的构造函数。这个属性可以用来判断对象是由哪个构造函数创建的。
  • 示例
    1
    2
    3
    4
    5
    6
    let obj = new Object();
    console.log(obj.constructor === Object); // 输出 true
    let arr = new Array();
    console.log(arr.constructor === Array); // 输出 true
    let func = new Function();
    console.log(func.constructor === Function); // 输出 true

二、方法

(一)hasOwnProperty(prop)
  • 功能:判断对象自身是否具有指定的属性,不考虑继承的属性。
  • 参数prop - 要检查的属性名称。
  • 返回值:如果对象自身具有指定的属性,返回 true;否则返回 false
  • 示例
    1
    2
    3
    4
    5
    let obj = {
    name: "Kimi"
    };
    console.log(obj.hasOwnProperty("name")); // 输出 true
    console.log(obj.hasOwnProperty("toString")); // 输出 false
(二)isPrototypeOf(object)
  • 功能:判断当前对象是否是另一个对象的原型。
  • 参数object - 要检查的对象。
  • 返回值:如果是当前对象是另一个对象的原型,返回 true;否则返回 false
  • 示例
    1
    2
    3
    let protoObj = {};
    let obj = Object.create(protoObj);
    console.log(protoObj.isPrototypeOf(obj)); // 输出 true
(三)propertyIsEnumerable(prop)
  • 功能:判断对象自身指定的属性是否可枚举。
  • 参数prop - 要检查的属性名称。
  • 返回值:如果对象自身指定的属性可枚举,返回 true;否则返回 false
  • 示例
    1
    2
    3
    4
    5
    let obj = {
    name: "Kimi"
    };
    console.log(obj.propertyIsEnumerable("name")); // 输出 true
    console.log(obj.propertyIsEnumerable("toString")); // 输出 false
(四)toLocaleString()
  • 功能:返回对象的本地字符串表示。这个方法通常会被 toString 方法调用。
  • 返回值:对象的本地字符串表示。
  • 示例
    1
    2
    3
    4
    5
    let obj = {
    name: "Kimi",
    age: 20
    };
    console.log(obj.toLocaleString()); // 输出 [object Object]
(五)toString()
  • 功能:返回对象的字符串表示。这个方法经常被用来获取对象的类型信息。
  • 返回值:对象的字符串表示。
  • 示例
    1
    2
    3
    4
    5
    6
    let obj = new Object();
    console.log(obj.toString()); // 输出 [object Object]
    let arr = new Array();
    console.log(arr.toString()); // 输出 [object Array]
    let func = new Function();
    console.log(func.toString()); // 输出 [object Function]
(六)valueOf()
  • 功能:返回对象的原始值。对于 Object 对象,它返回对象本身。
  • 返回值:对象的原始值。
  • 示例
    1
    2
    let obj = new Object();
    console.log(obj.valueOf() === obj); // 输出 true

三、注意事项

  • 由于所有对象都继承自 Object.prototype,所以在使用对象时,这些方法和属性都是可用的。但是,如果在对象上定义了同名的属性或方法,会覆盖继承自 Object.prototype 的属性或方法。
  • 在进行对象属性检查时,通常使用 hasOwnProperty 方法来区分对象自身的属性和继承的属性。例如,在遍历对象属性时,如果只关心对象自身的属性,可以结合 for...in 循环和 hasOwnProperty 方法使用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    let obj = {
    name: "Kimi",
    age: 20
    };
    for (let prop in obj) {
    if (obj.hasOwnProperty(prop)) {
    console.log(prop); // 输出 name 和 age
    }
    }
    这样可以避免遍历到继承自 Object.prototype 的属性。

Object

在 JavaScript 中,操作对象是常见的任务之一。对象是一种键值对的集合,可以用来存储和组织数据。以下是一些基本的操作对象的方法和示例:

创建对象

使用对象字面量

1
2
3
4
5
const person = {
name: 'Alice',
age: 25,
city: 'New York'
};

使用构造函数 Object()

1
2
3
4
const person = new Object();
person.name = 'Alice';
person.age = 25;
person.city = 'New York';

访问对象属性

使用点符号

1
console.log(person.name); // 输出: Alice

使用方括号

1
console.log(person['age']); // 输出: 25

方括号方式允许使用变量作为属性名,或者访问包含特殊字符的属性名。

修改对象属性

1
person.age = 26; // 修改属性值

添加新属性

1
person.job = 'Engineer'; // 添加新属性

删除属性

1
delete person.city; // 删除属性

检查属性是否存在

使用 in 操作符

1
2
3
if ('name' in person) {
console.log('Name property exists');
}

使用 hasOwnProperty 方法

1
2
3
if (person.hasOwnProperty('age')) {
console.log('Age property exists');
}

遍历对象

使用 for...in 循环

1
2
3
4
5
for (const key in person) {
if (person.hasOwnProperty(key)) {
console.log(key, person[key]);
}
}

for...in 循环会遍历对象的所有可枚举属性,包括继承的属性。使用 hasOwnProperty 可以确保只遍历对象自身的属性.

使用 Object.keys(), Object.values(), Object.entries()

1
2
3
4
5
6
7
8
9
// 获取对象的键数组
const keys = Object.keys(person);
console.log(keys); // ['name', 'age', 'job']
// 获取对象的值数组
const values = Object.values(person);
console.log(values); // ['Alice', 26, 'Engineer']
// 获取对象的键值对数组
const entries = Object.entries(person);
console.log(entries); // [['name', 'Alice'], ['age', 26], ['job', 'Engineer']]

拷贝对象

浅拷贝

1
const personCopy = { ...person }; // 使用展开运算符

展开运算符会创建一个新对象,并将原对象的属性复制到新对象中。注意,这是浅拷贝,对于对象属性中的对象或数组,拷贝的是引用,而不是值.

深拷贝

深拷贝可以通过递归函数或使用第三方库(如 Lodash 的 _.cloneDeep)来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepCopy(obj[key]);
}
}
return result;
}
const deepPersonCopy = deepCopy(person);

对象解构

1
2
const { name, age } = person;
console.log(name, age); // Alice 26

对象解构允许你从对象中提取多个属性,并将它们赋值给变量。

对象合并

使用对象字面量

1
2
3
4
5
6
7
8
9
const additionalInfo = {
email: 'alice@example.com'
};
const fullPerson = {
...person,
...additionalInfo
};
console.log(fullPerson);
// { name: 'Alice', age: 26, job: 'Engineer', email: 'alice@example.com' }

使用展开运算符可以合并多个对象。如果存在相同的属性,后面的属性值会覆盖前面的属性值.

对象的其他方法

  • Object.assign():用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它返回目标对象.
  • Object.freeze():冻结一个对象,使其不可修改.
  • Object.seal():密封一个对象,使其不可扩展,但属性值仍可修改.
  • Object.is():比较两个值是否严格相等,与 === 类似,但有一些细微差别,例如处理 NaN-0 等.
    这些是 JavaScript 中操作对象的一些常用方法和技巧。掌握它们可以帮助你更高效地处理和组织数据。

prototype和__proto__的区别

在 JavaScript 中,prototype__proto__ 是两个非常重要的概念,它们都与原型链有关,但它们的用途和含义有所不同。以下是对它们的详细区别:

一、定义和用途

(一)prototype

  • 定义prototype 是每个函数(Function)对象都有一个属性。当你使用一个函数作为构造函数来创建对象实例时,这个函数的 prototype 属性指向一个对象,这个对象被称为原型对象(prototype object)。所有通过这个构造函数创建的实例都会共享这个原型对象上的属性和方法。
  • 用途prototype 主要用于定义构造函数的原型对象,以便所有通过该构造函数创建的实例可以共享这些属性和方法。这有助于实现代码复用和节省内存。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function Person(name, age) {
    this.name = name;
    this.age = age;
    }
    Person.prototype.sayHello = function() {
    console.log("Hello, my name is " + this.name);
    };
    let person1 = new Person("Kimi", 20);
    let person2 = new Person("Moonshot", 30);
    person1.sayHello(); // 控制台输出 Hello, my name is Kimi
    person2.sayHello(); // 控制台输出 Hello, my name is Moonshot

(二)__proto__

  • 定义__proto__ 是一个对象实例的属性,它指向该对象的原型对象。这个属性是一个非标准的属性,但在大多数现代浏览器中都支持。它提供了一种方式来访问对象的原型对象。
  • 用途__proto__ 主要用于在运行时访问和修改对象的原型对象。通过 __proto__,你可以查看对象的原型链,或者动态地改变对象的原型。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let obj = {
    name: "Kimi"
    };
    console.log(obj.__proto__); // 输出 Object.prototype
    console.log(obj.__proto__ === Object.prototype); // 输出 true
    // 修改对象的原型
    let protoObj = {
    greet: function() {
    console.log("Hello");
    }
    };
    obj.__proto__ = protoObj;
    obj.greet(); // 控制台输出 Hello

二、访问方式

(一)prototype

  • 访问方式prototype 属性只能通过函数对象访问。例如,Person.prototype
  • 示例
    1
    console.log(Person.prototype); // 输出 { sayHello: [Function: sayHello], constructor: [Function: Person] }

(二)__proto__

  • 访问方式__proto__ 属性通过对象实例访问。例如,obj.__proto__
  • 示例
    1
    console.log(obj.__proto__); // 输出 { constructor: [Function: Object], ... }

三、标准性和兼容性

(一)prototype

  • 标准性prototype 是 ECMAScript 规范中定义的标准属性,是 JavaScript 原型链机制的核心部分。
  • 兼容性prototype 在所有现代 JavaScript 环境中都支持。

(二)__proto__

  • 标准性__proto__ 不是 ECMAScript 规范中定义的标准属性,但它在大多数现代浏览器中都支持。在 ES6 中,推荐使用 Object.getPrototypeOfObject.setPrototypeOf 来替代 __proto__
  • 兼容性:虽然 __proto__ 在大多数现代浏览器中都支持,但在一些严格遵循标准的环境中(如某些 Node.js 环境)可能不推荐使用。推荐使用标准的 Object.getPrototypeOfObject.setPrototypeOf 方法。
  • 示例
    1
    2
    3
    4
    5
    // 获取对象的原型
    console.log(Object.getPrototypeOf(obj)); // 输出 { constructor: [Function: Object], ... }
    // 设置对象的原型
    Object.setPrototypeOf(obj, protoObj);
    obj.greet(); // 控制台输出 Hello

四、总结

  • prototype
    • 定义:函数对象的属性,指向原型对象。
    • 用途:定义构造函数的原型对象,实现代码复用。
    • 访问方式:通过函数对象访问,例如 Person.prototype
    • 标准性:标准属性,广泛支持。
  • __proto__
    • 定义:对象实例的属性,指向该对象的原型对象。
    • 用途:在运行时访问和修改对象的原型对象。
    • 访问方式:通过对象实例访问,例如 obj.__proto__
    • 标准性:非标准属性,推荐使用 Object.getPrototypeOfObject.setPrototypeOf
      理解这些区别有助于更好地使用 JavaScript 的原型链机制,合理地选择使用 prototype 还是 __proto__,以实现更高效和标准的代码。
      prototype和__proto__ 的关系。
      主要有以下三点:
      1)函数(或构造函数)身上才有 prototype (prototype名字叫原型,原型是一个对象);
      2)而其他任何通过构造函数实例化出来的对象(不包括null、undefined)身上都有 __proto____proto__是隐式原型,隐式原型也一个对象)
      3)实例化对象的__proto__ 就是构造函数的 prototype   (=== 关系)
      附:undefind 和 null 既没有prototype也没有 proto  ,因为它俩不是函数,也不是函数实例化出来的对象
1
2
'a'.__proto__ === String.protptype //true
 true.__proto__  ===  Boolean.prototype //true
1
2
3
4
var obj = {}
console.log(obj.__proto__ === Object.prototype);
console.log(obj.constructor === Object);
console.log(Object.getPrototypeOf(obj) === Object.prototype);

getPrototypeOf

Object.getPrototypeOf() 是 JavaScript 中的一个静态方法,用于获取指定对象的原型。以下是关于 Object.getPrototypeOf() 的详细信息:

语法

1
Object.getPrototypeOf(obj)

参数

  • obj:要返回其原型的对象。

返回值

  • 给定对象的原型,可能是 null

示例

使用 getPrototypeOf

1
2
3
const proto = {};
const obj = Object.create(proto);
console.log(Object.getPrototypeOf(obj) === proto); // true

非对象强制类型转换

在 ES5 中,如果 obj 参数不是对象,则会抛出 TypeError 异常。在 ES2015 中,该参数将被强制转换为 Object

1
2
3
4
Object.getPrototypeOf("foo");
// TypeError: "foo" is not an object (ES5 code)
Object.getPrototypeOf("foo");
// String.prototype (ES2015 code)

检查两个对象是否具有相同的原型

1
2
3
4
5
6
// Creating a simple function
let first_var = function myFun() { };
// Creating a object
let second_var = Object.create(first_var);
// Getting the output
console.log(Object.getPrototypeOf(second_var) === first_var); // false

获取函数的原型

1
2
let func1 = function () {};
console.log(Object.getPrototypeOf(func1) === Function.prototype); // true

注意事项

  • Object.getPrototypeOf() 是获取对象原型的标准方法,推荐使用此方法而不是非标准的 __proto__ 属性。
  • 在 ES5 中,如果传入的参数不是对象,会抛出 TypeError 异常。在 ES2015 中,非对象参数会被强制转换为对象。

浏览器兼容性

Object.getPrototypeOf() 方法在现代浏览器中广泛支持,包括 Chrome、Edge、Firefox、Opera 和 Safari。

总结

Object.getPrototypeOf() 是一个非常有用的工具,用于获取对象的原型。它在处理对象继承和属性访问时非常方便,特别是在需要检查对象的原型链时。

判断两个object是否相同

在 JavaScript 中,判断两个对象是否相同是一个常见的需求。由于对象是引用类型,直接使用 ===== 只能比较它们的引用,而不能比较它们的内容。因此,需要通过其他方式来判断两个对象是否具有相同的结构和值。
以下是几种判断两个对象是否相同的方法:

1. 浅比较(Shallow Comparison)

浅比较只检查对象的第一层属性是否相同。如果对象的属性值是基本类型(如字符串、数字等),可以使用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function shallowEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
for (let key of keys1) {
if (obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}
// 示例
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
const obj3 = { a: 1, b: 3 };
console.log(shallowEqual(obj1, obj2)); // true
console.log(shallowEqual(obj1, obj3)); // false

缺点:这种方法只能比较对象的第一层属性,无法处理嵌套对象或数组。

2. 深比较(Deep Comparison)

深比较会递归地检查对象的所有属性,包括嵌套对象和数组。可以使用递归函数实现:

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
function deepEqual(obj1, obj2) {
if (obj1 === obj2) {
return true;
}
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
for (let key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
// 示例
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
const obj3 = { a: 1, b: { c: 3 } };
console.log(deepEqual(obj1, obj2)); // true
console.log(deepEqual(obj1, obj3)); // false

优点:这种方法可以处理嵌套对象和数组。
缺点:递归实现可能会导致性能问题,尤其是在处理大型对象时。

3. 使用 JSON 序列化

如果对象中没有函数、undefinedNaN 或循环引用,可以使用 JSON.stringify 进行比较:

1
2
3
4
5
6
7
8
9
function objectsEqual(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
// 示例
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
const obj3 = { a: 1, b: { c: 3 } };
console.log(objectsEqual(obj1, obj2)); // true
console.log(objectsEqual(obj1, obj3)); // false

优点:实现简单,代码简洁。
缺点

  1. 无法处理对象中的函数、undefinedNaN 或循环引用。
  2. 性能较差,因为 JSON.stringify 会递归遍历对象。

4. 使用库

如果需要更可靠的实现,可以使用现成的库,如 Lodash_.isEqual 方法:

1
2
3
4
5
6
import _ from 'lodash';
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
const obj3 = { a: 1, b: { c: 3 } };
console.log(_.isEqual(obj1, obj2)); // true
console.log(_.isEqual(obj1, obj3)); // false

优点_.isEqual 是一个经过优化的深比较方法,可以处理各种复杂情况,包括循环引用、特殊值等。
缺点:需要引入额外的库。

总结

  • 如果只需要比较对象的第一层属性,可以使用浅比较。
  • 如果需要处理嵌套对象和数组,可以实现深比较。
  • 如果对象结构复杂或需要处理特殊值,建议使用现成的库(如 Lodash)。
  • 如果对象结构简单且没有特殊值,可以使用 JSON.stringify 进行比较。
    根据具体需求选择合适的方法即可。