关于原型链
在JavaScript中我们会用typeof( )这个函数,那么使用typeof( )输出的值一般会有number、boolean、string、undefined、function、object等这些类型,那我们本文要研究的就是object和function这两种类型,返回值是这两种类型的就是我们本文所要研究的对象。
什么是对象?
那么对象该如何去定义呢?我个人认为对象就是一些属性的集合。举个栗子🌰
var Laowang={
'name':'老王',
'feature':'热心肠',
'skill':function( ){
alert('特长是修水管');
}
}
那么在上面的例子中'Laowang'就是一个对象,你肯定会有疑问关于我对对象的定义,在'Laowang'这个对象中出现了function方法,但是这个方法在'Laowang'这个对象中是以键值对的形式出现的,所以这个function就是'skill'这个属性的属性值。所以验证了对象就是一些属性的集合这句话。
上面的例子很好理解,但是数组和函数好像不能这样去定义属性,但他们也是对象啊,不要迷惑,他们有自己定义属性的方法。以函数为例:
var laowang=function( ){
alert('修水管');
}
laowang.skill='热心肠';
laowang.skill2='爱串门';
laowang.skill3={
'a':'亲切问候邻居家孩子'
}
这不,function就被赋予了skill、skill2、skill3这三个属性。
上面说到function和objec这两个返回值是对象,既然都是对象,为什么返回的不是一个值。由于function和object的关系比较特殊所以返回的值不同,我在下文会详细讲到function和object的'特殊关系'。
function和object的关系
上文说道function和object都是对象,但是function的返回值是function而不是object,那么他俩之间肯定有某种'神秘的关系'。
function和object的关系其实就像'先有蛋还是先有鸡'这种让你抓狂的问题。function是object的一种,但是object又是由function创建的,什么,你要打我脸?
var arr=['a','b','c'];
var obj={
'name':'老王',
'age':'99'
}
以上两个都是对象,但是都不是由function创建的,不要忘记了这种写法只是用字面量的方式来创建对象的。这种写法只是为了让代码更简单明了更容易理解。归根到底以上两种对象是由function创建的,请看以下代码:
var arr=new Arry('a','b','c');
var obj=new Object();
obj.name='老王';
obj.age='99';
在以上代码中Arry( )和Object( )都是函数,通常我们把他们当做构造函数,由构造函数我们可以new出很多实例对象,构造函数和我们平常自定义的函数没有语法上的区别,区分就是构造函数一般首字母是大写的。
是不是感觉很乱?为什么function和object的关系是这样的,不要慌张,耐心看完本文你就会豁然开朗。
原型(prototype)
上面扯了半天对象,到这里终于讲到本文的主要内容了--原型(prototype)。
那么prototype到底是什么呢?不要着急,让我们一步步来。
上面我们说到function也是一种对象,现在对这个应该没有任何疑问了,如果有疑问请滑动你的鼠标从头开始看!!
function作为对象,那么他肯定是若干属性的集合,在JavaScript中,function默认有一个属性,这个属性就是prototype,既然是属性那么肯定有相对应的属性值,prototype的属性值是一个对象,既然是对象,那么肯定是若干属性的集合,这个对象里有一个默认的属性:constructor,这个属性相当于一个'指针',指向这个函数本身。
以下图为例:
prototype既然作为对象,属性的集合,不可能就只有constructor这一个属性,肯定可以自定义的增加许多属性,如上图所示。
上图还出现了person1这个实例函数,他是由构造函数Person实例化出来的,上文说到每个function都有prototype这个属性,person1也不例外,他的prototype大家会发现和Person这个构造函数的是一样的,实例对象的原型指向的是其构造函数的原型对象。我们再看一段代码:
var Person=function(){};
Person.prototype.name='Nicholas';
Person.prototype.age='29';
Person.prototype.job='Software Engineer';
var person1=new Person();
console.log(person1.name); // 'Nicholas'
console.log(person1.age); // '29'
在上面代码中person1是由构造函数Person实例化出来的,而且我们也没有给person1定义任何属性,但是person1.name=='Nicholas';这是为什么?那我们就不得不说起-proto-这个属性了,每个对象都有这个属性,这个属性一般是隐藏的我们看不到,但是并不妨碍我们去了解他。
这个属性指向了创建这个对象的构造函数的prototype。即:person1._ proto_ ===Person.prototype,下面我们来看看这个'_ proto_'是什么鬼。
_ proto_,隐式原型
上文我们提到_ proto_,那到底这个_ proto_是什么呢?我看下面的代码:
var Person=function(){};
Person.prototype.name='Nicholas';
Person.prototype.age='29';
Person.prototype.job='Software Engineer';
var person1=new Person();
console.log(person1._proto_===Person.prototype);//true
通过看上面的代码会发现结果为true,你没有看错,这也不是巧合,这是必然的结果。
实质上person1是被Person实例化出来的,那么person1._ proto_===Person.prototype,下面用图给你展示一下:
上图的o1和o2是由Object实例化出来的,他们的_ proto_指向的是Object.prototype,这就说明:每个对象都有一个_ proto_属性,指向创建该对象的构造函数的prototype。
那么你肯定会问'每个对象都有一个_ proto_属性,指向创建该对象的构造函数的prototype',那Object也是一个对象,肯定也有_ proto_属性,那他指向谁?
关于Object._ proto_的指向问题很特殊,在这个Object._ proto_是个特例,它指向null,这个地方大家一定要牢记。
也许你还会有另一个疑问,函数也是对象,实例化出来的函数的_ proto_属性指向其构造函数,那么其构造函数的_ proto_指向谁?
Function这个前面没有提到,现在拿出来晒晒,构造函数是由谁创建的,就是由Function这个函数创建的,所以你上面的疑问就很好解答了。再用一张图让你更清晰的看清他们的关系:
这张图清晰的表明了自定义构造函数、Object、Function之间的关系!
眼神好的人会在上图发现一个问题:自定义函数Foo._ proto_指向Function.prototype,Object._ proto_指向Function.prototype,怎么Function._ proto_也指向Function.prototype,这不就是形成了一个'死循环'么,来,让我们仔细捋一捋,Function也是一个函数,既然是函数那么他肯定是由Function创建的,那么上面的'死循环'就解释通了。
在这里我还要解释一个地方,Function.prototype也是一个对象,那其肯定有_ proto_属性,那么指向谁呢?其指向Object.prototype,为什么呢?Function.prototype是一个普通的对象,就可以看成这个对象是由Object实例化出来的,那么Function.prototype._ proto_指向就是Object.prototype了。
下面上一张完整的图片,大家可以按照下面这种图片捋一下自己的思路,因为上面讲了那么多肯定会有些乱。
作者:我住隔壁我姓吴
链接:https://www.jianshu.com/p/700a2a579351
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。