前言
一、原型
- 原型
prototype
,是函数特有的属性 - 所有的函数都有原型,常规的对象与数组是没有原型的
- 可以在原型上挂载属性和方法
- 在函数的原型上挂属性与方法是为了继承
- 实例对象在被创建的时候会默认关联原型,并且从原型继承属性与方法
- 函数的原型下有一个
constructor
属性,这个属性指向的是这个原型关联的构造函数
function Person() {}
// 在函数的原型上挂载属性
Person.prototype.name = '小明'
Person.prototype.age = 18
// 在函数的原型上挂载方法
Person.prototype.getAge = function () {
console.log(this.age)
// 打印出 18
}
构造函数的实例对象
// person1 是构造出来的实例对象
let person1 = new Person()
// 在这个实例上可以访问到 Person() 的原型挂载的属性与方法
console.log(person1.name)
console.log(person1)
person1.getAge()
// 打印出
// 小明
// Person {}对象
// 18
二、原型链
原型链 __proto__
或是 [[prototype]]
要访问一个实例对象的属性或方法
函数有 __proto__
属性
函数都是由 function
原生构造函数创建的,所以函数的 __proto__
属性指向 function
的 prototype
属性
let fn = function(){}
// 函数(包括原生构造函数)的原型对象为 function.prototype
fn.__proto__ === function.prototype // true
- 从当前实例对象的属性去查找,如果找到了,就返回。否则顺着原型链
__proto__
一层一层往上找 - 直到找到 null 为止,如果找到 null 都没有找到这个属性或方法的话,找属性的话会报 undefined,找方法会错
- 实例对象的原型链
__proto__
指向的是构造它的函数的原型prototype
- Person() 的原型是由 Object() 构造的
三、看图分析
Person() 是一个函数,prototype
是函数的原型,age,getAge,name
是原型上挂载的属性与方法
plan1 是 Person() new 出来的实例对象
问:为什么 plan1 可以访问到 hasOwnProperty()
hasOwnProperty()
是在 Object() 上的,能访问到就是因为原型链
- 从 Person() new 出的实例对象 plan1 开始,沿着原型链
plan1.__proto__.__proto__.__proto__
的一直向上查找,直到找到的原型对象上有此属性或方法,或是找到Object.prototype
(原型链的末尾)为止。如果找到Object.prototype
也没有找到,那就是 null,就报错。
解释
- 实例对象 plan1 的原型链
__proto__
指向的是构造它的 Person() 函数的原型。即plan1.__proto__
指向Person().prototype
- Person() 的原型对象
Person().prototype
是由 Object() 构造的。所以 Person() 的原型对象指向构造它的 Object() 的原型。即Person().prototype.__proto__
指向Object().prototype
Person().prototype
原型对象下有一个constructor
属性,这个属性始终指向创建当前对象的构造函数。即在此指向这个原型关联的构造函数Person()
,即Person().prototype.constructor
指向Person()
。