JS `Object.values()` / `Object.entries()`:获取对象值数组与键值对数组

各位观众老爷们,大家好!今天咱们来聊聊 JavaScript 里两个非常实用,但有时候又容易被忽略的小可爱:Object.values()Object.entries()。它们就像是对象的挖掘机,能把对象里的宝贝值和键值对一股脑儿地挖出来,方便我们进行各种操作。

开场白:对象,你是个谜一样的存在

在 JavaScript 的世界里,对象就像一个百宝箱,里面装着各种各样的属性(key-value pair)。有时候,我们只想看看箱子里都有哪些宝贝(值),有时候我们又想知道每个宝贝都贴着什么标签(键),这时候 Object.values()Object.entries() 就派上大用场了。

第一部分:Object.values()——值你所值

Object.values() 方法会返回一个给定对象自身可枚举属性的值的数组,其排列顺序与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for...in 还会枚举原型链上的属性)。

语法:

Object.values(obj)

其中 obj 是要返回值的对象。

实例讲解:

  1. 基本用法:

    假设我们有这样一个对象:

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京"
    };
    
    const values = Object.values(myObject);
    console.log(values); // 输出: ["张三", 30, "北京"]

    看,是不是很简单? Object.values()myObject 里的 nameagecity 对应的值都提取出来了,放进了一个数组里。

  2. 与数组的区别:

    要注意的是,Object.values() 返回的是一个数组,不是一个迭代器或者其他什么奇怪的东西。这意味着你可以直接使用数组的各种方法,比如 mapfilterreduce 等。

  3. 处理非对象类型:

    如果 Object.values() 的参数不是一个对象,它会尝试将其转换为对象。如果无法转换,则会抛出一个 TypeError 异常。

    Object.values("hello"); // 返回 ["h", "e", "l", "l", "o"] (字符串会被当做类数组对象)
    Object.values(123); // 报错 TypeError: Object.values called on non-object
    Object.values(null); // 报错 TypeError: Cannot convert undefined or null to object

    字符串可以被当做类数组对象处理,每个字符对应一个索引和值。但是 nullundefined 无法转换为对象,所以会报错。

  4. 处理 Symbol 属性:

    Object.values() 只会返回对象自身可枚举属性的值。这意味着 Symbol 属性会被忽略。

    const myObject = {
      name: "张三",
      age: 30,
      [Symbol("city")]: "北京" // Symbol 属性
    };
    
    const values = Object.values(myObject);
    console.log(values); // 输出: ["张三", 30]  Symbol 属性被忽略了
  5. 继承属性

Object.values() 不会返回继承的属性的值。

const parentObject = {
  parentName: "老王"
};

const myObject = Object.create(parentObject);
myObject.name = "张三";
myObject.age = 30;

const values = Object.values(myObject);
console.log(values); // 输出: ["张三", 30]  parentName 没有被返回

总结:

Object.values() 就是一个简单粗暴的工具,用来获取对象的所有值,方便快捷。

特性 说明
返回值 包含对象所有可枚举属性值的数组
参数 要获取值的对象
Symbol 属性 忽略 Symbol 属性
非对象类型 尝试转换为对象,无法转换则抛出 TypeError
继承属性 不包括继承的属性

第二部分:Object.entries()——键值对,一个都不能少

Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,其排列顺序与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for...in 还会枚举原型链上的属性)。每个键值对都是一个数组,第一个元素是键,第二个元素是值。

语法:

Object.entries(obj)

其中 obj 是要返回键值对的对象。

