ES6对象扩展详解:6大特性彻底改变JavaScript写法
ES6(也叫ECMAScript 2015)是JavaScript发展的一个重要版本。它对原生对象进行了全面升级,不仅简化了创建和操作对象的语法,还补充了很多以前缺少的重要功能。这些改变彻底影响了JavaScript开发者的编程习惯。
本文将详细讲解ES6中对象的6个核心扩展特性,配合实际代码例子,让每个特性的使用场景一目了然。
一、对象字面量的简化写法
ES6大大简化了对象字面量的声明语法,减少了重复代码,让代码更易读。
1.1 属性简写
当对象属性名和变量名相同时,可以省略冒号和值,直接写属性名:
// ES5写法
const name = "张三";
const age = 28;
const user = {
name: name,
age: age
};
// ES6简写
const user = { name, age }; // 和上面ES5写法效果一样1.2 方法简写
对象里的方法声明可以省略function关键字和冒号:
// ES5写法
const obj = {
sayHi: function() {
console.log("你好");
}
};
// ES6简写
const obj = {
sayHi() {
console.log("你好"); // 不需要function关键字
},
async fetchData() { // 也支持async/await语法
const res = await fetch("/api");
}
};1.3 计算属性名
用方括号包住表达式,可以动态生成对象属性名,解决了ES5中不能直接声明动态属性的问题:
// ES5动态属性名(需要另外赋值)
const key = "gender";
const obj = {};
obj[key] = "男"; // 只能在声明后添加
// ES6计算属性名(声明时直接定义)
const obj = {
[key]: "男",
[`user_${10}`]: "管理员" // 支持表达式拼接
};二、对象属性的遍历方法
ES6新增了3个遍历对象属性的方法,配合for...of循环,让属性遍历更灵活。
2.1 Object.keys():获取属性名数组
返回对象自身可枚举属性的名称数组(不包含原型链上的属性):
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj)); // ["a", "b", "c"]2.2 Object.values():获取属性值数组
返回对象自身可枚举属性的值数组:
console.log(Object.values(obj)); // [1, 2, 3]2.3 Object.entries():获取键值对数组
返回对象自身可枚举属性的[key, value]数组,适合用for...of循环:
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`); // 输出 "a: 1" "b: 2" "c: 3"
}
// 快速转换成Map
const map = new Map(Object.entries(obj));注意:上面三个方法都遵循"自身可枚举"规则,会忽略enumerable: false的属性和原型链上的属性。
三、对象的合并与复制:Object.assign()
ES6新增Object.assign()方法,用于把多个源对象的属性复制到目标对象,实现对象合并或浅拷贝。
3.1 基本用法
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { a: 3, c: 4 };
// 合并:把source1、source2的属性复制到target
Object.assign(target, source1, source2);
console.log(target); // { a: 3, b: 2, c: 4 }(相同属性会被后面的源对象覆盖)3.2 重要特性
1. 浅拷贝:只复制属性值,如果属性值是对象或数组,只复制引用地址:
const source = { info: { age: 20 } };
const target = Object.assign({}, source);
target.info.age = 30;
console.log(source.info.age); // 30(源对象也被修改了,因为复制的是引用)2. 只复制可枚举属性:忽略enumerable: false的属性:
const source = Object.defineProperty({}, "hidden", {
value: 10,
enumerable: false // 不可枚举属性
});
const target = Object.assign({}, source);
console.log(target.hidden); // undefined(没有复制)3. 支持多个源对象:可以传入多个源对象,属性按顺序覆盖(后面传入的优先级更高)。
四、原型操作的标准化
ES6提供了直接操作对象原型的API,替代了ES5中不标准的__proto__属性。
4.1 Object.setPrototypeOf():设置原型
用来指定对象的原型:
const parent = { greet() { console.log("你好"); } };
const child = {};
// 把child的原型设为parent
Object.setPrototypeOf(child, parent);
child.greet(); // "你好"(从parent继承来的)4.2 Object.getPrototypeOf():获取原型
返回对象的原型对象,代替obj.proto:
console.log(Object.getPrototypeOf(child) === parent); // true4.3 super关键字:访问原型属性
在对象方法中,super指向当前对象的原型,可以直接调用原型的属性或方法:
const parent = { name: "父对象" };
const child = {
getParentName() {
return super.name; // 相当于 Object.getPrototypeOf(this).name
}
};
Object.setPrototypeOf(child, parent);
console.log(child.getParentName()); // "父对象"五、获取所有属性描述符:Object.getOwnPropertyDescriptors()
ES6新增这个方法,用来获取对象所有自身属性的完整描述符(包括value、writable、enumerable、configurable、get、set等),解决了Object.getOwnPropertyDescriptor()只能获取单个属性描述符的问题。
5.1 基本用法
const obj = {
name: "张三",
get age() { return 28; }, // getter属性
set age(val) { console.log(val); } // setter属性
};
// 获取所有自身属性的描述符
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors.name.value); // "张三"
console.log(descriptors.age.get); // 对应的getter函数5.2 重要应用:深拷贝包含getter/setter的对象
Object.assign()不能复制getter/setter属性(只能复制它们的返回值),结合Object.getOwnPropertyDescriptors()和Object.create()可以实现完整复制:
const clone = Object.create(
Object.getPrototypeOf(obj), // 继承原型
Object.getOwnPropertyDescriptors(obj) // 复制所有属性描述符
);
console.log(clone.age); // 28(getter正常工作)六、用Symbol作为对象属性名
ES6新增Symbol类型,生成的实例是唯一的,可以作为对象属性名,避免属性名冲突。
6.1 基本用法
// 创建Symbol实例(参数只是描述,不影响唯一性)
const id = Symbol("id");
const obj = {
[id]: 1001, // 作为属性名要用方括号
name: "李四"
};
// 访问Symbol属性
console.log(obj[id]); // 1001(必须用Symbol实例访问)
// 特性:不能用for...in/Object.keys()遍历到
for (const key in obj) {
console.log(key); // 只输出 "name",Symbol属性被忽略了
}
// 获取所有Symbol属性名
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id)]6.2 应用场景:模拟"私有"属性
虽然ES6没有真正的私有属性,但Symbol属性不能被常规遍历方法获取,可以用来模拟私有属性:
// 在模块内部定义Symbol,外部访问不到
const _private = Symbol("private");
export const obj = {
[_private]: "敏感数据",
publicMethod() {
return this[_private]; // 内部可以访问
}
};
// 外部不能用常规方式获取_private属性实际应用示例
合并默认配置
const defaultConfig = {
timeout: 5000,
retries: 3
};
const userConfig = {
timeout: 3000
};
const finalConfig = Object.assign({}, defaultConfig, userConfig);
console.log(finalConfig); // { timeout: 3000, retries: 3 }对象遍历和转换
const user = {
name: "王五",
age: 30,
city: "北京"
};
// 转成数组
const userArray = Object.entries(user);
console.log(userArray); // [["name", "王五"], ["age", 30], ["city", "北京"]]
// 转成Map
const userMap = new Map(Object.entries(user));
// 筛选属性
const publicInfo = Object.fromEntries(
Object.entries(user).filter(([key]) => key !== 'age')
);
console.log(publicInfo); // { name: "王五", city: "北京" }创建不可变对象
const data = {
name: "测试数据",
value: 100
};
// 创建浅不可变副本
const frozenData = Object.freeze({ ...data });
// 尝试修改会失败(严格模式下会报错)
frozenData.name = "新名字";
console.log(frozenData.name); // 还是"测试数据"总结
ES6对对象的扩展主要围绕三个目标:简化语法、完善功能、提高安全性。这些特性已经成为现代JavaScript开发的基础:
语法简化(属性/方法简写、计算属性名)减少重复代码
遍历与合并(Object.keys/values/entries、Object.assign)提高操作效率
原型与描述符操作(setPrototypeOf、getOwnPropertyDescriptors)规范底层逻辑
Symbol属性名解决命名冲突问题
这些特性不仅让对象操作更直观、更灵活,也为vue、react等框架的底层实现提供了支持。掌握这些扩展,能显著提升代码质量和开发效率,是前端开发者必备的技能。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!