2

I prefer the me/that/self method to resolve the binding 'problem' in Javascript. Anytime I declare a 'class' that will have functions that may be passed as parameters (e.g. event handlers). Here's an example of my general setup:

function Person(name)
{
    var me = this;

    this.name = name;

    this.alertName = function()
    {
        alert(me.name);
    };
}

var aPerson = new Person('Paul');
setTimeout(aPerson.alertName, 1000);

This works because I've defined var me = this; in the 'class' definition. This gets repetitive, so as an exercise, I wanted to abstract this and add the me = this definition to the prototype of Function so I don't have to do it manually every time. I hope the way I describe it makes sense. I thought something like this would work:

Function.prototype.me = function()
{
    return this;
}

I expected a call to me() anywhere in the Person 'class' would work, but it doesn't. I've tried different ways, for instance by extending Function with a BaseFunction class that has the definition, but I can't figure it out and I can't find anything by way of Google or on SO.

Here is a jsFiddle with what I've been playing around with if you're interested:

http://jsfiddle.net/qgjw47nt/

Thanks,

Paul

PVDM
  • 98
  • 7
  • Could you post an example implementation of your function that contains the `me` call? In your JSfiddle, there is no such example. – lxg Oct 12 '14 at 21:56
  • Btw, you're encouraged to not post examples on JSfiddle (although it's not forbidden), instead you can use Stack Overflow snippets to have the examples in context with your question. – lxg Oct 12 '14 at 21:57
  • 3
    What you are trying to do doesn't seem to make a lot of sense. For `Function.prototype.me = function() { return this; }` to work properly, you have to call the function in such a way that `this` refers to the newly created object. Assuming this function was assigned to the object, you could do `this.me()`. Or you have to pass the value of `this` explicitly with `me.call(this)`. In both cases you already have a reference to the object (`this`), i.e. `this === this.me()` or `this === me.call(this)`. There is no need to call a function which returns a value you already have. – Felix Kling Oct 12 '14 at 22:05
  • 4
    `window.setTimeout(aPerson.alertName.bind(aPerson), 1000);` – Mulan Oct 12 '14 at 22:27
  • The 'this' problem being not understanding what 'this' is is not solved by coding differently and ditching prototype in the process. There is no this problem since you know the instance when passing it's callback function so you can use bind or closures. More info on this here: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Oct 12 '14 at 23:34
  • @lxg, thanks for that tip, I'll remember to use SO snippets next time. Very cool feature! – PVDM Oct 28 '14 at 19:01

4 Answers4

2

The way you're trying to use me (if I understand your question correctly), it would be considered a function assigned to an inner variable, and not a member of the function/“object” itself.

To access the me prototype, you would have to use this.me – which of course would make the whole thing moot.

var foo = function()
{
    this.doSomething = function() {
        alert('yay!'); 
    };

    // this doesn't work! "me" would refer to a non-existent variable
    me.doSomething();

    // this would work, but it's obviously useless.
    this.me.doSomething();
}

Even worse, if you'd call this.me inside a function defined inside another function, you would access the this.me of the inner function, and could never access the outer one with this approach.

lxg
  • 12,375
  • 12
  • 51
  • 73
2

The way how you are trying to define me is saying about you need to get closer understanding about this. Prototype of the Function - is the in prototype chain of all the objects you have created by new. It is have absolutely no idea about your specific object.

The handler gets invoked with context depending on the place it was invoked, not the place you have defined it. So, there is no link from handler itself to object to which it belongs.

The way how you can define and bind specific this to a function to be able to pass it all around, apart from having this stored in closure, as you doing now, is the .bind() function. You can do something like:

var foo = function (name) {
   this.name = name;
   this.handler = function () {
     console.log('I am called, name=', this.name);
   }.bind(this);
 }

 var boo = new foo('Paul');
 setTimeout(boo.handler, 1000)

Actually this is the most 'true' way how it is done in JS. Of course there a lot of sugar libs to wrap around that in different forms, but all have that bind under the hood.

But, if you are worrying about having repetitive var me = this, any other option will be also too repetitve for you :) Check the CoffeeScript - it is dream of minimalistic coding, and nightmare from some other sides.

Karabur
  • 546
  • 3
  • 7
  • Thanks @Karabur. I did not realize Javascript had built-in functionality to bind 'this' as you showed. I definitely need to read up some more on prototypes and such to get a better understanding of the inner workings of Javascript. As I'm starting to write more complicated and substantial code, I'm trying to add more robustness and structure to my work. I'm going to start to use the bind functionality and try not to rely on `var me = this;` as @Winchestro also suggested. – PVDM Oct 28 '14 at 19:16
1

You are probably aware that event handler functions (or a regular method in your example) you define within your constructor will always have access to the arguments your constructor was called with through the closure? Try this.

function Person(name){
    this.alertName = function(){ 
        alert(name);
    };
}

the downside of this pattern is that you can't put alertName on the prototype of Person, because it needs to access the private arguments through the closure. If you don't make use of the closure there is absolutely no need to create a new alert name function on every instance of Person.

edit: correction. getters and setters are equally bad as they keep the closure in memory just like a method, didn't think it through. so unless you actually make good use of it you should avoid using it at all, and just use the prototype when in doubt.

you need getters and setters to publish variables that are passed by value. variables passed by reference can just be published via this.yourPropertyName = yourArgumentName, as they will just both reference the same object.

Object.defineProperty(this,"name",{
    get:function(){return name},
    set:function(newName){name = newName}
}); 

You shouldn't per se bind this and not use "this" ever. Especially when it comes to methods whenever possible you should put them on the prototype, and make them generic. The term "ducktype" is often used for that. Because if you use "this" in your method, anything that has a "name" property can use Person.prototype.callName and it will just work (even if it's a duck).

Winchestro
  • 2,138
  • 14
  • 18
0

If you find JavaScript too wordy, there's CoffeeScript for that:

class Person
    constructor: (@name) ->
    alertName: => alert "Hi, my name is #{@name}"

aPerson = new Person 'chka chka Slim Shady'
setTimeout aPerson.alertName, 1000

This uses the fat arrow (=>) which ensures @ (==this) binds to the right context.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • is the aPerson.name a defined property (aka is it a published property on the Person instance) in this example? (I don't at all know coffeescript) – Winchestro Oct 12 '14 at 22:48
  • 1
    Yup. (There's live compiles on the internets http://fiddlesalad.com/coffeescript/). `(@name)->` is a shortcut for `(name) -> @name = name` === `function(name){ this.name = name }` – Petr Skocik Oct 12 '14 at 23:02
  • woah crazy. I should really check that out :) I assume there are probably also shortcuts for property descriptors, too? – Winchestro Oct 12 '14 at 23:09