概述

对象的每个实例都具有一个proto属性,指向的构造函数的原型对象,而原型对象同样存在一个proto属性指向上一级构造函数的原型对象,就这样层层往上,直到最上层某个原型对象为null。

  1. // 定义一个构造函数,生成一个实例
  2. function Person(){}
  3. var perosn = new Person();
  4. // person实例沿着原型链第一次追溯,__proto__属性只想Person()构造函数的原型对象
  5. perosn.__proto__ === Person.prototype
  6. // person实例沿着原型链第二次追溯,Person原型对象的__proto__属性指向Object类型的原型对象
  7. perosn.__proto__.__proto__ === Person.prototype.__proto__ === Object.prototype;
  8. // person实例沿着原型链第三次追溯,Object类型的原型对象的__proto__属性为null
  9. perosn.__proto__.__proto__.__proto__ === Object.prototype.__proto__ === null;

原型链关系图

image.png

关系图详解

第一部分是自定义的Foo()函数,Foo()函数的prototype属性指向Foo.prototype对象,通过Foo()构造函数生成实例,f1,f2,它们的proto属性指向Foo.prototype对象。 Foo()函数本身可以作为Function对象的实例,可以理解为下面的代码 var Foo = New Function(); 因此 Foo.proto指向Function.prototype.

第二部分与Object()构造函数有关,Object()构造函数本身也是Function类型,因此,Object.proto指向Function.prototype,通过Object()构造函数o1,o2,它们的proto属性指向Object.prototype对象。

第三部分与Function()构造函数有关,Function.proto指向Function.prototype,而Function.prototype为一个对象,他的proto属性指向Object.prototype对象。 就这样所有的对象通过proto属性向上寻找都是一定会追溯到Object.prototype的。

原型链特点

1.原型链的特点—原型链的特点主要有两个, 特点1: 由于原型链的存在,属性查找的过程不再是只查找自身的原型对象,而是会沿着整个原型链一直向上,指导追溯到Obejct.prototype。如果Object.prototype上也找不到该属性,则会返回’undefind’。如果期间在实例本身或者某个原型对象上找到了该属性,则会返回结果,因此会存在属性覆盖的问题。 —由于特点1的存在,我们在生成自定义对象的实例时,也可以调用到某些未在自定义构造函数上的函数,例如toString()函数。 特点2:由于属性查找会经历整个原型链,因此查找的链路越长,对性能的影响越大。

  1. 属性区分, 对象属性的查找往往会涉及整个原型链,那么该怎么区分属性是实例自身的还是从原型链中继承呢,Object()构造函数的原型对象中提供了一个hasOwnProperty()函数,用于判断属性是否为自身拥有的。 name属性为实例属性,在调用hasOwnProperty()函数时,会返回“true”; age属性为原型对象上的属性,在调用hasOwnProperty()函数时,会返回“false”。
    3. 内置构造函数 JavaScript中有一些特定的内置构造函数,如String()构造函数、Number()构造函数、Array()构造函数、Object()构造函数等。它们本身的proto属性都统一指向Function.prototype
  2. proto 属性在JavaScript的原型链体系中,最重要的莫过于proto__属性,只有通过它才能将原型链串联起来。