⬆️ ⬇️

Easy way to make “cheap” Private in JS

In general, a way to create class members that will not be accessible from the outside, exists in JavaScript and is called a closure. But with this approach, there is one serious drawback - many instances of the same function are created, which is not good for resources.



I pondered a little how to make everything simple and efficient at the same time. First, I found a solution that saves resources rather than eliminates “overspending”. Here it is:



var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  1. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  2. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  3. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  4. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  5. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  6. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  7. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  8. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  9. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  10. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  11. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  12. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  13. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
  14. var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .
var Class_privateMethod = function (_this_privateVar) { alert(_this_privateVar); return null ; } var Class_publicMethod = function (_this_privateVar) { return Class_privateMethod.call( this , _this_privateVar); } var Class = function () { var privateVar = 'Hello, world!' ; this .publicMethod = function () { return Class_publicMethod.call( this , privateVar); } } * This source code was highlighted with Source Code Highlighter .


')

Here we save resources due to the fact that the function created each time in the constructor consists of just one line - calling the real method body. But the functions continue to multiply; they have become smaller.



How else can private members be implemented? You can put them in the element of a global array created for each object. But the object must somehow be identified. But you cannot make such an identifier in the form of an object field — the field can be changed (and we remind you, we are trying to close the methods — otherwise you can simply define for them a prefix like l_privateMethod and calm down). The object does not have any unique characteristics, except for the address in memory and / or numbers in the interpreter list, and we cannot access these characteristics. The object can not be an index in the array ... or stop! Maybe if it's a Number object! Thus, we come to a very simple but effective solution:



  1. var ObjectServer = function ()
  2. {
  3. var objects = new Array (), CURRENT_ID = 1;
  4. var result = function (_id)
  5. {
  6. if (! (_ id instanceof Number))
  7. {
  8. return ;
  9. }
  10. if (objects [_id] .external! = _id)
  11. {
  12. return ;
  13. }
  14. return objects [_id];
  15. }
  16. result.create = function (_class)
  17. {
  18. var object = new _class ();
  19. object .external = new Number (CURRENT_ID);
  20. for ( var key in _class.external)
  21. {
  22. object .external [key] = _class.external [key];
  23. }
  24. objects [CURRENT_ID ++] = object ;
  25. return object .external;
  26. }
  27. result.destroy = function (_id)
  28. {
  29. if (! (_ id instanceof Number))
  30. {
  31. return ;
  32. }
  33. if (objects [_id] .external! = _id)
  34. {
  35. return ;
  36. }
  37. delete objects [_id];
  38. }
  39. return result;
  40. }
  41. var objs = ObjectServer ();
* This source code was highlighted with Source Code Highlighter .




Now real objects are stored in the objects array of the object server, and only the public (external) part is given to the outside. To create an object based on the class constructor function, you need to write objs.create, to delete - objs.destroy (by the way, there is a great opportunity to include destructors here, if you modify the solution a bit - the link to the object will be stored in the array until we obviously not delete). To access the closed part of the object, you need to use the objs function itself:



objs(myObject).privateMember;



External methods are declared as members of the external subobject as well as closed ones as members of the prototype subobject. The only restriction is that the constructor should not have parameters; you cannot write new constructor.call (...).



Here is how you can use this mechanism to implement a module:



  1. var global = this ;
  2. ( function () {
  3. var objs = ObjectServer (); // The ObjectServer function is defined somewhere outside.
  4. var Class = function ()
  5. {
  6. this .privateVar = 'I \' ma private variable. ' ;
  7. }
  8. Class.prototype.privateMethod = function ()
  9. {
  10. alert ( this .privateVar);
  11. alert ( this .external.publicPrimitiveVar);
  12. this .external.publicCompoundVar.push ( this .privateVarDefinedInConstructor);
  13. alert ( this .external.publicCompoundVar);
  14. }
  15. Class.external = new Object ();
  16. Class.external.publicPrimitiveVar = 10;
  17. Class.external.publicMethod = function ()
  18. {
  19. objs ( this ). privateMethod ();
  20. objs ( this ). privateVar = 'Changed!' ;
  21. objs ( this ). privateMethod ();
  22. }
  23. global.Class = function (_arg)
  24. {
  25. var result = objs.create (Class);
  26. result.publicCompoundVar = new Array ( 'a' , 'b' , 'c' );
  27. objs (result). privateVarDefinedInConstructor = _arg;
  28. return result;
  29. }
  30. }) ();
  31. var object1 = Class ( 'd' );
  32. var object2 = Class ( 'e' );
  33. object2.publicPrimitiveVar = 50;
  34. object1.publicMethod ();
  35. object2.publicMethod ();
  36. alert (object1.privateMethod);
  37. object2 = new Number (object2 * 1);
  38. object2.publicMethod ();
* This source code was highlighted with Source Code Highlighter .




Calling from the outside global.Class (or simply Class is the name of the global space) we get the public interface of the class, while the closed interface will remain only in the annals of the object server - see for yourself alert (object1.privateMethod); will display undefined. The main thing to remember is to define the server of objects inside the module so that it is not accessible from the outside. The server creation function can be defined once somewhere outside the set of utilitarian functions. And you can not be afraid that someone will override the object number - the server will refuse such a request (see the last two drains).



I hope that the method will be useful to you, since it is as simple as a penny and allows you not to waste memory and time in vain (well, except for function pointers). If someone modifies it, so that you can do more and protected members for the roll between different modules, then it will be generally wonderful. Well, I bow to this! Thank you for your attention!



UPD 02.12.09



With the help of such a modification, you can make constructor functions with parameters (ie, objs.create (Class, 10, 20)):



  1. ObjectServer.create = function (_class)
  2. {
  3. var constructor = new Function ();
  4. constructor.prototype = _class.prototype;
  5. var object = new constructor ();
  6. object .constructor = _class;
  7. object .external = new Number ( this .CURRENT_ID);
  8. for ( var key in _class.external)
  9. {
  10. object .external [key] = _class.external [key];
  11. }
  12. this .objects [ this .CURRENT_ID ++] = object ;
  13. _class.apply ( object , Array.prototype.slice.call (arguments, 1));
  14. return object .external;
  15. }
* This source code was highlighted with Source Code Highlighter .




UPD 02.12.09



Thanks dsCode for the advice, I corrected the lack of a library in terms of copying external methods. Now the gain in comparison with the standard time solution is about 6000% on the same test (see comments): ^) (0.864 versus 0.013, i.e. ~ 60 times). The library can be found here:

JOS library

example

PS Now you need to remember two more keywords: external and id. They should not be used as object field identifiers.

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



All Articles