原型
JS 是一门基于原型的语言,其他语言通过类描述和实例化对象,JS 通过构造函数描述和实例化对象,通过原型实现对象继承。JS 中原型存在的意义就是让实例对象可以访问到公共方法
JS 中的原型分两种,一种是挂在对象身上的隐式原型 __proto__,另一种是挂在构造函数身上的显示原型 prototype
- 每个对象都有一个
__proto__属性,指向自己的原型对象 - 每个构造函数都有一个
prototype属性,是一个存放了共享的属性和方法的对象 - 原型对象里面的
constructor指向构造函数本身
对象的隐式原型等于它构造函数的显式原型
当对象上找不到某个属性时,他就会往原型上寻找,原型上没有就会向原型的原型上寻找,这也就是原型链的基本概念
原型机制的发展
Object.create()
早期对象的继承是通过 Object. create ()对象来继承原始对象的属性和方法,通过不停的克隆之前已有的对象形成了一条原型链
// 基础对象
const personProto = {
skills: ['JavaScript'], // 引用类型属性
greet() {
console.log(`Hello, I'm ${this.name}`)
}
};
// 创建实例
const person1 = Object.create(personProto);
person1.name = 'Alice'; // 手动添加属性
person1.skills.push('React'); // 修改共享属性
const person2 = Object.create(personProto);
person2.name = 'Bob';
// 所有实例的skills都被修改
console.log(person2.skills); // ['JavaScript', 'React']
当你使用 Object.create(someObject) 创建一个新对象时,这个新对象确实继承了 someObject 的属性和方法。但是,它共享了原型对象上的引用类型属性(如数组、对象)。如果新对象修改了这个共享的引用类型属性,所有继承自同一个原型的对象都会受到影响。同时,对新对象添加实例特有属性需要在创建后手动添加,比较繁琐且不利于封装。
原型+构造函数
随着 Javascript 的发展,演变为通过原型+构造函数来模拟类批量生成对象
构造函数的 prototype 属性 : 负责定义所有实例共享的方法(行为)。将方法放在原型上避免了每个实例重复创建相同的函数,节省内存。
// 构造函数(初始化实例属性)
function Person(name) {
this.name = name;
this.skills = ['JavaScript']; // 实例特有属性
}
// 原型方法(共享)
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
// 创建实例
const person1 = new Person('Alice');
person1.skills.push('React'); // 仅修改当前实例
const person2 = new Person('Bob');
console.log(person1.skills); // ['JavaScript', 'React']
console.log(person2.skills); // ['JavaScript']
// 不受其他实例影响
person1.greet === person2.greet // true
// 共享同一方法
这种分离(构造函数管状态初始化,prototype 管共享方法)提供了一种更接近“类”概念的模型,使得代码结构更清晰封装性更好,更容易理解和维护,也更容易被有面向对象背景的开发者接受
class 语法糖
到后来 ES6 引入了 class 语法糖,其底层逻辑还是运用了原型+构造函数这一核心机制
class Person {
// 构造函数逻辑
constructor(name) {
this.name = name;
this.skills = ['JavaScript'];
}
// 原型方法
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
// 继承演示
class Developer extends Person {
constructor(name, language) {
super(name); // 调用父类构造函数
this.language = language;
}
code() {
console.log(`${this.name} codes in ${this.language}`);
}
}
// 使用类
const dev = new Developer('Alice', 'JS');
dev.greet(); // 继承方法: "Hello, I'm Alice"
dev.code(); // 自身方法: "Alice codes in JS"
// 验证原型链
console.log(dev instanceof Person); // true
console.log(Developer.prototype.greet === Person.prototype.greet); // true
其中:
constructor:替代构造函数逻辑- 类方法:自动绑定到原型
extends:简化继承super:访问父类
原型链
V8再访问对象的属性时,会先访问对象中的属性,如果没找到,就会去这个对象的隐式原型中查找,依次类推,直到找到 Null 为止。 这种查找的链状关系就叫原型链