在浏览本文之前首先明白什么是对象?,在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,这个属性相当于一个'指针',指向这个函数本身。
以下图为例:
上图的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了。
下面上一张完整的图片,大家可以按照下面这种图片捋一下自己的思路,因为上面讲了那么多肯定会有些乱。
这张图完整的呈现出了实例对象、自定义函数、Object、Function之间种种错综复杂的关系,不要怕麻烦,一条一条的去找对应的关系。
继承
为什么会说到继承呢,因为继承是通过原型链来体现的,所以一并放在这里讲了。我们先看一段代码:
function Person(){ }
var p1=new Person();
Person.prototype.name='老王';
Person.prototype.age='99';
console.log(p1.name);//'老王'
以上代码中,p1是Person实例化出来的函数,我并没有给p1定义name这个属性,那p1.name是怎么来的--是从Person.prototype来的,因为p1._ proto_指向Person.prototype,当访问对象的某个属性时,现在这个对象本身去找,如果找不到那就顺着_ proto_往上找,直到找到或者Object.prototype为止。
由于所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这就是所谓的“继承”。
讲到这里,关于原型和原型链就结束了,希望各位能深刻的理解。
网友评论