JavaScript中的对象操作详解_javascript技巧
JavaScript中对象是数据和功能的集合,几乎所有值都可以视为对象(除原始类型外),掌握对象的创建、属性操作、遍历及继承等核心技能,是深入理解JavaScript的关键。
JavaScript提供了多种创建对象的方式,各有适用场景,选择合适的方式能让代码更高效。
对象字面量是创建对象最简洁的方式,用{}
包裹键值对,键名可省略引号(符合标识符规则时)。
// 基础语法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); // "北京"
特性:语法简洁,适合创建单个对象,无需定义构造函数,是日常开发的首选。
构造函数用于创建多个结构相同的对象,通过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会指向全局对象(严格模式下报错)。
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(); // "进食中"(继承的方法)
特性:直接指定原型,适合实现复杂继承关系;创建的对象默认无自身属性,需手动添加。
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 }
对象的核心是属性(包括数据属性和方法),掌握属性的增删改查是对象操作的基础。
属性访问有两种方式:点语法(.)和方括号语法([]),后者支持动态属性名。
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"])。
对象属性可动态添加,通过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(即使属性不存在),但不能删除变量或函数。
判断属性是否存在需区分“自身属性”和“继承属性”,常用方法如下:
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")); // trueconsole.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运算符检查是否可访问(包括继承)。
遍历对象的属性是常见需求,不同方法适用于不同场景,需注意遍历顺序和属性类型。
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
注意:
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一致。
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 }
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)](所有自身属性)
除基础操作外,对象的复制、合并、属性描述符等高级功能在复杂场景中非常实用。
对象是引用类型,直接赋值会导致共享内存,需通过方法实现拷贝。
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(原对象被修改)
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(原对象不受影响)
合并多个对象为一个,相同属性后面的会覆盖前面的。
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); // 同上
特性:均为浅合并,嵌套对象仍共享引用;适合合并配置对象等场景。
通过属性描述符可精确控制属性的行为(是否可修改、枚举等)。
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响应式原理)等。
const obj1 = { a: 1 };const obj2 = obj1; // 引用赋值,共享内存obj2.a = 2;console.log(obj1.a); // 2(原对象被修改)
解决方案:用浅拷贝或深拷贝创建新对象,避免直接引用赋值。
const obj = { a: 1 };// 给原型添加属性(会被for...in遍历到)Object.prototype.b = 2;for (const key in obj) { console.log(key); // "a", "b"(不希望遍历继承的b)}
解决方案:用hasOwnProperty()过滤继承属性:
符号(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
总结:对象操作的核心原则
到此这篇关于JavaScript之对象操作详解的文章就介绍到这了,更多相关js对象操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
本文地址: https://www.earthnavs.com/jishuwz/b86fa356da9f0846395a.html
























