JavaScript oop:正确设计类

Posted

技术标签:

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

拿这个类似问题的列表:

    How to set up javascript namespace and classes properly Javascript namespace declaration with function-prototype 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 在您的第二个示例中毫无用处。在您的第一个中,我更喜欢使用吸气剂。值得注意的是,这两种方法并不相互排斥,而是可以结合使用。 【参考方案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。跨度>

以上是关于JavaScript oop:正确设计类的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript高级程序设计读书笔记之OOP

javaScript设计模式之面向对象编程(object-oriented programming,OOP)

JavaScript 和 jQuery 中的 Java 式 OOP 失败

JavaScript的面向对象编程(OOP)——类

Java oop

javaScript设计模式之面向对象编程(object-oriented programming,OOP)(二)