Technical differences in creating a private member property

Here I found a javascript module template that allows private members of an object. If I understand correctly, this can be written like this:

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

But there is a more efficient way:

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

Is there a difference between the two? Are there other options for implementing private members?

This is the result of the Chrome debugger:

enter image description here

+6
source share
2 answers

There are only a couple of real technical differences between the two (Bergi pointed out in the comments). Differences are unlikely unless you make thousands and thousands of them. There is a difference in style, which is subjective, but the technical differences are small. See the Breakdown below for details.

At a high level, both forms create a new object, both relying on closure for a private participant. The second method uses a little more memory, because the function cannot be garbage collected after use, whereas in the first case it can be. Similarly, an object created as a property of the prototype function is returned in the first version, but not the second. The second method may also be less clear to some than the first (see @djechlin questions below), not least because of the implicit return value, but it is a style point.

Repeat your second question:

Are there any other options for implementing private members?

For disposable objects like yours, the way you do it is probably best (any form, really). But right now, there is another way, and with ES.Next (the next version of ECMAScript) there is a new, truly private way. However, in both cases, this is more useful if you are making object classes rather than one-time.

More on this blog post (which also talks about materials shipped with ES.Next), but I will go into the most important details here.

A typical template for private members in feature classes 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 is that every object created with new Foo gets its own method1 function. We do not get reuse with method2 , where only one of them is shared by all objects created by new Foo . (This is not necessarily significant for modern engines that can reuse method1 code, although a new method1 object is created for each Foo object. But there are some development patterns that dynamically change the prototype, which obviously cannot act on method1 above. )

Here's an almost closed template that 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 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 just that, it gives us a new random string every time we call it.

The property we create is not private, but it is really obscure. It does not appear in for-in loops (because the property created is not enumerable), and its name changes every time the code is executed. Therefore, any code trying to use personal data must first determine the name of the property, which is a non-trivial exercise, since this code cannot get a list of non-enumerable property names of the object. Naturally, however, one look at the object in the debugger shows you the property and its value. The property is really obscure, but not entirely personal.

This template is noticeably improved thanks to material appearing in ES.Next, the next version of ECMAScript. We get private name objects that are useful in their own right and that new classes are also used.

Here, how private names relate 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 names never appear in for-in enumerations at all , and their names are not strings. The code cannot access a property whose name is a private name object without this specific name object. Since the privateKey variable privateKey completely closed by the Foo class, no other code can use this property. This is completely personal. Naturally, they will appear in the debuggers, but then nothing will be closed from the debuggers.


@djechlin asked me to break down exactly how each of your private member forms works, and that helps us understand the difference between them that Bergi highlighted:

Your first example:

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

(This list lists a few details that, in my opinion, are not relevant).

  • A property called myObject1 is created in the current variable binding object (which can be window if it is global) with the value undefined .

  • The expression of the function is evaluated:
    A) An A Function object to be created.
    B) An empty object is created and assigned to the new prototype property.
    C) The constructor property is created on this object and a reference to the function is given.
    D) A reference to the current variable binding object is stored in the function.

  • A function is called that creates (among other things) a variable binding object for the context of the call.

  • A property called privateMember is created and assigned to the variable binding object in step 3.

  • The value 42 is assigned to the privateMember property for VBO.

  • An empty object is created and a prototype from Object.prototype provided.

  • The internal expression of the function created by the Function object is expressed (with an empty object for its prototype property and a constructor property placed on this object, and with reference to the binding of the current variable to the object [one of step 3]).

  • This function is assigned to the empty object property from step 5 as publicMember .

  • The reference to the object from step 6 is returned from the main anonymous function.

  • This object reference is stored in the myObject1 property created in step 1.

  • The main anonymous function (from stage 2) has no outstanding links and therefore can be fixed by GC; and therefore, the object referenced by its prototype property can also be restored by GC.

Second example:

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

(Again, some irrelevant details have not been taken into account.)

  • A property called myObject2 is created in the current variable binding object (which can be window , if it is global) with the value undefined .

  • The expression of the function is evaluated:
    A) An A Function object to be created.
    B) An empty object is created and assigned to the new prototype property.
    C) The constructor property is created on this object and a reference to the function is given.
    D) A reference to the current variable binding object is stored in the function.

  • A new empty object is created and its prototype is assigned from the property of the anonymous prototype function.

  • The function is called with the object from step 3 passed as this , creating (among other things) a variable binding object for the context of the call.

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

  • The value 42 is assigned to the privateMember property for VBO.

  • The internal expression of the function created by the Function object is expressed (with an empty object for its prototype property and a constructor property placed on this object, and with reference to the binding of the current variable to the object [the one made in step 4]).

  • This function is assigned to the empty object property from step 5 as publicMember .

  • The function returns, and since it does not return an object, the result of the new expression is a reference to the object created in step 3.

  • This object reference is stored in the myObject2 property created in step 1.

  • The main anonymous function (from step 2) cannot be restored by GC, because myObject2 base prototype has a link to it in the constructor property (and therefore both the function and the object assigned to its prototype stored in memory).

You can free the function (but not the object assigned to its prototype property) by adding this line to it:

 delete this.constructor.prototype.constructor; 

This removes the function reference from the object assigned to its prototype property. This object remains as myObject2 base prototype, but it no longer refers to the function, so the function is entitled to GC.

But at this moment we are in obscurity. :-)

conclusions

Thus, they are almost the same, with a slight difference, that the main anonymous function and object in the prototype property are not eligible for GC. It will require thousands and thousands of them to make a difference in the real world.

(Note: some implementations may delay some of the stages of creating a function - for example, create an empty object for its prototype property and set its constructor - until prototype , since, of course, in the vast majority of cases it was never used, because the vast majority of functions are never used as constructor functions, so your first form may be a little small, a little more efficient, because it can skip these steps. This is a difference that is hardly known chenie, if you do not thousands of them.)


FWIW, the first one can also be written like this if you are worried about the number of lines, the number of brackets or you don't like the object literal, etc .:

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

As a style, I prefer an explicit return, but this is a matter of style.

+5
source

Is there a difference between the two?

One significant difference is that in the second case, using new, the returned object has an additional object in the [[Prototype]] chain, which is a public prototype of the constructor function expression. This object can be accessed in some browsers using __proto__ .

In addition, myObject1.constructor is a built-in function of Object, and myObject2.constructor is a function created by a function expression.

Are there any other options for implementing private members?

I assume you can use an object (otherwise useless) __proto__ object, but this is more of an obfuscation than privacy (and not available in some browsers):

 var foo = new function() { this.__proto__.privateMember = 42; this.publicMember = function() { return this.__proto__.privateMember; } }; alert(foo.publicMember()); 

Pretty ugly.

Conclusions have been used to emulate private members in javascript for quite some time (over 10 years at least), but there was no real pressure to include them in ECMA-262 (as far as I know), I believe this indicates that private members convenient but not critical for implementing the functionality required for the type of host environment scripts for which javascript is commonly used.

Edit

As Bergi says, instead of ESL Object.getPrototypeOf instead of the non-standard> "rel =" nofollow "> __proto__ . My bad one because it didn’t indicate what it was originally.

+2
source

Source: https://habr.com/ru/post/945010/


All Articles