Very nice to see it starts off with a (correct) discussion of JS types! JS devs mostly don't know the actual types in JS, which is pretty crazy if you think about it.
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
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.
Using jspm, SystemJS and Babel for my latest project, and simply using let universally over var and arrow functions over anonymous functions has made this aspect of JavaScript behave like any sane language would.
I simply don't ever need to think about it anymore.
I agree, `this` is a disaster. But, I just can't hardly think of another language where the people working in it mostly don't know the types. (Well, OK, PHP devs probably mostly don't)
And I don't just mean run-of-the-mill blub programmers who dabble in jQuery, I mean some of the best devs I've ever seen in any language. I had a self-proclaimed JS expert tell me that JS has integers and floats as distinct types. Even the people I work with every day who are fantastic did not know that function and array are not types.
Of course, things like `typeof` don't help the situation, so I don't put the blame squarely on programmers' shoulders. And obviously you can get a lot done without knowing that, technically, functions are just objects that can be called. But it still strikes me as kinda crazy.
"technically, functions are just objects that can be called"
How can you create an object which can be called like a function, but will respond to 'typeof' with 'object' rather than 'function'?
What happens when you try to call an object - what's the error? Try it, in a few of your favourite javascript interpreters:
({})()
It seems clear to me that functions are more than "just" objects in JS. You can't start out with a random {} and turn it into a function. JS functions are a subtype of JS objects, but they are a distinct type. You can't substitute an object where a function is expected - you get a type error - but you can substitute a function where an object is expected.
Basically, an object which implements call will always cause `typeof` to return "function". But the broader point is that `typeof` will lie to you and you shouldn't trust it for, you know, determining the types of values. (For example, the type of null is null, but `typeof null` returns "object".)
As I noted above, this behavior of `typeof` is part of what causes the confusion around types in JS devs.
> You can't substitute an object where a function is expected - you get a type error - but you can substitute a function where an object is expected.
This is true, but not inconsistent with the notion that functions are objects which can be called. I encourage you to read the spec I linked above, or a draft of ES6.
There's types as defined in a spec, and types according to type theory.
In short, I think the spec is incorrect in its use of the concept of types; or incomplete.
You'll note that the spec uses the concept of "internal properties" to distinguish between the different things one can do with values. From a type theory perspective, these internal properties and the implied permitted operations come close to type definitions. It's a matter of perspective, then, when one is using the word 'type' as defined by a particular language's spec, or 'type' as in programming language pragmatics / type theory POV. For the generalist programmer who knows more than one language, a broader concept of types is usually more useful.
Further, the spec uses these internal properties solely to define the semantics of the language, and they are not necessarily visible artifacts of any implementation. Talking about them as if they were concrete confuses the map with the territory.
They have to. There's a bug in the JavaScript spec that makes you do this :-) they are perfectly correct in doing what they are doing, if they didn't then funny thing would start to happen. Sad, but true.
It's the most confusing part about Javascript. This context, var foo scope, what? Why is this value not updating? undefined? What???