JavaScript中的原型和原型链
# 原型
在JavaScript中, 每一个构造函数都拥有一个 prototype 属性, 这个属性指向一个对象,也就是 原型对象; 而原型对象默认拥有一个 constructor 属性,指向它关联的构造函数。 简单来说,就是当我们创建一个函数的时候,系统就会自动分配一个 prototype属性,prototype属性指向此构造函数的原型对象, 用来存储可以让所有实例共享的属性和方法;
# 原型链
说原型链之前我们先来看一个例子
//构造函数
function Cat(name,color){
this.name = name
this.color = color
}
// 添加一个sayHello方法到Cat构造函数的原型对象上,所有实例对象都可以共享此方法
Cat.prototype.sayHello = ()=>{
console.log("hello world")
}
//实例对象
let myCat = new Cat("狸花猫","棕色")
myCat.sayHello() // hello world
myCat.hasOwnProperty("sayHello") // false ,表明sayhello并不是实例对象的自有属性
上面的例子可以看出,由构造函数 Cat 创建而来的实例对象 myCat 身上并没有 sayHello 方法, 那为什么实例对象 myCat 又可以正确访问到这个方法输出 hello world 呢 ?
因为在JavScript中,每一个实例对象都有一个私有属性 ( _proto_ ), 指向它的构造函数的原型对象, 即 myCat._proto_ === Cat.prototype , 如下
这样,上面的那个问题就很清楚了, 虽然实例对象 myCat 自己身上没有 sayHello 方法,但是它的隐式的私有属性_proto_指向
了它的构造函数Cat的原型对象 Cat.prototype ; 而实例对象 myCat 在访问某个属性时, 会在自己身上寻找,如果它自身没有找到这个属性的话 ,它会通过_proto_去寻找这个属性, myCat. _proto_ 是指向它的构造函数Cat的原型对象 Cat.prototype 的, 所以在原型对象 Cat.prototype 的身上找到了sayHello( )
这个时候可能有人要说了, 要是在 myCat. _proto_ 指向的原型对象Cat.prototype 上还是没有找到想要访问的属性呢 ? 其实一样的, 那就去继续去原型对象的 _proto_ , 即 Cat.prototype._proto_ 上寻找这个属性, Cat.prototype._proto_指向谁呢?指向 原型对象 Cat.prototype 的构造函数的原型对象 Object.prototype , 即 Cat.prototype._proto_ === Object.prototype , 所以会在 Object.prototype 这个原型对象上寻找访问的属性
解释一下 :为什么 Cat.prototype 的构造函数是 Object , 而不是Function ?
Cat 的构造函数才是 Function , 而 Cat.prototype 的构造函数是 Object
同理,如果在 Object.prototype 上也没有找到正在访问的属性的话,就继续去 Object.prototype ._proto_ 上寻找 ,即在 null 中寻找 , 因为 Object.prototype ._proto_ === null 的 ,null 就是原型链的终点 , 这一整个过程就是 原型链
# 整体流程如下:
myCat. _proto_ === Cat.prototype
Cat.prototype. _proto_ === Object.prototype
Object.prototype. _proto_ === null
我们在浏览器控制台验证一下原型链流程
# 原型链总结
当对象去访问一个属性的时候,它会先在自己身上去寻找这个属性,如果自己身上没有这个属性的话,它会去它的原型对象的身上去寻找这个属性,如果它的原型对象的身上也没有这个属性的话,它会去它的原型对象的原型对象身上去寻找这个属性,这样层层往上,直到找到这个属性或者到达原型链的终点 null 为止。