构造函数、原型(prototype)、原型链

171 阅读3分钟

构造函数

函数又分为构造函数和普通函数,构造函数本身就是一个函数,与普通函数没有任何区别,不过为了规范一般将其首字母大写。构造函数和普通函数的区别在于,使用 new 生成实例的函数就是构造函数,直接调用的就是普通函数。

构造函数创建的实例拥有 constructor 属性,constructor 返回创建实例对象时构造函数的引用,例如:

function Person(name) {
	this.name = name
}
let p = new Person('liben')
console.log(p.constructor === Person) // true

实例p

原型prototype

prototype是构造函数的属性,在实例上并不存在,每个构造器函数function都有个prototype属性,原型方法和属性就在这个对象上,而非在对象实例本身中。prototype对象上有两个属性,分别是 constructor__proto__

prototype属性

从上面这张图可以发现,Parent 对象有一个原型对象 Parent.prototype,其上有两个属性,分别是 constructor__proto__

1、constructor

构造函数 Parent 有一个指向原型的指针,原型 Parent.prototype 有一个指向构造函数的指针 Parent.prototype.constructor,如下图所示,其实就是一个循环引用。

构造函数与prototype关系.jpg

function Parent() {}
console.log(Parent.prototype.constructor === Parent)   // true

2、__proto__

上上图可以看到 Parent 原型( Parent.prototype )上有 __proto__ 属性,这是一个访问器属性(即 getter 函数和 setter 函数),通过它可以访问到对象的内部 [[Prototype]] (一个对象或 null )

__proto__ 是每个实例上都有的属性,prototype 是构造函数的属性,这两个并不一样,但 p.__proto__Parent.prototype 指向同一个对象

function Parent() {}
let pa = new Parent();
console.log(pa.__proto__) // {constructor: Parent, __proto__: ...}
console.log(Parent.prototype === pa.__proto__)  // true

所以构造函数、原型、实例之间的关系如下图:

三者关系.jpg

修改或者读取原型的方法:

Object.getPrototypeOf()

Object.setPrototypeOf()

如果要创建一个新对象,同时继承另一个对象的 [[Prototype]] ,推荐使用 Object.create()

function Parent1 () {
	this.age = 50
}
let pp = new Parent1()
let child = Object.create(pp) // 等同=> child={} child.__proto__ = pp
console.log(pp)
console.log(child)  // child.__proto__指向pp实例,并继承pp实例的所有属性
console.log(child.__proto__ === pp)  // true
console.log(child.__proto__.__proto__ === Parent1.prototype)  // true

上面打印结果

这里 child 是一个新的空对象,有一个指向对象 p 的指针 __proto__

原型链

定义:每个对象拥有一个原型对象,通过__proto__指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这种关系被称为原型链

function Parent2 () {
	this.age = 27
}
let pp2 = new Parent2()  // 相当于 pp2.__proto__ = Parent.prototype

console.log(pp2)
console.log(Parent2.prototype)
console.log(pp2.__proto__ === Parent2.prototype) // true
console.log(pp2.__proto__.__proto__ === Object.prototype) // true
console.log(pp2.__proto__.__proto__.__proto__ === null) // true

打印结果如下: __proto__链

原型链的运作机制如下图:

原型链

小结

  • Symbol 作为构造函数来说并不完整,因为不支持语法 new Symbol(),但其原型上拥有 constructor 属性,即 Symbol.prototype.constructor。

  • 引用类型 constructor 属性值是可以修改的,但是对于基本类型来说是只读的,当然 null 和 undefined 没有 constructor 属性。

  • __proto__ 是每个实例上都有的属性,prototype 是构造函数的属性,在实例上并不存在,所以这两个并不一样,但 p.__proto__ 和 Parent.prototype 指向同一个对象。

  • __proto__ 属性在 ES6 时被标准化,但因为性能问题并不推荐使用,推荐使用 Object.getPrototypeOf()。

  • 每个对象拥有一个原型对象,通过__proto__指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这就是原型链。

参考:muyiy.cn/blog/5/5.1.…