Accessing private member variables from prototype-defined functions

前端 未结 25 861
孤城傲影
孤城傲影 2020-11-22 14:48

Is there any way to make “private” variables (those defined in the constructor), available to prototype-defined methods?

TestClass = function(){
    var priv         


        
相关标签:
25条回答
  • 2020-11-22 15:30

    There's a simpler way by leveraging the use of bind and call methods.

    By setting private variables to an object, you can leverage that object's scope.

    Example

    function TestClass (value) {
        // The private value(s)
        var _private = {
            value: value
        };
    
        // `bind` creates a copy of `getValue` when the object is instantiated
        this.getValue = TestClass.prototype.getValue.bind(_private);
    
        // Use `call` in another function if the prototype method will possibly change
        this.getValueDynamic = function() {
            return TestClass.prototype.getValue.call(_private);
        };
    };
    
    TestClass.prototype.getValue = function() {
        return this.value;
    };
    

    This method isn't without drawbacks. Since the scope context is effectively being overridden, you don't have access outside of the _private object. However, it isn't impossible though to still give access to the instance object's scope. You can pass in the object's context (this) as the second argument to bind or call to still have access to it's public values in the prototype function.

    Accessing public values

    function TestClass (value) {
        var _private = {
            value: value
        };
    
        this.message = "Hello, ";
    
        this.getMessage = TestClass.prototype.getMessage.bind(_private, this);
    
    }
    
    TestClass.prototype.getMessage = function(_public) {
    
        // Can still access passed in arguments
        // e.g. – test.getValues('foo'), 'foo' is the 2nd argument to the method
        console.log([].slice.call(arguments, 1));
        return _public.message + this.value;
    };
    
    var test = new TestClass("World");
    test.getMessage(1, 2, 3); // [1, 2, 3]         (console.log)
                              // => "Hello, World" (return value)
    
    test.message = "Greetings, ";
    test.getMessage(); // []                    (console.log)
                       // => "Greetings, World" (return value)
    
    0 讨论(0)
  • 2020-11-22 15:32

    You can also try to add method not directly on prototype, but on constructor function like this:

    var MyArray = function() {
        var array = [];
    
        this.add = MyArray.add.bind(null, array);
        this.getAll = MyArray.getAll.bind(null, array);
    }
    
    MyArray.add = function(array, item) {
        array.push(item);
    }
    MyArray.getAll = function(array) {
        return array;
    }
    
    var myArray1 = new MyArray();
    myArray1.add("some item 1");
    console.log(myArray1.getAll()); // ['some item 1']
    var myArray2 = new MyArray();
    myArray2.add("some item 2");
    console.log(myArray2.getAll()); // ['some item 2']
    console.log(myArray1.getAll()); // ['some item 2'] - FINE!
    
    0 讨论(0)
  • 2020-11-22 15:34

    Yes, it's possible. PPF design pattern just solves this.

    PPF stands for Private Prototype Functions. Basic PPF solves these issues:

    1. Prototype functions get access to private instance data.
    2. Prototype functions can be made private.

    For the first, just:

    1. Put all private instance variables you want to be accessible from prototype functions inside a separate data container, and
    2. Pass a reference to the data container to all prototype functions as a parameter.

    It's that simple. For example:

    // Helper class to store private data.
    function Data() {};
    
    // Object constructor
    function Point(x, y)
    {
      // container for private vars: all private vars go here
      // we want x, y be changeable via methods only
      var data = new Data;
      data.x = x;
      data.y = y;
    
      ...
    }
    
    // Prototype functions now have access to private instance data
    Point.prototype.getX = function(data)
    {
      return data.x;
    }
    
    Point.prototype.getY = function(data)
    {
      return data.y;
    }
    

    ...

    Read the full story here:

    PPF Design Pattern

    0 讨论(0)
  • 2020-11-22 15:37

    You can use a prototype assignment within the constructor definition.

    The variable will be visible to the prototype added method but all the instances of the functions will access the same SHARED variable.

    function A()
    {
      var sharedVar = 0;
      this.local = "";
    
      A.prototype.increment = function(lval)
      {    
        if (lval) this.local = lval;    
        alert((++sharedVar) + " while this.p is still " + this.local);
      }
    }
    
    var a = new A();
    var b = new A();    
    a.increment("I belong to a");
    b.increment("I belong to b");
    a.increment();
    b.increment();
    

    I hope this can be usefull.

    0 讨论(0)
  • 2020-11-22 15:39
    var getParams = function(_func) {
      res = _func.toString().split('function (')[1].split(')')[0].split(',')
      return res
    }
    
    function TestClass(){
    
      var private = {hidden: 'secret'}
      //clever magic accessor thing goes here
      if ( !(this instanceof arguments.callee) ) {
        for (var key in arguments) {
          if (typeof arguments[key] == 'function') {
            var keys = getParams(arguments[key])
            var params = []
            for (var i = 0; i <= keys.length; i++) {
              if (private[keys[i]] != undefined) {
                params.push(private[keys[i]])
              }
            }
            arguments[key].apply(null,params)
          }
        }
      }
    }
    
    
    TestClass.prototype.test = function(){
      var _hidden; //variable I want to get
      TestClass(function(hidden) {_hidden = hidden}) //invoke magic to get
    };
    
    new TestClass().test()
    

    How's this? Using an private accessor. Only allows you to get the variables though not to set them, depends on the use case.

    0 讨论(0)
  • 2020-11-22 15:39

    I have one solution, but I am not sure it is without flaws.

    For it to work, you have to use the following structure:

    1. Use 1 private object that contains all private variables.
    2. Use 1 instance function.
    3. Apply a closure to the constructor and all prototype functions.
    4. Any instance created is done outside the closure defined.

    Here is the code:

    var TestClass = 
    (function () {
        // difficult to be guessed.
        var hash = Math.round(Math.random() * Math.pow(10, 13) + + new Date());
        var TestClass = function () {
            var privateFields = {
                field1: 1,
                field2: 2
            };
            this.getPrivateFields = function (hashed) {
                if(hashed !== hash) {
                    throw "Cannot access private fields outside of object.";
                    // or return null;
                }
                return privateFields;
            };
        };
    
        TestClass.prototype.prototypeHello = function () {
            var privateFields = this.getPrivateFields(hash);
            privateFields.field1 = Math.round(Math.random() * 100);
            privateFields.field2 = Math.round(Math.random() * 100);
        };
    
        TestClass.prototype.logField1 = function () {
            var privateFields = this.getPrivateFields(hash);
            console.log(privateFields.field1);
        };
    
        TestClass.prototype.logField2 = function () {
            var privateFields = this.getPrivateFields(hash);
            console.log(privateFields.field2);
        };
    
        return TestClass;
    })();
    

    How this works is that it provides an instance function "this.getPrivateFields" to access the "privateFields" private variables object, but this function will only return the "privateFields" object inside the main closure defined (also prototype functions using "this.getPrivateFields" need to be defined inside this closure).

    A hash produced during runtime and difficult to be guessed is used as parameters to make sure that even if "getPrivateFields" is called outside the scope of closure will not return the "privateFields" object.

    The drawback is that we can not extend TestClass with more prototype functions outside the closure.

    Here is some test code:

    var t1 = new TestClass();
    console.log('Initial t1 field1 is: ');
    t1.logField1();
    console.log('Initial t1 field2 is: ');
    t1.logField2();
    t1.prototypeHello();
    console.log('t1 field1 is now: ');
    t1.logField1();
    console.log('t1 field2 is now: ');
    t1.logField2();
    var t2 = new TestClass();
    console.log('Initial t2 field1 is: ');
    t2.logField1();
    console.log('Initial t2 field2 is: ');
    t2.logField2();
    t2.prototypeHello();
    console.log('t2 field1 is now: ');
    t2.logField1();
    console.log('t2 field2 is now: ');
    t2.logField2();
    
    console.log('t1 field1 stays: ');
    t1.logField1();
    console.log('t1 field2 stays: ');
    t1.logField2();
    
    t1.getPrivateFields(11233);
    

    EDIT: Using this method, it is also possible to "define" private functions.

    TestClass.prototype.privateFunction = function (hashed) {
        if(hashed !== hash) {
            throw "Cannot access private function.";
        }
    };
    
    TestClass.prototype.prototypeHello = function () {
        this.privateFunction(hash);
    };
    
    0 讨论(0)
提交回复
热议问题