Javascript 编程基础(5)面向对象 | 5.2、原型系统
文章目录
- 一、原型系统
- 1、概念
- 2、核心特性
- 2.1、动态性
- 2.2、属性遮蔽
- 2.3、constructor属性
- 3、使用场景
- 3.1、方法共享
- 3.2、原型继承
- 4、注意事项
- 5、总结
前言:
在 JavaScript 中,原型(Prototype)是实现继承和对象间委托的核心机制。理解原型不仅能帮助开发者写出更高效的代码,也是深入掌握 JavaScript 语言特性的关键。
一、原型系统
1、概念
JavaScript是一种基于原型的语言,与传统的类继承不同,它通过原型对象实现属性和方法的共享。每个对象都有一个内部属性
[[Prototype]]
(在多数浏览器中可通过__proto__
访问),它指向该对象的原型对象。当访问一个对象的属性时,JavaScript会先在对象本身查找,若未找到则沿[[Prototype]]
链向上查找,直到找到该属性或到达原型链的终点(Object.prototype
)。下面是与原型相关的核心概念:
- 原型链(Prototype Chain):
- 每个对象都有一个原型对象,形成一条链式结构
- 原型链的终点是
Object.prototype
,其[[Prototype]]
为null
- 构造函数与原型的关系:
- 每个函数都有一个
prototype
属性,指向一个普通对象 - 当使用
new
关键字创建对象时,新对象的[[Prototype]]
指向构造函数的prototype
- 每个函数都有一个
- 动态特性:
- 原型对象的修改会立即反映在所有继承它的对象上
- 可在运行时修改原型链,影响后续属性查找
2、核心特性
2.1、动态性
原型对象的修改会立即影响所有实例:
function Dog() {}
let dog1 = new Dog();Dog.prototype.bark = function() {console.log('Woof!');
};dog1.bark(); // Woof!(即使实例在方法添加前创建)
2.2、属性遮蔽
当实例和原型有同名属性时,实例属性会"遮蔽"原型属性:
function Cat() {}
Cat.prototype.legs = 4;let myCat = new Cat();
myCat.legs = 3;console.log(myCat.legs); // 3(自身属性)
delete myCat.legs;
console.log(myCat.legs); // 4(原型属性)
2.3、constructor属性
每个原型对象默认都有
constructor
属性指向构造函数:
function Foo() {}
console.log(Foo.prototype.constructor === Foo); // truelet foo = new Foo();
console.log(foo.constructor === Foo); // true(通过原型链查找)
3、使用场景
3.1、方法共享
最典型的应用是在构造函数原型上定义方法,实现所有实例共享方法:
function Car(model) {this.model = model;
}// 所有Car实例共享同一个drive方法
Car.prototype.drive = function() {console.log(`${this.model} is driving`);
};let car1 = new Car('Tesla');
let car2 = new Car('BMW');car1.drive(); // Tesla is driving
car2.drive(); // BMW is driving
3.2、原型继承
JavaScript通过原型链实现继承的核心是让子类的原型指向父类的实例,从而使子类实例能够继承父类的属性和方法。以下是最简实现:
- 定义父类构造函数,并在其原型上添加方法。
- 定义子类构造函数,并在其中调用父类构造函数(继承属性)。
- 设置子类原型为父类的实例(继承方法)。
- 修复子类原型的constructor属性,指向子类自身。
下面是一个具体的示例,通过原型链实现继承,如下:
示例代码:
// 父类:动物
function Animal(type) {this.type = type; // 实例属性
}// 父类原型方法
Animal.prototype.speak = function() {console.log(`${this.type} makes a sound`);
};// 子类:狗
function Dog(name) {Animal.call(this, "Dog"); // 继承父类属性this.name = name; // 子类新增属性
}// 设置子类原型为父类的实例
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复constructor// 子类新增方法
Dog.prototype.bark = function() {console.log(`${this.name} barks!`);
};// 使用示例
const dog = new Dog("Buddy");
dog.speak(); // 输出: Dog makes a sound (继承自父类)
dog.bark(); // 输出: Buddy barks! (子类自有方法)
关键点解释:
Object.create(Animal.prototype)
:
创建一个新对象,其原型为
Animal.prototype
,避免直接修改Animal.prototype
。
Dog.prototype.constructor = Dog
:
修复原型链后,
Dog.prototype.constructor
会指向Animal
,需手动修正为Dog
。
- 属性查找顺序 :当访问
dog.speak()
时- 先检查
dog
实例本身 >> 无 - 再检查
dog.__proto__
(即Dog.prototype
) >> 无 - 继续检查
Dog.prototype.__proto__
(即Animal.prototype
)>> 找到speak
方法
- 先检查
4、注意事项
-
避免修改原生对象原型:
- 修改
Array.prototype
、Object.prototype
等会导致全局影响,可能引发冲突 - 优先使用继承或封装
- 修改
-
性能考量:
- 过深的原型链会影响属性查找性能
- 频繁访问的属性应直接定义在对象本身
-
原型与实例的关系:
- 实例属性会遮蔽原型属性,但不会修改原型
- 使用
hasOwnProperty()
区分实例属性和原型属性
const obj = { a: 1 }; console.log(obj.hasOwnProperty('a')); // true console.log(obj.hasOwnProperty('toString')); // false (继承自原型)
5、总结
JavaScript的原型系统是一种强大而灵活的继承机制,它通过原型链实现了对象间的属性共享和行为委托。理解原型的核心原理(构造函数、
[[Prototype]]
、属性查找机制)是掌握JavaScript高级特性的基础。