前言
宝图在手,天下无忧
JS的原型一直是一个很让人头大的问题,也是在面试中经常会被面试官问到的一个问题,如果事先没有准备,那么注定是致命的。在了解原型之前,我画了一张图,方便后面讲解~
普通对象和函数对象
在JavaScript中,一直有这么一种说法,即万物皆对象。事实上在js中,对象也是有很大区别的,我们可以将其划分成普通对象和函数对象,常见的Object与Function就是js自带的两个典型的函数对象。
怎么定义?
function f1(){}
const f2=function(){}
const f3=new Function(){}
const ob1={};
const ob2=new Object()
const ob3=new f1()
打印以上对象的类型,可以得到如下结果:
console.log(typeof ob1) //object
console.log(typeof ob2) //object
console.log(typeof ob3) //object
console.log(typeof f1) //function
console.log(typeof f2) //function
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属性),如果用一个表达式来表述关系的话,可以这样来表示:
p1.__proto__=Person.prototype
constructor
任何一个实例底层必然是被new出来的,那么这个实例必然要和它的“爸爸”建立一定的关系,才能确保实例是他爸爸new出来的,那么每一个实例就会有一个constructor的属性,这个属性指向的刚好是它爸爸本身,说的专业一点的就是实例的构造函数属性指向的是它的构造函数。用表达式表述就是:
p1.constuctor=Person
原型对象是构造函数的一个实例
再来看看下面这段代码,你会发现什么?
p1.__proto__=Person.prototype
我们可以这样理解,任何一个构造函数它都具有一个prototype属性,这个就被称为原型对象,可以称它为构造函数的孩子集,那么所有被构造函数new出来的对象就是构造函数的儿子(实例),而这些儿子却都有一个proto的属性,这个属性就相当于一个线,用于指明它是属于谁的儿子集,所以都会指向Person.prototype这个原型对象。
原型对象的constructor属性
既然前面说到了任何一个对象都会有constructor属性和proto属性,那么原型对象也不例外的啊~看下面的表达式不知道能不能理解
Person.prototype.constructor=Person
p1.__proto__.constructor=Person
Person.prototype.__proto__=Object.prototype
p1.__proto__.__proto__=Object.Prototype
其他
构造函数Person()除了是一个函数之外,它还是一个对象,那么他也有一个proto属性,指向的就是它的构造函数的原型对象,即函数的构造函数原型对象:Function.prototype。
那么Function.prototype也是一个对象,它指向的就是它的构造函数的原型对象,因为在JS中,一切皆为对象,所以原型对象为Object.prototype,最后要保证整个原型链有尾,那么Object.prototype.proto指向的为null。
JS中的6大内置(函数)对象的原型继承
两句话总结:
1、任何内置函数对象本身的proto都指向Function的原型对象,即Function.prototype 2、除了Object的原型对象的proto指向null,其他所有内置函数对象的原型对象的proto都指向Object
例如:
let reg=new RegExp();
console.log(reg.__proto__===RegExp.prototype) //true
console.log(RegExp.prototype.__proto__===Object.prototype) //true
console.log(RegExp.__proto__===Function.prototype) //true