• 读书笔记之JavaScript的类编写方法 - [Development]

    2007-11-21

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://fallenlord.blogbus.com/logs/10912295.html

    白天想实现一些页面上的东西,却有点郁闷,晚上看Nicholas C. Zakas的《Professional JavaScript for Web Developers》,前面几章是讲基础,看到第三、四两章,发现看的有些糊涂了,呵呵,总结一下,贴在这里备忘。

    编写类一般有如下几种方法

    1. 工厂函数方式(factory function)

     <script type="text/javascript">
    function createCar(sColor, iDoors, iMpg) {
      var oTempCar = new Object;
      oTempCar.color = sColor;
      oTempCar.doors = iDoors;
      oTempCar.mpg = iMpg;
      oTempCar.showColor = function () {
        alert(this.color);
      };
      return oTempCar;
    }

    var oCar1 = createCar("red", 4, 23);
    var oCar2 = createCar("blue", 3, 25);
    oCar1.showColor();  //outputs "red"
    oCar2.showColor();  //outputs "blue"
    </script>

    但以上方法有个问题,就是每次调用函数createCar()时,都会创建新函数showColor(),这意味着每个对象都有自己的showColor()版本,但事实上,每个对象都共享了同一个函数。
    以下方案可以解决这个问题: 

    function showColor() {
     alert(this.color);
    };

    function createCar(sColor, iDoors, iMpg) {
      var oTempCar = new Object;
      oTempCar.color = sColor;
      oTempCar.doors = iDoors;
      oTempCar.mpg = iMpg;
      oTempCar.showColor = showColor;
      return oTempCar;
    }

    var oCar1 = createCar("red", 4, 23);
    var oCar2 = createCar("blue", 3, 25);
    oCar1.showColor();  //outputs "red";
    oCar2.showColor();  //outputs "blue"

    以上黄色的部分为修改了的代码,这样就解决了重复创建函数对象的问题,但这个函数看起来不像是对象的方法,所以这个应该说也不是完美的解决方案

    2. 构造函数方式 

    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);
    oCar1.showColor();  //outputs "red"
    oCar2.showColor();  //outputs "blue"

    这种方式应该是现在用的最多的方式了,但和工厂方法有同样的问题,就是会重复生成函数 ,当然,也可以如工厂函数方法一般将函数提到外面去再引用,这同样不完美,呵呵

    3. 原型构造方法

    function Car() {
    }

    Car.prototype.color = "red";
    Car.prototype.doors = 4;
    Car.prototype.mpg = 23;
    Car.prototype.showColor = function() {
     alert(this.color);
    };
    var oCar1 = new Car();
    var oCar2 = new Car();

    以上方式有两个缺陷:1. 不能通过构造函数来给对象传递参数初始化属性,必须在创建以后才能改;2. 当属性指向的是对象而不是函数时。。看下面的例子:

    function Car() {
    }

    Car.prototype.color = "red";
    Car.prototype.doors = 4;
    Car.prototype.mpg = 23;
    Car.prototype.drivers = new Array("Mike", "Sue");
    Car.prototype.showColor = function() {
     alert(this.color);
    };
    var oCar1 = new Car();
    var oCar2 = new Car();

    oCar1.drivers.push("Matt");

    alert(oCar1.drivers);  //outputs "Mike,Sue,Matt"
    alert(oCar2.drivers);  //outputs "Mike,Sue,Matt"

    呵呵,是不是觉得很恶?看来这种方式显然也是不对的

    4. 混合构造函数/原型方式 

    function Car(sColor, iDoors, iMpg) {
     this.color = sColor;
     this.doors = iDoors;
     this.mpg = iMpg;
     this.drivers = new Array("Mike", "Sue");
    }

    Car.prototype.showColor = function() {
     alert(this.color);
    }

    var oCar1 = new Car("red", 4, 23);
    var oCar2 = new Car("blue", 3, 25);

    oCar1.drivers.push("Matt");

    alert(oCar1.drivers);  //outputs "Mike,Sue,Matt"
    alert(oCar2.drivers);  //outputs "Mike,Sue"

     呵呵,所有非函数属性都在构造函数中创建,而函数则绑定到原型上,这个就是很完美的解决方案了

    5. 动态原型方法

     function Car(sColor, iDoors, iMpg) {
     this.color = sColor;
     this.doors = iDoors;
     this.mpg = iMpg;
     this.drivers = new Array("Mike", "Sue");
     
     if (typeof Car._initialized == "undefined") {
      Car.prototype.showColor = function() {
       alert(this.color);
      };
      Car._initialized = true;
     }
    }

    var oCar1 = new Car("red", 4, 23);
    var oCar2 = new Car("blue", 3, 25);

    oCar1.drivers.push("Matt");

    alert(oCar1.drivers);  //outputs "Mike,Sue,Matt"
    alert(oCar2.drivers);  //outputs "Mike,Sue"

     呵呵,如何,这样就只会创建一次方法而方法又在类里面了,视觉上也要好看点

    显然,最后两种方法都是推荐使用的

    最后说明下,以上源代码均来自Nicholas C. Zakas的《Professional JavaScript for Web Developers》


    收藏到:Del.icio.us