JavaScript 面向对象
·
一、基础概念:对象与面向对象思想
1. 什么是对象?
- 本质:对象是属性和方法的集合,属性描述事物的 “特征”(如身高、颜色),方法描述事物的 “行为”(如吃饭、行驶)。
- 现实映射:现实世界中的万物皆可视为对象(如士兵、汽车、人),编程中通过对象模拟这些事物。
- JavaScript 特性:“一切皆可看作对象”—— 即使是基本数据类型(如
string、number),也可通过包装类转化为对象使用。
2. 面向对象 vs 面向过程
两者是不同的编程范式,核心区别在于 “解决问题的关注点” 不同:
| 对比维度 | 面向过程(POP) | 面向对象(OOP) |
|---|---|---|
| 核心关注点 | 解决问题的步骤和过程 | 解决问题的对象 |
| 编程特点 | 由独立函数实现步骤,无类 / 对象 | 先定义 “类”,再从类创建 “对象” |
| 调用者视角 | 需手动调用所有函数,关注实现细节 | 只需调用对象的方法,无需关注细节 |
| 典型场景 | 简单任务(如计算两数和) | 复杂项目(如电商系统、游戏) |
3. 面向对象三大核心特征
- 封装:将对象的属性和方法 “包裹” 在一起,隐藏内部实现细节,仅对外暴露接口(如调用
car.run()无需知道 “如何行驶”)。 - 继承:一个对象(子类)可获取另一个对象(父类)的属性和方法,减少代码重复(如 “电动汽车” 继承 “汽车” 的 “行驶” 方法)。
- 多态:同一方法在不同对象上有不同实现(如 “动物叫”,猫是 “喵喵”,狗是 “汪汪”),JavaScript 通过 “方法重写” 实现。
4. 关键概念补充
| 概念 | 定义 |
|---|---|
| 属性 | 对象的特征(如car.color、person.age),分为 “实例属性” 和 “原型属性” |
| 方法 | 对象的行为(如car.run()、arr.sum()),分为 “实例方法” 和 “原型方法” |
| 抽象 | 从多个对象中提取共性特征的过程(如从 “宝马”“比亚迪” 中抽象出 “汽车” 类) |
| 重写 | 子类修改父类继承的方法(如子类ElectricCar重写父类Car的run()) |
| 实例化对象 | 通过 “类 / 构造函数” 创建的具体对象(如var bmw = new Car(),bmw是实例) |
二、JavaScript 创建对象的 4 种方式
1. 方式 1:对象字面量(最常用)
直接用{}定义对象,适合创建单个、简单的对象。
// 定义汽车对象
var car = {
// 属性
color: "白色",
brand: "BMW",
price: "56W",
// 方法
run: function () {
console.log(this.brand + "在路上行驶"); // this指向当前对象car
},
stop: function (msg) {
console.log(this.brand + "刹不住车了!" + msg);
}
};
// 调用属性和方法
console.log(car.color); // 访问属性:白色
car.run(); // 调用方法:BMW在路上行驶
car.stop("危险!"); // 调用方法:BMW刹不住车了!危险!
2. 方式 2:Object 构造函数
通过new Object()创建空对象,再动态添加属性和方法,适合创建空对象后需动态扩展的场景。
// 1. 创建空对象
var car = new Object();
// 2. 动态添加属性
car.color = "白色";
car.brand = "BMW";
// 3. 动态添加方法
car.run = function () {
console.log(this.brand + "在路上行驶");
};
// 调用
console.log(car.brand); // BMW
car.run(); // BMW在路上行驶
3. 方式 3:在已有对象上扩展属性 / 方法
给数组、函数等内置对象添加自定义属性和方法(需谨慎,避免污染内置对象)。
// 给数组对象添加属性和方法
var arr = [1, 2, 3];
// 添加属性
arr.num = 10;
// 添加方法
arr.getNum = function () {
console.log(this.num); // this指向arr
};
// 调用
arr.getNum(); // 10
4. 方式 4:构造函数(批量创建对象)
当需要创建多个结构相同的对象(如多辆汽车、多个人)时,用构造函数批量生成,函数名首字母建议大写(区分普通函数)。
4.1 基础构造函数
// 定义“汽车”构造函数(相当于“类”)
function Car(width, color, brand) {
// 实例属性(每个实例独有)
this.width = width;
this.color = color;
this.brand = brand;
// 实例方法(每个实例都会创建新函数,浪费内存)
this.run = function () {
console.log(this.brand + "在路上行驶");
};
}
// 批量创建实例对象
var byd = new Car("1500mm", "红色", "比亚迪");
var bmw = new Car("2000mm", "白色", "BMW");
// 调用
byd.run(); // 比亚迪在路上行驶
bmw.run(); // BMW在路上行驶
console.log(byd.run === bmw.run); // false(每个实例的run是新函数,内存地址不同)
4.2 优化:原型(prototype)添加方法
为解决 “实例方法重复创建” 的内存浪费问题,将方法添加到构造函数的原型对象上(所有实例共享原型方法)。
// 1. 定义构造函数(只放实例属性)
function Car(width, color, brand) {
this.width = width;
this.color = color;
this.brand = brand;
}
// 2. 原型对象添加方法(所有实例共享)
Car.prototype.run = function () {
console.log(this.brand + "在路上行驶");
};
Car.prototype.stop = function () {
console.log(this.brand + "刹住车了");
};
// 3. 创建实例
var byd = new Car("1500mm", "红色", "比亚迪");
var bmw = new Car("2000mm", "白色", "BMW");
// 调用
byd.run(); // 比亚迪在路上行驶
bmw.stop(); // BMW刹住车了
console.log(byd.run === bmw.run); // true(共享原型方法,内存地址相同)
三、原型与原型链(JavaScript 面向对象的核心)
1. 核心概念:原型对象(prototype)
- 定义:所有函数都有一个
prototype属性,该属性指向一个 “原型对象”(普通对象),用于存储所有实例共享的属性和方法。 - 作用:实现继承和方法共享,减少内存占用(如
Car.prototype的run方法被所有Car实例共享)。
2. 构造函数(constructor)
- 定义:原型对象的
constructor属性指向 “创建该原型的构造函数”,形成闭环。 - 示例:
Car.prototype.constructor === Car(true)、Object.prototype.constructor === Object(true)。
3. 实例的__proto__属性
- 定义:每个对象(包括实例)都有
__proto__属性(浏览器实现,非标准但通用),指向 “其构造函数的原型对象”。 - 作用:连接实例与原型对象,实现 “属性 / 方法查找”(如实例
byd找不到run时,通过byd.__proto__找Car.prototype.run)。 - 示例:
var byd = new Car(); console.log(byd.__proto__ === Car.prototype); // true(实例的__proto__指向构造函数的原型) console.log(byd.__proto__.constructor === Car); // true(原型的constructor指向构造函数)
4. new关键字的 4 个执行步骤
当用new 构造函数()创建实例时,底层执行以下操作:
- 创建空对象:生成一个空的实例对象(如
var obj = {})。 - 绑定 this:让构造函数中的
this指向空实例对象(如Car的this指向obj)。 - 执行构造函数:给实例对象添加属性(如
obj.width = width)。 - 返回实例:默认返回
this(即创建好的实例对象,无需手动return)。
5. 原型相关的 4 个常用方法
| 方法 | 作用 | 示例 |
|---|---|---|
Object.getPrototypeOf(实例) |
获取实例的原型对象(替代__proto__,标准方法) |
Object.getPrototypeOf(byd) === Car.prototype(true) |
原型.isPrototypeOf(实例) |
判断 “原型对象” 是否是 “实例” 的原型 | Car.prototype.isPrototypeOf(byd)(true) |
实例.hasOwnProperty(属性名) |
判断属性是否是 “实例自身的属性”(不包含原型属性) | byd.hasOwnProperty("brand")(true,brand 是实例属性) |
属性名 in 实例 |
判断属性是否存在于 “实例自身” 或 “原型对象” 中(包含原型属性) | "run" in byd(true,run 是原型方法) |
四、实战案例
案例 1:给数组添加 “求和” 方法
需求:让所有数组都能调用sum()方法,计算数组元素的总和。
// 原型添加sum方法(所有数组共享)
Array.prototype.sum = function () {
var total = 0;
for (var i = 0; i < this.length; i++) {
total += this[i]; // this指向当前数组
}
return total;
};
// 测试
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [10, 20, 30];
console.log(arr1.sum()); // 15
console.log(arr2.sum()); // 60
案例 2:给数组添加 “去重” 方法
需求:让所有数组调用noRepeat()方法,去除重复元素。
// 原型添加去重方法
Array.prototype.noRepeat = function () {
var tempArr = [];
for (var i = 0; i < this.length; i++) {
// 若tempArr中没有当前元素,则添加
if (tempArr.indexOf(this[i]) === -1) {
tempArr.push(this[i]);
}
}
return tempArr; // 返回去重后的新数组
};
// 测试
var numArr = [9, 9, 3, 4, 8, 9, 3];
var uniqueArr = numArr.noRepeat();
console.log(uniqueArr); // [9, 3, 4, 8]
更多推荐


所有评论(0)