将基于类的设计应用于JavaScript程序是一种不好的做法吗?

将基于类的设计应用于JavaScript程序是一种不好的做法吗?,javascript,Javascript,JavaScript是一种基于原型的语言,但它能够模仿基于类的面向对象语言的一些特性。例如,JavaScript没有公共和私有成员的概念,但通过闭包的魔力,仍然可以提供相同的功能。类似地,方法重载、接口、名称空间和抽象类都可以以这样或那样的方式添加 最近,由于我一直在用JavaScript编程,我觉得我正在尝试将它变成一种基于类的语言,而不是按照它应该使用的方式使用它。似乎我在试图迫使语言符合我的习惯 下面是我最近编写的一些JavaScript代码。它的目的是将绘制HTML5画布元素所涉及的一些

JavaScript是一种基于原型的语言,但它能够模仿基于类的面向对象语言的一些特性。例如,JavaScript没有公共和私有成员的概念,但通过闭包的魔力,仍然可以提供相同的功能。类似地,方法重载、接口、名称空间和抽象类都可以以这样或那样的方式添加

最近,由于我一直在用JavaScript编程,我觉得我正在尝试将它变成一种基于类的语言,而不是按照它应该使用的方式使用它。似乎我在试图迫使语言符合我的习惯

下面是我最近编写的一些JavaScript代码。它的目的是将绘制HTML5画布元素所涉及的一些工作抽象出来

/*
Defines the Drawing namespace.
*/
var Drawing = {};

/*
Abstract base which represents an element to be drawn on the screen.
@param The graphical context in which this Node is drawn.
@param position The position of the center of this Node.
*/
Drawing.Node = function(context, position) {

    return {

        /*
        The method which performs the actual drawing code for this Node.  This method must be overridden in any subclasses of Node.
        */
        draw: function() {
            throw Exception.MethodNotOverridden;
        },

        /*
        Returns the graphical context for this Node.
        @return The graphical context for this Node.
        */
        getContext: function() {
            return context;
        },

        /*
        Returns the position of this Node.
        @return The position of this Node.
        */
        getPosition: function() {
            return position;
        },

        /*
        Sets the position of this Node.
        @param thePosition The position of this Node.
        */
        setPosition: function(thePosition) {
            position = thePosition;
        }
    };
}

/*
Define the shape namespace.
*/
var Shape = {};

/*
A circle shape implementation of Drawing.Node.
@param context The graphical context in which this Circle is drawn.
@param position The center of this Circle.
@param radius The radius of this circle.
@praram color The color of this circle.
*/
Shape.Circle = function(context, position, radius, color) {

    //check the parameters
    if (radius < 0)
        throw Exception.InvalidArgument;

    var node = Drawing.Node(context, position);

    //overload the node drawing method
    node.draw = function() {

        var context = this.getContext();
        var position = this.getPosition();

        context.fillStyle = color;
        context.beginPath();
        context.arc(position.x, position.y, radius, 0, Math.PI*2, true);
        context.closePath();
        context.fill();
    }

    /*
    Returns the radius of this Circle.
    @return The radius of this Circle.
    */
    node.getRadius = function() {
        return radius;
    };

    /*
    Sets the radius of this Circle.
    @param theRadius The new radius of this circle.
    */
    node.setRadius = function(theRadius) {
        radius = theRadius;
    };

    /*
    Returns the color of this Circle.
    @return The color of this Circle.
    */
    node.getColor = function() {
        return color;
    };

    /*
    Sets the color of this Circle.
    @param theColor The new color of this Circle.
    */
    node.setColor = function(theColor) {
        color = theColor;
    };

    //return the node
    return node;
};
/*
定义图形名称空间。
*/
变量绘图={};
/*
表示要在屏幕上绘制的元素的抽象基。
@param在其中绘制此节点的图形上下文。
@param position此节点中心的位置。
*/
Drawing.Node=函数(上下文、位置){
返回{
/*
为此节点执行实际绘图代码的方法。此方法必须在节点的任何子类中重写。
*/
绘图:函数(){
抛出Exception.MethodNotOverrided;
},
/*
返回此节点的图形上下文。
@返回此节点的图形上下文。
*/
getContext:function(){
返回上下文;
},
/*
返回此节点的位置。
@返回此节点的位置。
*/
getPosition:function(){
返回位置;
},
/*
设置此节点的位置。
@参数位置此节点的位置。
*/
设置位置:功能(位置){
位置=位置;
}
};
}
/*
定义形状名称空间。
*/
var Shape={};
/*
Drawing.Node的圆形实现。
@参数上下文绘制此圆的图形上下文。
@参数定位此圆的中心。
@参数半径此圆的半径。
@praram color这个圆圈的颜色。
*/
圆形=函数(上下文、位置、半径、颜色){
//检查参数
如果(半径<0)
抛出Exception.InvalidArgument;
变量节点=绘图节点(上下文、位置);
//重载节点绘制方法
node.draw=函数(){
var context=this.getContext();
var position=this.getPosition();
context.fillStyle=颜色;
context.beginPath();
弧(位置x,位置y,半径0,数学PI*2,真);
closePath();
context.fill();
}
/*
返回此圆的半径。
@返回此圆的半径。
*/
node.getRadius=函数(){
返回半径;
};
/*
设置此圆的半径。
@param theRadius这个圆的新半径。
*/
node.setRadius=函数(theRadius){
半径=半径;
};
/*
返回此圆的颜色。
@返回此圆的颜色。
*/
node.getColor=函数(){
返回颜色;
};
/*
设置此圆的颜色。
@param颜色此圆的新颜色。
*/
node.setColor=函数(颜色){
颜色=颜色;
};
//返回节点
返回节点;
};

