【问题标题】:JavaScript oop: Designing classes correctlyJavaScript oop:正确设计类
【发布时间】:2015-08-24 06:23:39
【问题描述】:

拿这个类似问题的列表:

  1. How to set up JavaScript namespace and classes properly
  2. Javascript namespace declaration with function-prototype
  3. Best OOP approach to these two small JavaScript classes

我得出的结论是,在 JS 中实现 实例 有两种可能的方法:使用内部函数或使用原型。
因此,假设我们在命名空间 BOX_LOGIC 中有一个 Box 类,其中包含一个简单的代码。我可以编写以下代码:

BOX_LOGIC.Box = (function() {
    // private static
    var boxCount = 0;

    var classDefinition = function(x) {
        x = x || 0;
        var capacity = x;
        var id = ++boxCount;

        // public methods
        this.getCapacity = function() { return capacity; };
        this.getId = function() { return id; };
        this.add = function(weight) { 
            weight = weight || 0;
            if (capacity >= weight) {
                capacity -= weight;
            }   
            return capacity;
        };
    };
    return classDefinition;
})();

以及我能够编码:

BOX_LOGIC.Box = (function () {
    var boxCount;

    var Box= function (x) {
        x = x || 0;
        this.capacity = x;
        this.id = ++boxCount;
    };

    Box.prototype = {
        Constructor: Box,
        add: function (weight) {
            weight = weight || 0;
            if (this.capacity >= weight) {
                this.capacity -= weight;
            }   
            return this.capacity;
        }
    };
    return Box;
})();

我的问题是:使用 Box 原型 到底有什么区别?出于任何原因(成本、易读性、标准......),有什么方法更好吗? 第二种方法是否可以模拟static idvariable? THX!

【问题讨论】:

  • @T.J.Crowder 打错字,已更正
  • getCapacity 在您的第二个示例中毫无用处。在您的第一个中,我更喜欢使用吸气剂。值得注意的是,这两种方法并不相互排斥,而是可以结合使用。

标签: javascript oop closures prototype


【解决方案1】:

我的问题是:使用 Box 原型到底有什么区别?

出于任何原因(成本、易读性、标准...)是否有更好的方法?

原型上的函数(和其他属性)在实例之间共享;如果您在构造函数中创建它们,每个实例都会获得所有这些函数的自己的副本。

主要好处是您可以添加到原型中,甚至已经存在的实例也可以看到添加的内容,因为它们动态使用原型。特别是,这有助于面向方面的编程以及各种调试和日志记录技术,因为您可以在原型上动态包装函数以捕获调用。当每个实例都有自己的实例时,你不能这样做(除非你有对每个实例的引用,这不太可能)。

理论上,使用原型也意味着更低的内存消耗。在实践中,您必须关心数百万个实例,而现代引擎擅长重用函数的底层代码,即使所涉及的函数对象是不同的。

因此,除非您要动态扩充原型,否则其中一个很大程度上是风格问题。

第二种方法是否可以模拟静态id 变量?

我不会称它为“静态的”;它是每个实例的私有。有多种方法可以通过原型方法接近私有信息(即原型函数可以访问的接近私有信息),但不可能真正获得私有信息信息原型功能可以访问。可能会有 ES7(不是 ES6,目前正在完成;ES7)。我解决了其中一种近乎私人的机制in this blog post。该帖子中的 ES6 信息现已过时;隐私的东西被推回 ES7,“private Name”对象变成了 Symbol,它根本不提供任何真正的隐私。

我应该标记您的第三个选项,您现在可以将其与 ES6 转译器一起使用:ES6 的 class:

// This is similar to the prototype version; `getCapacity` and
// `add` end up on `Box.prototype`
BOX_LOGIC.Box = (function () {

    class Box {
        constructor() {
            x = x || 0;
            this.capacity = x;
        }

        getCapacity() {
            return this.capacity;
        }

        add(weight) {
            weight = weight || 0;
            if (this.capacity >= weight) {
                this.capacity -= weight;
            }   
            return this.capacity;
        }
    }

    return Box;
})();

旁注:由于您在标题中使用了“正确”一词,因此我将在您的原型示例中标记一个非常小的问题:您搞砸了 constructor 属性。默认情况下,Box.prototype 有一个属性 constructor,它引用回 Box。但是通过将Box.prototype 上的对象替换 为完全不同的对象,您将删除它。为了与标准函数保持一致,我会这样修改它:

Box.prototype = {
    constructor: Box,
    //...the existing stuff goes here...
};

这有关系吗?仅当您最终得到依赖于 constructor 属性的代码时(某些库可能)。 JavaScript 本身没有任何作用,即使 JavaScript 规范定义了属性。

【讨论】:

  • 我无法测试您的第三个选项。上面写着Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
  • @Manu:是的,ES6 还没有最终确定,浏览器支持是可变的。发现该错误消息,您正在使用 Chrome。将代码置于严格模式会修复它(将 "use strict"; 放在文件的开头,或者放在匿名函数的顶部),但同样,如果不转译到 ES5,你就不能依赖它(例如,使用Babel 或类似的)在野外还没有。
  • @Manu:在原型示例的add 中,您的意思是使用this.capacity 吗?否则,该示例实际上没有任何意义,可能不需要或不需要var capacity。 (我已将其删除并将this. 添加到class 示例中。)
  • @Manu:原型不是BOX_LOGIC.Box.Box,但是如果您在控制台中查看,可能是控制台试图为您提供尽可能多的上下文信息。 o 的原型将是一个对象,它像所有对象一样可以从任意数量的地方引用。但在上面的代码中,BOX_LOGIC.Box.Box 并不是其中之一。
  • FWIW; here's a bit.ly link to babeljs.io/repl(链接对于评论框来说太大了,因为它在 URL 中包含所有代码)演示了上面的类并在 b.__proto__ 上执行了 console.log。跨度>
猜你喜欢
  • 2011-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
  • 2015-12-23
相关资源
最近更新 更多