实例讲解:

  1. 基本用法:

    还是用上面的 myObject

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京"
    };
    
    const entries = Object.entries(myObject);
    console.log(entries);
    // 输出:
    // [
    //   ["name", "张三"],
    //   ["age", 30],
    //   ["city", "北京"]
    // ]

    这次,Object.entries()myObject 里的每个键值对都提取出来了,放进了一个数组里,每个键值对又是一个数组。

  2. for...in 的关系:

    Object.entries() 返回的顺序和 for...in 循环遍历对象的顺序是一样的(除了 for...in 还会遍历原型链上的属性)。

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京"
    };
    
    for (const key in myObject) {
      console.log(key, myObject[key]);
    }
    
    // 输出:
    // name 张三
    // age 30
    // city 北京
    
    const entries = Object.entries(myObject);
    entries.forEach(([key, value]) => {
      console.log(key, value);
    });
    
    // 输出:
    // name 张三
    // age 30
    // city 北京
  3. 配合解构赋值:

    Object.entries() 配合解构赋值可以更方便地访问键和值。

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京"
    };
    
    const entries = Object.entries(myObject);
    entries.forEach(([key, value]) => {
      console.log(`属性名: ${key}, 属性值: ${value}`);
    });
    
    // 输出:
    // 属性名: name, 属性值: 张三
    // 属性名: age, 属性值: 30
    // 属性名: city, 属性值: 北京
  4. 转换为 Map:

    Object.entries() 可以很方便地将一个对象转换为 Map 对象。

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京"
    };
    
    const myMap = new Map(Object.entries(myObject));
    console.log(myMap);
    // 输出: Map(3) {
    //   'name' => '张三',
    //   'age' => 30,
    //   'city' => '北京'
    // }

    Map 对象提供了一些比普通对象更强大的功能,比如可以存储任意类型的数据作为键。

  5. 处理非对象类型:

    Object.values() 一样,如果 Object.entries() 的参数不是一个对象,它会尝试将其转换为对象。如果无法转换,则会抛出一个 TypeError 异常。

    Object.entries("hello"); // 返回 [ [ '0', 'h' ], [ '1', 'e' ], [ '2', 'l' ], [ '3', 'l' ], [ '4', 'o' ] ] (字符串会被当做类数组对象)
    Object.entries(123); // 报错 TypeError: Object.entries called on non-object
    Object.entries(null); // 报错 TypeError: Cannot convert undefined or null to object
  6. 处理 Symbol 属性:

    Object.entries() 只会返回对象自身可枚举属性的键值对。这意味着 Symbol 属性会被忽略。

    const myObject = {
      name: "张三",
      age: 30,
      [Symbol("city")]: "北京" // Symbol 属性
    };
    
    const entries = Object.entries(myObject);
    console.log(entries);
    // 输出:
    // [
    //   ["name", "张三"],
    //   ["age", 30]
    // ]  Symbol 属性被忽略了
  7. 继承属性

Object.entries() 不会返回继承的属性的键值对。

const parentObject = {
  parentName: "老王"
};

const myObject = Object.create(parentObject);
myObject.name = "张三";
myObject.age = 30;

const entries = Object.entries(myObject);
console.log(entries);
// 输出:
// [
//   ["name", "张三"],
//   ["age", 30]
// ]  parentName 没有被返回

总结:

Object.entries() 提供了一种方便的方式来遍历对象的键值对,可以用于各种场景,比如数据转换、对象比较等。

特性 说明
返回值 包含对象所有可枚举属性键值对的数组,每个键值对都是一个数组
参数 要获取键值对的对象
Symbol 属性 忽略 Symbol 属性
非对象类型 尝试转换为对象,无法转换则抛出 TypeError
for...in 返回顺序与 for...in 循环遍历对象的顺序一致 (除了原型链上的属性)
应用场景 数据转换、对象比较、转换为 Map 对象等
继承属性 不包括继承的属性

