原型(prototype),原型指针(proto),原型链,构造器指针(constructor)到底都是些什么?
让我们先从含义层看看这四个到底是什么意思👇
原型(prototype)
所有函数都有一个prototype(原型)属性,属性值是一个普通的对象。
原型指针(proto)
指向构造该对象的构造函数的原型(prototype)
原型链
在javascript中,每个函数都会初始化一个属性:原型(prototype),当我们需要访问这个函数的某个属性时,就会去到prototype中寻找该属性,若没有找到这个属性,prototype中也存在自己的prototype,于是会一直往上查找。
当访问一个对象的某个属性时,会先在这个对象的本身属性上查找,如果没有找到,则会去它的隐式原型_proto_上查找(即它的构造函数的prototype)如果还找不到,就会再在构造函数prototype的_proto_上查找,这样一层一层向上查找所形成的一个链式结构,称之为原型链。
构造器指针(constructor)
原型prototype中有一个属性称为constructor,该属性的值就是指向原型的构造器(构造函数)。
注意:
- 所有引用类型(函数,数组,对象)都拥有_proto_属性(隐式原型)。
- 所有函数都拥有prototype属性(显示原型)。
- 原型对象:拥有prototype属性的对象,在定义函数的时候就被创建。
- __proto__和constructor属性是对象独有的,prototype属性是函数所独有的。在js中,万事万物皆对象,所以函数也有__proto__和prototype属性
看完四个定义还是很乱?别急,让我们来捋捋它们之间的关系。
首要,要明确原型是一个对象。
其次,每个函数都有一个叫做原型的属性,这个属性指向一个对象。
那么JavaScript中的Object(对象)到时是什么呢?M$官方教程上是这样说的:提供所有JScript对象通用的功能。打个比方,如果从数据结构上来说,一个object(Object的实例)就是一个无序集合,类似java中的
hashmap这样的一个结构,并且它包含了JS语言系统赋予的原始值。Object有个方法叫做valurOf,它的功能是返回指定对象的原始值。也就是说Array,Boolean,Date等等的对象,都是用一个祖先Object,它们表现不同的语言特效,比如Array有length属性,Boolean只有true和false值…Object实际上只是一个概念,JS这个语言基于对象,所有内建类型都被抽象出一组公用的方法和属性(或叫做行为和状态)。实际上Object在编程中没有太大的用处,我们都是在使用Object的实例object,将object扩充成我们希望的东西。
当我们定义一个函数的时候,函数的原型属性也就被定义出来了,也就可以使用了,如果不对它进行显示赋值的话,那么它的初始值就是一个空的对象Object。
综上所述,我们讨论原型的时候,都是基于函数的,有了一个函数对象,就有了原型。讨论原型绝不能脱离了函数,因为原型是函数的一个属性。
使用原型给对象添加方法和属性
不使用原型的情况下,给对象添加属性和方法是通过this,例子如下:
function person(name,age){
this.name=name;
this.age=age;
this.whatAreYou=function(){
return 'I am'+this.name+''+this.age;
}
}
上述例子中,person是一个构造函数,作为函数它自带一个属性叫做原型,该属性指向一个对象,目前我们没有设置这个属性,所以它是一个空对象。
当我们有了原型后,可以给构造函数的原型对象添加属性和方法。
person.prototype.height=170;
person.prototype.getHeight=function(){
return 'I am' + this.height + "cm";
}
如何使用原型对象的属性和方法
我们使用原型的对象和方法不会直接在构造函数上使用,而是通过构造函数new出一个新对象,这样new出来的新对象自然就会有构造函数原型里的属性和方法。
强调一下a这个new出来的对象是没有原型的,原型只是函数对象的一个属性,而a是通过构造函数new出来的对象,它本身不是函数对象,也就没有prototype属性,因此我们在控制台里也就访问不到a.prototype。
但是我们知道每个对象都有constructor属性,a的constructor属性就指向先了它的构造函数person,所以我们通过a.constructor可以访问到prototype。
因此可以解释我们为什么要通过construct.prototype来访问构造函数里的属性。
同时,上文也提到了_proto_是由一个对象指向一个对象,即指向它的原型对象。它的作用是当访问一个对象的属性时,如果该对象内部不存在该属性,那么就会去它的父对象里面找,如果父对象也不存在该属性,就继续往父对象的__proto__属性所指的那个对象(爷爷对象)里找,若还没找到,则继续向上找,直到到达原型链顶端null(null为原型链的终点)。因此上述例子中,a.proto === person.prototype,这两个完全一样。
切记,原型是函数的一个属性,只有函数才有原型。