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

Lan*_*opp 10 javascript

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

最近,当我用JavaScript编程时,我觉得我正在尝试将其转换为基于类的语言,而不是按照它的使用方式使用它.似乎我试图强迫语言符合我以前的习惯.

以下是我最近编写的一些JavaScript代码.它的目的是抽象出绘制到HTML5 canvas元素所涉及的一些工作.

/*
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;
};
Run Code Online (Sandbox Code Playgroud)

代码的工作方式与Shape.Circle的用户完全一样,但感觉它与Duct Tape一起使用.有人可以对此提供一些见解吗?

Wes*_*n C 13

这将是一个由意见驱动的问题.但我会投入我的$ .02.

tl/dr:不要太担心这个.JavaScript非常灵活,可以支持很多方法.组织良好,组织良好.你可能没事.

更详细的答案:

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

2)你甚至不必使用闭包/模块模式/任何东西.在编写类时,大多数情况下使用JavaScript中可用的本机类功能没有任何问题 - 只需定义构造函数,然后为构造函数定义原型对象并将方法放在其上.如果要从该类继承,请将子类的原型对象分配给要派生的类的实例.

(例如:

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;
})()
Run Code Online (Sandbox Code Playgroud)

)

私人会员/方法怎么样?这是一种观点,但大多数时候,我认为隐私作为一种运行时强制机制被过度使用甚至被滥用.开发人员有很多事情要做; 他们可能宁愿不注意任何给定抽象的内部,除非它泄漏了一些有害的东西.如果您的类不会导致问题,抛出/返回有用的错误,提供真正有用的方法,并且记录得很好,您将不需要任何类型的隐私执行机制,因为每个人都会对您的课程所节省的工作感到满意他们不会在里面同行.如果您的课程不符合该标准,那么缺乏隐私执行机制并不是您真正的问题.

这有一个例外,那就是当您在页面/应用程序中混合来自不同(通常是不受信任的)来源的JavaScript代码时.此时,出于安全原因,您有时必须仔细考虑隔离代码和代码本身可以访问的给定范围内的一些关键函数/方法.

编辑/编

在回答关于为什么我有这些立即评估的函数的问题时,请考虑这种编写Drawing.Node定义的替代方法:

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; }
};
Run Code Online (Sandbox Code Playgroud)

这与上面的代码完全相同.它也是,恕我直言,完全可以接受,可能更清晰,更不狡猾.

另一方面,我发现将所有内容放在一个立即执行的匿名函数的范围内,这给了我至少两个优点:

  1. 如果我确定我需要定义任何私有方法或者做一些仅与特定类定义相关的设置工作,它会为我提供一个很好的私有范围.

  2. 如果我决定需要Node在其他位置移动命名空间对象层次结构中的位置,那么如果与其定义相关的所有内容都绑定在一个方便的位置,那么它会很方便.

有时候这些优势很小.有时他们会更有吸引力.因人而异.