使用私有成员创建对象的技术差异

时间:2023-01-15 15:17:25

Here I found a pattern of javascript module enabling private members of object. If I got it right, it can be written this way:

在这里,我发现了一个javascript模块模式,可以启用对象的私有成员。如果我做对了,可以这样写:

var myObject1 = (function(){
  var privateMember = 42;
  return {
    publicMember: function() {
      return privateMember;
    }
  }
})();

But there is more effective way:

但是有更有效的方法:

var myObject2 = new function() {
  var privateMember = 42;
  this.publicMember = function() {
    return privateMember;
  }
}

Is there any difference between those two? Are there any other possibilities to implement private members?

这两者有什么区别吗?是否有其他可能实施私人成员?

This is the result from Chrome debugger:

这是Chrome调试器的结果:

使用私有成员创建对象的技术差异

2 个解决方案

#1


5  

There's only a couple of real technical differences between those two (pointed out by Bergi in the comments). The differences aren't likely to matter unless you're doing thousands and thousands of these. There's a style difference, which is subjective, but the technical differences are small. See the breakdown below for the gory details.

这两者之间只有几个真正的技术差异(Bergi在评论中指出)。除非你做成千上万的这些差异,否则这些差异可能不大。有一种风格差异,这是主观的,但技术差异很小。请参阅下面的细分,了解血淋淋的细节。

At a high level, both forms create a new object, they both rely on a closure for the private member. The second way uses a bit more memory, because the function can't be garbage-collected after use, whereas in the first one, it can be. Similarly, the object created to be the function's prototype property is reclaimable in the first version, but not the second. The second way may also be less clear to some than the first (witness the questions below from @djechlin), not least because of the implicit return value, but that's a style point.

在高级别,两个表单都创建一个新对象,它们都依赖于私有成员的闭包。第二种方式使用更多的内存,因为该函数在使用后不能被垃圾收集,而在第一种方式中,它可以是。类似地,创建为函数原型属性的对象可在第一个版本中回收,但不能在第二个版本中回收。对于某些人来说,第二种方式也可能不那么清楚(见下面来自@djechlin的问题),尤其是因为隐含的返回值,但这是一个风格点。

Re your second question:

你的第二个问题:

Are there any other possibilities to implement private members?

是否有其他可能实施私人成员?

For one-off objects like yours, the way you're doing it is probably best (either form, really). But right now, there's sort of another way, and as of ES.Next (the next version of ECMAScript), there's a new truly private way. In both cases, though, it's more useful for when you're doing classes of objects rather than one-offs.

对于像你这样的一次性物品,你做它的方式可能是最好的(无论是形式,真的)。但是现在,还有另一种方式,从ES.Next(ECMAScript的下一个版本)开始,有一种新的真正私有方式。但是,在这两种情况下,当你进行对象类而不是一次性对象时,它更有用。

More in this entry on my blog (which also talks about the stuff coming with ES.Next), but I'll go into the most salient details here.

更多关于我博客上的这篇文章(也谈到ES.Next附带的内容),但我会在这里详细介绍。

The typical pattern for private members in classes of objects looks something like this:

对象类中私有成员的典型模式如下所示:

function Foo(toBePrivate) {
    var privateMember = toBePrivate;

    this.method1 = function() {
        // ...do something with the truly private `privateMember`
    };
}
Foo.prototype.method2 = function() {
    // ...doesn't have access to the truly private `privateMember`
};

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() cannot

