初识原型链——怎么画一条完整的原型链

关于原型链的知识,我刚开始就是看一篇一篇的博客,知识有点零碎,后面看了下《JS高级程序设计》,又大体整合了一下。下面记录的是,对于一条完整的原型链长什么样这个问题的思考。javascript

1、疑惑

借用一段在《JS高级程序设计》中的一段代码:html

function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

此段代码中,构造函数Person,原型对象Person.prototype以及实例对象person1person2的关系以下图所示:java

按照书上的解释,很好理解,但很显然,它并非一条完整的原型链,而且我也有疑问:书中给出的示例图中没有画构造函数Person和原型对象Person.prototype的_proto_属性。segmentfault

首先:_proto_属性是个对象都有,那么,在万物皆对象的JS中,构造函数Person和原型对象Person.prototype_proto_属性又指向哪里呢??其次,就这个问题,结合上面给出的代码,能够将一条完整的原型链用相似上面的图画出来吗??函数

2、再疑惑

以前看了一篇博文,其中分析了_proto_应该指向谁的问题,举了下面这个例子,可能直接看会有点蒙圈,原文: JS原型链简单图解 - 最骚的就是你 - 博客园工具

对应的示例代码以下:this

var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(A.prototype即A的原型对象)
console.log(a.__proto__.__proto__); //Object {}(Object.prototype即Object的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null

其实,对于a._proto_ ==A.prototype以及Object.prototype._proto_==null比较好理解:.net

使用函数表达式方法建立的函数变量A,就是新的函数变量a的构造函数,因此a的原型对象就是其构造函数的prototype所指的对象:A.prototype
而Object.prototype._proto_为何是null能够参考:为何原型链的终点是null,而不是Object.prototype?- 余百炼的博客 - CSDN博客prototype

然而,为何A.prototype的原型是Object.prototype,即A.prototype._proto_==Object.prototype怎么解释呢??设计

3、解答

其实,原型链是指对象的原型链,这个链上的节点都是一级一级的原型对象,因此原型链上的全部节点都是对象!!!所以这就好理解了,A.prototype 是一个对象,而后它是Object的一个实例,因此它的原型是Object.prototype

这里须要注意的是,a虽然是原型链上的起点,它也是对象,可是它的原型并非直接就是Object.prototype,它的原型是其构造函数的prototype所指的对象:A.prototype

其实这个图描述的还只是局部 ,不利于理解,请看下面的图:(图片取自javascript - 为何原型链的终点是null,而不是Object.prototype - SegmentFault 思否



从这张大图中咱们能够看出来,

  1. 全部函数对象的原型(即fun._proto_)都是Function.prototype,不管是JS原生的构造函数如Function仍是Object等仍是用户自定义的构造函数如上图的Foo;
  2. 全部构造函数对应的原型对象的原型(即fun.prototype._proto_)都是Object.prototype,不管是自定义的仍是原生的(Object除外);
  3. 存储在函数对象中的属性有_proto_、prototype(其实也有constructor,通通指向Function,就连Function本身的constructor也是指向本身,也就是说全部函数对象都是Function的实例,包括它本身);
  4. 存储在原型对象中的属性有_proto_、constructor;
  5. 存储在实例对象中的属性有_proto_(其实也有constructor不过和原型对象中的指向一致)

根据这个图,能够更好的理解下面这两句话:

  1. 因为_proto_是任何对象都有的属性,而JS里万物都是对象,因此会造成一条_proto_连起来的链条,递归访问_proto_必须最终到头,而且值是null;
  2. 当JS引擎查找对象的属性时,先查找对象自己是否存在该属性,若是不存在,会在原型链上查找,但不会查找自身的prototype

4、再解答

这样的话,关系就比较明了了。因此,文章开头的那段代码的原型链应该以下图所示:




因为绘图工具问题,function Function应该还有一个指向本身的constructor没画出来。这个图对应的代码又贴在了下面,离得近方便看…………

function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

趁着这个机会,能够讨论一下原生构造函数ObjectFunction到底什么关系?
从上图中咱们能够把FunctionObject部分单独摘出来:




用代码将上面的原型链表示以下:

Function._proto_==Function.prototype==function(){}
Function._proto_._proto_==Object.prototype
Function._proto_._proto_._proto_==Object.prototype._proto_==null

Object._proto_ === Function.prototype==function(){}
Object._proto_._proto_==Object.prototype
Object._proto_._proto_._proto_==null

从上图中能够看到构造函数之间的关系以下:

Object.prototype.constructor===Object
Object instanceof Function;//true
Object.constructor===Function

Function.prototype.constructor===Function
Function instanceof Object;//true
Function.constructor===Function

5、小结

  1. 全部函数对象的原型(即fun._proto_)都是Function.prototype不管是JS原生的构造函数如Function仍是Object等仍是用户自定义的构造函数;
  2. 全部构造函数对应的原型对象的原型(即fun.prototype._proto_)都是Object.prototype,不管是自定义的仍是原生的
  3. 存储在函数对象中的属性有_proto_、prototype(其实也有constructor,通通指向Function,就连Function本身的constructor也是指向本身)
  4. 存储在原型对象中的属性有_proto_、constructor
  5. 存储在实例对象中的属性有_proto_(其实也有constructor不过和原型对象中的指向一致)

Reference:

  1. JS原型链简单图解 - 最骚的就是你 - 博客园
  2. javascript - 为何原型链的终点是null,而不是Object.prototype - SegmentFault 思否
  3. 为何原型链的终点是null,而不是Object.prototype?- 余百炼的博客 - CSDN博客
  4. 《JS高级程序设计》
相关文章
相关标签/搜索