最通俗易懂的原型、原型链理解

最通俗易懂的原型、原型链理解 前言
首先原型、原型链,算是前端进阶里面必不可少,十分重要的一块了。在面试,面试官很喜欢用这一块来辨别你的底层知识掌握的怎么样;用的第三方框架,库里面,很多功能模块化了,但大部分功能都继承自一个基类,所以了解原型、原型链对我们使用第三方的框架、库也有着很大的帮助。
理解什么是__proto__prototypeconstructor
很多人在看这一块知识的时候,刚开头看可能还能理解,看久了就懵了,那是因为代码中充斥着各种x.__proto__.__proto__x.__proto__.constructor.prototypex.prototype.__proto__,这当然会懵掉。所以我们要理解原型、原型链是什么,一定要先搞明白,__proto__prototypeconstructor是什么东西。
下面笔者会比较用通俗的话来解释,带着大家更好的理解原型,原型链是什么(因为为了大家更好的理解,所以有些地方可能会稍微有点不恰当,敬请见谅)。
__proto__: 这个属性可以通俗的理解成,所有对象拥都有的属性(函数也是一种特殊的对象,所以构造函数也会有这个属性)。所以实例出来的对象,构造函数都会有__proto__这个属性。它最后一定指向某个构造函数的原型(x.prototype)。因此,当我们看到最后结尾如果是.__proto__,那它的返回值一定是x.prototype

但只有一个例外,那就是Obeject.prototype.__proto__,它的末端是null,所以我们看到.__proto__结尾时,一定要判断好前面是不是Obeject.prototype
因此我们可以总结__proto__以下特点:
  • 对象都拥有的属性,构造函数也有
  • 最后一定指向某个构造函数的prototypex.prototype
  • 只有一个例外,Obeject.prototype.__proto__指向的是null
  • 构造函数的__proto__都直接指向Function.prototype
prototype: 这个属性可以通俗的理解成,只有构造函数才会拥有的属性,实例出来的对象,是不会有这个属性的。
【最通俗易懂的原型、原型链理解】因此我们可以总结prototype以下特点:
  • 构造函数独有的属性
constructor: 这个属性存在于两个地方
  1. 构造函数的原型对象(x.prototype
  2. 构造函数本身也有(继承自Function.prototype.constructor
实例出来的对象也可以访问到constructor,是因为实例出来的对象constructor继承自构造函数的原型对象(x.prototype),可以用hasOwnProperty('constructor')验证
验证:
function Person() {} var lMC = new Person(); console.log(lMC.constructor.name); // Person console.log(lMC.hasOwnProperty('constructor')); // false console.log(Person.prototype.constructor.name); // Person console.log(Person.prototype.hasOwnProperty('constructor')); // true

因此我们可以总结constructor以下特点:
  • 构造函数的原型对象(x.prototype)拥有的属性,指回构造函数本身
  • 构造函数本身也有,指向Function
什么是原型链 当我们用构造函数Func实例化了一个对象A后,访问A的方法或者属性时,会现在A自身找有没有对应的方法属性,没有的话则通过A.__proto__去构造函数的原型对象Func.prototype找,如果Func.prototype也没有,则在往Func.prototype.__proto__Obeject.prototype找,如果还没有则再通过Obeject.prototype.__proto__找,在这过程中,如果有则返回相应的方法属性,没有的话则再通过Obeject.prototype.__proto__找,但此时Obeject.prototype.__proto__已经到顶,指向的是null,所以此时没有对应的方法属性,返回undefined
在查找的过程中会遍历以上的一条链,这条链就是原型链:
最通俗易懂的原型、原型链理解
文章图片

等同于
最通俗易懂的原型、原型链理解
文章图片

方法总结
假定我们用Func表示构造函数;obj表示Func的实例对象
  1. 如果最后以__proto__结尾,返回的一定是x.prototypeObject.prototype.__proto__除外),所以我们先确定是谁的__proto__
    • 如果是Func__proto__,那么直接指向Function.prototype
    • 如果是obj__proto__,那么直接指向其实例的构造函数的prototype(Func.prototype)
    • 如果是Func.prototype__proto__,那么直接指向Object.prototype,(因为Func.prototype是对象,其构造函数是Object
  2. 如果是以prototype结尾,只用判断是谁的prototype,只有构造函数才有prototype属性
    • obj.prototype,返回undefined,因为obj是实例,不是构造函数
    • Func.prototype,返回Func这个构造函数的prototype所有内容
  3. 如果是以constructor结尾,先弄清楚前面是对象还是构造函数;
    • 如果是构造函数的constructor,则直接指向Function
      • Func.constructor,直接指向Function,因为构造函数的构造器,当然是Function
    • 如果是对象的constructor
      • obj.constructor,直接指向Func,因为obj是由Func构造而来,所以当然是Funcobj.constructor实际上是继承自Func.prototype.constructorobj本身是没有constructor的)
    • 构造函数.prototype.constructor指回这个构造函数
    • Func.prototype.constructor,指回Func本身
    • Object.prototype.constructor,指回Obeject构造函数
    • Obeject.constructor,指向Function
牛刀小试
根据上面对__proto__prototypeconstructor的特点总结,还有方法总结,我们可以拿下面这道题来试试,如果大家都可以正确无误的答出来,那大家对原型应该就了解的差不多了
function Person(name) { this.name = name } var p2 = new Person('king'); console.log(p2.__proto__); // Person.prototypeconsole.log(p2.__proto__.__proto__); // Object.prototypeconsole.log(p2.__proto__.__proto__.__proto__); // nullconsole.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__); // 报错console.log(p2.constructor); // Personconsole.log(p2.prototype); // undefinedconsole.log(Person.constructor); // Functionconsole.log(Person.prototype); // 输出Person.prototype这个对象里所有的方法和属性console.log(Person.prototype.constructor); // Personconsole.log(Person.prototype.__proto__); // Obejct.prototypeconsole.log(Person.__proto__); // Fuction.prototypeconsole.log(Function.prototype.__proto__); // Obeject.prototypeconsole.log(Function.__proto__); // Function.prototypeconsole.log(Object.__proto__); // Function.prototypeconsole.log(Object.prototype.__proto__); // null

最后
原型、原型链本来就挺绕的,所以建议大家先了解__proto__prototype之间的链接,熟悉了,再把constructor加上一起理解,循环渐进。等理解以后,多画几遍原型链图加深理解。OK,最后假定我们用Animal表示构造函数;dog表示Animal的实例对象,祭出一张原型链图:
红色链表示的是我们平时实例出来的对象的原型链
最通俗易懂的原型、原型链理解
文章图片

    推荐阅读