自定义对象
一、定义类或对象
1.工厂方式
创建对象car
1 var oCar = new Object; 2 oCar.color = "red"; 3 oCar.doors = 4; 4 oCar.mpg = 23; 5 oCar.showColor = function(){ 6 alert(this.corlor); 7 }; 8 9 创建多个car 10 function createCar(color, doors, mpg) { 11 var tempcar = new Object; 12 tempcar.color = color; 13 tempcar.doors = doors; 14 tempcar.mpg = mpg; 15 tempcar.showColor = function () { 16 alert(this.color) 17 }; 18 return tempcar; 19 } 20 21 var car1 = createCar("red", 4, 23); 22 var car2 = createCar("blue", 3, 25); 23 car1.showColor(); //outputs "red" 24 car2.showColor(); //outputs "blue"
这个例子中,每次调用函数createCar(),都要创建新函数showColor(),意味着每个对象都有自己的showColor()版本,事实上,每个对象都共享了同一个函数。
有些开发者在工厂函数外定义对象的方法,然后通过属性指向该方法,从而避开这个问题。
1 function showColor(){ 2 alert(this.color); 3 } 4 function createCar(color, doors, mpg) { 5 var tempcar = new Object; 6 tempcar.color = color; 7 tempcar.doors = doors; 8 tempcar.mpg = mpg; 9 tempcar.showColor = showColor; 10 return tempcar; 11 } 12 13 var car1 = createCar("red", 4, 23); 14 var car2 = createCar("blue", 3, 25); 15 car1.showColor(); //outputs "red" 16 car2.showColor(); //outputs "blue"
从功能上讲,这样解决了重复创建函数对象的问题,但该函数看起来不像对象的方法。所有这些问题引发了开发者定义的构造函数的出现。
2.构造函数方法
1 function Car(sColor, iDoors, iMpg) { 2 this.color = sColor; 3 this.doors = iDoors; 4 this.mpg = iMpg; 5 this.showColor = function () { 6 alert(this.color) 7 }; 8 } 9 10 var oCar1 = new Car("red", 4, 23); 11 var oCar2 = new Car("blue", 3, 25); 12 oCar1.showColor(); //outputs "red" 13 oCar2.showColor(); //outputs "blue"
就像工厂函数,构造函数会重复生成函数,为每个对象都创建独立的函数版本。不过,也可以用外部函数重写构造函数,同样,这么做语义上无任何意义。
3.原型方式
1 function Car(){ 2 } 3 Car.prototype.color = "red"; 4 Car.prototype.doors= 4; 5 Car.prototype.mpg= 23; 6 Car.prototype.showColor = function(){ 7 alert(this.color); 8 } 9 10 var oCar1 = new Car(); 11 var oCar2 = new Car();
它解决了前面两种方式存在的两个问题。但并不尽人意。首先,这个构造函数没有参数。使用原型方式时,不能通过构造函数传递参数初始化属性的值,这点很令人计厌,但还没完,真正的问题出现在属性指向的是对象,而不是函数时。考虑下面的例子:
1 function Car(){ 2 } 3 Car.prototype.color = "red"; 4 Car.prototype.doors= 4; 5 Car.prototype.mpg= 23; 6 Car.prototype.drivers = new Array("Mike","Sue"); 7 Car.prototype.showColor = function(){ 8 alert(this.color); 9 } 10 11 var oCar1 = new Car(); 12 var oCar2 = new Car(); 13 oCar1.drivers.push("Matt"); 14 alert(oCar1.drivers); //outputs "Mike,Sue,Matt" 15 alert(oCar2.drivers); //outputs "Mike,Sue,Matt" 16 17 4.混合的构造函数/原型方式 18 function Car(sColor, iDoors, iMpg) { 19 this.color = sColor; 20 this.doors = iDoors; 21 this.mpg = iMpg; 22 this.drivers = new Array("Mike", "Sue"); 23 } 24 25 Car.prototype.showColor = function () { 26 alert(this.color); 27 }; 28 29 var oCar1 = new Car("red", 4, 23); 30 var oCar2 = new Car("blue", 3, 25); 31 32 oCar1.drivers.push("Matt"); 33 34 alert(oCar1.drivers); //outputs "Mike,Sue,Matt" 35 alert(oCar2.drivers); //outputs "Mike,Sue"
现在就更像创建一般对象了。所有的非函数属性都有构造函数中创建,意味着又可用构造函数的参数赋予属性默认值了。因为只创建showColor()函数的一个实例,所以没有内存浪费。
5.动态原型方法
1 function Car(sColor, iDoors, iMpg) { 2 this.color = sColor; 3 this.doors = iDoors; 4 this.mpg = iMpg; 5 this.drivers = new Array("Mike", "Sue"); 6 7 if (typeof Car._initialized == "undefined") { 8 9 Car.prototype.showColor = function () { 10 alert(this.color); 11 }; 12 13 Car._initialized = true; 14 } 15 } 16 17 18 var oCar1 = new Car("red", 4, 23); 19 var oCar2 = new Car("blue", 3, 25); 20 21 oCar1.drivers.push("Matt"); 22 23 alert(oCar1.drivers); //outputs "Mike,Sue,Matt" 24 alert(oCar2.drivers); //outputs "Mike,Sue"
动态原型方法的基本想法与混合的构造函数/原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置。
6.混合工厂方式
这种方式通常是在不能应用前一种方式时的变通方法。它的目的是创建假构造函数,只返回另一种对象的新实例。
function Car() { var tempcar = new Object; tempcar.color = "red"; tempcar.doors = 4; tempcar.mpg = 23; tempcar.showColor = function () { alert(this.color) }; return tempcar; }
与经典方式不同,这种方式使用new运算符,使它看起来像真正的构造函数。
7.采用哪种方式
如前所述,目前使用最广泛的是混合的构造函数/原型方式。些外,动态原型方法也很流行,在功能上与前者等价,可以采用这两种方式中的任何一种。
二、修改对象
1.创建新方法
可以用prototype属性为任何已有的类定义新方法,就像处理自己的类一样。
例:
Array.prototype.indexOf = function(vItem){ for(var i=0;i<this.length;i++){ if(vItem == this[i]){ return i; } } retunr -1; }
最后,如果想给ECMAScript中的每个本地对象添加新方法,必须在Object对象的prototype属性上定义它。
2.重定义已有方法
就像能给自己的类定义新方法一样,也可重定义已有的方法。函数名只是指向函数的指针,因此可以轻易地使它指向其他函数。
如
1 Function.prototype.toString = function(){ 2 return "Function code hidden"; 3 } 4 function sayHi(){ 5 alert("hi"); 6 } 7 alert(sayHi.toString()); //outputs "Function code hidden" 8 9 10 11