构造函数方式
- function Car(sColor,iDoors,iMpg) {
- this.color = sColor;
- this.doors = iDoors;
- this.mpg = iMpg;
- this.showColor = function() {
- alert(this.color);
- };
- }
- var oCar1 = new Car("red",4,23);
- var oCar2 = new Car("blue",3,25);
例子中, 每次 new 一个新对象, 都要创建新函数 showColor(), 意味着每个对象都有自己的 showColor() 版本, 导致重复创建函数对象的问题.
原型方式
每个函数都有一个 prototype(原型) 属性, 这个属性是一个指针, 指向一个对象, 这个对象时该函数的原型对象.
- function Car() {} // 用空构造函数来设置类名
- // 所有的属性和方法都被直接赋予 prototype 属性
- Car.prototype.color = "blue";
- Car.prototype.doors = 4;
- Car.prototype.mpg = 25;
- Car.prototype.showColor = function() {
- alert(this.color);
- };
- var oCar1 = new Car();
- var oCar2 = new Car();
通过原型方法创建的对象实例 Car1 和 Car2 的 prototype 都指向同一个原型对象, 这个对象上保存了相应的属性和方法, 即解决了重复创建函数对象的问题.
但单纯运用原型方式创建对象也存在问题: 首先, 这个构造函数没有参数. 使用原型方式, 不能通过给构造函数传递参数来初始化属性的值, 因为 Car1 和 Car2 的 color 属性都等于 "blue",doors 属性都等于 4,mpg 属性都等于 25. 这意味着必须在对象创建后才能改变属性的默认值; 另外将属性定义在 prototype 上, 若 Car1 改变了 color 的值, oCar2.color 也会改变.
混合的构造函数 / 原型方式
- function Car(sColor,iDoors,iMpg) {
- this.color = sColor;
- this.doors = iDoors;
- this.mpg = iMpg;
- this.drivers = new Array("Mike","John");
- }
- Car.prototype.constructor = Car;
- Car.prototype.showColor = function() {
- alert(this.color);
- };
- var oCar1 = new Car("red",4,23);
- var oCar2 = new Car("blue",3,25);
- oCar1.drivers.push("Bill");
- alert(oCar1.drivers); // 输出 "Mike,John,Bill"
- alert(oCar2.drivers); // 输出 "Mike,John"
所有的非函数属性都在构造函数中创建, 意味着又能够用构造函数的参数赋予属性默认值了. 而由所有实例共享的属性 constructor 和方法 showColor() 则是在原型中定义的, 所以没有内存浪费. 此外, 给 oCar1 的 drivers 数组添加 "Bill" 值, 不会影响到 oCar2 的数组, 因为它们分别引用不同的数组, 所以输出这些数组的值时, oCar1.drivers 显示的是 "Mike,John,Bill", 而 oCar2.drivers 显示的是 "Mike,John".
这种混合的构造函数 / 原型方式, 是目前在 ECMAScript 中使用最广泛, 认同度最高的一种创建自定义对象的方法. 另外, 还有动态原型方式, 寄生构造函数函数模式, 稳妥构造函数模式等方式.
来源: http://www.qdfuns.com/note/50410/d48871375ea32a00ee9256d70e430aa4.html