The problem with it is that each and every object created via new Foo gets its own method1 function. We don't get the reuse we get with method2, where there's only one of them shared by all objects created by new Foo. (This isn't necessarily a big deal with modern engines, which are able to reuse the code of method1 even though a new method1 object is created for each Foo object. But there are some development patterns that dynamically change the prototype, which are obviously unable to act on method1 above.)

它的问题是通过新Foo创建的每个对象都有自己的method1函数。我们没有得到我们用method2获得的重用,其中只有一个被新Foo创建的所有对象共享。 (这对于现代引擎来说不一定是大问题,它们能够重用方法1的代码,即使为每个Foo对象创建了一个新的method1对象。但是有一些开发模式可以动态地改变原型,这显然是无法对上面的方法1采取行动。)

Here's the nearly-private pattern we can do today:

这是我们今天可以做的近乎私密的模式:

var Foo = (function() {
    // Create a private name object for our private property
    var privateKey = makeRandomString();

    // Our constructor    
    function Foo(toBePrivate) {
        this[privateKey] = toBePrivate;
    }

    // Define the private property so it's non-enumerable
    Object.defineProperty(Foo.prototype, privateKey, {
        writable: true
    });

    // Methods shared by all Foo instances
    Foo.prototype.method1 = function() {
        // ...use this[privateKey] here...
    };
    Foo.prototype.method2 = function() {
        // ...use this[privateKey] here...
    };

    return Foo;
})();

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() can too!
// Both `method1` and `method2` are *reused* by all `Foo` objects

...where makeRandomString does exactly that, it gives us a new random string every time we call it.

... makeRandomString正是这样做的,它每次调用它时都会给我们一个新的随机字符串。

The property we create is not private, but it's really obscure. It doesn't show up in for-in loops (because the property we created is non-enumerable) and its name changes every time the code runs. So any code attempting to use the private data must first figure out the property name, which is a non-trivial exercise as that code can't get a list of the non-enumerable property names of the object. Naturally, though, one glance at the object in a debugger shows you the property and its value. The property is really obscure, but not quite truly private.

我们创建的属性不是私有的,但它真的很模糊。它不会出现在for-in循环中(因为我们创建的属性是不可枚举的),并且每次代码运行时它的名称都会更改。因此,任何试图使用私有数据的代码必须首先找出属性名称,这是一个非常重要的练习,因为该代码无法获取对象的非可枚举属性名称的列表。但是,当然,只需一瞥调试器中的对象就会显示属性及其值。该物业真的很模糊,但不是真正的私人。

This pattern is improved markedly by the stuff coming in ES.Next, the next version of ECMAScript. We'll get private name objects, which are useful on their own and are also used by the the new classses.

ES.Next(ECMAScript的下一个版本)中的内容显着改善了这种模式。我们将获得私有名称对象,这些对象本身很有用,并且也被新类使用。

Here's how private names apply to the above:

以下是私人名称适用于上述内容的方式:

// **ES.Next, not yet available in the wild**
import Name from "@name";
var Foo = (function() {
    // Create a private name object for our private property
    var privateKey = new Name();

    function Foo(toBePrivate) {
        this[privateKey] = toBePrivate;
    }
    Foo.prototype.method1 = function() {
        // ...use this[privateKey] here...
    };
    Foo.prototype.method2 = function() {
        // ...use this[privateKey] here...
    };

    return Foo;
})();

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() can too!
// Both `method1` and `method2` are *reused* by all `Foo` objects

Properties created with private name objects never show up in for-in enumerations at all, and their names are not strings. Code cannot access a property whose name is a private name object without having that specific name object. Since the privateKey variable above is completely private to the Foo class, no other code can use that property. It's completely private. Naturally, these will show up in debuggers, but then, nothing is private from debuggers.

使用私有名称对象创建的属性根本不会显示在for-in枚举中,并且它们的名称不是字符串。代码无法访问名称为私有名称对象的属性,而无需具有该特定名称对象。由于上面的privateKey变量对Foo类完全是私有的,因此没有其他代码可以使用该属性。它是完全私密的。当然,这些将显示在调试器中,但是,调试器没有任何内容。


@djechlin asked for a breakdown of exactly how each of your forms for a private member work, and it helps us understand the difference between them that Bergi highlighted:

@djechlin要求详细说明私人会员的每个表格如何运作,这有助于我们了解Bergi强调的两者之间的区别:

Your first example:

var myObject1 = (function(){
  var privateMember = 42;
  return {
    publicMember: function() {
      return privateMember;
    }
  }
})();

(This list leaves out a few details that I don't think are relevant.)

(这个列表遗漏了一些我认为不相关的细节。)

  1. A property called myObject1 is created on the current variable binding object (which may be window if this is global) with the value undefined.

    在当前变量绑定对象(如果这是全局的情况下可能是窗口)上创建名为myObject1的属性,其值为undefined。

  2. The function expression is evaluated:
    A) A Function object to be created.
    B) A blank object is created and assigned to the new function's prototype property.
    C) A constructor property is created on that object and given a reference to the function.
    D) A reference to the current variable binding object is stored on the function.

    评估函数表达式:A)要创建的Function对象。 B)创建一个空白对象并将其分配给新函数的prototype属性。 C)在该对象上创建构造函数属性,并给出对该函数的引用。 D)对当前变量绑定对象的引用存储在函数中。

  3. The function is called, creating (amongst other things) a variable binding object for the execution context of the call.

    调用该函数,为调用的执行上下文创建(除其他外)一个变量绑定对象。

  4. A property called privateMember is created and assigned to the variable binding object from Step 3.

    将创建一个名为privateMember的属性,并将其分配给步骤3中的变量绑定对象。

  5. The value 42 is assigned to the privateMember property of the VBO.

    值42分配给VBO的privateMember属性。

  6. A blank object is created and given a prototype from Object.prototype.

    创建一个空白对象,并从Object.prototype中获取原型。

  7. The inner function expression is evaluated, a Function object created (with a blank object for its prototype property and a constructor property put on that object, and with a reference to the current variable binding object [the one from Step 3]).

    计算内部函数表达式,创建一个Function对象(其原型属性为空白对象,该对象上放置构造函数属性,并引用当前变量绑定对象[步骤3中的一个])。

  8. That function is assigned to a property on the blank object from Step 5 as publicMember.

    该函数被分配给步骤5中空白对象的属性作为publicMember。

  9. A reference to the object from Step 6 is returned from the main anonymous function.

    从主匿名函数返回对步骤6中的对象的引用。

  10. That object reference is stored in the myObject1 property created in Step 1.

    该对象引用存储在步骤1中创建的myObject1属性中。

  11. The main anonymous function (from Step 2) has no outstanding references, and so can be reclaimed by GC; and so the object referenced by its prototype property can also be reclaimed by GC.

    主要的匿名函数(来自第2步)没有未完成的引用,因此可以通过GC回收;所以它的prototype属性引用的对象也可以由GC回收。

