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 id
variable? 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设计模式之面向对象编程(object-oriented programming,OOP)