【问题标题】:Best OOP approach to these two small JavaScript classes这两个小型 JavaScript 类的最佳 OOP 方法
【发布时间】:2012-07-02 07:12:24
【问题描述】:

我正在着手处理一个相当大的 JS 项目,并希望确保我的代码结构清晰有效。我一直在阅读大量关于 JS 中 OOP 的不同方法,但没有找到我真正喜欢的方法。

我很欣赏使用prototype 属性所提供的性能改进,但是当您尝试将真正的私有方法和变量添加到混合中时,它会变得相当混乱。另一方面,我真的很喜欢closure 方法,但我对性能下降感到不舒服。我查看了module 模式,但发现它非常冗长。某处有快乐的媒介吗?

为了说明我正在努力解决的问题,这里有两个非常小的示例类:

点等级:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

// Get the x coordinate.
Point.prototype.getX = function() {
  return this.x;
}

// Get the y coordinate.
Point.prototype.getY = function() {
  return this.y;
}

// Get the (x, y) coordinate.
Point.prototype.getXY = function() {
  return {x: this.x, y: this.y};
}

// Set the x coordinate.
Point.prototype.setX = function(x) {
  this.x = x;
}

// Set the y coordinate.
Point.prototype.setY = function(y) {
  this.y = y;
}

// Set the (x, y) coordinate.
Point.prototype.setXY = function(x, y) {
  this.x = x;
  this.y = y;
}

用户类:

function User(guid) {
  var guid = guid;

  // Return the user's GUID.
  this.getGuid = function() {
    return guid;    
  }
}

在任何给定时间都可能有数千个点对象在使用,所以我觉得prototype 方法是那里的最佳选择。但是,如果我想为坐标设置添加任何验证,只需调用Point.x = <value> 即可绕过它。同样,我可能会在大部分时间调用Point.getXY(),这将为本质上是公共属性的内容构建新对象。我是否应该摆脱 Point 类的 getter 和 setter 的概念,让它创建完全开放的公共对象?这在 OOP 的上下文中似乎是错误的。

对于User 类,我想在内部存储一个GUID,这样它就可以被请求但永远不会被修改。这不能用prototype 方法完成(除非我遗漏了什么),所以我不得不使用某种closure。我预计有数百名用户同时处于活动状态,因此性能不如Point 类那么重要。但是,我真的不想在整个代码库中切换不同类型的 OOP 样式。

所以,作为一个普遍的问题,是否有一种我缺少的 JS 中的 OOP 方法可以让我使用 prototype 属性,同时干净地将选择函数和变量私有化?或者,我看错了方向?我应该以不同的方式处理这个问题吗?

【问题讨论】:

  • 如果你真的想要隐私,请使用 Dart 或 GWT。你真的想要吗?
  • @Esailija 我认为隐私在整个代码库中都很重要,所以我想相应地进行计划。然而,我觉得 Dart 和 GWT 作为解决这个问题的方法有点过头了。
  • 您只能使用 _ 前缀之类的约定将方法标记为“内部”,但 javascript 中没有真正的隐私。闭包与它完全不同,只要你提出一个新观点,你就会重新创建每个函数对象。如果有 50 个方法并且您创建了 1000 个点,那就是创建了 49950 个额外的函数对象。最重要的是,闭包拥有数据而不是对象,如果您不经常意识到它可能会产生有趣的结果。请注意,您可以毫无问题地创建“私有方法”,但这并不是一个巨大的胜利。
  • @Esailija 我同意并理解你所说的一切。这个项目在发布时可能会被第三方扩展,所以我正在努力关注适当的封装和隐私。如果您忽略创建大量函数对象的性能影响,那么闭包方法正是我想要的。所以,我希望也许有一些我没有听说过的混合方法可以同时获得性能和隐私。
  • 嗯,它适用于流行的库(Google Closure、jQuery 及其插件等)。我敢肯定,如果有人会无视约定和文档并覆盖它,他们可能会在其他语言中使用反射来做到这一点?

标签: javascript oop design-patterns


【解决方案1】:

您只能使用 _ 前缀之类的文档和约定将方法标记为“内部”,但在 javascript 中没有真正的、强制的对象私有成员。

闭包是不可接受的——它们将拥有数据而不是可能产生“有趣”结果的对象,尤其是当人们想以这种方式使用闭包扩展“类”时。 另一点是,您将使用 O(n) 内存来存储所有函数对象,如果库的用户无法控制,他们可以很容易地以一种会导致内存问题。

您可以像这样使用下划线前缀约定:

function Point(x, y) {
  this._x = x;
  this._y = y;
}

// Get the x coordinate.
Point.prototype.getX = function() {
  return this._x;
}

// Get the y coordinate.
Point.prototype.getY = function() {
  return this._y;
}

【讨论】:

    【解决方案2】:

    您可以在开发过程中使用闭包方法,这样您就知道没有人会绕过验证,然后在发布代码时切换到更快的原型方法。

    var Release = true;
    if (Release) {
        function Point(x, y) {
            this.x = x;
            this.y = y;
        }
    
        // Get the x coordinate.
        Point.prototype.getX = function () {
            return this.x;
        }
    
        // Get the y coordinate.
        Point.prototype.getY = function () {
            return this.y;
        }
    
        // Get the (x, y) coordinate.
        Point.prototype.getXY = function () {
            return {
                x: this.x,
                y: this.y
            };
        }
    
        // Set the x coordinate.
        Point.prototype.setX = function (x) {
            this.x = x;
        }
    
        // Set the y coordinate.
        Point.prototype.setY = function (y) {
            this.y = y;
        }
    
        // Set the (x, y) coordinate.
        Point.prototype.setXY = function (x, y) {
            this.x = x;
            this.y = y;
        }
    } else {
        function Point(x, y) {
            var _x = x,
                _y = y;
            return {
                // Get the x coordinate.
                getX: function () {
                    return _x;
                },
    
                // Get the y coordinate.
                getY: function () {
                    return _y;
                },
    
                // Get the (x, y) coordinate.
                getXY: function () {
                    return {
                        x: _x,
                        y: _y
                    };
                },
    
                // Set the x coordinate.
                setX: function (x) {
                    _x = x;
                },
    
                // Set the y coordinate.
                setY: function (y) {
                    _y = y;
                },
    
                // Set the (x, y) coordinate.
                setXY: function (x, y) {
                    _x = x;
                    _y = y;
                }
            }
        }
    }
    

    【讨论】:

    • 维护两个代码路径根本不切实际。不过,感谢您的建议。
    【解决方案3】:

    由于基于原型的面向对象并不是真正的面向对象,我认为您可能找错地方了。

    基于原型的对象是它们子类的克隆。因此,如果您声明您的对象具有属性 getXY() ,那么它对任何从中克隆的东西都是开放的。

    所以我想真正的问题是,你认为私有函数在这个实现中会给你带来什么?

    如果您真的需要私有函数,我建议您处理闭包模式中必须处理的混乱。

    【讨论】:

    • 私有函数和变量允许我保护将封装在每个Point 中的信息。例如,如果我最终决定负坐标无效,我希望能够在班级内强制执行。我认为实现这一点的唯一方法是通过闭包,但我对性能影响持谨慎态度。
    【解决方案4】:

    我不知道你的背景,但可能是的,所以请查看coffee-script

    【讨论】:

    • 如果可能的话,我想在不使用外部库的情况下开发一个解决方案。不过还是谢谢你的推荐。
    猜你喜欢
    • 2021-08-13
    • 2014-09-16
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多