Hacker News new | ask | show | jobs
by csandreasen 4054 days ago
I actually found the Javascript 'this' keyword more understandable after working with Python.

  >>> class foo():
  ...   def test(this, x):
  ...     print this, x
  ... 
  >>> x = foo()
  >>> x
  <__main__.foo instance at 0xb71928ac>
  >>> x.test(1)
  <__main__.foo instance at 0xb71928ac> 1
  >>> y = foo.test
  >>> y(1)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: unbound method test() must be called with foo instance as first argument (got int instance instead)
  >>> y(x,1)
  <__main__.foo instance at 0xb71928ac> 1
In Python, the reference to the current object is an explicitly declared parameter in the method definition, but the parameter is passed implicitly - the value of "this" is whatever is comes before the period when the method is called. If you call it in any other manner, it throws an unbound method error. Javascript is the same way, but instead of throwing an error it will instead pass 'window' implicitly. The equivalent code in Javascript:

  function foo() { }
  foo.prototype.test = function(x) {
    console.log(this, x);
  }
  var x = new foo();
  x.test(1); // prints VM1061:4 foo {test: function} 1
  var y = x.test;
  y(1); // prints VM1061:4 Window {top: Window, window: Window, …} 1
3 comments

Actually, what you get when referencing x.test in Python is a bound method:

  >>> foo.test
  <unbound method foo.test>
  >>> x.test
  <bound method foo.test of <__main__.foo instance at 0xf74d68ac>>
  >>> z = x.test
  >>> z(123)
  <__main__.foo instance at 0xf74d68ac> 123
I really like this behavior in Python. Unfortunately, JavaScript does not behave the same way, as you see in the last couple of lines in your example. You can however get the same result by manually binding the method to the object:

  z = x.test.bind(x)
  // z is: function () { [native code] }
  z(123)
  // foo {test: function} 123
I came (back) to JS after doing a lot of Python and I think that may have helped understanding `this` in JS.

Note that the behaviour of `this` defaulting to `window` (or `global`) is only the legacy behaviour. You should be using strict mode (there's really no justification not to -- except for extremely rare edge cases, e.g. having to delete global variables without having a reliable reference to the global object other than `this`).

In strict mode, `this` would be undefined. As expected, if you consider `x.y()` syntactic sugar for `x.y.call(x)` (and therefore `z()` syntactic sugar for `z.call(undefined)`).

Under the hood, invoking a function translates to a call of the functions `[[Call]]` method, which takes an explicit `this` just like `Function.prototype.call` does.

I find that testing works better when I define my functions separately from binding in objects.

  var foo = {
    doSomething:(...argv)=doSomethingMethod(foo, argv)
  };
  return foo;
In this way, the execution is always predictable, as I avoid the use of this altogether... It's my one niggle with koa, that it uses this (execution context) as the request/response context. I tend to it in practice.

It's similar to what Crockford now presents as a preferred approach in dealing with having context for functions... I just like discrete functions that can be tested independently of their execution context... by passing the context as a parameter, this becomes explicit... with binding, the process becomes transparent.