第三部分:进阶应用与技巧

  1. 动态创建对象:

    Object.entries() 配合 Object.fromEntries() 可以实现动态创建对象。Object.fromEntries()Object.entries() 的反向操作,可以将一个键值对数组转换为对象。

    const entries = [
      ["name", "张三"],
      ["age", 30],
      ["city", "北京"]
    ];
    
    const myObject = Object.fromEntries(entries);
    console.log(myObject);
    // 输出:
    // {
    //   name: "张三",
    //   age: 30,
    //   city: "北京"
    // }

    这种方式在处理动态数据时非常有用,比如从服务器获取数据后,可以根据数据动态创建对象。

  2. 过滤对象属性:

    结合 Object.entries() 和数组的 filter 方法,可以方便地过滤对象属性。

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京",
      salary: 10000
    };
    
    const filteredObject = Object.fromEntries(
      Object.entries(myObject).filter(([key, value]) => value !== 30) // 过滤掉 age 为 30 的属性
    );
    
    console.log(filteredObject);
    // 输出:
    // {
    //   name: "张三",
    //   city: "北京",
    //   salary: 10000
    // }
  3. 转换对象属性值:

    结合 Object.entries() 和数组的 map 方法,可以方便地转换对象属性值。

    const myObject = {
      name: "张三",
      age: 30,
      city: "北京"
    };
    
    const transformedObject = Object.fromEntries(
      Object.entries(myObject).map(([key, value]) => [key, String(value)]) // 将所有属性值转换为字符串
    );
    
    console.log(transformedObject);
    // 输出:
    // {
    //   name: "张三",
    //   age: "30",
    //   city: "北京"
    // }
  4. 对象比较:

    Object.entries() 可以用于比较两个对象是否相等。但是需要注意的是,这种比较方式是浅比较,只能比较对象的直接属性,不能比较嵌套对象。

    function areObjectsEqual(obj1, obj2) {
      const entries1 = Object.entries(obj1);
      const entries2 = Object.entries(obj2);
    
      if (entries1.length !== entries2.length) {
        return false;
      }
    
      for (let i = 0; i < entries1.length; i++) {
        const [key1, value1] = entries1[i];
        const [key2, value2] = entries2[i];
    
        if (key1 !== key2 || value1 !== value2) {
          return false;
        }
      }
    
      return true;
    }
    
    const obj1 = { name: "张三", age: 30 };
    const obj2 = { name: "张三", age: 30 };
    const obj3 = { name: "李四", age: 25 };
    
    console.log(areObjectsEqual(obj1, obj2)); // 输出: true
    console.log(areObjectsEqual(obj1, obj3)); // 输出: false

    如果需要进行深比较,需要递归地比较嵌套对象。

第四部分:兼容性问题

Object.values()Object.entries() 是 ES2017 (ES8) 引入的特性。这意味着在一些旧版本的浏览器或 Node.js 环境中可能不支持。

解决方案:

  1. 使用 Polyfill:

    可以使用 Polyfill 来为旧环境提供 Object.values()Object.entries() 的支持。有很多现成的 Polyfill 可以使用,比如 core-js

    npm install core-js

    然后在你的代码中引入 Polyfill:

    require('core-js/features/object/values');
    require('core-js/features/object/entries');
    
    // 现在就可以使用 Object.values() 和 Object.entries() 了
  2. 手动实现:

    如果不想使用 Polyfill,也可以手动实现 Object.values()Object.entries()

    if (!Object.values) {
      Object.values = function(obj) {
        if (obj === null || obj === undefined) {
          throw new TypeError('Cannot convert undefined or null to object');
        }
    
        const values = [];
        for (const key in obj) {
          if (Object.prototype.hasOwnProperty.call(obj, key)) {
            values.push(obj[key]);
          }
        }
        return values;
      };
    }
    
    if (!Object.entries) {
      Object.entries = function(obj) {
        if (obj === null || obj === undefined) {
          throw new TypeError('Cannot convert undefined or null to object');
        }
    
        const entries = [];
        for (const key in obj) {
          if (Object.prototype.hasOwnProperty.call(obj, key)) {
            entries.push([key, obj[key]]);
          }
        }
        return entries;
      };
    }
    
    // 现在就可以使用 Object.values() 和 Object.entries() 了

    这种方式虽然比较麻烦,但是可以更好地控制代码的兼容性。

第五部分:总结

Object.values()Object.entries() 是 JavaScript 中处理对象的两个非常实用的方法。它们可以方便地获取对象的值和键值对,并配合数组的各种方法进行各种操作。虽然存在兼容性问题,但是可以通过 Polyfill 或手动实现来解决。

希望今天的讲解对大家有所帮助!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注