原型(prototype)
每个函数都有一个属于自己的原型(prototype)这个原型指向一个对象(又称“原型对象”)。核心作用是存放所有实例共享的属性 / 方法,节省内存。通过这个构造函数创建的所有实例,都会隐式指向这个原型对象(通过 __proto__ 或 Object.getPrototypeOf())
js
// 普通函数
function Fun() {
}
// 未挂载任何数据,所以这个原型对象是个空对象(当然还有一些默认的属性)
console.log(Fun.prototype);
// 挂载数据
Fun.prototype.name = '张三'
Fun.prototype.say = function () {
console.log('hello');
}
// 挂载数据后,原型对象就有了 name 和 say 这两个属性
console.log(Fun.prototype);
/* --------- 输出 ---------
* {}
* { name: '张三', say: [Function (anonymous)] }
* */通过 prototype 挂载的数据是全局可用的。也就是说,当使用 new 关键字去创建对象的时候,不会对挂载的数据进行拷贝,而是会直接复用
js
function Fun(name) {
// 无法复用
this.name = name
this.say = function () {
console.log('hello');
}
}像是上述的写法,数据是挂载到 this 中,直接对其 new 创建对象的话会拷贝一份一摸一样的数据到内存中
而原型对象不是一个空的对象,它是有两个隐藏属性的(不会再控制台打印出来)
| 属性名 | 类型 | 描述 |
|---|---|---|
constructor | 函数 | 指向创建该原型对象的构造函数(即当前函数) 用于标记原型对象所属的构造函数 |
__proto__ | 对象 / Null | 指向 Object.prototype(原型链上一级)构成原型链的核心,实现属性 / 方法向上查找 |
在创建实例的时候实例的 constructor ≈ 构造函数,因为实例本身没有 constructor,是通过 __proto__ 继承了 构造函数.prototype.constructor,而这个属性默认指向构造函数
js
function Person(name) {
this.name = name;
}
// 原型对象的 constructor 指向 Person
console.log(Person.prototype.constructor === Person); // true
// 实例可通过原型链访问 constructor
const p1 = new Person('张三');
console.log(p1.constructor === Person); // true(p1.__proto__.constructor)原型链(__proto__)
__proto__:原型对象的 “向上链接”,原型对象作为 “普通对象”,也有 __proto__ 属性(非隐藏,是 ES6 规范保留的兼容属性),默认指向 Object.prototype(Object.prototype.__proto__ 为 null)
基本上只要是对象都会有 __proto__ 属性(除了使用 Object.create(null) 创建出来的纯净对象),常用的 toString() 就是通过 __proto__ 向上查找到 Object 的原型来调用的
在 JS 中实现继承就是改变子类原型的原型链的指向即可
js
// 父类构造函数
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
// 子类构造函数
function Child(name, age) {
// 继承父类的实例属性(如果不加的话子类实例无法继承 name 属性)
Parent.call(this, name);
this.age = age;
}
// 核心 - 让子类原型的原型链指向父类原型
Child.prototype.__proto__ = Parent.prototype;
// 创建子类实例
const child = new Child(18);
console.log(child.__proto__ == Child.prototype); // true 子类实例的原型链指向子类原型
console.log(Child.prototype.__proto__ == Parent.prototype); // true 而子类原型的原型链指向父类原型