0

I'm not being able to access private variables (or arguments) from a prototyped functions. Take this code:

function My_controller(url) {
  this.list = [1, 2, 3];
}

My_controller.prototype.order_list = function() {
  console.log("url: ", url);
  this.list.push(this.list.splice(0, 1)[0]);
  console.log(this.list);
}

var ct = new My_controller("http://");
ct.order_list();

It works if the function is defined inside de object:

function My_controller(url) {
  this.list = [1, 2, 3];

  this.order_list = function() {
    console.log("url: ", url);
    this.list.push(this.list.splice(0, 1)[0]);
    console.log(this.list);
  }
}

var ct = new My_controller("http://");
ct.order_list();

Meanwhile this is not the best choice for optimizing the code, is it? So I was wondering why is it not possible to get that variable and how to solve this?

I know one choice would be to save it inside this like:

function My_controller(url) {
  this.list = [1, 2, 3];
  this.url = url;
}

And then accessing this.url, instead of url.

Can you think on a better way to do this?

Vandervals
  • 5,774
  • 6
  • 48
  • 94
  • 2
    there is no way to do it. you can find it here. http://stackoverflow.com/questions/436120/javascript-accessing-private-member-variables-from-prototype-defined-functions – Sabyasachi Behera Oct 21 '16 at 11:13
  • 1
    Why don't you want to save `url` in `My_controller`? It makes sense to save it there as a property if you want to use it in your controller's methods. – Simon Hänisch Oct 21 '16 at 11:14
  • 1
    You are needlessly getting lost in the weeds here. If you are so concerned about information hiding that you feel the need for 'private' variables (which JS doesn't even *have*) then you don't use prototypes. How many of these things are you really creating anyway? Is this optimization (use of prototype methods to avoid allocation of function objects) saving you megabytes or just bytes? – Jared Smith Oct 21 '16 at 11:36
  • This is due to inheritance, really – Vandervals Oct 21 '16 at 11:40
  • @Reason its possible in ES6+, see my answer. – Jared Smith Oct 21 '16 at 11:56
  • I concur that bending over backwards for "private" properties is often misguided. Also see my rant here: http://stackoverflow.com/a/39869308/476 – deceze Oct 21 '16 at 12:11
  • @deceze yup, pretty much spot on. It makes for an interesting academic exercise, but is of limited practical use. – Jared Smith Oct 21 '16 at 14:42
  • @deceze apologize if this is poor protocol but saw on your profile you are familiar with operating systems and unicode, any chance you could help? http://stackoverflow.com/questions/40049861/having-trouble-converting-itunes-xml-playlist-in-node-js – Jared Smith Oct 21 '16 at 14:46

1 Answers1

1

This can be accomplished in ES 6, but I still think its largely unnecessary except at the module-level:

const Foo = (() => {
  let privateData = new WeakMap();

  return class {
    constructor () {
      privateData.set(this, {
        hidden: 'bar'
      });
    }

    // this is a prototype function, we could equivalently define
    // it outside the class definition as Foo.prototype.getPrivate
    // and it would be the same  
    getPrivate () {
      return privateData.get(this).hidden;
    }
  }
})();

console.log(new Foo().getPrivate()); // logs 'bar'

Importance of the WeakMap:

It allows arbitrary objects as keys, but those keys are weakly held: they don't count as references to prevent the key object from being garbage collected. Once the class is returned, code outside the IIFE its defined in can't access privateData. See this for more info.

Note that this won't necessarily break inheritance depending on how you're doing it:

class Baz extends Foo {
  constructor () {
    super(); // all good thanks to super
  }
}

// using non ES-6
function Foo () {
  privateData.set(this, {...});
}

Foo.prototype.getPrivate = function() {
  return privateData.get(this); 
});

function Bar() {
  Foo.call(this); // fine here
}

Bar.prototype = Foo.prototype;

The only limitation is that anything you want to be able to access the private data has to be defined inside the IIFE, you can't come along later and add a method to Foo's prototype. Although, if you're that concerened with performance, you shouldn't be altering prototypes willy-nilly anyway.

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • `privateData` is equal to `undefined` in prototype object (non ES6) – The Reason Oct 21 '16 at 11:56
  • @TheReason that's the entire point, `privateData` is *private* and held in the WeakMap in the closure. Otherwise, if it were attached to the prototype, it'd be public. The point of my answer is that the `getPrivate` method is *shared*, each instance doesn't need its own copy to access the closure. – Jared Smith Oct 21 '16 at 12:02