在javascript中正确构建类

时间:2012-04-02 22:16:22

标签: javascript oop class

我正在学习Javascript中的OO编程,似乎有很多方法可以创建一个类。我设法将我读到的大部分内容提炼成通用结构,但我觉得我可能会错过原型设计以节省膨胀的目的。是否包括原型定义在这个"类"使它们毫无意义(即不减小对象大小)?这整体结构有缺陷吗?

谢谢,

var Car = (function () {

    //Private fields
    var _make;
    var _model;

    //Constants (protected by encapsulation)    
    var NUM_OF_WHEELS = 4;

    //Private methods
    function getDesc() {
        return _make + " " + _model;
    }

    //Public constructor
    function thisCar(make, model, color) {
        _make = make;
        _model = model;

        //public properties
        thisCar.prototype.color = color;
    }

    //static properties
    thisCar.carsInTheWorld = 50;

    //static methods
    thisCar.getNumberOfWheels = function () {
        return NUM_OF_WHEELS * thisCar.carsInTheWorld;
    };

    //public properties
    thisCar.prototype.color = "";

    //public method
    thisCar.prototype.startEngine = function () {
        console.log(getDesc() + " engine started");
    };

    return thisCar;
})();

修改 阅读评论后,这里提供的链接是我理解的。

需要访问私有成员的类成员无法添加到原型中并且必须具有特权。同样,通常的做法是不在构造函数中包含原型。

var Car = function (make, model, color) {//Public constructor
    //Private fields
    var _make = make;
    var _model = model;

    //Public fields 
    this.color = color; 

    //Private methods
    function getDesc() {
        return _make + " " + _model;
    }

    //Privileged functions (requiring private members) 
    this.startEngine = function () {
        console.log(getDesc() + " engine started");
    };
};

//public method
Car.prototype.sprayPaint = function (newColor) {
    var oldColor = this.color;
    this.color = newColor;
    console.log("Your car has changed from " + oldColor + " to " + newColor);
};

//Public fields  (if not declared in constuctor)
Car.prototype.fuel = 0; 

//static properties
Car.carsInTheWorld = 0;

//static methods
Car.getNumberOfWheels = function () {               
    var NUM_OF_WHEELS = 4;
    return NUM_OF_WHEELS * Car.carsInTheWorld;
};

感谢您的所有回复,他们真的很有帮助。

4 个答案:

答案 0 :(得分:3)

不使用原型,常见的模式是从包含公共访问器/ mutator的构造函数返回一个对象:

jsfiddle

function Car() {
  var myPrivateVar = "foo";
  var myPublicVar = "bar";

  function myPrivateFunc() {console.log(myPrivateVar);};
  myPrivateFunc();      

  return {
    getMyPublicVar: function() {
      return myPublicVar;
    }
  }
}
var honda = Car();
console.log(honda.getMyPublicVar());
honda.myPrivateFunc();    // fail

在类定义之外使用原型为其添加属性。在 Car.prototype.myFunc = function() {};已定义之后,您将使用Car myFunc添加到将来实例化的Car个对象中。这些新增成员是公开的。

Here's a question that should help you

Here's the standard article from Crockford on doing inheritance this way.

我应该重申,使用JS.Class这样的类框架可以在代码变大时为您节省无数麻烦。

答案 1 :(得分:2)

你混淆了一个闭包和objectorientated构造函数。通常,你从一个闭包开始。在JS中,您可以通过构造函数定义“类”:

var Car = function Car(model) { //Public constructor

    // Private fields of one instance
    // model is an argument
    var make;

    // Private methods of one instance
    function getDesc() {
        return make + " " + model;
    }

    // Public fields of one instance
    this.color = "#555";

    // Public methods of one instance, privileged the access private fields and functions
    this.setMake = function(m) {
        make = m;
    };
    this.desc = function() {
        return getDesc();
    };
} // end of constructor

/* now the prototype things: */   

// public "default" fields, may get overwritten per instance
Car.prototype.color = "";

// public methods, can only access other public things (not "privileged")
thisCar.prototype.startEngine = function () {
    console.log(this.desc() + " engine started");
};

就是这样。现在,您可以添加静态内容。它们与Car函数无关,但通常它们通过构造函数的属性来命名。但它们也可能是自由变量。

var carsInTheWorld = 50;
// or
Car.livingInstances = 50;
// or something

Car.NUM_OF_WHEELS = 4; // constants are uppercase

仅当您需要静态私有变量时 - 例如实例 - 计数器 - 你可以围绕一切建立一个闭包。然后只能从闭包函数内部访问unnamespaced变量。

答案 2 :(得分:2)

将prototype视为直接附加到实例构造函数的方法的备份存储库。如果您尝试在没有它的实例上调用属性/方法,则会检查实例的构造函数原型以查找该方法。糟糕的是,prototype属性无法在实例内部看到(访问实例变量)。

我鼓励你避免立即在链接原型的库上进行反弹,以便允许复杂的类类继承方案。

主要是因为这种方法很容易发臭。它缺乏灵活性,难以阅读,并且不适合解决JS在一开始就善于处理的各种问题。 (实际上,这种过度的计划是糟糕的,并建议用任何语言进行反对)。我不是说继承是坏的,只是JavaScript的圣杯不是写坏Java而且我的意思是20层深层次的级联继承完全难以理解和不可改变的任何人都没有花几天时间看着他们最近。

不要担心封装。不完全是。你最后一次听说一个前端或UI开发人员在一个复杂的电子商务网站上重写jQuery的关键内容,或者直接更改JQ对象方法和其他属性,这对于他们正在处理的事情而不关心所有其他可能破坏的东西?正如Python孩子们所说的那样,“信任用户”。私有实例变量可以帮助澄清什么是界面,什么不界面。除了这个实用程序,把它放在你的脑海里,在互联网上,无论如何每个人总能看到我们的代码。

学习JS中有关函数的所有知识。那里有很多东西在JS OOP中很有用,其中重点往往更多地放在实例上而不是构造函数上。函数,上下文和调用/应用方法是提出适合您的结构的关键。

答案 3 :(得分:0)

大部分都没问题。但这个位错了:

function thisCar(make, model, color) {
    // [ ... ]
    //public properties
    thisCar.prototype.color = color;
}

在构造函数中,您不应该为原型分配任何内容。这将color属性分配给从构造函数创建的所有Cars。

相反,这样做:

    this.color = color;

回答一般性问题,以这种方式使用原型并非毫无意义。在解析脚本之后,外部函数将只运行一次。它会将内部thisCar构造函数分配给Car。正确放置原型属性,但上述情况除外。

另一个建议是,即使你的IIFE内部,你也应该使用一个大写的首字母作为名称。我个人也会在这里使用Car,因为它看起来更清晰。

编辑:

比起我看到的错误更多。这也是错误的

var _make;
var _model;

// [ ... ]
function thisCar(make, model, color) {
    _make = make;
    _model = model;
}

这些不是实例属性。

此外,我并不知道如何将原型的使用与基于闭包的私有属性结合起来。所以我觉得你在一起做的太多了。