JS是一种基于对象的语言,在使用过程中不免遇到复制对象的问题,但通常我们采用的直接赋值‘obj1=obj2’这种做法会出现数据覆盖问题。也就是对象引用过程中引用地址一致,导致对象数据被修改的问题。这时我们可以采用工厂模式来对对象进行实例化,从而实现对象的引用地址一致导致的数据覆盖问题。但此时,问题随之又来了,使用工厂模式,对于具体的实例所属的具体对象又搞不清楚,在JS中采用构造方法来解决对象实例的数据覆盖问题(这里和其它语言的原理是类似的)。
一、构造函数
在其它语言中接触过构造函数,通常它与类的名称一致。但在JS中并没有明确的‘类’这个概念。在我看来,它声明的对象从某种程度上说就是一种类,因为它的每个对象都可以有自己对应的实例。
构造函数解决数据覆盖:
<span style="font-family:KaiTi_GB2312;font-size:18px;"> function Box(name,age){ this.name=name; this.age=age; this.run=function(){ return this.name+this.age+'运行中...'; } } function Desk(name,age){ this.name=name; this.age=age; this.run=function(){ return this.name+this.age+'运行中...'; } }</span>
对它进行调用:
<span style="font-family:KaiTi_GB2312;font-size:18px;"> var box1=new Box('lee',22); var box2=new Desk('john',66); </span>
<span style="font-family:KaiTi_GB2312;font-size:18px;"> alert(box1 instanceof Box);//TRUE alert(box2 instanceof Box); //可以识别具体哪个对象的实例,FALSE </span>
同时,构造函数可以进行对象冒充,来改变对象自身的作用域,实现自身所不能实现的行为。但同时,又引出了另外一个问题,构造函数体内对于引用类型数据引用地址出现不一致现象,这说明在内存中对于完全相等的两个或多个数据,要用对应大小的内存来盛放,这样无疑造成了内存浪费现象。当然,我们可以采用在构造函数体外将引用类型进行单独声明,但是这种方法封装性很差,很容易被外界恶意调用。所以,这里JS中用到了原型。
二、原型函数
JS中创建的每一个函数都有一个prototype原型属性,这个属性也是一个对象。它的用途是包含可以由特定类型的所有实例共享的属性和方法。如下图所示,函数的原型对象为该函数的所有实例所共享。
上图中__proto__是原型对象的指针,它指向原型对象,同时constructor为原型对象的构造属性,执行具体的所属的构造函数对象。在实际应用中可以通过constructor属性来改变一个原型对象所属的构造函数对象。
为函数创建原型属性和方法:
function Box(){} //构造函数体内什么都没有,如果有,就叫做实例属性和实例方法 Box.prototype.name='lee';//原型属性 Box.prototype.age=22; Box.prototype.run=function(){ //原型方法 return this.name+this.age+'运行中...'; }
用这种方法,可以解决上面遗留的引用地址不一致的问题(构造函数+原型)。
可以通过Box.prototype.isPrototypeOf(box1)来对实例所属的原型对象进行判断。同时在创建函数的原型时,也可以通过字面量的方法来创建:
Box.prototype={ name:'lee', age:100, run:function(){ return this.name+this.age+'运行中...'; } }
以上是有关JS中构造函数和原型函数的一些基础知识,小结一下:
1.构造函数在实例化时,必须使用new来操作,声明时不需要new object,隐含自身已经new了
2、构造函数可以用来解决对象实例化中数据覆盖问题,但会造成引用地址不一致问题
3、原型函数解决了引用地址不一致问题
4、但原型函数可以实现共享但不能进行重写,会将原来的信息覆盖。
对于重写问题,可以采用原型+构造函数结合来各司其职。后面再做详细总结。