原型链类型的题在面试中属于高频考点,下面用一个经典的面试题还原js中原型链的真面目。

请问:f有方法a和方法b吗?

var F = function () {};
Object.prototype.a = function () {};
Function.prototype.b = function () {} ;
var f = new F()

在做这题之前,一定要了解js中原型链的一些继承关系,把那种继承关系图记在脑子里,然后看到题目后把各自一一对应寻找继承关系,之前走过弯路,初学js时在网上找原型链的图,又复杂又记不住,类似这种:
201622785912987.jpg
不要看了,反正我是看不下去。

正言

在js中,对象都有__proto__属性,一般这个是被称为隐式原型,该隐式原型指向构造该对象的构造函数的原型。
函数比较特殊,它除了和其他对象一样有__proto__属性,还有自己特有的属性----prototype,这个属性是一个指针,指向一个包含所有实例共享的属性和方法的对象,称之为原型对象。原型对象也有一个constructor属性,该属性指回该函数。

按照上述的理论可以把构造函数(函数)、对象(构造函数new出来的)、Object(顶层的对象)之间的大概关系串起来。类似如下:
原型链.jpg

这张图是我在吴华的javascript高级进阶课程中看到的截图,受用至今。
记住这四个最主要的点就ok了,对照上图理解记忆:

  1. 对象都有个隐式原型__proto__),它指向构造函数的原型对象(prototype)。
  2. 构造函数的原型对象也有个隐式原型__proto__)它指向Object的原型对象(prototype).
  3. Object的原型对象有个constructor指向Object本身。
  4. 构造函数的constructor指向构造函数本身。

这里看完你就能明白对象上的toString()或者valueof()是怎么来的了,打印一下Object的原型对象,就能得到结果,你去访问对象的属性(包括方法),他会先找自身有没有这个属性,没有的话会沿着原型链一层一层往上找,例如上图,我要访问objshowAge方法,他本身有,返回他本身的showAge方法,要访问showUserNameobj没有,会沿着隐式原型去看看构造函数的原型对象有没有,有的话返回,没有的话继续去Object的原型对象去找,依次往上。这也不难理解为什么对象上都有toString()方法了。

解题思路

理解原型链后,再回到最初的那道面试题:

  1. 首先,f是什么?
    f是构造函数F new出来的对象,按照上面我说的,对象都有个隐式原型,它指向构造函数的原型对象,即f的__proto__指向F.prototype。
  2. 沿着原型链,F.prototype.__proto__指向Object.prototype,即得出第一个答案了,f可以取到a方法,由于f的原型链上没经过Function.prototype,Function.prototype是Object.prototype创建的,只能沿着_proto_向上找所以取不到b方法。
  3. 函数都是由Function new出来的,这里你可以把它当成一个被另一个构造函数new出来的普通对象,所以F这个构造函数的隐式原型也指向了Function的原型对象,即F.__proto__指向Function.prototype,所以F函数可以取到b方法(可能有点晕,下面会解释)

Function

在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数。构造函数首字母一般大写

那我理解的是理论上任何一个函数都可以当成一个构造函数。
其实上面的原型图还有一部分Function的没画出来。
了解这个之前,带着另一个面试题去思考:
原型链顶层是什么?按照上面的图你可能会认为是Object或者Object.prototype,其实不是。
注意:

要理解原型链顶层要和原型链其他地方的理解有一些不同,这里Object和Function及其原型要分开看,不能将原型和本身看作是理所当然的关系
  1. Object.prototype和Object的关系
    Object.prototype是JS世界中最早生成的,理论上有了原型(Object.prototype)就会有本身(Object),然而Object的内容却是由Function构成的。用通俗一点的话来解释就是,有了Object.prototype就有了Object的肉体,然而Object的灵魂却是由Function构成的。
  2. Object.prototype.__proto__
    Object.prototype是由上层对象创建的 (一些静态语言,像C++和Java),在这里为了不引起误会将其指向了null

顶层图.png

总结原型链顶层关系

  1. Object.prototype是由上层对象创建的 (一些静态语言,像C++和Java),在这里为了不引起误会将其指向了null
  2. Object.prototype创造了Function.Prototype
  3. Function.prototype创造了Function
  4. Object创造了其他对象(window, document, 自定义对象)
  5. Object是Function的一个实例
  6. 任何函数继承自Function.prototype
  7. 任何对象最终继承自 Object.prototype

MangoGoing
774 声望1.2k 粉丝

开源项目:详见个人详情