前言

宝图在手,天下无忧

JS的原型一直是一个很让人头大的问题,也是在面试中经常会被面试官问到的一个问题,如果事先没有准备,那么注定是致命的。在了解原型之前,我画了一张图,方便后面讲解~

FtDc4Bm32atgt2oLNqgnJO6ikJBj (2).jpg

普通对象和函数对象

image.png
在JavaScript中,一直有这么一种说法,即万物皆对象。事实上在js中,对象也是有很大区别的,我们可以将其划分成普通对象和函数对象,常见的Object与Function就是js自带的两个典型的函数对象。

怎么定义?

  1. function f1(){}
  2. const f2=function(){}
  3. const f3=new Function(){}
  4. const ob1={};
  5. const ob2=new Object()
  6. const ob3=new f1()

打印以上对象的类型,可以得到如下结果:

  1. console.log(typeof ob1) //object
  2. console.log(typeof ob2) //object
  3. console.log(typeof ob3) //object
  4. console.log(typeof f1) //function
  5. console.log(typeof f2) //function
  6. console.log(typeof f3) //function

如上所示,ob1、ob2、ob3都可以被称为普通对象,也可以理解为Object的实例,而f1、f2、f3均为Function的实例,故而称之为函数对象。

怎么区别?

所有Function的实例都是函数对象,而其他的都是普通对象。

有什么区别?

  • 普通对象只具备proto属性
  • 函数对象不仅具备proto属性,还具备prototype属性
  • 解释下为什么普通对象没有prototype属性,那是因为普通对象就是通过函数对象实例化的,而一个实例不可能再次进行实例化,也就不会让另一个对象的proto指向它的prototype,因此普通对象没有prototype属性。

    属性大杂烩

    proto属性和prototype属性

    JS创建对象会产生一个内置属性,proto,而且这个属性指向的是创建它的构造函数的原型对象。
    如上图所示,Person实例化了一个对象p1,那么p1就有一个proto属性,创建它的构造函数为Person,那么构造它的原型对象就为Person.prototype(!!!注意只有函数对象才具有prototype属性),如果用一个表达式来表述关系的话,可以这样来表示:
  1. p1.__proto__=Person.prototype

constructor

任何一个实例底层必然是被new出来的,那么这个实例必然要和它的“爸爸”建立一定的关系,才能确保实例是他爸爸new出来的,那么每一个实例就会有一个constructor的属性,这个属性指向的刚好是它爸爸本身,说的专业一点的就是实例的构造函数属性指向的是它的构造函数。用表达式表述就是:

  1. p1.constuctor=Person

原型对象是构造函数的一个实例

再来看看下面这段代码,你会发现什么?

  1. p1.__proto__=Person.prototype

我们可以这样理解,任何一个构造函数它都具有一个prototype属性,这个就被称为原型对象,可以称它为构造函数的孩子集,那么所有被构造函数new出来的对象就是构造函数的儿子(实例),而这些儿子却都有一个proto的属性,这个属性就相当于一个线,用于指明它是属于谁的儿子集,所以都会指向Person.prototype这个原型对象。

原型对象的constructor属性

既然前面说到了任何一个对象都会有constructor属性和proto属性,那么原型对象也不例外的啊~看下面的表达式不知道能不能理解

  1. Person.prototype.constructor=Person
  2. p1.__proto__.constructor=Person
  3. Person.prototype.__proto__=Object.prototype
  4. p1.__proto__.__proto__=Object.Prototype

其他

构造函数Person()除了是一个函数之外,它还是一个对象,那么他也有一个proto属性,指向的就是它的构造函数的原型对象,即函数的构造函数原型对象:Function.prototype。

那么Function.prototype也是一个对象,它指向的就是它的构造函数的原型对象,因为在JS中,一切皆为对象,所以原型对象为Object.prototype,最后要保证整个原型链有尾,那么Object.prototype.proto指向的为null。

JS中的6大内置(函数)对象的原型继承

image.png
两句话总结:

1、任何内置函数对象本身的proto都指向Function的原型对象,即Function.prototype 2、除了Object的原型对象的proto指向null,其他所有内置函数对象的原型对象的proto都指向Object

例如:

  1. let reg=new RegExp();
  2. console.log(reg.__proto__===RegExp.prototype) //true
  3. console.log(RegExp.prototype.__proto__===Object.prototype) //true
  4. console.log(RegExp.__proto__===Function.prototype) //true

参考自万物皆空之JavaScript原型