Your second example:

var myObject2 = new function() {
  var privateMember = 42;
  this.publicMember = function() {
    return privateMember;
  }
}

(Again, some irrelevant details left out.)

(再次,遗漏了一些不相关的细节。)

  1. A property called myObject2 is created on the current variable binding object (which may be window if this is global) with the value undefined.

    在当前变量绑定对象(如果是全局的,可以是窗口)上创建名为myObject2的属性,其值为undefined。

  2. The function expression is evaluated:
    A) A Function object to be created.
    B) A blank object is created and assigned to the new function's prototype property.
    C) A constructor property is created on that object and given a reference to the function.
    D) A reference to the current variable binding object is stored on the function.

    评估函数表达式:A)要创建的Function对象。 B)创建一个空白对象并将其分配给新函数的prototype属性。 C)在该对象上创建构造函数属性,并给出对该函数的引用。 D)对当前变量绑定对象的引用存储在函数中。

  3. A new, blank object is created and assigned its prototype from the anonymous function's prototype property.

    创建一个新的空白对象,并从匿名函数的prototype属性中分配其原型。

  4. The function is called with the object from Step 3 passed in as this, creating (amongst other things) a variable binding object for the execution context of the call.

    调用该函数时,将步骤3中的对象作为此传入,为调用的执行上下文创建(除其他外)一个变量绑定对象。

  5. A property called privateMember is created and assigned to the variable binding object from Step 4.

    将创建一个名为privateMember的属性,并将其分配给步骤4中的变量绑定对象。

  6. The value 42 is assigned to the privateMember property of the VBO.

    值42分配给VBO的privateMember属性。

  7. The inner function expression is evaluated, a Function object created (with a blank object for its prototype property and a constructor property put on that object, and with a reference to the current variable binding object [the one from Step 4]).

    评估内部函数表达式,创建一个Function对象(其原型属性为空白对象,该对象上放置构造函数属性,并引用当前变量绑定对象[步骤4中的一个])。

  8. That function is assigned to a property on the blank object from Step 5 as publicMember.

    该函数被分配给步骤5中空白对象的属性作为publicMember。

  9. The function returns, and because it doesn't return an object, the result of the new expression is a reference to the object created in Step 3.

    函数返回,并且因为它不返回对象,所以新表达式的结果是对在步骤3中创建的对象的引用。

  10. That object reference is stored in the myObject2 property created in Step 1.

    该对象引用存储在步骤1中创建的myObject2属性中。

  11. The main anonymous function (from Step 2) cannot be reclaimed by GC, because myObject2's underlying prototype has a reference to it on the constructor property (and so both the function and the object assigned to its prototype property remain in memory).

    GC无法回收主要的匿名函数(来自第2步),因为myObject2的底层原型在构造函数属性上有对它的引用(因此,分配给其prototype属性的函数和对象都保留在内存中)。

