ECMAScript对象
本章将详细介绍ECMAScript中的对象,包括对象的基本概念、创建方式、属性操作和常用方法,帮助您深入理解JavaScript对象系统。
11.1 对象概述
对象是ECMAScript中的一种复合数据类型,用于存储键值对集合。对象是JavaScript的核心概念,几乎所有的值都是对象或可以像对象一样使用。
对象的基本特征
- 属性集合:对象包含多个属性,每个属性都有键和值
- 动态性:对象的属性可以在运行时添加、修改或删除
- 引用类型:对象是引用类型,变量存储的是对象的引用而非对象本身
- 原型继承:对象可以通过原型链继承其他对象的属性和方法
11.2 对象的创建方式
ECMAScript提供了多种创建对象的方式。
对象字面量
// 对象字面量是最常用的创建方式
let person = {
name: "张三",
age: 25,
greet: function() {
return `你好, 我是${this.name}`;
}
};
// ES6增强对象字面量
let name = "李四";
let age = 30;
let user = {
name, // 属性简写
age,
greet() { // 方法简写
return `你好, 我是${this.name}`;
},
[ "user" + age ]: true // 计算属性名
};
new Object()构造函数
// 使用Object构造函数
let obj1 = new Object();
obj1.name = "王五";
obj1.age = 28;
// 等价于
let obj2 = {};
obj2.name = "王五";
obj2.age = 28;
工厂函数
// 工厂函数
function createPerson(name, age) {
return {
name: name,
age: age,
greet: function() {
return `你好, 我是${this.name}`;
}
};
}
let person1 = createPerson("张三", 25);
let person2 = createPerson("李四", 30);
构造函数
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
return `你好, 我是${this.name}`;
};
}
let person = new Person("张三", 25);
console.log(person instanceof Person); // true
Object.create()方法
// Object.create()方法
let proto = {
greet: function() {
return `你好, 我是${this.name}`;
}
};
let person = Object.create(proto);
person.name = "张三";
person.age = 25;
console.log(person.greet()); // "你好, 我是张三"
11.3 对象属性操作
ECMAScript提供了丰富的API来操作对象属性。
属性访问
let person = {
name: "张三",
age: 25,
"full-name": "张三丰" // 包含特殊字符的属性名
};
// 点表示法
console.log(person.name); // "张三"
// 方括号表示法
console.log(person["age"]); // 25
console.log(person["full-name"]); // "张三丰"
// 动态属性访问
let prop = "name";
console.log(person[prop]); // "张三"
属性添加和修改
let person = {name: "张三"};
// 添加属性
person.age = 25;
person["city"] = "北京";
// 修改属性
person.name = "李四";
person["age"] = 30;
console.log(person); // {name: "李四", age: 30, city: "北京"}
属性删除
let person = {name: "张三", age: 25, city: "北京"};
// 删除属性
delete person.city;
delete person["age"];
console.log(person); // {name: "张三"}
属性检测
let person = {name: "张三", age: 25};
// in操作符
console.log("name" in person); // true
console.log("city" in person); // false
// hasOwnProperty方法
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("toString")); // false
// propertyIsEnumerable方法
console.log(person.propertyIsEnumerable("name")); // true
11.4 对象属性描述符
ES5引入了属性描述符,可以更精确地控制对象属性的行为。
数据属性描述符
let obj = {};
// 定义带描述符的属性
Object.defineProperty(obj, "name", {
value: "张三",
writable: false, // 不可写
enumerable: true, // 可枚举
configurable: false // 不可配置
});
console.log(obj.name); // "张三"
obj.name = "李四"; // 严格模式下会报错
console.log(obj.name); // "张三" (未改变)
// 获取属性描述符
let descriptor = Object.getOwnPropertyDescriptor(obj, "name");
console.log(descriptor);
// {value: "张三", writable: false, enumerable: true, configurable: false}
访问器属性描述符
let obj = {
_name: "张三"
};
Object.defineProperty(obj, "name", {
get: function() {
return this._name;
},
set: function(value) {
if (value.length > 0) {
this._name = value;
}
},
enumerable: true,
configurable: true
});
console.log(obj.name); // "张三"
obj.name = "李四";
console.log(obj.name); // "李四"
obj.name = ""; // 不会改变_name的值
console.log(obj.name); // "李四"
批量定义属性
let obj = {};
Object.defineProperties(obj, {
name: {
value: "张三",
writable: true,
enumerable: true,
configurable: true
},
age: {
value: 25,
writable: true,
enumerable: true,
configurable: true
},
fullName: {
get: function() {
return this.name + " (" + this.age + "岁)";
},
enumerable: true,
configurable: true
}
});
console.log(obj.fullName); // "张三 (25岁)"
11.5 对象遍历
ECMAScript提供了多种遍历对象属性的方法。
for...in循环
let person = {
name: "张三",
age: 25,
city: "北京"
};
// for...in遍历对象属性
for (let key in person) {
console.log(key, person[key]);
}
// name 张三
// age 25
// city 北京
Object.keys()方法
let person = {
name: "张三",
age: 25,
city: "北京"
};
// 获取对象的所有可枚举属性名
let keys = Object.keys(person);
console.log(keys); // ["name", "age", "city"]
keys.forEach(key => {
console.log(key, person[key]);
});
Object.values()方法 (ES2017)
let person = {
name: "张三",
age: 25,
city: "北京"
};
// 获取对象的所有可枚举属性值
let values = Object.values(person);
console.log(values); // ["张三", 25, "北京"]
Object.entries()方法 (ES2017)
let person = {
name: "张三",
age: 25,
city: "北京"
};
// 获取对象的所有可枚举属性的键值对数组
let entries = Object.entries(person);
console.log(entries);
// [["name", "张三"], ["age", 25], ["city", "北京"]]
// 转换为Map
let map = new Map(Object.entries(person));
console.log(map); // Map(3) {"name" => "张三", "age" => 25, "city" => "北京"}
11.6 对象保护
ECMAScript提供了几种保护对象不被修改的机制。
Object.preventExtensions()
let obj = {name: "张三"};
// 防止扩展对象
Object.preventExtensions(obj);
obj.age = 25; // 非严格模式下静默失败,严格模式下抛出错误
console.log(obj.age); // undefined
console.log(Object.isExtensible(obj)); // false
Object.seal()
let obj = {name: "张三"};
// 密封对象
Object.seal(obj);
obj.age = 25; // 不能添加新属性
delete obj.name; // 不能删除属性
obj.name = "李四"; // 可以修改现有属性
console.log(Object.isSealed(obj)); // true
Object.freeze()
let obj = {name: "张三"};
// 冻结对象
Object.freeze(obj);
obj.age = 25; // 不能添加新属性
delete obj.name; // 不能删除属性
obj.name = "李四"; // 不能修改属性(严格模式下抛出错误)
console.log(Object.isFrozen(obj)); // true
11.7 对象合并与复制
ECMAScript提供了多种合并和复制对象的方法。
Object.assign()
// 浅拷贝和合并对象
let target = {a: 1, b: 2};
let source1 = {b: 4, c: 5};
let source2 = {c: 3, d: 6};
Object.assign(target, source1, source2);
console.log(target); // {a: 1, b: 4, c: 3, d: 6}
// 克隆对象
let original = {name: "张三", age: 25};
let clone = Object.assign({}, original);
console.log(clone); // {name: "张三", age: 25}
扩展运算符 (ES2018)
// 对象扩展运算符
let obj1 = {a: 1, b: 2};
let obj2 = {c: 3, d: 4};
// 合并对象
let merged = {...obj1, ...obj2};
console.log(merged); // {a: 1, b: 2, c: 3, d: 4}
// 克隆对象
let original = {name: "张三", age: 25};
let clone = {...original};
console.log(clone); // {name: "张三", age: 25}
// 属性覆盖
let user = {name: "张三", age: 25};
let updatedUser = {...user, age: 26};
console.log(updatedUser); // {name: "张三", age: 26}
11.8 对象的实际应用
配置对象
// 使用对象作为配置参数
function createChart(options = {}) {
const defaultOptions = {
width: 800,
height: 600,
color: "#000000",
backgroundColor: "#ffffff"
};
const config = {...defaultOptions, ...options};
console.log(`创建${config.width}x${config.height}的图表`);
// 实际的图表创建逻辑...
}
createChart({width: 1000, color: "#ff0000"});
createChart(); // 使用默认配置
模块模式
// 使用对象实现模块模式
const calculator = (function() {
let history = []; // 私有变量
return {
add: function(a, b) {
const result = a + b;
history.push(`${a} + ${b} = ${result}`);
return result;
},
subtract: function(a, b) {
const result = a - b;
history.push(`${a} - ${b} = ${result}`);
return result;
},
getHistory: function() {
return [...history]; // 返回副本
}
};
})();
console.log(calculator.add(5, 3)); // 8
console.log(calculator.subtract(10, 4)); // 6
console.log(calculator.getHistory());
// ["5 + 3 = 8", "10 - 4 = 6"]
提示:对象是JavaScript的核心概念,深入理解对象的创建、操作和保护机制对于编写高质量的JavaScript代码至关重要。在实际开发中,合理使用对象可以大大提高代码的组织性和可维护性。