为啥不对对象属性使用闭包?
Posted
技术标签:
【中文标题】为啥不对对象属性使用闭包?【英文标题】:Why not to use closures for object attributes?为什么不对对象属性使用闭包? 【发布时间】:2011-12-11 17:05:53 【问题描述】:我目前正在用 javascript 编写对象,我想以一种清晰、漂亮的方式,使用最佳实践等。但我很困扰我必须总是写 this.
来处理属性,不像其他OO 语言。
所以我明白了 - 为什么不只对对象属性使用闭包?看看我的示例对象。所以代替这个,经典的方式:
var MyObjConstructor = function (a, b)
// constructor - initialization of object attributes
this.a = a;
this.b = b;
this.var1 = 0;
this.var2 = "hello";
this.var3 = [1, 2, 3];
// methods
this.method1 = function ()
return this.var3[this.var1] + this.var2;
// terrible - I must always write "this." ...
;
...我会这样做使用闭包,然后我不需要每次都写this.
来访问属性!
var MyObjConstructor = function (a, b)
// constructor - initialization of object attributes
// the attributes are in the closure!!!
var a = a;
var b = b;
var var1 = 0;
var var2 = "hello";
var var3 = [1, 2, 3];
// methods
this.method1 = function ()
return var3[var1] + var2;
// nice and short
;
// I can also have "get" and "set" methods:
this.getVar1 = function () return var1;
this.setVar1 = function (value) var1 = value;
此外,它还有一个隐藏的好处,就是属性真的不能通过 get/set 方法以外的任何其他方式访问!!
所以问题是:
-
这是个好主意吗?它是否“干净”,是否符合最佳做法?
这两种解决方案之间是否存在其他语义差异?
有没有像这样使用闭包的陷阱?
【问题讨论】:
也许对象复制不起作用? 这是两个不同的东西 - 使用this.prop
定义公共属性,使用 var prop
定义私有属性。你不能用一个代替另一个,它们有不同的用途。
@ŠimeVidas var prop
定义了一个局部变量,只能在构造函数内部或在构造函数中定义的函数内部访问。 private
这个词具有误导性。
为了减少混淆:javascript.crockford.com/private.html
【参考方案1】:
95% 的性能下降。
一个实际的Benchmark,所以对于您的(简单)示例来说,跨浏览器的性能下降了 50%-85%。
说真的,关闭速度非常慢。
现在对数据使用闭包不是问题,但对函数/方法使用闭包是问题。你不能没有另一个。原型上的方法没有访问构造函数内的本地变量的机制。
另一个问题是您的“经典”示例不使用原型:\
你真正想要的是
Doing OO properly Understanding prototypes as classes所以下面的也不好。
var MyObjConstructor = function (a, b)
// constructor - initialization of object attributes
this.a = a;
this.b = b;
this.var1 = 0;
this.var2 = "hello";
this.var3 = [1, 2, 3];
// methods
this.method1 = function ()
return this.var3[this.var1] + this.var2;
;
你想要
// constructor - initialization of object attributes
var MyObjConstructor = function (a, b)
this.a = a;
this.b = b;
Object.extend(MyObjConstructor.prototype,
var1: 0,
var2: "hello",
var3: [1, 2, 3],
// methods
method1: function ()
return this.var3[this.var1] + this.var2;
);
对于Object.extend 的某个值。这里是在原型上放置任何通用数据或方法,并在 所有 实例之间共享这些数据。这样,我们就不会每次都在记忆复制所有内容。
// 太糟糕了——我必须总是写“这个”。 ...
另一种方法是为每个对象复制状态。闭包模式很好,但性能不佳。
【讨论】:
感谢您的链接,但是我不喜欢使用新库 (pd
) - 是否有一些类似的指令如何做 OO,但只是“赤手空拳”? (或者,也许使用 jquery)。
@TomasT。肯定有。以详细的方式进行。 pd
只是让它不那么冗长。或者用“经典”的方式来做。
如果我错了,请纠正我,但 jQuery 框架不是几乎完全使用闭包吗?如果这是一个性能问题,他们为什么要对整个库使用闭包?
我在摆弄你的图书馆,但我一定是做错了什么。您的代码似乎扩展了构造函数,而不是它的原型。 jsfiddle.net/mK4ws 实例没有var1
可用。
@MrMisterMan 因为 jQuery 慢得要命。我的意思是,嗯,jQuery 没有在 javascript 级别进行优化,而是在 DOM 级别进行了优化。【参考方案2】:
直接回答您的问题:
1.使用对象作为闭包可以吗?是否符合最佳做法?
是的。在某些情况下,您确实希望拥有私有字段,因此这是唯一的方法,其中一种方法可以做到这一点。对于一个真实的、具体的示例,请查看 Dojo 或 JQuery 中的 Deferreds/Promises。 Promises 只实现了 deferred 的非可变子集,因此它们需要保持其内部 Deferred 私有,以避免其他人直接更改它。
请记住,您应该只在真正需要它们的地方真正使用隐藏字段(而不是因为不必键入“this”等琐碎的原因)。使用普通的旧公共字段和“普通”对象也很好,特别是如果您认为它们具有闭包版本没有的一些优点:
2。这两个版本在语义上有区别吗?
是的。
您不能直接检查私有字段 (duh)。这也意味着您无法轻松克隆对象或对它们进行其他类型的反射。 通过对象属性而不是直接引用访问字段允许您使用原型继承。重要的部分是: 您很难覆盖子类中的内容(闭包中的变量是静态的,而不是虚拟的) 您不能添加其他使用这些隐藏字段的方法(因为它们只能在构造函数内部访问)。特别讨厌子类化。3.使用这样的闭包有什么陷阱吗?
与使用原型的代码相比,如今大多数 Javascript 引擎在具有大量闭包的代码上的性能较差。这不是造成差异的“真正”原因(因为 LISP 引擎一直以来都可以使用闭包),而是您必须忍受的东西。
【讨论】:
你可以在没有闭包的情况下实现 Deferreds/Promises。闭包与原型实际上只是一种风格或“做你想做的事”。 好吧,如果你真的希望 promise 隐藏它们的私有状态,那就不要了。否则你只会得到 Deferred 部分。 您最终将不得不使用.bind
和某种代理模式。
真的......我仍然认为必须支持旧的东西,其中闭包是执行bind
的唯一方法。 :) (虽然我确信找到更多关于闭包更惯用的例子并不难)
感谢您的回复,很好的分析。以上是关于为啥不对对象属性使用闭包?的主要内容,如果未能解决你的问题,请参考以下文章