Skip to content

1. 原型(Prototype)

每一个 JavaScript 对象( null 除外)在创建时会与之关联另一个对象,这个被关联的对象称之为 原型(原型对象)。每一个对象都会从原型中‘继承’(委托)原型对象的属性。原型对象就是用来存放实例中共有的那部分属性。每个函数都有一个特殊的属性叫作原型(prototype),这个属性指向调用该构造函数而创建的实例的原型。原型对象中有一个属性 constructor, 它指向函数对象。

javascript
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log('Hello, ' + this.name + '!');
};

const person1 = new Person('Kimi');
const person2 = new Person('Alice');

person1.greet(); // 输出: Hello, Kimi!
person2.greet(); // 输出: Hello, Alice!

在这个例子中,Person.prototypeperson1person2的原型。它们共享greet方法,但有自己的name属性。

什么是 prototype

prototype 是函数才有的属性,这个属性指向一个对象,该对象正是调用该构造函数而创建的实例的原型。指向原型对象箭头函数是没有 prototype 属性的。在最新 ES 规范里,prototype 被定义为:给其它对象提供共享属性的对象。每个函数都有一个 prototype 属性,它默认指向一个 Object 空对象(即称为:原型对象)。

注意

并不是所有的函数都有 prototype 属性,由函数 bind()方法返回的函数就没有 prototype 属性。

函数的 prototype 属性,在定义函数时自动添加 prototype,默认是一个空 Object 对象

什么是 __proto__

隐式原型

每一个 JavaScript 对象( null 除外)都有一个属性,叫 __proto__ ,这个属性指向该对象的原型指向原型对象, 原型对象其实就是通过 Object 构造函数生成的。它是历史遗留,在某些环境中,比如 Deno,它是不被支持的。所有函数的 __proto__ 指向他们的原型对象。

什么是 constructor

每个原型都有一个 constructor 属性指向关联的构造函数。

constructor 属性其实就是一个拿来保存自己构造函数引用的属性,没有其他特殊的地方。默认 constructor 实际上是被当做共享属性放在它们的原型对象中。

2. 原型链(Prototype Chain)

一个实例对象在调用属性和方法的时候,会依次从实例本身、构造函数原型、原型的原型上去查找

原型链是一个用于属性查找的对象链表。当访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript引擎会沿着原型链向上查找,直到找到该属性或到达链的末端。

原型链的起点是对象的内部属性[[Prototype]](通常通过Object.getPrototypeOf()__proto__属性访问),终点是null,因为Object.prototype的原型是null

javascript
const obj = {
  a: 1
};

const protoObj = Object.create(obj);
protoObj.b = 2;

console.log(protoObj.a); // 输出: 1,因为protoObj的原型上有a属性
console.log(protoObj.b); // 输出: 2,因为protoObj上有b属性

在这个例子中,protoObj的原型是obj,所以当访问protoObj.a时,会沿着原型链找到obj上的a属性。

通过原型链的继承,一个对象可以访问其父对象或更高级别原型链上的属性和方法:

js
class User {
    constructor(username, password) {
        this.username = username;
        this.password = password;
    }
    login() {
        console.log("登录");
    }
}

function Admin() {
    this.deletePerson = function() {
        console.log("删除");
    };
}

Admin.prototype = new User();
let admin = new Admin();
admin.login(); // 输出:登录

在这个例子中,Admin 对象没有 login 方法,但它通过原型链从 User 对象继承了这个方法。