You could release the function (but not the object assigned to its prototype property) by adding this line inside it:

您可以通过在其中添加此行来释放该函数(但不是分配给其prototype属性的对象):

delete this.constructor.prototype.constructor;

That removes the reference to the function from the object assigned to its prototype property. That object remains as myObject2's underlying prototype, but it no longer refers back to the function, so the function is eligible for GC.

这将从分配给其prototype属性的对象中删除对该函数的引用。该对象仍然是myObject2的底层原型,但它不再引用该函数,因此该函数符合GC的条件。

But at that point, we're well into obscurity land. :-)

但就在那时,我们已经进入了默默无闻的土地。 :-)

Conclusions

So they're very nearly the same, with the small difference that the main anonymous function and the object on its prototype property aren't eligible for GC. It would take thousands and thousands of these for that to matter in the real world.

因此它们几乎相同,主要的匿名函数和原型属性上的对象不符合GC的条件差别很小。在现实世界中,需要成千上万的这些才能实现。

(Side note: Some implementations may well defer some of the steps of creating a function — such as creating a blank object for its prototype property and setting its constructor — until/unless that prototype property is used, since of course in the vast majority of cases, it's never used, because the vast majority of functions are never used as constructor functions. So your first form may be a little tiny teeny bit more efficient because it can skip those steps. It's a difference that is unlikely to matter unless you're doing thousands of these.)

(旁注:某些实现可能会推迟创建函数的一些步骤 - 例如为其原型属性创建空白对象并设置其构造函数 - 直到/除非使用该原型属性,因为当然绝大多数例如,它从未被使用过,因为绝大多数函数从未被用作构造函数。所以你的第一个形式可能会有点小,因为它可以跳过这些步骤。这是一个不太重要的区别,除非你'重做千万次。)


FWIW, the first can also be written like this, if your concern is the number of lines, number of parentheses, or not liking the object literal, etc.:

FWIW,第一个也可以这样写,如果你关注的是行数,括号数,或不喜欢对象文字等:

var myObject1 = function(){
  var obj = {};
  var privateMember = 42;
  obj.publicMember = function() {
    return privateMember;
  };
  return obj;
}();

As a matter of style, I prefer the explicit return, but that's a matter of style.

作为一种风格问题,我更喜欢明确的回归,但那是风格问题。

#2


2  

Is there any difference between those two?

这两者有什么区别吗?

One major difference is that in the second case using new, the returned object has an additional object on it's [[Prototype]] chain that is the public prototype of the constructor function expression. That object can be accessed in some browsers using __proto__.

一个主要的区别是在第二种情况下使用new,返回的对象在它的[[Prototype]]链上有一个额外的对象,它是构造函数表达式的公共原型。可以使用__proto__在某些浏览器中访问该对象。

Also, myObject1.constructor is the built–in Object function, while myObject2.constructor is the function created by the function expression.

另外,myObject1.constructor是内置的Object函数,而myObject2.constructor是函数表达式创建的函数。

Are there any other possibilities to implement private members?

是否有其他可能实施私人成员?

I suppose you could use the object's (otherwise useless) __proto__ object, but that's more obfuscation than privacy (and not available in some browsers):

我想你可以使用对象的(无用的)__ proto__对象,但这比隐私更加混淆(在某些浏览器中不可用):

var foo = new function() {

  this.__proto__.privateMember = 42;

  this.publicMember = function() {
    return this.__proto__.privateMember;
  }
};

alert(foo.publicMember());

Pretty ugly really.

真的很难看。

Closures have been used to emulate private members in javascript for quite some time (more than 10 years at least) but there hasn't been any real pressure to include them in ECMA-262 (as far as I'm aware). I suppose that indicates that private members are handy, but not critical to implement functionality required for the type of host environment scripting that javascript is typically used for.

闭包已经被用来模拟javascript中的私人成员很长一段时间(至少超过10年)但是没有任何真正的压力将它们包含在ECMA-262中(据我所知)。我想这表明私有成员很方便,但对于实现javascript通常用于的主机环境脚本类型所需的功能并不重要。

Edit

As Bergi says, the ES5 Object.getPrototypeOf method should be used in stead of the (non standard) __proto__ property. My bad for no pointing that out initially.

正如Bergi所说,应该使用ES5 Object.getPrototypeOf方法代替(非标准)__ proto__属性。我最初没有指出这是不好的。

#1


5  

There's only a couple of real technical differences between those two (pointed out by Bergi in the comments). The differences aren't likely to matter unless you're doing thousands and thousands of these. There's a style difference, which is subjective, but the technical differences are small. See the breakdown below for the gory details.

这两者之间只有几个真正的技术差异(Bergi在评论中指出)。除非你做成千上万的这些差异,否则这些差异可能不大。有一种风格差异,这是主观的,但技术差异很小。请参阅下面的细分,了解血淋淋的细节。

At a high level, both forms create a new object, they both rely on a closure for the private member. The second way uses a bit more memory, because the function can't be garbage-collected after use, whereas in the first one, it can be. Similarly, the object created to be the function's prototype property is reclaimable in the first version, but not the second. The second way may also be less clear to some than the first (witness the questions below from @djechlin), not least because of the implicit return value, but that's a style point.

在高级别,两个表单都创建一个新对象,它们都依赖于私有成员的闭包。第二种方式使用更多的内存,因为该函数在使用后不能被垃圾收集,而在第一种方式中,它可以是。类似地,创建为函数原型属性的对象可在第一个版本中回收,但不能在第二个版本中回收。对于某些人来说,第二种方式也可能不那么清楚(见下面来自@djechlin的问题),尤其是因为隐含的返回值,但这是一个风格点。

Re your second question:

你的第二个问题:

Are there any other possibilities to implement private members?

是否有其他可能实施私人成员?

For one-off objects like yours, the way you're doing it is probably best (either form, really). But right now, there's sort of another way, and as of ES.Next (the next version of ECMAScript), there's a new truly private way. In both cases, though, it's more useful for when you're doing classes of objects rather than one-offs.

对于像你这样的一次性物品,你做它的方式可能是最好的(无论是形式,真的)。但是现在,还有另一种方式,从ES.Next(ECMAScript的下一个版本)开始,有一种新的真正私有方式。但是,在这两种情况下,当你进行对象类而不是一次性对象时,它更有用。

More in this entry on my blog (which also talks about the stuff coming with ES.Next), but I'll go into the most salient details here.

更多关于我博客上的这篇文章(也谈到ES.Next附带的内容),但我会在这里详细介绍。

The typical pattern for private members in classes of objects looks something like this:

对象类中私有成员的典型模式如下所示:

function Foo(toBePrivate) {
    var privateMember = toBePrivate;

    this.method1 = function() {
        // ...do something with the truly private `privateMember`
    };
}
Foo.prototype.method2 = function() {
    // ...doesn't have access to the truly private `privateMember`
};

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() cannot

The problem with it is that each and every object created via new Foo gets its own method1 function. We don't get the reuse we get with method2, where there's only one of them shared by all objects created by new Foo. (This isn't necessarily a big deal with modern engines, which are able to reuse the code of method1 even though a new method1 object is created for each Foo object. But there are some development patterns that dynamically change the prototype, which are obviously unable to act on method1 above.)

它的问题是通过新Foo创建的每个对象都有自己的method1函数。我们没有得到我们用method2获得的重用,其中只有一个被新Foo创建的所有对象共享。 (这对于现代引擎来说不一定是大问题,它们能够重用方法1的代码,即使为每个Foo对象创建了一个新的method1对象。但是有一些开发模式可以动态地改变原型,这显然是无法对上面的方法1采取行动。)

Here's the nearly-private pattern we can do today:

这是我们今天可以做的近乎私密的模式:

var Foo = (function() {
    // Create a private name object for our private property
    var privateKey = makeRandomString();

    // Our constructor    
    function Foo(toBePrivate) {
        this[privateKey] = toBePrivate;
    }

    // Define the private property so it's non-enumerable
    Object.defineProperty(Foo.prototype, privateKey, {
        writable: true
    });

    // Methods shared by all Foo instances
    Foo.prototype.method1 = function() {
        // ...use this[privateKey] here...
    };
    Foo.prototype.method2 = function() {
        // ...use this[privateKey] here...
    };

    return Foo;
})();

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() can too!
// Both `method1` and `method2` are *reused* by all `Foo` objects

...where makeRandomString does exactly that, it gives us a new random string every time we call it.

... makeRandomString正是这样做的,它每次调用它时都会给我们一个新的随机字符串。

The property we create is not private, but it's really obscure. It doesn't show up in for-in loops (because the property we created is non-enumerable) and its name changes every time the code runs. So any code attempting to use the private data must first figure out the property name, which is a non-trivial exercise as that code can't get a list of the non-enumerable property names of the object. Naturally, though, one glance at the object in a debugger shows you the property and its value. The property is really obscure, but not quite truly private.

我们创建的属性不是私有的,但它真的很模糊。它不会出现在for-in循环中(因为我们创建的属性是不可枚举的),并且每次代码运行时它的名称都会更改。因此,任何试图使用私有数据的代码必须首先找出属性名称,这是一个非常重要的练习,因为该代码无法获取对象的非可枚举属性名称的列表。但是,当然,只需一瞥调试器中的对象就会显示属性及其值。该物业真的很模糊,但不是真正的私人。

This pattern is improved markedly by the stuff coming in ES.Next, the next version of ECMAScript. We'll get private name objects, which are useful on their own and are also used by the the new classses.

ES.Next(ECMAScript的下一个版本)中的内容显着改善了这种模式。我们将获得私有名称对象,这些对象本身很有用,并且也被新类使用。

Here's how private names apply to the above:

以下是私人名称适用于上述内容的方式:

// **ES.Next, not yet available in the wild**
import Name from "@name";
var Foo = (function() {
    // Create a private name object for our private property
    var privateKey = new Name();

    function Foo(toBePrivate) {
        this[privateKey] = toBePrivate;
    }
    Foo.prototype.method1 = function() {
        // ...use this[privateKey] here...
    };
    Foo.prototype.method2 = function() {
        // ...use this[privateKey] here...
    };

    return Foo;
})();

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() can too!
// Both `method1` and `method2` are *reused* by all `Foo` objects

Properties created with private name objects never show up in for-in enumerations at all, and their names are not strings. Code cannot access a property whose name is a private name object without having that specific name object. Since the privateKey variable above is completely private to the Foo class, no other code can use that property. It's completely private. Naturally, these will show up in debuggers, but then, nothing is private from debuggers.

使用私有名称对象创建的属性根本不会显示在for-in枚举中,并且它们的名称不是字符串。代码无法访问名称为私有名称对象的属性,而无需具有该特定名称对象。由于上面的privateKey变量对Foo类完全是私有的,因此没有其他代码可以使用该属性。它是完全私密的。当然,这些将显示在调试器中,但是,调试器没有任何内容。


@djechlin asked for a breakdown of exactly how each of your forms for a private member work, and it helps us understand the difference between them that Bergi highlighted:

@djechlin要求详细说明私人会员的每个表格如何运作,这有助于我们了解Bergi强调的两者之间的区别:

Your first example:

var myObject1 = (function(){
  var privateMember = 42;
  return {
    publicMember: function() {
      return privateMember;
    }
  }
})();

(This list leaves out a few details that I don't think are relevant.)

(这个列表遗漏了一些我认为不相关的细节。)

  1. A property called myObject1 is created on the current variable binding object (which may be window if this is global) with the value undefined.

    在当前变量绑定对象(如果这是全局的情况下可能是窗口)上创建名为myObject1的属性,其值为undefined。

  2. The function expression is evaluated:
    A) A Function object to be created.
    B) A blank object is created and assigned to the new function's prototype property.
    C) A constructor property is created on that object and given a reference to the function.
    D) A reference to the current variable binding object is stored on the function.

    评估函数表达式:A)要创建的Function对象。 B)创建一个空白对象并将其分配给新函数的prototype属性。 C)在该对象上创建构造函数属性,并给出对该函数的引用。 D)对当前变量绑定对象的引用存储在函数中。

  3. The function is called, creating (amongst other things) a variable binding object for the execution context of the call.

    调用该函数,为调用的执行上下文创建(除其他外)一个变量绑定对象。

  4. A property called privateMember is created and assigned to the variable binding object from Step 3.

    将创建一个名为privateMember的属性,并将其分配给步骤3中的变量绑定对象。

  5. The value 42 is assigned to the privateMember property of the VBO.

    值42分配给VBO的privateMember属性。

  6. A blank object is created and given a prototype from Object.prototype.

    创建一个空白对象,并从Object.prototype中获取原型。

  7. The inner function expression is evaluated, a Function object created (with a blank object for its prototype property and a constructor property put on that object, and with a reference to the current variable binding object [the one from Step 3]).

    计算内部函数表达式,创建一个Function对象(其原型属性为空白对象,该对象上放置构造函数属性,并引用当前变量绑定对象[步骤3中的一个])。

  8. That function is assigned to a property on the blank object from Step 5 as publicMember.

    该函数被分配给步骤5中空白对象的属性作为publicMember。

  9. A reference to the object from Step 6 is returned from the main anonymous function.

    从主匿名函数返回对步骤6中的对象的引用。

  10. That object reference is stored in the myObject1 property created in Step 1.

    该对象引用存储在步骤1中创建的myObject1属性中。

  11. The main anonymous function (from Step 2) has no outstanding references, and so can be reclaimed by GC; and so the object referenced by its prototype property can also be reclaimed by GC.

    主要的匿名函数(来自第2步)没有未完成的引用,因此可以通过GC回收;所以它的prototype属性引用的对象也可以由GC回收。

