首先,我们要明确在JS中,引用类型只有Object。**也就是说Array、Function、Object等都可以称为对象。**但是想想我们使用一个构造函数的场景:
function Parent(name) {
this.name = name;
}
var p = new Parent('zs');
是不是觉得很熟悉呢,当我们创建一个数组、函数、对象的时候,是不是也使用的这种语法?
let arr = new Array()
var sum = new Function("num1,num2","return num1+num2");
const o =new Object()
虽然这并不是我们常用的方法,但我们用的 var o1 ={}这种写法实际上执行的也是上述的逻辑。所以还可以推出一个结论:对象都是通过函数创建出来的。
Array就是一种key值为从0开始的有序数字的对象,value就是我们存在对应index索引下的值;
Function就是可以执行的函数对象,他最大的特点就是内部语句可执行。
这里我们就可以知道函数和对象的关系了:函数也是对象,对象都是通过函数创建的。
原型:一个函数可以看成一个类,原型是所有类都有的一个属性,原型的作用就是给这个类的每一个对象都添加一个统一的方法。
显式原型和隐式原型:
- prototype,是函数特有的属性,显式原型,只有函数有prototype
- _proto_,是对象具有的属性,隐式原型,每个对象都有隐式原型
constructor属性:每个函数自带prototype属性,prototype属性值是个对象,这个对象默认有constructor属性,指向这个函数本身。即:fun.prototype.constructor === fun
原型链是隐式原型的查找过程。只是查找路径上的某些对象是其他函数的显式原型。
这是原型链一个经典的图。我们先从上面简单的逻辑开始看。可以看出:
-
首先 function Foo(){},这是创建了一个类,或者说是一个构造函数。而一个函数,必定有个prototype属性指向Foo.prototype这个对象。
-
f1、f2就是Foo的实例对象(上面写了…=new Foo())。实例对象有__ptoto__,它指向它的构造函数(我想称为父类可能好理解一些,但是这么叫起来不知道准不准确,但是下文还是会用到,方便理解)的protopype对象。也就是说 Foo.prototype === f1.__proto__。这两种写法指向的是同一个对象
-
由于函数.prototype.constructor === 这个函数,所以我们的构造函数和它的原型对象也有这层关系:Foo.prototype.constructor === Foo
-
这里是我打印的上述关系的图,从代码里可以看到:f1为Foo的一个实例对象,Foo身上只有一个属性名为Prototype的对象。这里面的constructor就是Foo。这也就是那个关系:Foo.prototype.constructor===Foo; f1.__proto__.constructor === Foo
-
这个Prototype的对象身上也有个Prototype属性,我们点击看到的和上面的是不一样的。上面的Foo是构造函数,是函数,所以他里面的constructor有个name属性,指向Foo。而就控制台打印内容来说,对象的prototype应该是它继承到的一些属性方法。
-
Foo.prototype是个对象,对象是没有prototype的,控制台打印出来的结果也是undefined。所以我们后面考虑原型链的时候,对于对象,只探讨__proto__属性。
那么又出现了一个新问题,既然prototype是函数特有的,那么__proto__可是对象都有的,函数对象也是对象,那么函数的__proto__又是什么呢,按照上面的逻辑来说,构造函数的__proto__应该等于它的上级(父)的prototype属性,控制台打印出来是这样的。
> Foo.__proto__
< ƒ () { [native code] }
> Object.__proto__
< ƒ () { [native code] }
看不懂这个没关系,我们可以比较一下Foo.__proto__ === Object.__proto__
,输出true。也就是说,确实这些构造函数,他们都是函数,所以Function应该是他们所有人的父亲。那么就有下面这个成立:**构造函数.__proto__ === Function.prototype。所有的函数都有这个共同的祖先。**同理,对象也是通过函数方法创造出来的,那么Object.__proto__===Function.prototype也该成立。
那么同理,**一切都可以看成对象,也就是说Object也可以看作所有对象类型的父,那么所有的对象类型,也应该有指向它的原型即Object.prototype的途径。**每个构造函数.prototype都是一个对象,对象的__proto__可以指向它的父的prototype。也就是说对任意的构造函数,都有:
构造函数.prototype.__proto__===Object.prototype成立。(除了Object.prototype本身)
- 前面铺垫完成了。同理,o1是构造函数Object()的实例对象,所以它的隐式原型属性指向构造函数Object的显式原型属性。prototypeo1.__proto__===Object.prototype肯定是成立的。
- 同样的,Object.prototype.constructor === Object,即o1.__proto__ .constructor === Object成立。
- Function同5,Funciton.prototype.constructor === Function
-
最后,根据前面推出来的构造函数.__proto__ === Function.prototype可知,Foo.__proto__===Object.__proto__===Function.__proto__===Function.prototype
-
根据构造函数.prototype.__proto__===Object.prototype同样可以知道,Foo.prototype.__proto__===Function.__proto__ ===Object.prototype。
但是,最后根据这个理论,Object.prototype.__proto__也应该指向Object.prototype。
事实却并不是这样:Object.prototype.__proto__,指向的是原型链的尽头,一个null。如果按照我的理论把它画出来,最后的地方是一个死循环即:这种循环对于原型链的查找没有什么意义。
所以说,为了不出现这种情况,它本身的__proto__属性就指向了一个空对象null。当然这只是一种猜测。
原型链就是指的是根据__proto__这条隐式原型链查找父身上的属性或方法的链式结构。由于原型链的存在,很多方法和属性即使实例对象身上没有,也可以根据原型链向上查找使用。