🎓 作者简介 : 前端领域优质创作者
🚪 资源导航: 传送门=>
🎬 个人主页: 江城开朗的豌豆
🌐 个人网站: 江城开朗的豌豆 🌍
📧 个人邮箱: [email protected] 📩
💬 个人微信: y_t_t_t_ 📱
📌 座 右 铭: 生活就像心电图,一帆风顺就证明你挂了。 💔
👥 QQ群: 906392632 (前端技术交流群) 💬
记得刚学习JavaScript
时,面对原型和原型链这个概念,我一度感到非常困惑。直到在项目中遇到一个实际问题,才让我真正理解了它的重要性。今天,就让我们通过几个实际的例子,来揭开JavaScript对象继承的神秘面纱。
从一个实际案例说起
杨涛在开发一个员工管理系统时,写了如下代码:
javascript
function Employee(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`大家好,我是${this.name}`);
};
}
const emp1 = new Employee('杨涛', 28);
const emp2 = new Employee('李四', 30);
console.log(emp1.sayHello === emp2.sayHello); // false
他发现每个Employee
实例都创建了自己的sayHello
方法副本,这显然造成了内存浪费。如何解决这个问题呢?这就是原型大显身手的时候了。
原型的基本概念
在JavaScript中,每个函数都有一个特殊的prototype
属性,它指向一个对象,这个对象就是我们所说的原型对象。当我们使用new
操作符创建实例时,实例会继承其构造函数的原型对象上的属性和方法。
改进后的代码:
javascript
function Employee(name, age) {
this.name = name;
this.age = age;
}
Employee.prototype.sayHello = function() {
console.log(`大家好,我是${this.name}`);
};
const emp1 = new Employee('杨涛', 28);
const emp2 = new Employee('李四', 30);
console.log(emp1.sayHello === emp2.sayHello); // true
现在,所有实例共享同一个sayHello方法,大大节省了内存。
原型链的运作机制
原型链是JavaScript实现继承的基础。当访问一个对象的属性时,JavaScript引擎会:
- 先在对象自身属性中查找
- 如果找不到,就去对象的原型(
__proto__
)上查找 - 如果还找不到,就继续往原型的原型上查找
- 直到找到属性或者到达原型链顶端(null)为止
javascript
// 创建经理类,继承自Employee
function Manager(name, age, department) {
Employee.call(this, name, age);
this.department = department;
}
// 设置原型继承
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
// 添加经理特有方法
Manager.prototype.assignTask = function(task) {
console.log(`${this.name}经理正在分配任务:${task}`);
};
const mgr = new Manager('杨涛', 35, '技术部');
mgr.sayHello(); // 继承自Employee
mgr.assignTask('完成项目原型设计'); // Manager自有方法
原型链的实际应用
1. 内置方法的继承
javascript
const arr = [1, 2, 3];
console.log(arr.map); // 来自Array.prototype
console.log(arr.toString); // 来自Object.prototype
2. 属性遮蔽(Property Shadowing)
javascript
Employee.prototype.position = '员工';
const emp = new Employee('杨涛', 28);
console.log(emp.position); // "员工"
emp.position = '高级工程师'; // 创建实例自身属性
console.log(emp.position); // "高级工程师"
delete emp.position;
console.log(emp.position); // 又变回"员工"
3. 检查原型关系
javascript
console.log(emp instanceof Employee); // true
console.log(Employee.prototype.isPrototypeOf(emp)); // true
console.log(Object.getPrototypeOf(emp) === Employee.prototype); // true
ES6中的class语法糖
ES6的class语法让原型继承更加直观:
javascript
class Employee {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`大家好,我是${this.name}`);
}
}
class Manager extends Employee {
constructor(name, age, department) {
super(name, age);
this.department = department;
}
assignTask(task) {
console.log(`${this.name}经理正在分配任务:${task}`);
}
}
const mgr = new Manager('杨涛', 35, '技术部');
mgr.sayHello();
原型链的调试技巧
在Chrome开发者工具中,可以通过以下方式查看原型链:
- 使用
console.dir()
查看对象的完整结构 - 在控制台输入对象名,展开
__proto__
属性 - 使用
Object.getPrototypeOf()
获取对象的原型
常见误区与最佳实践
-
不要直接修改内置对象的原型
javascript// 不推荐的做法 Array.prototype.myMethod = function() { // ... };
-
注意原型链的性能影响
javascript// 过长的原型链会影响查找性能
-
优先使用Object.create而不是__proto__
javascript// 更好的方式 const child = Object.create(parent); // 不推荐的方式 child.__proto__ = parent;
总结
理解原型和原型链是掌握JavaScript面向对象编程的关键。通过原型链,JavaScript实现了灵活的对象继承机制。记住几个要点:
- 每个函数都有prototype属性,每个对象都有
__proto__
属性 - 原型链的终点是
Object.prototype
,再往上就是null - ES6的class语法是原型继承的语法糖
正如JavaScript之父Brendan Eich所说:"JavaScript的继承是基于原型的,这与传统的类继承有很大不同。
"掌握原型链,你就能真正理解JavaScript对象系统的精妙之处。