📝javaScript中的「原型和原型链」

66 阅读4分钟

constructor

在js设计之初,constructor 是专门为 function 设计的,所以每一个函数的原型上都有constructor属性,并且当前函数的原型上的constructor属性指向函数自身。

  let fo = function () {};
  console.log(fo.prototype.constructor === fo); // true

什么是原型,原型链?

原型:每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,其实就是 prototype 对象。

原型链:由相互关联的原型组成的链状结构就是原型链。

image.png

如图所示:我们通过new的形式构造一个实例对象(person),每一个实例对象身上都有隐藏的属性__proto__,这是链接实例对象和原型的桥梁,( __proto__ 就是一条原型链中的一环),通过这种方式我们可以访问原型对象上的属性和方法。由于原型对象本身也是一个对象,它也有__proto__属性,同样它也可以向上查找它的原型属性和方法,这种链式的查找结构就是原型链。

函数和对象的关系

函数是对象,对象同时都是通过函数创建的(js中一切皆对象,故而存在函数对象和普通对象,函数和对象之间不是简单的包含关系,这个需要悟,不再赘述。。。。)

原型的类别

显示原型:prototype,函数(function)独有的属性
  let obj = {};
  let fo = function () {};
  console.log(fo.prototype); //  {constructor: ƒ, …}
  console.log(obj.prototype); // undefined

从上面可以看出只有函数才有显示原型,对象并不才存在显示原型。

隐式原型:__ proto __,对象都具有的属性
  let obj = {};
  let fo = function () {};
  console.log(fo.__proto__);  //  ƒ () { [native code] }
  console.log(obj.__proto__); // {constructor: ƒ, …}

每个对象都有__proto__属性,Object、Function都是js内置的函数, 类似的还有我们常用到的Array、RegExp、Date、Boolean、Number、String

js中两个最顶层的概念

最顶层的构造器---Function(function是js中最顶层的构造器,它构造了所有的对象,包括它自己)

  let fo = function () {};
  console.log(fo.prototype); //  ƒ () { [native code] }
  console.log(fo.prototype.__proto__); // Object.prototype
  console.log(fo.prototype.__proto__.__proto__); // null

image.png 每一个函数都有自己显示原型(fo.prototype),又由于显示原型本身也是一个对象,所以fo.prototype拥有自己的隐式原型指向Object的原型对象,而Object的原型对象指向null。

最顶层的对象---Object(所有的对象都继承了Object原型,Object也是function构造出来的)

  let obj = {};
  console.log(obj.__proto__); // {constructor: ƒ, …}
  console.log(obj.__proto__.__proto__); // null 

image.png

从上面可以看出普通对象的隐式原型指向Object的原型,所以可以推导出 obj.__proto__ === Object.prototype;。又由第一幅图中的普通函数中原型对象也是指向Object的原型,同样可以推导出 fo.prototype.__proto__ = Object.prototype;两者都和Object的原型对象存在对等关系。由此我们似乎可以得出函数和对象的某种关系,于是联想到构造函数...

在构造函数中,实例对象(即person)的隐式原型(__proto__)等于构造该对象的函数的显示原型,(即每个对象的隐式原型都指向构建该对象的函数的显示原型)

  function Person() {} 
  let person = new Person(); 
  console.log(person.__proto__=== Person.prototype); //true

推导公式

1.普通对象的隐式原型和Object的显示原型相等

    let obj = {} 
    console.log(obj.__proto__ === Object.prototype); //true

2.函数对象的隐式原型和Function的显示原型相等

    function Fn() {} 
    console.log(Fn.__proto__ === Function.prototype); //true

3.所有的构造器都是函数对象,函数对象都是 Function 构造产生的

    Object.__proto__ === Function.prototype

解释:从上面的三点可以得出,普通对象的产生是由Object构造器构造的,而函数对象的产生是由Function构造器构造的,所有构造器都是函数,所以Object是函数对象,又由于函数对象都是 Function 构造产生的。

4.原型对象(Foo.prototype)本身是一个普通对象,而普通对象的构造函数都是Object,所以普通对象的隐式原型和构造它的Object的显示原型相等。(参考1)

    function Foo(){} 
    Foo.prototype.__proto__ = Object.prototype;

5.Object 的原型对象也有__proto__属性指向null,null是原型链的顶端

    Object.prototype.__proto__ = null;

6.Function.prototype本质也是普通对象,普通对象的隐式原型指向Object的显示原型。

    Function.prototype.__proto__ = Object.prototype;

7.参考constructor

    Function.prototype.constructor = Function;

四、原型链的查找方式

当JavaScript引擎发现一个对象访问一个属性时,会首先查找对象的“自有属性”,如果没有找到则会在[[proto]]属性指向的原型属性中继续查找,如果还没有找到的话,你知道其实原型属性也是一个对象,所以它也有一个隐式的[[proto]]属性指向它的原型属性...,正如你所料,如果一直没有找到该属性,JavaScript引擎会一直这样找下去,直到找到最顶部构造函数Object的prototype原型属性,如果还是没有找到,会返回一个undefined值。这个不断查找的过程,有一个形象生动的名字“攀爬原型链”。

五、图解

image.png