Your second example:

var myObject2 = new function() {
  var privateMember = 42;
  this.publicMember = function() {
    return privateMember;
  }
}

(Again, some irrelevant details left out.)

(再次,遗漏了一些不相关的细节。)

  1. A property called myObject2 is created on the current variable binding object (which may be window if this is global) with the value undefined.

    在当前变量绑定对象(如果是全局的,可以是窗口)上创建名为myObject2的属性,其值为undefined。

  2. The function expression is evaluated:
    A) A Function object to be created.
    B) A blank object is created and assigned to the new function's prototype property.
    C) A constructor property is created on that object and given a reference to the function.
    D) A reference to the current variable binding object is stored on the function.

    评估函数表达式:A)要创建的Function对象。 B)创建一个空白对象并将其分配给新函数的prototype属性。 C)在该对象上创建构造函数属性,并给出对该函数的引用。 D)对当前变量绑定对象的引用存储在函数中。

  3. A new, blank object is created and assigned its prototype from the anonymous function's prototype property.

    创建一个新的空白对象,并从匿名函数的prototype属性中分配其原型。

  4. The function is called with the object from Step 3 passed in as this, creating (amongst other things) a variable binding object for the execution context of the call.

    调用该函数时,将步骤3中的对象作为此传入,为调用的执行上下文创建(除其他外)一个变量绑定对象。

  5. A property called privateMember is created and assigned to the variable binding object from Step 4.

    将创建一个名为privateMember的属性,并将其分配给步骤4中的变量绑定对象。

  6. The value 42 is assigned to the privateMember property of the VBO.

    值42分配给VBO的privateMember属性。

  7. The inner function expression is evaluated, a Function object created (with a blank object for its prototype property and a constructor property put on that object, and with a reference to the current variable binding object [the one from Step 4]).

    评估内部函数表达式,创建一个Function对象(其原型属性为空白对象,该对象上放置构造函数属性,并引用当前变量绑定对象[步骤4中的一个])。

  8. That function is assigned to a property on the blank object from Step 5 as publicMember.

    该函数被分配给步骤5中空白对象的属性作为publicMember。

  9. The function returns, and because it doesn't return an object, the result of the new expression is a reference to the object created in Step 3.

    函数返回,并且因为它不返回对象,所以新表达式的结果是对在步骤3中创建的对象的引用。

  10. That object reference is stored in the myObject2 property created in Step 1.

    该对象引用存储在步骤1中创建的myObject2属性中。

  11. The main anonymous function (from Step 2) cannot be reclaimed by GC, because myObject2's underlying prototype has a reference to it on the constructor property (and so both the function and the object assigned to its prototype property remain in memory).

    GC无法回收主要的匿名函数(来自第2步),因为myObject2的底层原型在构造函数属性上有对它的引用(因此,分配给其prototype属性的函数和对象都保留在内存中)。

