为什么不使用对象属性的闭包?

TMS*_*TMS 9 javascript oop closures

我目前正在用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." ...
    };

}
Run Code Online (Sandbox Code Playgroud)

...我会使用闭包这样做,然后我不需要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; }
}
Run Code Online (Sandbox Code Playgroud)

此外,它还有一个隐藏的好处,即除了get/set方法之外,其他任何方式都无法访问这些属性!

所以问题是:

  1. 这是一个好主意吗?它是"干净的",是否符合最佳做法?
  2. 这两种解决方案之间是否存在其他语义差异?
  3. 是否有像这样使用闭包的陷阱?

Ray*_*nos 9

95%的性能下降.

一个实际的基准测试,所以对于你的(简单)例子,浏览器的性能下降了50%-85%.

说真的,闭包很慢.

现在使用闭包数据不是问题,但是使用函数/方法的闭包是.如果没有另一个,你就无法做到.生活在原型上的方法没有访问构造函数内部的局部变量的机制.

而另一个问题是你的"经典"例子没有使用原型:

你真正想要的是什么

所以以下也不好.

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; 
    };

}
Run Code Online (Sandbox Code Playgroud)

你要

// 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; 
    }
});
Run Code Online (Sandbox Code Playgroud)

对于Object.extend的某些值.以下是在原型上放置任何常见数据或方法,并在所有实例之间共享该数据.这样我们就无法随时随地记录所有内容.

//太糟糕了 - 我必须写"这个"....

另一种方法是复制每个对象的状态.闭合模式很好,除了它之外它不是很好.

  • @MrMisterMan因为jQuery很慢.我的意思是,meh jQuery没有在javascript级别进行优化,它在DOM级别进行了优化. (2认同)

hug*_*omg 2

直接回答您的问题:

1. 使用对象作为闭包可以吗?它符合最佳实践吗?

是的。在某些情况下,您确实希望拥有私有字段,因此这是实现这一目标的唯一方法之一。对于真实、具体的示例,请查看 Dojo 或 JQuery 中的 Deferreds/Promises。Promise 只实现了 deferred 的非变异子集,因此它们需要保持内部 Deferred 的私有性,以避免其他人直接更改它。

请记住,您应该只在真正需要的地方使用隐藏字段(而不是出于诸如不必键入“this”之类的琐碎原因)。使用普通的旧公共字段和“普通”对象也完全没问题,特别是如果您认为它们具有闭包版本所没有的一些优点:

2. 这两个版本之间有语义差异吗?

是的。

  • 你不能直接检查私有字段(废话)。这也意味着您无法轻松克隆对象或对其进行其他类型的反射。
  • 通过对象属性而不是直接引用访问字段允许您使用原型继承。重要的部分是:
    • 你很难覆盖子类中的东西(闭包中的变量是静态的,而不是虚拟的)
    • 您不能添加使用这些隐藏字段的其他方法(因为它们只能在构造函数内部访问)。对于子类化尤其令人讨厌。

3、使用这样的闭包有没有什么陷阱?

与使用原型的代码相比,当今大多数 Javascript 引擎在具有大量闭包的代码上的性能较差。这并不是造成差异的“真正”原因(因为 LISP 引擎一直以来都可以很好地处理闭包),但这是您必须忍受的。