首页 > 代码库 > JavaScript定义类和对象以及实现继承
JavaScript定义类和对象以及实现继承
定义类和对象
工厂模式
function createCar(color, doors, mpg){ var car = {}; car.color = color, car.doors = doors, car.mpg = mpg; car.showColor = function(){alert(this.color);}; return car; }
工厂模式比较简单,所以只适用于创建比较简单的对象。
构造函数方式
function Car(color, doors, mpg){ this.color = color, this.doors = doors, this.mpg = mpg, this.showColor = function(){alert(this.color);}; } var car1 = new Car(‘red‘, 4, 23), car2 = new Car(‘blue‘, 2, 25);
这种方式与工厂模式类似,都会重复地为每个对象创建独立的函数版本。
原型方式
function Car(){} Car.prototype.color = ‘red‘, Car.prototype.doors = 4, Car.prototype.mpg = 23, Car.prototype.drivers = [‘kobe‘, ‘t-mac‘, ‘james‘], Car.prototype.showColor = function(){alert(this.color);}; var car1 = new Car(), car2 = new Car();
这个方式的缺点是首先不能传参,只能创建后再一个个改;而另外一个重要的问题是属性里的对象一般不会共享的,但这种方式的对象属性如drivers
是每个实例所共享的,所以这种方式下会出现问题是一个实例的对象属性被改,会影响到另外的一个实例的相应属性。
car1.drivers.push(‘yao‘); alert(car1.drivers); // ‘kobe, t-mac, james, yao‘ alert(car2.drivers); // ‘kobe, t-mac, james, yao‘
混合的构造函数/原型方式
这种方式用构造函数来定义对象的所有非函数的属性,用原型方式定义对象的函数属性(方法)。
function Car(color, doors, mpg){ this.color = color, this.doors = doors, this.mpg = mpg; this.drivers = [‘kobe‘, ‘t-mac‘, ‘james‘]; } Car.prototype.showColor = function(){alert(this.color);};
这种方式具有其他方式的特性,却没有它们的副作用。不过,有些开发者觉得这种方式不够完美。
动态原型方式
function Car(color, doors, mpg){ this.color = color, this.doors = doors, this.mpg = mpg; this.drivers = [‘kobe‘, ‘t-mac‘, ‘james‘]; if(‘undefined‘ === typeof Car._initialized){ Car.prototype.showColor = function(){alert(this.color);}; Car._initialized = true; } }
该方法利用标志_initialized
判断是否已经给原型赋予了任何方法,该方法只创建并赋值一次。这种方式使得代码样子更像其他语言中的类定义了。
混合工厂方式
function Car(){ var car = {}; car.color = ‘red‘, car.doors = 4, car.mpg = 23; car.drivers = [‘kobe‘, ‘t-mac‘, ‘james‘]; car.showColor = function(){alert(this.color);}; return car; }
与经典的工厂方式不同,这个使用new
运算符,使它更像构造函数。
var car1 = new Car(), car2 = new Car(); car1.drivers === car2.drivers; // false car1.showColor === car2.showColor; // false
这种方式也是独立拥有属性,不能共享方法。但不推荐用这种方式。
总结
目前使用最广泛的是混合的构造函数/原型方式,此外,动态原型方式也很流行,这俩种方式是等价的。推荐使用这俩种方式的任何一种。
对象继承
混合方式
与创建类的最好方式类似,继承也是采用 混合构造函数和原型链 的方式,用对象冒充继承构造函数的属性,用原型链继承prototype对象:
function ClassA(color){this.color = color;} ClassA.prototype.sayColor = function(){alert(this.color);}; /*ClassB继承ClassA*/ function ClassB(color, name){ClassA.call(this, color); this.name = name;} ClassB.prototype = new ClassA(); // 注意是空参数。这是原型链中最标准的做法,确保构造函数没有任何参数。 ClassB.prototype.sayName = function(){alert(this.name);};
动态原型方式
作为创建类和对象的第二种最好的方式,动态原型方式适合对象继承吗?答案是否定的。
function Polygon(sides){ this.sides = sides; if(‘undefined‘ === typeof Polygon._initialized){ Polygon.prototype.getArea = function(){return 0;}; Polygon._initialized = true; } } function Triangle(base, height){ Polygon.call(this, 3), this.base = base, this.height = height; if(‘undefined‘ === typeof Triangle._initialized){ // 注意下面这行代码 Triangle.prototype = new Polygon(), Triangle.prototype.getArea = function(){ return .5 * this.base * this.height; }; Triangle._initialized = true; } }
分析上面代码,错误在于需要注意的那行代码。从逻辑上来说,它的位置是正确的,但从功能上来说是无效的。从技术上来说,在代码运行前,对象已被实例化,并与原始的prototype对象联系在一起。虽然可以用 极晚绑定 (即实例化对象后再修改其原型链,实例仍会拥有新修改的属性/方法)可使对原型对象的修改可以反映出来,但替换prototype对象却不会对该对象产生任何影响。因此只有未来的对象实例才会反映出这种改变,而第一个实例对象就变得不正确了。
要正确使用动态原型实现继承,必须在构造函数外赋予新的prototype对象:
function Triangle(base, height){ Polygon.call(this, 3), this.base = base, this.height = height; if(‘undefined‘ === typeof Triangle._initialized){ Triangle.prototype.getArea = function(){ return .5 * this.base * this.height; }; Triangle._initialized = true; } } Triangle.prototype = new Polygon();
可这种方式又不能把代码完全封装在构造函数中了,违反了动态原型的主旨,还不如用混合方式。
zInherit继承插件
利用zInherit库,未重写prototype对象,支持多重继承。
xbObjects
xbObjects目的是为JavaScript提供更强的面向对象范型,不止支持继承,还支持方法的重载和调用超类方法的能力。
JavaScript定义类和对象以及实现继承