Skip to main content

JS 原型和原型链

一、构造函数

任何一个函数,只要被 new 操作符使用,就可以是一个构造函数(构造函数建议以大写开头)

function Person() {
// ...
}
const person = new Person();

person.name = 'Tim';
console.log(person.name) // Tim

上面 Person 就是一个构造函数,使用 new 创建了一个实例对象 person。

在 JS 的内置对象中,所有的函数对象都是 Function 构造函数的实例,比如:ObjectArray 等。

对象上的 constructor 属性可以指明这个对象的构造函数是什么。举个例子:

const person1 = new Person();
const person2 = new Person();

person1.constructor === Person; // true
person2.constructor === Person; // true

Person 函数是由 JS 内置函数 Function 函数实例化,Person.constructor === Function。而 Function 本身还是个构造函数,它的 constructor 是它本身。

二、原型及原型链

1、prototype 指向原型

JS 每个构造函数都有一个 prototype 属性,这个 prototype 属性对应的值就是原型,包含了该构造函数的属性和方法;

function Person() {
// ...
}
// prototype 是函数才会有的属性
Person.prototype.name = 'Tim';

const person = new Person();
console.log(person.name) // Tim

函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,即上面例子中 person 的原型。构造函数与实例原型的关系如下:

2、__proto__ 指向构造函数的原型

JS 每个对象都有一个 __proto__(隐式原型)属性,指向它构造函数的 prototype 属性,即:

对象.__proto__ === 构造函数.prototype

function Person() {
// ...
}

const person = new Person();
console.log(person.__proto__ === Person.prototype); // true

关系图如下:

3、constructor 指向原型的构造函数

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

function Person() {
// ...
}

console.log(Person === Person.prototype.constructor); // true

关系图如下:

综上:

function Person() {
// ...
}

const person = new Person();

console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 可通过 getPrototypeOf 获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true

4、什么是原型链

当对象读取属性时,先在自身找,有就直接用,没有就沿着 __proto__ 这条链往上找,直到 Object 原型的位置,有就返回相应的值,没有就返回 null,这个过程就形成了原型链。下面用蓝色线表示原型链:

举个例子:

Function.prototype.a = "a";
Object.prototype.b = "b";

function Person() { }
console.log(Person); // ƒ Person() { }

const person = new Person();
console.log(person); // Person {}
console.log(person.a); // undefined
console.log(person.b); // b

上面代码中,由于 personPerson() 的实例,是一个 Person 对象,它拥有一个属性值 __proto__,并且 __proto__ 是一个对象,包含两个属性值 constructor__proto__

console.log(person.__proto__);               // {constructor: ƒ}
console.log(person.__proto__.constructor); // ƒ Person() { }
console.log(person.__proto__.__proto__); // Object.prototype
console.log(person.__proto__.__proto__.constructor); // Object()

上面代码中:

  • person.__proto__ 返回构造函数 Person 的原型,即 Person.prototype
  • person.__proto__.constructor 等价于 Person.prototype.constructor,返回构造函数 Person 本身。
  • person.__proto__.__proto__,返回 Object.prototype,有很多参数。
  • person.__proto__.__proto__.constructor,返回 Object() 本身。

因此,person.b 打印结果为 bp 本身没有 b 属性,会一直通过 __proto__ 向上查找,最后当查找到 Object.prototype 时找到,最后打印出 b,向上查找过程中,得到的是 Object.prototype,而不是 Function.prototype,找不到 a 属性,所以 person.aundefined,这就是原型链,通过 __proto__ 向上进行查找,最终到 null 结束。

console.log(person.__proto__.__proto__.__proto__);   // null
console.log(Object.prototype.__proto__); // null

同理,得出以下结果

function Function() { }

console.log(Function); // ƒ Function() { }
console.log(Function.prototype.constructor); // ƒ Function() { }
console.log(Function.prototype.__proto__); // Object.prototype
console.log(Function.prototype.__proto__.constructor); // Object()
console.log(Function.prototype.__proto__.__proto__); // null

5、原型链的继承

function Sex() {
this.sex = 'man';
}

function Star() {
this.name = 'Tim';
}

Star.prototype = new Sex();

const man = new Star();
console.log(man.sex) // man

四、总结

  • Object 是所有对象的父级,所有对象都可以通过 __proto__ 找到它。
  • Function 是所有函数的父级,所有函数都可以通过 __proto__ 找到它。
  • 函数的原型 prototype 是一个对象。
  • 对象的 __proto__ 属性指向原型, __proto__ 将对象和原型连接起来组成了原型链。