各位观众老爷们,大家好!今天咱们来聊聊 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
是要返回值的对象。
实例讲解:
-
基本用法:
假设我们有这样一个对象:
const myObject = { name: "张三", age: 30, city: "北京" }; const values = Object.values(myObject); console.log(values); // 输出: ["张三", 30, "北京"]
看,是不是很简单?
Object.values()
把myObject
里的name
、age
和city
对应的值都提取出来了,放进了一个数组里。 -
与数组的区别:
要注意的是,
Object.values()
返回的是一个数组,不是一个迭代器或者其他什么奇怪的东西。这意味着你可以直接使用数组的各种方法,比如map
、filter
、reduce
等。 -
处理非对象类型:
如果
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
字符串可以被当做类数组对象处理,每个字符对应一个索引和值。但是
null
和undefined
无法转换为对象,所以会报错。 -
处理 Symbol 属性:
Object.values()
只会返回对象自身可枚举属性的值。这意味着 Symbol 属性会被忽略。const myObject = { name: "张三", age: 30, [Symbol("city")]: "北京" // Symbol 属性 }; const values = Object.values(myObject); console.log(values); // 输出: ["张三", 30] Symbol 属性被忽略了
-
继承属性
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
是要返回键值对的对象。
实例讲解:
-
基本用法:
还是用上面的
myObject
:const myObject = { name: "张三", age: 30, city: "北京" }; const entries = Object.entries(myObject); console.log(entries); // 输出: // [ // ["name", "张三"], // ["age", 30], // ["city", "北京"] // ]
这次,
Object.entries()
把myObject
里的每个键值对都提取出来了,放进了一个数组里,每个键值对又是一个数组。 -
与
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 北京
-
配合解构赋值:
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, 属性值: 北京
-
转换为 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
对象提供了一些比普通对象更强大的功能,比如可以存储任意类型的数据作为键。 -
处理非对象类型:
和
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
-
处理 Symbol 属性:
Object.entries()
只会返回对象自身可枚举属性的键值对。这意味着 Symbol 属性会被忽略。const myObject = { name: "张三", age: 30, [Symbol("city")]: "北京" // Symbol 属性 }; const entries = Object.entries(myObject); console.log(entries); // 输出: // [ // ["name", "张三"], // ["age", 30] // ] Symbol 属性被忽略了
-
继承属性
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 对象等 |
继承属性 | 不包括继承的属性 |
第三部分:进阶应用与技巧
-
动态创建对象:
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: "北京" // }
这种方式在处理动态数据时非常有用,比如从服务器获取数据后,可以根据数据动态创建对象。
-
过滤对象属性:
结合
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 // }
-
转换对象属性值:
结合
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: "北京" // }
-
对象比较:
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 环境中可能不支持。
解决方案:
-
使用 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() 了
-
手动实现:
如果不想使用 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 或手动实现来解决。
希望今天的讲解对大家有所帮助!下次再见!