You could release the function (but not the object assigned to its prototype property) by adding this line inside it:

您可以通过在其中添加此行来释放该函数(但不是分配给其prototype属性的对象):

delete this.constructor.prototype.constructor;

That removes the reference to the function from the object assigned to its prototype property. That object remains as myObject2's underlying prototype, but it no longer refers back to the function, so the function is eligible for GC.

这将从分配给其prototype属性的对象中删除对该函数的引用。该对象仍然是myObject2的底层原型,但它不再引用该函数,因此该函数符合GC的条件。

But at that point, we're well into obscurity land. :-)

但就在那时,我们已经进入了默默无闻的土地。 :-)

Conclusions

So they're very nearly the same, with the small difference that the main anonymous function and the object on its prototype property aren't eligible for GC. It would take thousands and thousands of these for that to matter in the real world.

因此它们几乎相同,主要的匿名函数和原型属性上的对象不符合GC的条件差别很小。在现实世界中,需要成千上万的这些才能实现。

(Side note: Some implementations may well defer some of the steps of creating a function — such as creating a blank object for its prototype property and setting its constructor — until/unless that prototype property is used, since of course in the vast majority of cases, it's never used, because the vast majority of functions are never used as constructor functions. So your first form may be a little tiny teeny bit more efficient because it can skip those steps. It's a difference that is unlikely to matter unless you're doing thousands of these.)

(旁注:某些实现可能会推迟创建函数的一些步骤 - 例如为其原型属性创建空白对象并设置其构造函数 - 直到/除非使用该原型属性,因为当然绝大多数例如,它从未被使用过,因为绝大多数函数从未被用作构造函数。所以你的第一个形式可能会有点小,因为它可以跳过这些步骤。这是一个不太重要的区别,除非你'重做千万次。)


FWIW, the first can also be written like this, if your concern is the number of lines, number of parentheses, or not liking the object literal, etc.:

FWIW,第一个也可以这样写,如果你关注的是行数,括号数,或不喜欢对象文字等:

var myObject1 = function(){
  var obj = {};
  var privateMember = 42;
  obj.publicMember = function() {
    return privateMember;
  };
  return obj;
}();

As a matter of style, I prefer the explicit return, but that's a matter of style.

作为一种风格问题,我更喜欢明确的回归,但那是风格问题。

#2


2  

Is there any difference between those two?

这两者有什么区别吗?

One major difference is that in the second case using new, the returned object has an additional object on it's [[Prototype]] chain that is the public prototype of the constructor function expression. That object can be accessed in some browsers using __proto__.

一个主要的区别是在第二种情况下使用new,返回的对象在它的[[Prototype]]链上有一个额外的对象,它是构造函数表达式的公共原型。可以使用__proto__在某些浏览器中访问该对象。

Also, myObject1.constructor is the built–in Object function, while myObject2.constructor is the function created by the function expression.

另外,myObject1.constructor是内置的Object函数,而myObject2.constructor是函数表达式创建的函数。

Are there any other possibilities to implement private members?

是否有其他可能实施私人成员?

I suppose you could use the object's (otherwise useless) __proto__ object, but that's more obfuscation than privacy (and not available in some browsers):

我想你可以使用对象的(无用的)__ proto__对象,但这比隐私更加混淆(在某些浏览器中不可用):

var foo = new function() {

  this.__proto__.privateMember = 42;

  this.publicMember = function() {
    return this.__proto__.privateMember;
  }
};

alert(foo.publicMember());

Pretty ugly really.

真的很难看。

Closures have been used to emulate private members in javascript for quite some time (more than 10 years at least) but there hasn't been any real pressure to include them in ECMA-262 (as far as I'm aware). I suppose that indicates that private members are handy, but not critical to implement functionality required for the type of host environment scripting that javascript is typically used for.

闭包已经被用来模拟javascript中的私人成员很长一段时间(至少超过10年)但是没有任何真正的压力将它们包含在ECMA-262中(据我所知)。我想这表明私有成员很方便,但对于实现javascript通常用于的主机环境脚本类型所需的功能并不重要。

Edit

As Bergi says, the ES5 Object.getPrototypeOf method should be used in stead of the (non standard) __proto__ property. My bad for no pointing that out initially.

正如Bergi所说,应该使用ES5 Object.getPrototypeOf方法代替(非标准)__ proto__属性。我最初没有指出这是不好的。