6

So, first question here - please be gentle.

I'm working on a fairly JavaScript heavy project with a few other developers from various non-web programming backgrounds and we've decided to try and use public and private methods and properties in our JavaScript psuedo-classes purely as a good coding practice (ie. we know theres no actual advantage or security in it)

We've toyed with a few different ways of doing public & private (ie. using locally scoped variables and functions with privileged methods for public consumption) and we've currently settled on having our JavaScript class constructors actually return an object that represents only their public interface, effectively hiding everything else.

Here's an example:

function MyObject()
 {
  var _this = this;

  this._privateProperty = 'somevalue';

  this._privateMethod = function()
  {
   // Do Something
  }

  this.public = 
  {
   publicProperty : _this._privateProperty,
   publicMethod : function(){ return _this.privateMethod() }
  }

  return this.public;
 }

Which when instantiated and logged in Chrome:

 var obj = new MyObject();
 console.log(obj);

Outputs:

> Object
    > publicMethod: function (){ return _this.privateMethod() }
    > publicProperty: "somevalue"
    >__proto__: Object

Now to my question: Because were returning the public interface from the constructor as new object, when you console.log you'll notice that it identifies itself as > Object - whereas if we don't return that public interface it is identified as > MyObject.

Ideally, we'd like to have the latter displayed for debugging purposes and I know how to access the "MyObject" name of the contstructor with _this.constructor.name, but have no idea how to set it so its recognized that way.

Does anyone know how to manually set this?

Note:
I know this is in some ways a bastardization of JavaScript convention and trying to fit a square peg in a round hole, but we found it to be a very obvious and readable way to accomplish what we were trying to do. I'm open to suggestions on how to accomplish this with a different design, but I'm ultimately looking for an answer that fits our current design.

drewdah
  • 91
  • 2
  • 7
  • I was a little surprised to see that you received the return value. Must be because it is a property of the constructed object. Didn't know that. Anyway, you're returning a plain Object, so that's how it'll likely be reported by the developer tools. If you're looking for an answer that fits your design, I guess I'd have to ask what sort of deviations from your design will you allow? – user113716 Jan 27 '11 at 21:40
  • ...regarding the return value, I thought it was ignored when the constructor is called with `new`. Looks like it will return any type of Object. – user113716 Jan 27 '11 at 21:49
  • Well, we did think having all of the public interfaces in a single named object was novel - that way we can actually make internal references to a classes public methods and properties. I was hoping for a "oh yeah, just return object.name = _this.constructor.name" in public - but I'm willing to entertain design changes that are still obvious and readable :) – drewdah Jan 28 '11 at 00:42

3 Answers3

3

You should be able to set the 'toString' function of the obj. E.g.

obj.constructor.prototype.toString = function() { return "MyObject"; }

This will make the console.log display 'MyObject' instead of Object.

cellcortex
  • 3,166
  • 1
  • 24
  • 34
  • We are actually setting the toString in some of our public interfaces, but it unfortunately does not change Object to MyObject in console.log. However, it does display the returned value in toString if you "alert" the instantiated object. – drewdah Jan 27 '11 at 22:25
3

Despite the valiant efforts of my team members and the very helpful users of this site who offered solutions we've decided that this design is ultimately unmaintainable and have instead opted for a design that is a little less obvious, but achieves the same result.

function MyObject()
{
    var _privateProperty = 'somevalue';

    function _privateMethod()
    {
        // Do Something
    }

    // Public Interface
    this.publicProperty = _privateProperty;
    this.publicMethod = _privateMethod; 

}

With this approach we are basically making EVERY property and method private by default, and at the bottom of our "classes" we expose the ones we want available publicly. I think most would agree that this follows the normal JavaScript convention better than our initial design, which hopefully means it will be easier to read and maintain for others.

Thanks again for those who took the time to try a come up with a solution.

drewdah
  • 91
  • 2
  • 7
1

This is kind-of weird (but then, you're probably used to that by now :-) but you could override the actual "MyObject" constructor with a local one:

function MyObject() {
  function MyObject(p) {
    for (var k in p) if (p.hasOwnProperty(k)) this[k] = p[k];
  }

  this.public = new MyObject({
    publicMethod: function() { /* whatever */ },
    publicProperty: "something"
  });
  return this.public;
}

Clearly you could factor out the code for most of that, so that you could have a function that, given a function to perform the initialization for a particular class, would set all that up for you. (Having to maintain a system like you're proposing without some library support to handle the details seems like a real chore, and long-term a very hard thing to keep working, and even harder to change.)

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • I don't have the rep to mark this as useful, but if I did I would. It's not quite the answer I was looking for, but it does offer an interesting approach. I'm going to try taking this concept and making a simple "public interface" method or class (maybe extend the base Object construct) that I can instantiate and set context/constructor with. – drewdah Jan 28 '11 at 01:10
  • @drewdah Javascript is a *functional* language -- instead of thinking about making "base classes", think about making **functions** -- that's what the language is all about. Functions can make functions, and that's an extremely powerful paradigm. .... Also, you can vote answers up no matter how many SO points you have :-) – Pointy Jan 28 '11 at 01:45
  • Well, aside from this public / private issue were also trying to do some basic inheritance. We added an inHeritsFrom() method to our base Object construct (as describe here http://phrogz.net/js/classes/OOPinJS2.html) to accomplish is in a more familiar/readable way. So when I say "base class" I really just mean a base function that were extending on so we aren't rewriting the same methods over and over for common functionality. Oh, and apparently SO disagrees with you - just tried to vote up your answer again and it says I need 15 rep to do that :( – drewdah Jan 28 '11 at 20:18
  • Oh well - thanks for trying :-) Good luck with your project! – Pointy Jan 28 '11 at 20:30