JS原型+构造函数创建对象
2015-06-06 12:39
661 查看
JS原型+构造函数创建对象在JavaScript中,创建对象的方式包括两种:对象字面量和使用new表达式。对象字面量是一种灵活方便的书写方式,例如: |
1 2 3 4 5 6 | var o1={ p:”I’m inObjectliteral”, alertP:function(){ alert( this .p); } } |
new表达式是配合构造函数使用的,例如newString(“astring”),调用内置的String函数构造了一个字符串对象。下面我们用构造函数的方式来重新创建一个实现同样功能的对象,首先是定义构造函数,然后是调用new表达式:
1 2 3 4 5 6 7 | function CO(){ this .p =“I’minconstructedobject”; this .alertP =function(){ alert( this .p); } } var o2=newCO(); |
1 2 3 4 | var obj={}; obj.__proto__ =CO.prototype; CO.call(obj); return obj; |
第二行,将这个空对象的__proto__成员指向了构造函数对象的prototype成员对象,这是最关键的一步,具体细节将在下文描述。
第三行,将构造函数的作用域赋给新对象,因此CA函数中的this指向新对象obj,然后再调用CO函数。于是我们就给obj对象赋值了一个成员变量p,这个成员变量的值是”I’minconstructedobject”。
第四行,返回新对象obj。当构造函数里包含返回语句时情况比较特殊,这种情况会在下文中说到。
正确定义JavaScript构造函数
不同于其它的主流1、在函数内部对新对象(this)的属性进行设置,通常是添加属性和方法。
2、构造函数可以包含返回语句(不推荐),但返回值必须是this,或者其它非对象类型的值。
上文定义的构造函数CO就是一个标准的、简单的构造函数。下面例子定义的函数C1返回了一个对象,我们可以使用new表达式来调用它,该表达式可以正确返回一个对象:
1 2 3 4 5 6 7 8 | function C1(){ var o={ p:”I’m pinC1” } return o; } var o1= new C1(); alert(o1.p); //I’m pinC1 |
一个构造函数在某些情况下完全可以作为普通的功能函数来使用,这是JavaScript灵活性的一个体现。下例定义的C2就是一个“多用途”函数:
1 2 3 4 5 6 7 8 9 10 | function C2(a,b){ this .p =a+b; this .alertP =function(){ alert( this .p); } return this .p; //此返回语句在C2作为构造函数时没有意义 } var c2= new C2( 2 , 3 ); c2.alertP(); //结果为5 alert(C2( 2 , 3 )); //结果为5 |
为什么要使用构造函数
根据上文的定义,在表面上看来,构造函数似乎只是对一个新创建的对象进行初始化,增加一些成员变量和方法;然而构造函数的作用远不止这些。为了说明使用构造函数的意义,我们先来回顾一下前文提到的例子。执行varo2=newCO();创建对象的时候,发生了四件事情:1 2 3 4 | var obj={}; obj.__proto__ =CO.prototype; CO.call(obj); return obj; |
在JavaScript标准中,并没有__prop__这个属性,不过它现在已经是一些主流的JavaScript执行环境默认的一个标准属性,用于指向构造函数的原型。该属性是默认不可见的,而且在各执行环境中实现的细节不尽相同,例如IE
在构造对象的四个步骤中,我们可以看到,除第二步以外,别的步骤我们无须借助new表达式去实现,因此new表达式不仅仅是对这四个步骤的简化,也是要实现继承的必经之路。
容易混淆的地方
关于按照面向对象的习惯性思维,我们说构造函数相当于“类”的定义,从而可能会认为constructor属性就是该类实际意义上的构造函数,在new表达式创建一个对象的时候,会直接调用constructor来初始化对象,那就大错特错了。new表达式执行的实际过程已经在上文中介绍过了(四个步骤),其中用于初始化对象的是第三步,调用的初始化函数正是“类函数”本身,而不是constructor。如果没有考虑过这个问题,这一点可能不太好理解,那就让我们举个例子来说明一下吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function this .p =a+b; this .alertP =function(){ alert( this .p); } } //我们定义一个函数来覆盖C3原型中的constructor,试图改变属性p的值 function fake(){ this .p = 100 ; } C3.prototype.constructor =fake; //覆盖C3原型中的constructor var c3= new C3( 2 , 3 ); c3.alertP(); //结果仍然为5 |
1 2 | var myArray=[ 1 , 2 , 3 ]; (myArray.constructor ==Array); // true |
1 2 3 4 5 6 7 | function f(){ this .foo = 1 ;} function s(){ this .bar = 2 ; } s.prototype = new f(); // s继承自f var new s(); // 用构造函数s创建一个子类对象 (son.constructor // false (son.constructor // true |
使用JS对象的原型有利于数据的共享,但这个也是它的缺点,若创建对象的时候采用了原型中定义的数据,那么若一旦某个对象修该了数据就会影响到其他的对象,也就是说原型在保持数据共享的基础上也破坏了数据的独立性。所以在用JS创建对象的时候,最好是采用组合构造函数+原型模式
下面举一例子:
functionBox(name,age){
this.name=name;
this.age=age;
this.family=['父亲','母亲','妹妹'];
}
Box.prototype={
constructor:Box,//让原型指向Box对象
run:function(){
returnthis.name+this.age;
}
}
varbox1=newBox('xiaofeng',18);
box1.family.push('弟弟');
alert(box1.family);
结果为:父亲,母亲,妹妹,弟弟
alert(box1.run);
结果为:xiaofeng18
varbox2=newBox('bin',20);
alert(box2.famuly);
结果为:父亲,母亲,妹妹//保持了数据的独立性
alert(box2.run);
结果为:bin20//数据共享
PS:上面采用的这种组合构造函数+原型模式创建对象确实比较好的解决了对象数据的共享与独立,不过,这种方式看上去不太符合面向对象的类型,因此,可以采用动态的组合构造函数+原型模式
如下:
functionBox(name,age){
this.name=name;
this.age=age;
this.family=['父亲','母亲','妹妹'];
//判断是不是已经存在,若不存在泽创建(若不经过这一步判断,则每次创建对象的时候都会创建一次run方法)
if(typeofthis.run!='function'){
Box.prototype.run=function(){
returnthis.name+this.age;
}
}
}
JavaScript中定义"类"的时候可以通过构造函数和原型的方式来实现,它们之间有何区别和利弊呢?
首先来看一个通过构造函数实现类定义的例子:
[javascript]view
plain
functionCar(color,model,drivers){
this.color=color;
this.model=model;
this.drivers=drivers;
this.blar=function(){
alert("Iamacar:"+model+"in"+color+"."+drivers+"candriveme");
}
}
varcar1=newCar('red','BMW',['Mike','Kevin']);
car1.blar();
varcar2=newCar('red','BMW',['Mike','Kevin']);
car2.blar();
在上面的例子中,每一个实例中,函数blar都会拷贝到实例中,弊端就是浪费内存。
在来看一个通过利用prototype来定义类的例子:
[javascript]view
plain
functionDrivers(){
varnames="";
this.addDriver=function(name){
names=names+""+name;
}
this.toString=function(){
returnnames;
}
}
functionCar(){
}
Car.prototype.color="red";
Car.prototype.model="BMW";
//Car.prototype.drivers=['Mike','Kevin'];
Car.prototype.drivers=newDrivers();
Car.prototype.blar=function(){
alert("Iamacar:"+this.model+"in"+this.color+"."+this.drivers+"candriveme");
};
varcar1=newCar();
car1.color='Blue';
//car1.drivers.push('Luios');
car1.drivers.addDriver('Mike');
car1.drivers.addDriver('Kevin');
car1.drivers.addDriver('Luios');
car1.blar();//outputsIamacar:BMWinBlue.Mike,Kevin,Luioscandriveme
varcar2=newCar();
car2.blar();//outputsIamacar:BMWinred.Mike,Kevin,Luioscandriveme
prototype方式定义的方式,函数不会拷贝到每一个实例中,所有的实例共享prototype中的定义,节省了内存。但是属性如果是对象的话,所有实例也是共享同一个对象,
如上例中的drivers使用自定义对象或者数组的时候,如果其中某一个实例改变了其中的值,所有的实例的值都被改变。因为所有实例的属性指向的是同一个对象的引用,如果上面的例子中car2.drivers=['Ivy','Lili'];来改变实例car2的属性内容,相对于car2.drivers指向了另一个对象(这时候car2有一个实例属性,有一个prototype属性都是drivers)。
Javascript是一种动态语言,实例创建之后可以动态添加属性和方法,在“构造函数”的定义之外也可以添加属性和方法。其实,下面例子中sayHi本身也是一个全局的类型为Function的实例。
[javascript]view
plain
varobj={};
obj.prop="value";
obj.test=function(){
alert("testfunction:"+this.prop);
}
obj.test();
functionsayHi(){
alert("hi");
}
sayHi.sayHello=function(){
alert("hello");
};
sayHi.sayHello();//outputshello
注意如果想让添加的属性或者方法能够被实例使用,要使用prototype添加,下面的例子说明了这一点
[javascript]view
plain
functionsayHi(){
alert("hi");
}
sayHi.sayHello=function(){
alert("hello");
};
sayHi.sayHello();//outputshello
varosayHi=newsayHi();//outputshi
osayHi.sayHello();//TypeError:osayHi.sayHelloisnotafunction
因为不是通过在prototype中以这样的方式添加的(sayHi.prototype.sayHello),实例是不能访问sayHello方法的,只能通过sayHi.sayHello(类似如静态方法)的方式访问。同样如果添加的时候是通过prototype方式,则不能用静态方式访问。
[javascript]view
plain
functionsayHi(){
alert("hi");
}
sayHi.prototype.sayHello=function(){
alert("hello");
};
varosayHi=newsayHi();//outputshi
osayHi.sayHello();//outputshello
sayHi.sayHello();//TypeError:sayHi.sayHelloisnotafunction
相关文章推荐
- Javascript匿名函数以及闭包的特性
- js切换搜索引擎效果
- Javascript模块化和命名空间管理
- javascript事件冒泡详解和捕获、阻止方法
- javascript展开滑块特效
- 原生js粒子时钟
- Javascript笔记
- JavaScript中的toLocaleLowerCase()方法使用详解
- JavaScript语言基础知识8
- javascript实战第一讲:聊天窗口
- JavaScript中的substr()方法使用详解
- Emberjs之Observer
- JavaScript中的slice()方法使用详解
- 简介JavaScript中search()方法的使用
- JavaScript中的replace()方法使用详解
- javaScript中私有属性和方法
- JavaScript中String.match()方法的使用详解
- Javascript闭包——懂不懂由你,反正我是懂了
- JavaScript中操作字符串之localeCompare()方法的使用
- JavaScript中的lastIndexOf()方法使用详解