JavaScript中的对象操作详解
作者:AA-代码批发V哥
JavaScript中对象是数据和功能的集合,几乎所有值都可以视为对象(除原始类型外),掌握对象的创建、属性操作、遍历及继承等核心技能,是深入理解JavaScript的关键。
一、对象的创建方式
JavaScript提供了多种创建对象的方式,各有适用场景,选择合适的方式能让代码更高效。
1.1 对象字面量(最常用)
对象字面量是创建对象最简洁的方式,用{}
包裹键值对,键名可省略引号(符合标识符规则时)。
// 基础语法 const user = { name: "张三", age: 25, isStudent: false, // 方法简写(ES6+) greet() { console.log(`你好,我是${this.name}`); }, // 嵌套对象 address: { city: "北京", district: "海淀区" } }; // 调用属性和方法 console.log(user.name); // "张三" user.greet(); // "你好,我是张三" console.log(user.address.city); // "北京"
特性:语法简洁,适合创建单个对象,无需定义构造函数,是日常开发的首选。
1.2 构造函数与new
关键字
构造函数用于创建多个结构相同的对象,通过this
绑定属性,new
关键字实例化对象。
// 定义构造函数(首字母大写,约定俗成) function Person(name, age) { // 实例属性 this.name = name; this.age = age; // 实例方法(每次创建实例都会重复定义,不推荐) this.greet = function() { console.log(`我是${this.name}`); }; } // 原型方法(所有实例共享,推荐) Person.prototype.sayAge = function() { console.log(`我${this.age}岁`); }; // 实例化对象 const person1 = new Person("李四", 30); const person2 = new Person("王五", 28); console.log(person1.name); // "李四" person1.greet(); // "我是李四" person2.sayAge(); // "我28岁"
特性:适合创建多个同类型对象,通过原型(prototype
)共享方法可节省内存;需注意忘记写new
时this
会指向全局对象(严格模式下报错)。
1.3 Object.create()方法
Object.create()
通过指定原型对象创建新对象,是实现继承的灵活方式。
// 原型对象 const animal = { type: "动物", eat() { console.log("进食中"); } }; // 以animal为原型创建新对象 const cat = Object.create(animal); cat.name = "咪咪"; cat.meow = function() { console.log("喵喵叫"); }; console.log(cat.name); // "咪咪"(自身属性) console.log(cat.type); // "动物"(继承自原型) cat.eat(); // "进食中"(继承的方法)
特性:直接指定原型,适合实现复杂继承关系;创建的对象默认无自身属性,需手动添加。
1.4 其他方式(ES6+)
ES6新增了class
语法糖(本质还是构造函数)和对象扩展运算符,简化对象创建:
// 1. class语法(ES6+) class Car { constructor(brand) { this.brand = brand; } drive() { console.log(`${this.brand}在行驶`); } } const bmw = new Car("宝马"); bmw.drive(); // "宝马在行驶" // 2. 对象扩展运算符(复制对象) const obj1 = { a: 1 }; const obj2 = { ...obj1, b: 2 }; // 复制obj1并添加新属性 console.log(obj2); // { a: 1, b: 2 }
二、对象属性的操作方法
对象的核心是属性(包括数据属性和方法),掌握属性的增删改查是对象操作的基础。
2.1 访问与修改属性
属性访问有两种方式:点语法(.
)和方括号语法([]
),后者支持动态属性名。
const user = { name: "赵六", age: 22 }; // 1. 访问属性 console.log(user.name); // "赵六"(点语法,属性名固定) console.log(user["age"]); // 22(方括号语法,属性名可动态) // 2. 修改属性 user.age = 23; user["name"] = "赵六六"; console.log(user); // { name: "赵六六", age: 23 } // 3. 动态属性名(ES6+) const propKey = "gender"; user[propKey] = "男"; // 等价于user.gender = "男" console.log(user); // { name: "赵六六", age: 23, gender: "男" }
注意:方括号中需用字符串或变量(变量值为字符串);属性名若为数字会自动转为字符串(如obj[1]
等价于obj["1"]
)。
2.2 添加与删除属性
对象属性可动态添加,通过delete
关键字删除。
const book = { title: "JavaScript入门" }; // 添加属性 book.author = "张三"; book["price"] = 59; console.log(book); // { title: "JavaScript入门", author: "张三", price: 59 } // 删除属性 delete book.price; console.log(book.price); // undefined(属性已删除) console.log("price" in book); // false(检查属性是否存在)
特性:delete
仅删除对象自身属性,无法删除继承的属性;删除成功返回true
(即使属性不存在),但不能删除变量或函数。
2.3 检查属性是否存在
判断属性是否存在需区分“自身属性”和“继承属性”,常用方法如下:
const obj = { a: 1 }; // 继承自Object.prototype的属性 console.log(obj.toString); // 存在(继承) // 1. in运算符:检查自身+继承属性 console.log("a" in obj); // true(自身属性) console.log("toString" in obj); // true(继承属性) // 2. hasOwnProperty():仅检查自身属性 console.log(obj.hasOwnProperty("a")); // true console.log(obj.hasOwnProperty("toString")); // false(继承属性) // 3. 检查属性值是否为undefined(不可靠,因属性可能存在但值为undefined) obj.b = undefined; console.log(obj.b === undefined); // true(但属性b存在) console.log("b" in obj); // true(正确判断)
最佳实践:用hasOwnProperty()
检查自身属性,in
运算符检查是否可访问(包括继承)。
三、对象的遍历方法
遍历对象的属性是常见需求,不同方法适用于不同场景,需注意遍历顺序和属性类型。
3.1 for...in循环
for...in
遍历对象的可枚举属性(包括继承的),适合简单遍历。
const car = { brand: "奔驰", price: 300000 }; // 添加不可枚举属性 Object.defineProperty(car, "color", { value: "黑色", enumerable: false // 不可枚举 }); // 遍历可枚举属性(自身+继承的可枚举属性) for (const key in car) { // 过滤继承属性 if (car.hasOwnProperty(key)) { console.log(`${key}: ${car[key]}`); } } // 输出: // brand: 奔驰 // price: 300000
注意:
- 会遍历继承的可枚举属性(需用
hasOwnProperty()
过滤); - 遍历顺序:数字属性按升序,字符串属性按插入顺序,符号属性不遍历。
3.2 Object.keys()与Object.values()
Object.keys()
返回对象自身可枚举属性的键名数组,Object.values()
返回值数组。
const user = { name: "孙七", age: 26, gender: "男" }; // 获取键名数组 const keys = Object.keys(user); console.log(keys); // ["name", "age", "gender"] // 获取值数组 const values = Object.values(user); console.log(values); // ["孙七", 26, "男"] // 遍历键值对 keys.forEach(key => { console.log(`${key}: ${user[key]}`); });
特性:仅包含自身可枚举属性,不包括继承和不可枚举属性;返回数组的顺序与for...in
一致。
3.3 Object.entries()与Object.fromEntries()
Object.entries()
返回键值对数组,Object.fromEntries()
则将键值对数组转为对象(互为逆操作)。
const obj = { a: 1, b: 2 }; // 转为键值对数组 const entries = Object.entries(obj); console.log(entries); // [["a", 1], ["b", 2]] // 遍历键值对 entries.forEach(([key, value]) => { console.log(`${key}: ${value}`); }); // 键值对数组转回对象 const newObj = Object.fromEntries(entries); console.log(newObj); // { a: 1, b: 2 }
应用场景:结合数组方法处理对象,例如过滤属性:
// 过滤值大于1的属性 const filteredEntries = entries.filter(([key, value]) => value > 1); const filteredObj = Object.fromEntries(filteredEntries); // { b: 2 }
3.4 其他方法(获取所有属性)
Object.getOwnPropertyNames(obj)
:返回自身所有属性(包括不可枚举,不包括符号属性);Object.getOwnPropertySymbols(obj)
:返回自身所有符号属性;Reflect.ownKeys(obj)
:返回自身所有属性(包括不可枚举和符号属性),最全面。
const sym = Symbol("id"); const obj = { a: 1 }; obj[sym] = 100; Object.defineProperty(obj, "b", { value: 2, enumerable: false }); console.log(Object.getOwnPropertyNames(obj)); // ["a", "b"](不含符号属性) console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id)] console.log(Reflect.ownKeys(obj)); // ["a", "b", Symbol(id)](所有自身属性)
四、对象的高级操作
除基础操作外,对象的复制、合并、属性描述符等高级功能在复杂场景中非常实用。
4.1 对象的复制(浅拷贝与深拷贝)
对象是引用类型,直接赋值会导致共享内存,需通过方法实现拷贝。
4.1.1 浅拷贝(仅复制顶层属性)
const obj = { a: 1, b: { c: 2 } }; // 1. 对象扩展运算符 const copy1 = { ...obj }; // 2. Object.assign() const copy2 = Object.assign({}, obj); // 浅拷贝的问题:嵌套对象仍共享引用 copy1.b.c = 3; console.log(obj.b.c); // 3(原对象被修改)
4.1.2 深拷贝(复制所有层级)
const obj = { a: 1, b: { c: 2 }, d: [3, 4] }; // 1. JSON方法(简单场景,有局限性:不支持函数、符号、循环引用等) const deepCopy1 = JSON.parse(JSON.stringify(obj)); // 2. 递归实现深拷贝(完整方案) function deepClone(target) { if (typeof target !== "object" || target === null) { return target; // 非对象直接返回 } let clone = Array.isArray(target) ? [] : {}; for (const key in target) { if (target.hasOwnProperty(key)) { clone[key] = deepClone(target[key]); // 递归拷贝 } } return clone; } const deepCopy2 = deepClone(obj); deepCopy2.b.c = 3; console.log(obj.b.c); // 2(原对象不受影响)
4.2 对象的合并
合并多个对象为一个,相同属性后面的会覆盖前面的。
const obj1 = { a: 1, b: 2 }; const obj2 = { b: 3, c: 4 }; const obj3 = { d: 5 }; // 1. Object.assign() const merged1 = Object.assign({}, obj1, obj2, obj3); console.log(merged1); // { a: 1, b: 3, c: 4, d: 5 } // 2. 对象扩展运算符 const merged2 = { ...obj1, ...obj2, ...obj3 }; console.log(merged2); // 同上
特性:均为浅合并,嵌套对象仍共享引用;适合合并配置对象等场景。
4.3 属性描述符(Object.defineProperty())
通过属性描述符可精确控制属性的行为(是否可修改、枚举等)。
const obj = {}; // 定义属性描述符 Object.defineProperty(obj, "name", { value: "属性值", // 属性值 writable: false, // 是否可修改(默认false) enumerable: true, // 是否可枚举(默认false) configurable: false // 是否可删除或修改描述符(默认false) }); obj.name = "新值"; // 无效(writable为false) delete obj.name; // 无效(configurable为false) console.log(Object.keys(obj)); // ["name"](enumerable为true)
应用场景:定义常量属性、实现数据劫持(如Vue响应式原理)等。
五、常见问题与避坑指南
5.1 引用类型的赋值陷阱
const obj1 = { a: 1 }; const obj2 = obj1; // 引用赋值,共享内存 obj2.a = 2; console.log(obj1.a); // 2(原对象被修改)
解决方案:用浅拷贝或深拷贝创建新对象,避免直接引用赋值。
5.2 for...in遍历的继承属性问题
const obj = { a: 1 }; // 给原型添加属性(会被for...in遍历到) Object.prototype.b = 2; for (const key in obj) { console.log(key); // "a", "b"(不希望遍历继承的b) }
解决方案:用hasOwnProperty()
过滤继承属性:
for (const key in obj) { if (obj.hasOwnProperty(key)) { console.log(key); // "a"(仅自身属性) } }
5.3 符号属性的遍历问题
符号(Symbol
)作为属性名时,for...in
、Object.keys()
等方法无法遍历:
const sym = Symbol("id"); const obj = { a: 1, [sym]: 2 }; console.log(Object.keys(obj)); // ["a"](不含符号属性) for (const key in obj) { console.log(key); // "a"(不含符号属性) }
解决方案:用Object.getOwnPropertySymbols()
获取符号属性:
const symbols = Object.getOwnPropertySymbols(obj); console.log(symbols); // [Symbol(id)] console.log(obj[symbols[0]]); // 2
总结:对象操作的核心原则
- 选择合适的创建方式:简单对象用字面量,多实例对象用
class
或构造函数,复杂继承用Object.create()
;- 区分自身与继承属性:操作属性时注意
hasOwnProperty()
的使用,避免误操作继承属性;- 遍历方法的选择:
Object.keys()
/values()
适合简单遍历,Reflect.ownKeys()
适合获取所有属性;- 深浅拷贝的区别:浅拷贝适合扁平对象,嵌套对象需用深拷贝避免引用问题。
到此这篇关于JavaScript之对象操作详解的文章就介绍到这了,更多相关js对象操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!