Over a million developers have joined DZone.

JavaScript Gotcha for Pythonistas: Bound Methods

· Web Dev Zone

Learn why developers are gravitating towards Node and its ability to retain and leverage the skills of JavaScript developers and the ability to deliver projects faster than other languages can.  Brought to you in partnership with IBM.

Today while writing some JavaScript code I ran into an interesting gotcha I wanted to briefly mention. It has to do with method binding; more specifically, the behavior I’m used to from Python. Here’s a code sample:

class MyKlass:
    def __init__(self):
        self.num = 2

        f = self.foo
        self.result = f(10)

    def foo(self, othernum):
        return self.num + othernum

mk = MyKlass()
print(mk.result)

The interesting part is invoking self.foo indirectly via f(). This works and prints 12 as expected. It works because the assignment f = self.foo assigns a bound method to f. By "bound", I mean that the method knows which object it belongs to, and when its body is executed self is set to the right thing.

The same does not hold in JavaScript. Here’s a straightforward translation:

'use strict';

var MyKlass = function() {
  this.num = 2;

  var f = this.foo;
  this.result = f(10);
}

MyKlass.prototype.foo = function(othernum) {
  return this.num + othernum;
}

var mk = new MyKlass();
console.log(mk);

This doesn’t work. I get an error from Node.js:

  return this.num + othernum;
             ^
TypeError: Cannot read property 'num' of undefined

So what’s going on? The reason is that, unlike in Python, the assignment f = this.foo does not assign a bound method to f. It’s simply a property lookup on the object this; it indeed has the property foo, which is a function. f now refers to that function. That’s it. There is no implicit binding going on.

Once the problem is understood, it’s easy to solve. The most direct solution is to bind the method explicitly on assignment, using Function.prototype.bind:

var bound_f = this.foo.bind(this);
this.result = bound_f(10);

An alternative solution is not to bind foo when a new reference to it is made, but do it when it’s invoked through that reference. This can be done using Function.prototype.call (or equivalently using its cousin apply):

var f = this.foo;
this.result = f.call(this, 20);

This difference between the two languages exists because unlike Python, JavaScript does not have a first-class entity for a bound method. This is not surprising, because the Python method is bound to an instance, but instances in the "classical" sense don’t exist in JavaScript (see how the class and method concepts from "classical OO" are simulated above with JS’s constructor functions and prototypal inheritance).

What does exist in JavaScript is lexical binding and closures. While the bind method is new in ES5, its effect can be simulated very easily:

Function.prototype.bind = function(obj) {
  var self = this;
  return function () {
    return self.apply(obj, arguments);
  };
}

Note that the actual implementation of bind would be just a little more complicated because bind also supports partial application of arguments (some of the arguments can be provided during binding, and the rest on invocation).





 

Make the transition to Node.js if you are Java, PHP, Rails or .NET developer with these resources to help jumpstart your Node.js knowledge plus pick up some development tips.  Brought to you in partnership with IBM.

Topics:

Published at DZone with permission of Eli Bendersky, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}