JS 原型和原型链
一、构造函数
任何一个函数,只要被 new
操作符使用,就可以是一个构造函数(构造函数建议以大写开头)
function Person() {
// ...
}
const person = new Person();
person.name = 'Tim';
console.log(person.name) // Tim
上面 Person 就是一个构造函数,使用 new
创建了一个实例对象 person。
在 JS 的内置对象中,所有的函数对象都是 Function
构造函数的实例,比如:Object
、Array
等。
对象上的 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
上面代码中,由于 person 是 Person() 的实例,是一个 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 打印结果为 b,p 本身没有 b 属性,会一直通过 __proto__
向上查找,最后当查找到 Object.prototype
时找到,最后打印出 b,向上查找过程中,得到的是 Object.prototype
,而不是 Function.prototype
,找不到 a 属性,所以 person.a 为 undefined
,这就是原型链,通过 __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__
将对象和原型连接起来组成了原型链。