新增Object.values()和Object.entries()
想象我们管理一个存放水果的仓库(对象)
js
const fruitStock = {
apple: 10, // 🍎苹果:10箱
orange: 15, // 🍊橙子:15箱
banana: 8 // 🍌香蕉:8箱
};
Object.values()
:只需清点库存数量(值)
js
const stockNumbers = Object.values(fruitStock);
console.log(stockNumbers); // [10, 15, 8] → 只关心数量
Object.entries()
:记录完整货品清单(键+值)
js
const allRecords = Object.entries(fruitStock);
console.log(allRecords);
// [ ["apple", 10], ["orange", 15], ["banana", 8] ] → 完整台账
一些常见的使用场景
快速提取值集合(Object.values
)
js
// 计算水果总库存
const total = Object.values(fruitStock).reduce((sum, num) => sum + num, 0);
console.log(total); // 33
对象转 Map(Object.entries
)
js
const fruitMap = new Map(Object.entries(fruitStock));
console.log(fruitMap.get("orange")); // 15 → 转为 Map 后查询
过滤对象属性
js
// 找出库存 > 10 的水果
const highStock = Object.entries(fruitStock)
.filter(([fruit, count]) => count > 10)
.map(([fruit]) => fruit);
console.log(highStock); // ["orange"]
替代 for...in
循环
js
// 更安全地遍历对象(避免原型污染)
Object.entries(fruitStock).forEach(([fruit, count]) => {
console.log(`${fruit}: ${count}箱`);
});
对象深拷贝技巧
js
const copy = Object.fromEntries(Object.entries(fruitStock));
console.log(copy); // {apple:10, orange:15, banana:8} → 全新对象
特殊情况
非对象参数自动转对象
js
Object.values("abc"); // ["a", "b", "c"](字符串转包装对象)
Object.entries(42); // [](数字无属性,返回空数组)
Symbol 属性会被忽略
js
const obj = { [Symbol("id")]: 100, name: "test" };
Object.values(obj); // ["test"] → 跳过 Symbol
Object.entries(obj); // [["name", "test"]]
原型链属性不包含
js
function Fruit() { this.type = "food"; }
Fruit.prototype.color = "red";
const apple = new Fruit();
Object.values(apple); // ["food"] → 不包含原型属性
与ES5方案对比
操作需求 | ES5 方案 | ES8 方案 | 优势 |
---|---|---|---|
获取所有值 | Object.keys(obj).map(k => obj[k]) |
Object.values(obj) |
代码减少 60% |
获取键值对 | 手动 for...in 循环 |
Object.entries(obj) |
避免原型链污染风险 |
对象转二维数组 | 无直接方法 | Object.entries(obj) |
一行代码完成 |
选择哪个方法
-
如下情况使用
Object.values()
- 统计值总和/平均值
- 检查值是否满足条件
- 仅需值集合的场景
-
如下情况使用
Object.entries()
- 需同时访问键和值
- 对象转 Map 或其他数据结构
- 重构对象属性
Object.getOwnPropertyDescriptors()
对象的"体检报告单"
想象我们要检查一台手机的完整配置
- 传统方法 (
Object.getOwnPropertyDescriptor()
):只能查单个部件(如摄像头参数) Object.getOwnPropertyDescriptors()
:直接生成整机完整参数表(镜头+芯片+屏幕...)
js
const phone = {
brand: "Pixel",
price: 699,
get discount() { return this.price * 0.9 } // getter 计算属性
};
// 获取手机完整配置表
const fullReport = Object.getOwnPropertyDescriptors(phone);
解决两大痛点
完整复制对象(含 getter/setter)
js
const deepCopy = Object.defineProperties(
{},
Object.getOwnPropertyDescriptors(phone) // 保留所有特性
);
deepCopy.price = 800;
console.log(deepCopy.discount); // 720(getter 动态计算值)
继承原型方法时不污染目标对象
js
// 安全混合对象(不覆盖原型链)
const securityPhone = Object.create(
cameraPrototype, // 继承相机功能
Object.getOwnPropertyDescriptors(phone) // 添加自有属性
);
对比其他属性查询方法
方法 | 作用范围 | 能否获取 getter/setter | 返回结构 |
---|---|---|---|
Object.keys() |
自身可枚举属性 | ❌ | 属性名数组 |
Object.getOwnPropertyNames() |
所有自身属性 | ❌ | 属性名数组 |
Object.getOwnPropertyDescriptor() |
单个属性 | ✅ | 单个描述符对象 |
Object.getOwnPropertyDescriptors() |
所有自身属性 | ✅ | 描述符对象集合 |
一些常见的使用场景
深度克隆对象(保留特性)
js
function trueClone(obj) {
return Object.defineProperties(
{},
Object.getOwnPropertyDescriptors(obj)
);
}
精确控制类继承
js
class PremiumPhone extends Phone {
constructor() {
super();
// 添加会员专属属性(带 getter)
Object.defineProperties(
this,
Object.getOwnPropertyDescriptors({
get vipDiscount() { return this.price * 0.7 }
})
);
}
}
修复 JSON 序列化缺陷
js
// JSON.stringify 会忽略 getter!
const phoneJSON = JSON.stringify(phone); // {brand:"Pixel",price:699}
// 通过描述符重建对象
const restoredPhone = Object.defineProperties(
{},
Object.getOwnPropertyDescriptors(phone)
);
创建不可变对象
js
// 锁定所有属性
const sealedPhone = Object.defineProperties(
{},
Object.entries(Object.getOwnPropertyDescriptors(phone)).map(([key, desc]) => {
return [key, {...desc, writable: false}] // 全部设为不可写
})
);