1

One can think of this as implicit variable set on runtime. If it can be set to different values it may affect behavior of a function and value it returns, making the function impure. In below example objOne.addTwo function calls only another pure function, but because of this it can be changed.

Example

const objOne = {
  getTwo: () => 2,
  addTwo: function (num) {
    return num + this.getTwo();
  }
}
const objTwo = {
  getTwo: () => 3
}
console.log(objOne.addTwo(2)); // Returns 4
console.log(objOne.addTwo.call(objTwo, 2)); // Returns 5 instead of 4

Question

As a principle, should this be considered "mutable state"?

Community
  • 1
  • 1
M. Twarog
  • 1,496
  • 3
  • 15
  • 34
  • 1
    Btw, different expressions (method invocations here) may evaluate to different values, hence your example is pure – scriptum Jan 10 '20 at 12:07

3 Answers3

3

No, the this keyword by itself isn't considered mutable state. Consider the following program.

const objOne = {
  getTwo: () => 2,
  addTwo: function (self, num) {
    return num + self.getTwo();
  }
}

const objTwo = {
  getTwo: () => 3
}

console.log(objOne.addTwo(objOne, 2)); // Returns 4
console.log(objOne.addTwo(objTwo, 2)); // Returns 5 instead of 4

The above program clearly has no mutation. It's exactly the same as your original program except that it uses a formal parameter instead of using the this keyword.


However, this is usually used together with mutation in OOP. For example, consider the following class.

class Vec2 {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    add(that) {
        this.x += that.x;
        this.y += that.y;
    }
}

const v1 = new Vec2(1, 2);
const v2 = new Vec2(3, 4);

v1.add(v2);

console.log(v1); // { x: 4, y: 6 }

Clearly, you can have mutation without using this too.

const Vec2 = (x, y) => ({ x, y });

const add = (self, that) => {
    self.x += that.x;
    self.y += that.y;
};

const v1 = Vec2(1, 2);
const v2 = Vec2(3, 4);

add(v1, v2);

console.log(v1); // { x: 4, y: 6 }

So, this is just a special parameter of sorts. By itself, it isn't mutable. However, you can mutate it just as you can mutate any other parameter.


The bigger problem with the this keyword, and the new keyword too, are that they are special cases. Normally, you'd call a function as foo(x, y, z), but with this you'd have to call the function as foo.call(x, y, z), and with new you'd have to call the function as new foo(x, y, z).

These special cases make it difficult to reuse higher order functions. For example, normally you can do [1, 2, 3].map(foo) but what if the call to foo needs to be prefixed with new? Then, you'd have to write [1, 2, 3].map((...args) => new foo(...args)). Similarly, what if foo needs to be called using .call? Then, you'd have to write [1, 2, 3].map((...args) => foo.call(...args)).

Clearly, these special cases are making life more complicated, at least in the case of functional programming. To quote the Zen of Python.

Special cases aren't special enough to break the rules.

TLDR: Avoid using this and new when writing programs in a functional style.

Aadit M Shah
  • 67,342
  • 26
  • 146
  • 271
2

It's rather a philosophical question IMO. I think it depends on what you understand by mutable state. From the docs we read:

It can't be set by assignment during execution, and it may be different each time the function is called.

This clearly explains that this cannot be set during function execution but it may be different depending on how you call the function. So if we are talking about reassigning this then no, it's not a mutable state because it's not possible to change its value after it's been set. But we can change it by calling a function differently.

You can read about those ways of changing this by calling a function differently here.

Sebastian Kaczmarek
  • 7,135
  • 4
  • 17
  • 36
-1

From my Perspective I don't say this is mutable state.

And what happened is something related to Javascript Lexical scope and ScopeManager

When ScopeManager finds this in your function , ScopeManager starts searching in his Lexical Scope about the closest this value unless you are using Hard Binding as you did when you use call

Ahmed Kesha
  • 711
  • 4
  • 10
  • 1
    `this` has nothing to do with *scope* per se. It's unclear whether what you've written is wrong or just misleading. – deceze Jan 10 '20 at 11:16
  • Imagine it when you write `this` in your function the Scope Manager will search for variable with name `this` and If It couldn't find any variable with `this` name Scope Manager will search for the nearest outer scope ... example when you write this in normal function the value will be the `window` value and that is not in strict mode. – Ahmed Kesha Jan 10 '20 at 11:29
  • And you can force `this` value by using `Hard Binding` using `call`,`bind` and `apply` – Ahmed Kesha Jan 10 '20 at 11:29
  • 1
    `this` isn't a normal variable and doesn't work that way. Each function has its own `this`. You'll never get a parent scope's `this`. (You can argue about that with fat arrow functions perhaps, but that's an implementation detail.) – deceze Jan 10 '20 at 11:30
  • I didn't say `this` is normal variable I said imagine it as normal variable and what is your explanation when you write `this` inside normal function not constructor function why `this` in this case will return the `window` value ? – Ahmed Kesha Jan 10 '20 at 11:33
  • 1
    *"why `this` in this case will return the `window` value"* — Because of the rules how `this` is bound at calltime. If it was regular *scope* resolution, and you'd have nested functions, then instead of `window` you'd expect the *parent scope's `this`* to be used instead, which is not the case. – deceze Jan 10 '20 at 12:20
  • Because in this case also the parent function doesn't have `this` so it will search in the outer scope until it goes to global scope – Ahmed Kesha Jan 10 '20 at 13:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205754/discussion-between-ahmed-kesha-and-deceze). – Ahmed Kesha Jan 10 '20 at 13:32