该代码的工作原理与Shape.Circle用户的工作原理完全相同,但感觉像是用胶带粘在一起的。有人能对此提供一些见解吗?

我相信99%的情况都是这样。 这段代码看起来像Java。它不使用原型继承。它会在每次对象创建时创建新方法

尤其是:
抛出异常。methodNotOverrided
。就是那种我带我的程序员去会议室的JS代码

几个月前,我写了一篇关于JavaScript过度抽象的文章:


这个问题提供了更多关于这个主题的见解:

这将是一个观点驱动的问题。但我会把我的0.02美元投进去

tl/dr:不要太担心这个问题。JavaScript非常灵活,可以支持很多操作方式。有条理就是有条理。你可能很好

更详细的答覆:

1) 在有意义的地方使用类:问题域适合类/类层次结构建模的地方。这是一个问题域,在这个问题域中,您拥有各种形状对象,这些对象具有从基类继承的通用方法和其他多态方法。。。好吧,这(字面上)是一个教科书上的例子,其中类层次结构是显而易见的,可能是有用的,以类为中心的代码在那里是有意义的,并且没有任何问题

2) 您甚至不必使用闭包/模块模式/任何东西。在编写类时,大多数情况下,使用JavaScript中可用的本机类ish功能并没有什么错——只需定义构造函数,然后为构造函数定义原型对象并将方法放在其上即可。当您想要从该类继承时,将子类的原型对象指定给从中派生的类的实例

(例如:

Drawing.Node = (function() {

    var Node = function (context,position) {
        this.context = context;
        this.position = position;
    }

    Node.prototype = {
        draw: function() { throw Exception.MethodNotOverridden; },
        getContext: function() { return this.context; },
        getPosition: function() { return this.position; },
        setPosition: function(newPosition) { this.position = newPosition; }
    };

    return Node;
})();

Shape.Circle = (function () {

    var Circle = // Circle constructor function

    Circle.prototype = new Draw.Node;

    Circle.prototype.overriddenmethod1 = function () {

    }

    Circle.prototype.overriddenmethod2 = function () {

    }

    return Circle;
})()
)

私有成员/方法如何?这是一种观点,但大多数时候,我认为隐私作为一种运行时强制机制被过度使用,甚至被滥用。开发商有很多事情要做;他们可能宁愿不关注任何给定抽象的内部,除非它泄漏了有害的东西。如果您的类不会引起问题,不会抛出/返回有用的错误,不会提供真正有用的方法,也不会记录得很好,那么您就不需要任何隐私了
Drawing.Node = function (context,position) {
    this.context = context;
    this.position = position;
}

Drawing.Node.prototype = {
    draw: function() { throw Exception.MethodNotOverridden; },
    getContext: function() { return this.context; },
    getPosition: function() { return this.position; },
    setPosition: function(newPosition) { this.position = newPosition; }
};