`this` is hard to learn.
No it isn't. Its a function argument that is implicitly passed "left of the dot". Whatever object that function was attached to when you call it? Thats the object thats gonna become the `this` argument.
Normal arguments, you pass them like so: `someFunction(normalArgument1, normalArgument2)`
The argument `this` you pass it like so: `thisArgument.someFunction(normalArgument1, normalArgument2)`
From this it immediately becomes clear why you can't pass that function away by itself. When whoever got it calls it, its going to have nothing "left of the dot" to call it with.
Done, `this` explained.
It's not that simple. You've mentioned only one of the possible meanings of `this`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.
// In web browsers, the window object is also the global object:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b) // "MDN"
console.log(b) // "MDN"
Inside a function, the value of this depends on how the function is called.== Simple call ==
Since the following code is not in strict mode, and because the value of this is not set by the call, this will default to the global object, which is "window" in a browser.
function f1() {
return this;
}
// In a browser:
f1() === window; // true
// In Node:
f1() === global; // true
In strict mode, however, the value of this remains at whatever it was set to when entering the execution context, so, in the following case, this will default to undefined: function f2() {
'use strict'; // see strict mode
return this;
}
f2() === undefined; // true
So, in strict mode, if this was not defined by the execution context, it remains undefined.== The bind method ==
ECMAScript 5 introduced Function.prototype.bind. Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function, in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.
function f() {
return this.a;
}
var g = f.bind({a: 'azerty'});
console.log(g()); // azerty
var h = g.bind({a: 'yoo'}); // bind only works once!
console.log(h()); // azerty
== Arrow functions ==In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object:
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
Note: if thisArg is passed to call, bind, or apply on invocation of an arrow function it will be ignored. You can still prepend arguments to the call, but the first argument (thisArg) should be set to null.No matter what, foo's this is set to what it was when it was created. The same applies for arrow functions created inside other functions: their this remains that of the enclosing lexical context.
== As an object method ==
When a function is called as a method of an object, its this is set to the object the method is called on.
In the following example, when o.f() is invoked, inside the function this is bound to the o object.
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37
Note that this behavior is not at all affected by how or where the function was defined. In the previous example, we defined the function inline as the f member during the definition of o. However, we could have just as easily defined the function first and later attached it to o.f. Doing so results in the same behavior: var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // 37
This demonstrates that it matters only that the function was invoked from the f member of o.Similarly, the this binding is only affected by the most immediate member reference. In the following example, when we invoke the function, we call it as a method g of the object o.b. This time during execution, this inside the function will refer to o.b. The fact that the object is itself a member of o has no consequence; the most immediate reference is all that matters.
== "this" on the object's prototype chain ==
The same notion holds true for methods defined somewhere on the object's prototype chain. If the method is on an object's prototype chain, this refers to the object the method was called on, as if the method were on the object.
== "this" with a getter or setter ==
Again, the same notion holds true when a function is invoked from a getter or a setter. A function used as getter or setter has its this bound to the object from which the property is being set or gotten.
== As a constructor ==
When a function is used as a constructor (with the new keyword), its this is bound to the new object being constructed.
While the default for a constructor is to return the object referenced by this, it can instead return some other object (if the return value isn't an object, then the this object is returned).
== As a DOM event handler ==
When a function is used as an event handler, its this is set to the element the event fired from (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener).
== In an in–line event handler ==
When code is called from an in–line on-event handler, its this is set to the DOM element on which the listener is placed:
Show this
The above alert shows button. Note however that only the outer code has its this set this way:
Show inner this
In this case, the inner function's this isn't set so it returns the global/window object (i.e. the default object in non–strict mode where this isn't set by the call).So theargument has a default value when in non strict mode, and like all other arguments you can partially apply it with bind. Got it.
Everything else, still explained by ”argument passed left of the dot at call time“
Oh yeah and arrow functions dont have a this argument so whatever this was already in scope is the binding.
I'm a little amazed that you read all that, and still think it's so simple. Here's a simple counter argument to your "left of the dot" hypothesis:
setTimeout(foo.doStuff, 0);
When `doStuff` executes, `this` will not point to `foo`, even though it was "left of the dot". This same counter argument applies to every usage of a function as a first-class value.Because I said left to the dot at "call time". More succintly:
> When you call a function, whatever is left of the dot is passed as the `this` argument
The analogy is pretty good - you can't pass an argument to a function until you actually call it. In your example, the function hasn't been called yet, so no argument has been passed.
foo.doStuff // <-- syntax to access a field member
foo.doStuff() // syntax to call a field member with a `this` argument passed left of the dot.
Furthermore, on the receiving side, the setTimeout function is seeing: function setTimeout(fn, delay) {
// just fn, but not the argument.
}
Which makes it clear that you can no longer call that function with the proper `this` argument.My point was that `this` is only as confusing as you make it, and there is a simple and straightforward way to teach it (function argument). If you do that, the dozens of seemingly special cases go down to just a couple.
Its unfortunate we don't yet have the bind operator https://github.com/tc39/proposal-bind-operator
If we had it, the explanation could be made even clearer. You would be able to call free-standing functions with a `this` argument left of the double colon, i.e. `thisArgument::freeStandingFunction()` - then the dot method call syntax `thisArgument.method()` could be explained as a syntax sugar for `thisArgument::(thisArgument.method)()` - a syntax sugar to at the same time access a function attached as a member field of an object and call it with that object as the `this` argument.
The bind operator would make all those other `this` cases seem less special too. Of course you can call various bits of code with whatever `this` argument you want. The event handler code? `someElement::eventHandlerCode(event)`. A constructor? `Object.create(constructor.prototype)::constructor()`. Nothing special here, just a function argument, can have different values in different functions, depending what was passed as `this`.
Its bizarre that this operator got stuck in stage 0 and is being subsumed by the pipeline operator (a terrible match for JavaScript). Looks like TC39 isn't interested in untangling the mess.
The thing is, I don't think anyone is really getting confused about `this` in your simplistic call-site left-of-the-dot model. They get confused when they do exactly the code I put, or similarly in a DOM event handler. And then there's weird cases, like calling bind on a bound function does nothing...
If we what to talk about what we wish it looked like, I wish it was more like Rust, where left-of-the-dot is an implicit argument to an explicit parameter within the function.
That example is still explained by the simplistic model. You have a function
function setTimeout(callback, delay) { do the set timeout thing, at the right time, call callback(); }
You pass a function argument to it, but you're not calling that function, so you haven't passed the `this` argument: setTimeout(someObject.someFn, delay)
From within setTimeout, the code gets that function and calls it without anything left of the dot callback();
So the `this` argument gets the default value in that environment (undefined/global/window)Calling bind on a bound function is also explained by the simplistic model:
function bind(f, o) { return function(...args) { return o::f(...args) } }
Once you partially apply the `this` argument you get a new function that calls the old function with the specified object as the `this` argument; that newly created function has its own implicit `this` argument, but it doesn't use it for anything, so it doesn't matter if you bind it.Prototypical inheritance is also explained by the simplistic model. You call a method:
o.f()
The method is not on the object, so its looked up in its prototype chain. Lets say its found after the 2nd prototype: [o.__proto__.f, o.__proto__.__proto__.f]
Once found its called, but left of the dot is still the original object `o` so that is what is going to become the `this` argument. o::(o.__proto__.__proto__.f)()
To me this is simpler than any other late binding explanation I've read, for any language, ever.The confusing part for non-experts is that there are two "callable" types in JavaScript, functions and methods, but the language pretends that these two types are equivalent. Functions are self-contained and can be called without an object; methods depend on an implicit 'this' parameter. The fact that you can define and call a method using the syntax for a normal function call, without generating a type error, is a key part of the problem. The sequence "g = x.f; g()" should either be equivalent to "x.f()" or result in an error. Accepting both forms, but with subtly different behavior regarding implicit parameters, is bound to cause confusion.
Note that in C++ there is a type-level distinction between pointers to ordinary functions (without 'this') and pointers to member functions, so the concepts expressed above are not without precedent in "real" languages.
> To me this is simpler than any other late binding explanation I've read, for any language, ever.
The object system in Lua achieves the same effect with much less complexity, IMHO. Methods in Lua are just ordinary functions which take an object as an explicit first parameter; there is no implicit 'this' argument. Lua objects are tables (associative arrays), with metatables providing a fallback for missing keys much like JavaScript's prototype chain. The dot syntax (x.f) is nothing more than syntactic sugar for the table lookup x['f']. To call methods idiomatically you use the colon operator x:f(...), which is equivalent to x.f(x, ...) except that x is only evaluated once. Like JavaScript, Lua is dynamically typed, with missing arguments defaulting to nil, so you won't get a clean type error if you forget to pass the first parameter. However, there is no hidden, context-sensitive 'this' to worry about, and if you pass an argument fn=x.f to a function which calls it as fn(...) then the arguments will be exactly the same as if you had written x.f(...) directly—most likely not what you intended, but it is at least apparent why it failed.
function f() { return this + 5; }
But there are only two ways to call it: f.call(5) // calls with `this` argument set to 5
and let x = new Number(5)
x.f = f;
x.f(); // syntax sugar for to x.f.call(x)
The confusing part here is that `x.f()` is very different from `x.f`, and is most definitely NOT the same as `(x.f)()`, but more of an `(x.f).call(x)` Other than that and the fact that the argument is always there even if you don't specify it, its exactly the same as Lua, metatables (prototypes) and all.To resolve this confusion once and for all, we desperately need the bind operator [1] to provide a primitive on top of which the rest can be explained.