JavaScript对象详解
编程小白也能轻松掌握的JavaScript对象核心知识
1. 对象是什么?
JavaScript中的对象就像现实生活中的物体。比如一辆汽车,它有属性(颜色、品牌、型号)和行为(启动、停止、加速)。
💡 现实类比:把对象想象成盒子,里面装有各种特性(属性)和能力(方法)。
在JavaScript中,对象是键值对的集合:
- 键(key):属性的名称(字符串)
- 值(value):属性的值(可以是任何类型:字符串、数字、数组、函数,甚至其他对象)
let car = {
brand: “Toyota”, // 属性: 品牌
model: “Camry”, // 属性: 型号
year: 2020, // 属性: 年份
start: function() { // 方法: 启动
console.log(“Engine started!”);
}
};
📝 注意:JavaScript中几乎所有东西都是对象,包括数组、函数、日期等。
2. 如何创建对象?
有几种常见方式来创建对象:
2.1 对象字面量(最常用)
使用大括号 {} 直接创建
name: “张三”,
age: 30,
job: “工程师”
};
2.2 使用 new Object()
person.name = “张三”;
person.age = 30;
person.job = “工程师”;
2.3 使用构造函数(稍后详细介绍)
this.name = name;
this.age = age;
this.job = job;
}
let person1 = new Person(“张三”, 30, “工程师”);
2.4 使用类(ES6)
constructor(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
}
let person1 = new Person(“张三”, 30, “工程师”);
3. 对象的属性
3.1 访问属性
两种方式:点记法(.)和方括号记法([])
// 点记法
console.log(person.name); // 输出: 张三
// 方括号记法(属性名作为字符串)
console.log(person[“age”]); // 输出: 30
// 方括号记法在属性名是变量时特别有用
let prop = “name”;
console.log(person[prop]); // 输出: 张三
3.2 添加/修改属性
// 添加新属性
person.age = 30;
// 修改现有属性
person.name = “李四”;
console.log(person); // { name: “李四”, age: 30 }
3.3 删除属性
使用 delete 操作符
delete person.age;
console.log(person); // { name: “张三” }
console.log(person.age); // undefined
3.4 枚举属性
使用 for…in 循环遍历对象属性
name: “张三”,
age: 30,
job: “工程师”
};
for (let key in person) {
console.log(key + “: ” + person[key]);
}
// 输出:
// name: 张三
// age: 30
// job: 工程师
4. 对象的方法
对象的方法是对象属性的一种,只不过它的值是一个函数。
name: “张三”,
sayHello: function() {
console.log(“你好,我是” + this.name);
}
};
person.sayHello(); // 输出: “你好,我是张三”
💡 理解方法:把方法看作对象的能力或行为。比如手机有”打电话”的能力,汽车有”启动”的能力。
📝 简洁写法(ES6):
let person = {
name: “张三”,
sayHello() {
console.log(`你好,我是${this.name}`);
}
};
5. this关键字
this 在 JavaScript 中是一个特殊关键字,它引用的是当前执行上下文。
⚠️ 重要:this 的值取决于函数是如何被调用的,而不是定义的位置。
常见情况:
let person = {
name: “张三”,
sayName() {
console.log(this.name); // this引用person对象
}
};
person.sayName(); // “张三”
// 情况2:单独使用
console.log(this); // 在浏览器中,this指向window对象
// 情况3:在函数中
function test() {
console.log(this); // 严格模式为undefined,非严格模式为window
}
test();
// 情况4:事件处理函数中
button.onclick = function() {
console.log(this); // this指向触发事件的button元素
}
🎯 解决this问题:使用箭头函数或bind()方法固定this的指向
let person = {
name: “张三”,
friends: [“李四”, “王五”],
showFriends() {
this.friends.forEach(function(friend) {
// 这里的this不是person对象!
console.log(this.name + “的朋友: ” + friend);
});
}
};
person.showFriends(); // 输出undefined的朋友…
// 解决方法1:箭头函数
showFriends() {
this.friends.forEach(friend => {
console.log(this.name + “的朋友: ” + friend);
});
}
// 解决方法2:使用bind
showFriends() {
this.friends.forEach(function(friend) {
console.log(this.name + “的朋友: ” + friend);
}.bind(this));
}
6. 构造函数
构造函数是用于创建多个相似对象的特殊函数。
💡 类比:构造函数就像对象的蓝图或模具。
function Person(name, age) {
// this指向新创建的对象
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`你好,我是${this.name}`);
};
}
// 2. 使用 new 关键字创建对象实例
let person1 = new Person(“张三”, 30);
let person2 = new Person(“李四”, 25);
person1.sayHello(); // 你好,我是张三
person2.sayHello(); // 你好,我是李四
📝 new 关键字的四个步骤:
- 创建一个新的空对象 {}
- 将这个新对象的原型指向构造函数的prototype属性
- 将这个新对象绑定到构造函数中的this
- 执行构造函数中的代码(添加属性)
- 返回这个新对象(除非构造函数返回另一个对象)
7. 原型(prototype)
原型是JavaScript实现继承的机制。每个对象都有一个隐藏属性 __proto__(原型),指向其构造函数的prototype对象。
💡 理解原型:把原型看作对象的基因或家族特征。
this.name = name;
}
// 给Person的原型添加方法
Person.prototype.sayHello = function() {
console.log(“你好,” + this.name);
};
let person1 = new Person(“张三”);
person1.sayHello(); // 输出: “你好,张三”
// 检查sayHello方法是不是在原型上
console.log(person1.hasOwnProperty(‘sayHello’)); // false
console.log(Person.prototype.hasOwnProperty(‘sayHello’)); // true
原型链
当访问对象的属性时,JavaScript引擎会:
- 检查对象本身是否有该属性
- 如果没有,查找对象的原型(__proto__)
- 如果还没有,查找原型的原型,依此类推
- 直到找到属性或到达原型链的末端(null)
console.log(person1.hasOwnProperty(‘toString’)); // false
// 但可以调用
console.log(person1.toString()); // “[object Object]”
// 因为原型链:
// person1 -> Person.prototype -> Object.prototype -> null
8. 类(Class)语法(ES6)
ES6引入了class语法,让JavaScript开发者能用更接近传统面向对象语言的方式创建对象和实现继承。
📝 注意:JavaScript的class本质上是基于原型的语法糖,它没有改变JavaScript的原型继承机制。
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 方法定义
sayHello() {
console.log(`你好,我是${this.name}`);
}
}
// 使用类创建对象
let person1 = new Person(“张三”, 30);
person1.sayHello(); // “你好,我是张三”
继承
constructor(name, age, grade) {
super(name, age); // 调用父类的构造函数
this.grade = grade;
}
study() {
console.log(`${this.name}正在学习`);
}
}
let student1 = new Student(“李四”, 18, “高三”);
student1.sayHello(); // “你好,我是李四”(继承自Person)
student1.study(); // “李四正在学习”
9. 对象的拷贝
复制对象时有浅拷贝和深拷贝之分:
⚠️ 注意:直接赋值(=)只是创建了一个新引用,指向同一个对象,不是真正的复制!
9.1 浅拷贝
只复制第一层属性,嵌套对象还是引用
let obj = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, obj);
// 方法2:展开运算符(…)
let shallowCopy2 = { …obj };
// 浅拷贝后
shallowCopy.b.c = 999; // 修改会影响原对象!
console.log(obj.b.c); // 999
9.2 深拷贝
完全复制对象及其嵌套对象
let obj = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(obj));
deepCopy.b.c = 999; // 不会影响原对象
console.log(obj.b.c); // 2
// 方法2:使用第三方库如lodash的_.cloneDeep()
// 或者自己实现递归复制函数
📝 JSON方法的限制:无法复制函数、undefined、循环引用等
10. 对象相关的常用静态方法
Object构造函数提供了一些实用的静态方法:
Object.keys(obj)
返回对象自身可枚举属性组成的数组
console.log(Object.keys(person)); // [“name”, “age”]
Object.values(obj)
返回对象自身可枚举属性值组成的数组
Object.entries(obj)
返回对象自身可枚举属性的键值对数组
Object.assign(target, …sources)
将多个源对象的属性复制到目标对象(浅拷贝)
let obj2 = { b: 2 };
let merged = Object.assign({}, obj1, obj2);
console.log(merged); // { a: 1, b: 2 }
Object.freeze(obj)
冻结对象,使其不能被修改(添加、删除、修改属性)
frozen.name = “李四”; // 静默失败(严格模式下报错)
console.log(frozen.name); // “张三”