1

I'm still learning quite a bit about the quirks of javascript.

So I have a "class", or function created like:

function Parser(){
    ....
}

Parser.prototype = {
    init: function(){...},
    parseChar: {}
}

Then, I'm adding some sub-methods of the parseChar object to the prototype like this:

Parser.prototype.parseChar["*"] = function(){...}
Parser.prototype.parseChar["/"] = function(){...}

So how can I make the this reference inside those functions refer to the created instance of Parser? And not the object parseChar

I've tried:

Parser.prototype.parseChar["*"] = function(){...}.bind(??)

I'm guessing that obviously won't work, since the instance doesn't even exist at this point.

I just want the this reference to refer to each respective instance, to be able to access properties of the Parser inside those prototype function.

I imagine this might require some magic in a constructor? I don't know, I just hope I don't have to radically alter this, because I really like the way functions are defined with the Parser.prototype.whatever syntax.


Current Code

function Parser(){
    var that = this;

}

Parser.prototype = {
    state: {...},
    parseChar: {},
    init: function(){},
    parse: function(fileName){
        //random stuff
    }
}

Parser.prototype.parseChar["*"] = function(){
    console.log(that);
}

And then in another test file:

var Parser = require('./Parser.js');
var parser = new Parser();
parser.parse("Parser.js");

Btw, this is node.js ;)


Potential Solution

It looks like this solved my problem no, it didn't, though I'm still not sure it's the best solution, so I'm not going to accept it just yet.

In the constructor, I added:

//Bind prototype methods to the instance
for (key in this.parseChar){
    var property = this.parseChar[key];

    if(this.parseChar[key] instanceof Function){
        this.parseChar[key] = this.parseChar[key].bind(this);
    }
}

Now, in the prototype methods, I can add stuff like:

console.log(this.state);

And it works.

I could see foresee 1 of 2 problems with this approach. If either are correct or incorrect, please, let me know!

1) This method works, but it actually creates an instance of the function on each object. In which case, the whole attraction of minimizing redundant instantiation that comes with prototypes is gone. That would be fine though, because I won't be creating too many instances, and I like the readability of this solution.

Not the case. Each instance still calls the prototype's method

2) Every time a new instance is created, it actually binds the prototype's method to that actual instance, thus if I create multiple instances of Parser, after the prototype method is bound to the new instance and all of its properties, then all preceeding instances will then reference the latest. And that would be bad.

Almost, except in reverse. For some reason, each successive instance references the first, not the latest created

Still looking for a solution...

krb686
  • 1,726
  • 9
  • 26
  • 46
  • Put `var that = this` as the first line inside the `Parser()`. It will be available in the entire scope of the function. – PM 77-1 Aug 16 '14 at 01:31
  • I've added `var that = this` as the first line inside `Parser()`, and then tried `console.log(that)` inside `Parser.prototype.parseChar["*"] ` and received `that is not defined` – krb686 Aug 16 '14 at 01:34
  • Please post the code that you tested. – PM 77-1 Aug 16 '14 at 01:36
  • @PM77-1 I'm guessing this is happening because this isn't the typical case of just creating prototype methods on the "class". The prototype methods are *sub-methods* of the `parseChar` object, wherein `that` would not be defined. – krb686 Aug 16 '14 at 01:46
  • Suppose in the constructor I did this: `this.parseChar["*"] = this.parseChar["*"].bind(this)` This seems to correctly set the `this` reference, but does this negate the use of a prototype in the first place? Would that create a new function on every instance when used? – krb686 Aug 16 '14 at 01:56
  • Put all functions inside `Parser`. It will create closure and make `that` available (or so I hope). – PM 77-1 Aug 16 '14 at 02:00
  • I suppose that would be the standard approach, but I'd like to avoid it for 2 reasons. 1), because that would recreate every method and property for every instance created...avoiding that is the attraction of prototypes after all, and 2) the syntax of prototype methods seems far more readable to me as well – krb686 Aug 16 '14 at 02:02
  • `but does this negate the use of a prototype in the first place?` - `Would that create a new function on every instance when used?` yes and yes. – HMR Aug 16 '14 at 03:46
  • How/when do you actually call ```parseChar["*"]``` ? Maybe you can use ```parseChar["*"].apply(parser, arguments)``` at that place. – Volune Aug 16 '14 at 23:01

2 Answers2

1

The following could be a solution:

function ParseChar(parser){
  this.parser=parser;
}
ParseChar.prototype["*"] = function(){
    console.log(this.parser);
}


function Parser(name){
  //here is where you know the instance
  this.parseChar = new ParseChar(this);
  this.name=name;
}
Parser.prototype.init = function(){};

var p = new Parser('p');
p.parseChar['*']();
p1 = new Parser('p1');
p1.parseChar['*']();

In your second block you try to use closures to get the value of this. It only works if the that variable is in the same function block as the functions using that.

You could do the following but it'll create closures and parseChar functions for every Parser instance so in my opinion it's not as good as the first solution mentioned in this answer.

function Parser(){
  //begin function block Parsers
  var that = this;
  this.parseChar = {
    //function using that is within the function
    //  declaring that so it is available now
    '*':function(){
      console.log(that);
    }
  }
}

More info on prototype and constructor functions here.

More info on closures here.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
0

What if you create a single closure just to return that?

Inside your Parser:

var that = this;
self: function {return that ;} 

And then use self().

PM 77-1
  • 12,933
  • 21
  • 68
  • 111