methods that rewrite the object they live on

this Mutation

A method quietly rewrites the object it lives on — or loses its receiver entirely — so a later read sees a value nobody assigned on purpose.

When a method mutates this, every caller is coupled to call order.

01in the wild

In the wild

Mutating this In Place

A method quietly rewrites the object it lives on, so a later read sees a value nobody assigned on purpose.

example.js
class Cart {
  constructor() { this.items = []; }

  // SMELL: mutates this and returns nothing useful
  add(item) { this.items.push(item); }
}

// RIGHT: return a new cart, never mutate in place
const add = (cart, item) => ({ items: [...cart.items, item] });
Mutating this couples every caller to call order. Returning a fresh value makes the operation a pure function.
// observed
mutating: order of add() calls changes shared state
pure:     each call is independent and replayable
example.py
class Counter:
    def __init__(self):
        self.n = 0

    # SMELL: side effect on self, no return
    def bump(self):
        self.n += 1

# RIGHT: a value object that returns a new instance
from dataclasses import dataclass, replace

@dataclass(frozen=True)
class Counter:
    n: int = 0
    def bump(self):
        return replace(self, n=self.n + 1)
A frozen dataclass cannot be mutated, so bump() must hand back a new value. State changes become traceable.
// observed
mutable: c.bump() changes c in place
frozen:  c2 = c.bump(); c is untouched

Losing this in a Callback

Pass a method as a callback and it forgets which object it belonged to.

example.js
class Timer {
  constructor() { this.count = 0; }
  tick() { this.count++; }      // depends on `this`
}
const t = new Timer();

// SMELL: passing the method drops its receiver
setInterval(t.tick, 1000);      // TypeError: this is undefined

// RIGHT: bind the receiver, or wrap in an arrow
setInterval(() => t.tick(), 1000);
A bare method reference loses its `this`. An arrow wrapper (or .bind(t)) preserves the receiver.
// observed
bare:  this is undefined inside tick()
arrow: t.count increments as expected
example.py
class Timer:
    def __init__(self): self.count = 0
    def tick(self): self.count += 1

t = Timer()

# A *bound* method keeps its self -- this is safe in Python
schedule(t.tick)            # fine: t.tick is bound to t

# SMELL: the *unbound* function needs self passed in
schedule(Timer.tick)        # TypeError: missing 'self'
Python binds self when you access t.tick. Reaching through the class instead drops the receiver, mirroring the JS trap.
// observed
bound:   t.count increments
unbound: TypeError, no self supplied
02cross-pollination

Where this compounds

Nondeterminism
Runtime Errors
03weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.