Javascript 中定义对象的几种方式.
方式1:基于已有对象(obj) 扩充其属性和方法
js 中没有类的概念,只有对象。
<script type="text/javascript"> var obj = new Object(); obj.name = "zhangsan"; //定义一个属性 obj.sayName = function (name)//定义一个方法 { this.name = name; alert (this.name); } obj.sayName("lisi"); </script>
弊端:
对象只有一个(只有一个 obj),如果再想要一个只能在来一个 obj2
方式2: 工厂方式创建对象
类似于 java 中的静态方法。调用一个就new出来一个对象。
<script type="text/javascript"> //工厂方式创建对象 function createObject() { var obj = new Object(); obj.username = "zhangsan"; obj.password = "123"; obj.get = function () { alert (this.username + ", " + this.password); } return obj; } var obj1 = createObject(); //通过调用工厂方法 createObject()创建第一个对象。 var obj2 = createObject(); //通过调用工厂方法 createObject()创建第二个对象。 obj1.get(); obj2.get(); </script>
结果:创建出来了2个对象
但是我们注意到上面把 username passwword 写死了。稍微改一下:
<script type="text/javascript"> //工厂方式创建对象 function createObject(username, password) { var obj = new Object(); obj.username = username; obj.password = password; obj.get = function () { alert (this.username + ", " + this.password); } return obj; } var obj1 = createObject("allen","qqq"); //通过调用工厂方法 createObject()创建第一个对象。 var obj2 = createObject("jenny", "ooo"); //通过调用工厂方法 createObject()创建第二个对象。 obj1.get(); obj2.get(); </script>
结果:打印出 allen,qqq jenny,ooo
但是我们仍然觉得可以改进。我们注意看,上面的 obj.get 函数是定义在 createObject()中的。也就是说任何调用 createObject() 方法创建对象的时候,都要在内存中给 obj.get() 函数一个空间。这样明显很浪费。
最好的方式是只要多次调用 createOjbect() 就可以创建出多个对象。而这些对象在内存中共享一份 get() 函数代码。
我们可以改一下,把 get() 放在外面。如下:
<script type="text/javascript">
//工厂方式创建对象
function createObject(username, password)
{
var obj = new Object();
obj.username = username;
obj.password = password;
obj.get = get;
return obj;
}
function get()
{
alert (this.username + ", " + this.password);
}
var obj1 = createObject("allen","qqq"); //通过调用工厂方法 createObject()创建第一个对象。
var obj2 = createObject("jenny", "ooo"); //通过调用工厂方法 createObject()创建第二个对象。
obj1.get();
obj2.get();
</script>
那么最后这种做法是比较好的做法:让一个函数 被多个对象所共享,而不是每一个对象拥有一个函数对象。
当然也有一个潜在问题,就是一般我们希望属性和方法等都定义在一起,但是现在分开了。但这个没有解决方式。
3。 构造函数方式:
<script type="text/javascript"> //构造函数方式创建对象 function Person()//这是一个构造函数 { //如果我们用new的方式,那么在执行第一行代码之前,js引擎会为我们生成一个对象 this.username = "zhangsan"; this.password = "qqq"; this.getInfo = function() { alert(this.username + ", " + this.password); }
//这里有一个隐藏的 return 语句, 用于将之前生成的对象返回。所以不需要显示的 return } var person = new Person(); person.getInfo(); </script>
我们看,过去我们利用 createObject()创建对象的时候,代码中要return obj;
但是我们现在这个构造函数方法没有 return。而是利用了隐式的 return。
我们继续改一下,不把 username password hard code.
<script type="text/javascript"> //构造函数方式创建对象 function Person(username, password) { //如果我们用new的方式,那么在执行第一行代码之前,js引擎会为我们生成一个对象 this.username = username; this.password = password; this.getInfo = function() { alert(this.username + ", " + this.password); } //这里有一个隐藏的 return 语句, 用于将之前生成的对象返回。所以不需要显示的 return } var person = new Person("user", "passwd"); person.getInfo(); </script>
这个方法可以在构造对象的时候传递参数。
方式4:原型(prototype)方式
<script type="text/javascript">
//使用原型(prototype)方式创建对象
function Person()
{
}
Person.prototype.username = "zhangsan"; //给 Person 的原型附加属性
Person.prototype.password = "qqq";
Person.prototype.getInfo = function()
{
alert(this.username + ", " + this.password);
}
var person = new Person();
var person2 = new Person();
person.username = "lisi"; //修改 person对象的 username 值
person.getInfo();
person2.getInfo();
结果:
zhangsan, qqq
lisi, qqq
单纯使用 prototype 方式有2个问题:
1. 无法在构造函数中通过传参数为属性赋初值,只能在对象生成后再后续修改属性值。
2. 容易导致程序错误。
为什么容易导致程序错误?我们把code改一下, 把 username 变成 array
<script type="text/javascript">
//使用原型(prototype)方式创建对象
function Person()
{
}
Person.prototype.username = new Array("mary"); //username 变成 Array初始值 Mary
Person.prototype.password = "123";
Person.prototype.getInfo = function()
{
alert(this.username + ", " + this.password);
}
var person = new Person();
var person2 = new Person();
person.username.push("zhangsan");
person.username.push("lisi");
person.password = "456";
person.getInfo();
person2.getInfo(); //注意,这里我们没有对 person2 的属性进行修改
</script>
我们注意,上面的程序只对 person 进行了属性修改, 没有对 person2对象更改属性,那么结果是不是应该是
mary, zhangsan, lisi, 456
mary, 123
但结果却是:
mary, zhangsan, lisi,456
mary, zhangsan, lisi, 123
奇怪,为什么没有改 person2 的 username, 它却也变成和person一样了呢?这是为什么呢?
这是因为我们是通过原型创建的对象,他们的属性值只有一份,是共享给 person 和 person2 的。
注意,这里 username 是 Array, Array是引用型的数据类型; password 是 string, string是常量型的数据类型。
当 person 对象更改 username 的时候,由于 Array是引用型的,所以不会创建新的 Array,仅仅是把内容变化了。
而当 person 对象更改 password 的时候,由于 String 是常量型的,所以会创建出一个新的 String 出来让 person去指向。而 person2仍然在指向原来的 String。
因此,我们看到在结果中 person 和 person2的 username 都相同,而 password各自指向自己的。
总结一下就是:如果使用原型方法生成对象, 那么生成的所有对象会共享原型中的属性, 这样如果一个对象更改了属性,那么也会反映到其他对象中。
4. 使用原型+构造函数的方式来定义对象
前面我们看到如果使用纯原型的方式生成对象,那么所有生成对象都会共享圆形中的一份属性,这样显然不太好。因为我们希望的是不同的对象有不同的属性,但是方法是一样的用一份就行了。那么怎么办呢?我们使用原型+构造函数的方法。
<html> <head> <script type="text/javascript"> function Person() { this.username = new Array(); this.password = "123"; } Person.prototype.getInfo = function() { alert(this.username + ", " + this.password); } var p = new Person(); var p2 = new Person(); p.username.push("zhangsan"); p2.username.push("lisi"); p.getInfo(); p2.getInfo(); </script> </head> <body> </body> </html>
结果:
zhangsan, 123
lisi, 123
当然你可以给这种方式传递参数
5. 动态原型方式
那么我们看上面的方式 getInfo() 函数只能写在构造函数外面。我们是否能够写在里面,但是仍然该方法只被调用一次,即不会每个对象都要有一份浪费内存空间?
我们可以这样:
<html> <head> <script type="text/javascript"> function Person(username, password) { this.username = username; this.password = password; if (typeof Person.flag == "undefined") { alert("invoked"); //判断是否确实只被执行了一次。 Person.prototype.getInfo = function() { alert(this.username + ", " + this.password); } Person.flag = true; } } var p = new Person("zhangsan", "qqq"); var p2 = new Person("lisi", "ff"); p.getInfo(); p2.getInfo(); </script> </head> <body> </body> </html>
结果:一次弹出
invoked
zhangsan, qqq
lisi, ff
动态原型方式:在构造体中通过标帜量让所有对象共享一个方法,而每个对象拥有自己的属性。