浅谈Javascript的原型和原型链

date
Jan 21, 2022
slug
1
status
Published
tags
Learn
summary
Javascript的原型和原型链
type
Post
Book
notion image
图中Parent()是构造函数,p1Parent()构造出来的实例对象。

前置知识

  • 想要弄清楚原型和原型链,这几个属性必须搞清楚:__proto__prototypeconstructor
  • 函数是对象的一种
  • 任何函数都可以作为构造函数,当函数被new调用时,该函数就成为一个构造函数。
    • 举个例子:
var Parent = {

}
//定义一个函数,此时它只是一个普通函数

var p1 = new Parent();
//此时Parent()不是一个普通函数,它是一个构造函数。因为我们通过new关键字调用它。
  • __proto__constructor、属性是对象独有的。但函数也是对象,所以函数也有这两个属性。
  • prototype属性是函数独有的

prototype属性

为了方便举例,我们模拟一个场景。父类比作师傅,子类比作徒弟。师傅收徒弟,徒弟还可以收徒弟。然后徒弟可以得到师傅传授的武功,然后徒弟再传给自己的徒弟。师傅想要传授给徒弟的武功就放到“prototype”这个属性里面。徒弟徒孙们就去这里学习武功。
prototype属性可以看成是一块特殊的存储空间,存储着提供给子孙后代使用的方法和属性。
notion image
prototype是函数独有的属性,从图中可以看到它指向另一个对象。我们可以把这个对象看成是这个函数的原型,通过这个对象定义的方法和属性都可以继承到后代。

proto属性

问题来了,p1怎么知道它的原型对象上有这个方法和属性呢?
notion image
从图中可以看出__proto__属性相当于通往prototype的路。而这条路也是通往该对象的原型对象(也可以理解成父对象)。
p1.__proto__ === Parent.prototype; //true
__proto__通常称为隐式原型,prototype通常称为显式原型。一个对象的隐式原型指向了该对象的构造函数的显式原型。
Parent.prototype.__proto__ === Object.prototype //true
来到这里,有一个比较绕的点要注意。我们知道一个对象的隐式原型指向该对象的构造函数的显式原型,但构造函数的原型本身也是对象,那么它的隐式原型指向哪呢?从上面的代码可以看出,构造函数的原型对象上的隐式原型对象是指向Object的原型对象。由此可以验证一个结论,万物继承自Object.prototype。这也说明了为什么我们实例化一个对象,并且可以调用对象上没有的属性和方法。
//我们并没有在Parent定义任何方法属性,但是我们可以调用
p1.toString();//hasOwnProperty 等等的一些方法
在这里我们就要引出原型链这个概念,当我们调用p1.toString()的时候,先在p1对象本身寻找,没有找到则通过p1.__proto__找到原型对象Parent.prototype,也没有找到,又通过Parent.prototype.__proto__找到上一层原型对象Object.prototype。在这一层找到了toString() 方法,返回给p1对象使用。但如果在Object.prototype也没有找到的话,就在Object.prototype.__proto__上找,但Object.prototype.__proto__==null,所以就返回undefined

Constructor属性

constuctor属性是让“徒弟”、“徒孙”们知道谁创造了自己,这里可不是找”师傅“,而是找自己的父母。追溯到头就是Function()
notion image
constructor是对象才有的属性,从图中可以看出:对象的constructor是指向它的构造函数,而函数的原型对象则是指向它的本身。要注意的一点是:Object的构造函数指向的是Function()
 

© LewisWong 2021 - 2025