What indeed is `this` in JavaScript
Tags: javascript, beginners
Published: 2023-02-26 11:42:13

## Functions and their invocations
Before we talk about `this` let's talk about function invocations first.

A function, in general is a `(...args: TArgs) => TReturn`. The code inside a function are given access to a list of arguments (or parameters if you like), but those arguments are undecided before the function is invoked, that is why they are called "formal". They are kind of "imaginary". The code "imagine" there will be a list of real values to use, but that happens only when the function is invoked, and only for and during this specific invocation.

Arguments are finally bond to a "actual" value upon each invocation, and are different among different invocations.

But `this` is not in the list. How can a function access an argument/a parameter, when it is not in the list ?

## Is `this` a capture ?

High level programming languages, like JavaScript, has an ability to "capture" identifiers (or usually, variables, if you like, or you may just call it "an item") from the current "lexical scope" and make those captures usable inside the function. This is indeed not that trivial as it seems. A function can capture an identifier if and only if it is inside the lexical scope of that identifier.

But, Is there an identifier called `this` to capture? Usually no. For "arrow expressions", yes this, if there is one, is a capture. But for functions that are not arrow expressions, no.

## Then what is `this` indeed ?

If it is not a capture, it is an argument. It "has to" be an argument or it will never be bond to an actual value.

But it is not in the list. Usually arguments are explicitly defined in the list, not for the two "implicit arguments" in JavaScript. One of the two is `this`, and the other one is `arguments`. Arrow expressions have no implicit arguments therefore no `this` and `arguments`.

So, implicit argument, is an argument but passed implicitly.

## But how you pass an argument implicitly ?

Each argument must be decided upon "invocation". Therefore, the implicit argument `this` is passed upon invocation. But you don't pass it via the list, you pass it by the "member expressions" (in ecma262) or "property accessors" (in MDN).

Member expressions include, the dot in `a.b`, the bracket pair in `a[b]`, the `new` in `new b()`, the `.#` in `a.#b`, and `super`. These are pretty much what we would care about, since you would never worry about `this` in `new.target`, `import.meta` and template literal calls.


That is to say, if you call a function like `a.b()` or `a[b]()`, `a` will be passed as `this`. If you call it like `new b()`, the object created by `new` will be passed. That is the basic rule of `this`.

The implicit argument `arguments` is passed as the whole actual argument list, including those without a formal argument bond. It is less useful today thanks to the super cool rest syntax.

Let's make it short: you pass `this` by "member expression". That's it. Although ecma262 is not describing it this way, it pretty much follows this logic. It is pretty much a lexical thing.

## corner cases ?

If a function `foo` is called without using member expression, it is implicitly a `globalThis.foo` (if it is not strict mode), or the `this` argument is simply not passed, therefore it is `undefined` (in strict mode).
Or let's make it clear: why would you ever want to access `this` if you are not planning to pass it ?

If you are not calling a function directly, it is the direct caller to decide how it is called. eg. `function.prototype.apply`, `function.prototype.call` and `function.prototype.bind`. As well as `Array.prototype.map` and so on: if you are not the direct caller, ask its direct caller to pass `this`.

These are not really corner cases. One is to say "if it is not passed it is not passed", and the other is to say "if you are not the caller you are not the caller". It is just that the runtime gives those internal things some cheating power, so you are allowed to `b.bind(a)` even if `b` is not a member of `a`.

So,
you pass `this` by "member expression". That's it.

## But why it seems so dynamic if it is indeed lexical ?

In statically typed OOP languages like C++, `this` is exactly an implicit argument. The "left hand side value of the member operator" (`.` or `->`) is passed as an argument, and it depends on the calling convention (or ABI) how it is practically passed.

For rust, and surprisingly, lua, `this` is an explicit formal argument but an implicit or explicit actual argument, so you won't need the keyword. If you are passing it implicitly, it will be "the left hand side value of the member operator", or it will be what you put in the list.

Lua uses a special member operator `:` for this particular case, which means, "call the function with the left hand side value of operator `:` as the first argument". So `a:b(...)` means exactly `a.b(a, ...)`. A smart and thoughtful design.

You should see how "dynamic" the `this` argument could be in lua even though it is such beautifully designed. It is the same case in JavaScript.

In statically typed languages, if a function is a member function (or "method" but it really is just a function),  it will "always" be called with a "correctly" typed `this`. It is totally predictable. However, in dynamically typed languages like lua and JavaScript (and Python), you may pass whatever as `this`. It is lexical, but it is dynamically **AND** weakly typed.

For example you can never do a `f = a.b; f()` in  C++, but you do it everyday in JavaScript. It is impossible to `f = a.b.bind(c)` in C++, but this is what JavaScript may do. You need to make it a "[delegate](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/)" or "callable object" (objects with operator() overloaded) in C++, CSharp and Java but a "closure" is simply as useful as a general "callable object". 

C++ treats "member function pointer" differently from a normal function pointer, obviously because there is an implicit `this` argument to pass, if you don't know what to pass as `this` you cannot call it. But in JavaScript ? `a.b` is a function and lets forget about passing `this`.

TypeScript has a "this argument type restriction" to make it less weak. It works when it works but you have to explicitly apply this restriction, otherwise it would just assume you are being a nice kid and not making troubles around.

But let's say making troubles is the JavaScript way. We enjoy the dynamic world and hopefully it can be a bit lexical and strong. That's JavaScript.