Skip to content

原型(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.prototypeObject.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 而子类原型的原型链指向父类原型