JavaScript对象(Object)研究_01_基本介绍
在JavaScript中,对象(Object)是核心概念之一,是构建一切的基础。无论是数组、函数,还是DOM元素,都可以看作对象。本篇博客将详细介绍JavaScript对象的基本概念、创建方法、属性操作、原型等内容,配以代码示例,帮助您深入理解对象的工作原理。
一、什么是对象?
对象是由一组键值对(key-value pair)组成的数据结构,键(Key)是字符串(在ES6中也可以是Symbol),值(Value)可以是任何类型的数据,包括基本类型和引用类型。
const person = {
name: 'Alice',
age: 25,
greet: function() {
console.log('Hello!');
}
};
在上述代码中,person
是一个对象,包含了name
、age
和greet
三个属性。其中,greet
属性的值是一个函数,也称为方法。
二、创建对象的方法
1. 对象字面量
最常用、最简单的方式。
const obj = {}; // 创建一个空对象
const car = {
brand: 'Toyota',
model: 'Camry',
year: 2020
};
2. 构造函数
使用内置的Object
构造函数。
const obj = new Object();
obj.name = 'Bob';
obj.age = 30;
3. 自定义构造函数
通过函数构造特定类型的对象。
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person('Charlie', 28);
4. Object.create()
从现有对象创建新对象,实现原型继承。
const animal = {
eats: true
};
const rabbit = Object.create(animal);
rabbit.jumps = true;
console.log(rabbit.eats); // 输出: true
5. 类(Class)创建对象(ES6+)
使用class
关键字定义类,然后创建对象。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
const person2 = new Person('Diana', 27);
person2.greet(); // 输出: Hello, I'm Diana
三、访问和修改对象属性
1. 点表示法(Dot Notation)
const user = {
name: 'David',
age: 22
};
console.log(user.name); // 输出: David
user.age = 23;
console.log(user.age); // 输出: 23
2. 方括号表示法(Bracket Notation)
适用于属性名包含特殊字符或变量。
const user = {
'first-name': 'Eve',
age: 24
};
console.log(user['first-name']); // 输出: Eve
const prop = 'age';
console.log(user[prop]); // 输出: 24
3. 动态添加属性
user.email = '[email protected]';
console.log(user.email); // 输出: [email protected]
4. 从对象中删除属性
使用delete
操作符可以从对象中删除属性。
delete user.age;
console.log(user.age); // 输出: undefined
需要注意的是,delete
只能删除对象自身的属性,无法删除从原型链继承的属性。同时,删除属性可能会影响程序的逻辑,应谨慎使用。
四、遍历对象属性
1. for...in
循环
for (let key in user) {
console.log(`${key}: ${user[key]}`);
}
2. Object.keys()
、Object.values()
、Object.entries()
const keys = Object.keys(user);
console.log(keys); // 输出: ['first-name', 'email']
const values = Object.values(user);
console.log(values); // 输出: ['Eve', '[email protected]']
const entries = Object.entries(user);
console.log(entries); // 输出: [['first-name', 'Eve'], ['email', '[email protected]']]
五、对象的方法
方法是对象属性中的函数。
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
console.log(calculator.add(5, 3)); // 输出: 8
console.log(calculator.subtract(5, 3)); // 输出: 2
六、对象的拷贝
1. 浅拷贝
只复制对象的第一层属性,内部的嵌套对象仍然是引用。
方法1:Object.assign()
const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);
copy.b.c = 3;
console.log(original.b.c); // 输出: 3
方法2:展开运算符(Spread Operator)
const copy2 = { ...original };
copy2.b.c = 4;
console.log(original.b.c); // 输出: 4
2. 深拷贝
彻底复制对象,内部嵌套的对象也会被复制,互不影响。
方法1:JSON序列化
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.b.c = 5;
console.log(original.b.c); // 输出: 4
注意:JSON.parse(JSON.stringify())
无法复制函数、undefined
、Symbol
等特殊类型。
方法2:使用递归或第三方库
可以使用递归函数或诸如lodash
的_.cloneDeep()
方法。
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Array) {
let arr = [];
for (let item of obj) {
arr.push(deepClone(item));
}
return arr;
}
if (obj instanceof Object) {
let clonedObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
}
const deepCopy2 = deepClone(original);
deepCopy2.b.c = 6;
console.log(original.b.c); // 输出: 4
七、对象比较
1. 引用比较
对象是引用类型,直接比较两个对象实际上是比较它们的引用是否相同。
const obj1 = { x: 1 };
const obj2 = { x: 1 };
console.log(obj1 === obj2); // 输出: false
const obj3 = obj1;
console.log(obj1 === obj3); // 输出: true
2. 内容比较
需要手动比较每个属性的值。
function isEqual(objA, objB) {
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;
for (let key of keysA) {
const valA = objA[key];
const valB = objB[key];
const areObjects = isObject(valA) && isObject(valB);
if (
(areObjects && !isEqual(valA, valB)) ||
(!areObjects && valA !== valB)
) {
return false;
}
}
return true;
}
function isObject(object) {
return object != null && typeof object === 'object';
}
console.log(isEqual(obj1, obj2)); // 输出: true
八、对象的属性特性
每个属性都有一些内置的特性:value
、writable
、enumerable
、configurable
。
1. 查看属性描述符
const descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
// 输出: { value: 'Eve', writable: true, enumerable: true, configurable: true }
2. 定义或修改属性特性
Object.defineProperty(user, 'gender', {
value: 'female',
writable: false,
enumerable: true,
configurable: false
});
console.log(user.gender); // 输出: female
user.gender = 'male';
console.log(user.gender); // 输出: female(无法修改)
通过设置writable: false
,属性变为只读。设置configurable: false
后,无法再删除或修改该属性的特性。
九、对象的原型与继承
1. 原型链
每个对象都有一个原型对象,属性和方法可以沿着原型链访问。
const parent = {
sayHello() {
console.log('Hello from parent');
}
};
const child = Object.create(parent);
child.sayHello(); // 输出: Hello from parent
2. 构造函数的原型
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
const person = new Person('Frank');
person.greet(); // 输出: Hello, I'm Frank
3. null原型对象
在JavaScript中,几乎所有的对象都是Object
的实例;一个典型的对象从Object.prototype
继承属性(包括方法),尽管这些属性可能被覆盖(或者说重写)。唯一不从Object.prototype
继承的对象是那些null
原型对象,或者是从其他null
原型对象继承而来的对象。
可以使用Object.create(null)
创建一个没有原型的对象:
const obj = Object.create(null);
console.log(Object.getPrototypeOf(obj)); // 输出: null
console.log(obj.toString); // 输出: undefined
由于obj
没有Object.prototype
上的属性和方法,所以toString
方法是undefined
。
4. 原型链的影响
通过原型链,所有对象都能观察到Object.prototype
对象的改变,除非这些改变所涉及的属性和方法沿着原型链被进一步重写。尽管有潜在的危险,但这为覆盖或扩展对象的行为提供了一个非常强大的机制。为了使其更加安全,Object.prototype
是核心JavaScript语言中唯一具有不可变原型的对象——Object.prototype
的原型始终为null
且不可更改。
示例:
Object.prototype.customMethod = function() {
console.log('This is a custom method');
};
const arr = [];
arr.customMethod(); // 输出: This is a custom method
const func = function() {};
func.customMethod(); // 输出: This is a custom method
const obj = {};
obj.customMethod(); // 输出: This is a custom method
上述代码在Object.prototype
上添加了一个方法,所有对象都能访问到它。但需要谨慎,因为这可能会引发命名冲突或影响第三方库的行为。
十、对象的强制类型转换
当需要将对象转换为原始类型(如字符串、数字或布尔值)时,JavaScript会尝试调用对象的toString
或valueOf
方法。
1. toString()
方法
对象默认的toString()
方法返回[object Object]
。
const obj = { a: 1 };
console.log(obj.toString()); // 输出: [object Object]
可以自定义toString()
方法:
const obj = {
a: 1,
toString() {
return `a is ${this.a}`;
}
};
console.log(String(obj)); // 输出: a is 1
2. valueOf()
方法
valueOf()
方法通常返回对象本身,但可以自定义以返回一个原始值。
const obj = {
a: 10,
valueOf() {
return this.a;
}
};
console.log(obj + 5); // 输出: 15
3. Symbol.toPrimitive
在ES6中,可以使用Symbol.toPrimitive
定义对象的强制类型转换行为。
const obj = {
a: 100,
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return this.a;
}
if (hint === 'string') {
return `Value is ${this.a}`;
}
return this.a;
}
};
console.log(+obj); // 输出: 100
console.log(`${obj}`); // 输出: Value is 100
console.log(obj + 10); // 输出: 110