Originally published on php-to-js.learnio.dev — part of the PHP to JavaScript course. View this lesson on the original site.

Regular functions get this from how they are called

PHP methods always have $this when called on an object. JavaScript regular functions choose this at the call site — four common patterns, one of which bites PHP developers most often.

PHP habit

<?php
class Invoice {
    public function total(): int {
        return 100;
    }
}

$invoice = new Invoice();
$invoice->total(); // $this is always $invoice

JavaScript equivalent: four bindings

// 1. Default — standalone call (strict: undefined)
function show() {
  console.log(this);
}
show();

// 2. Implicit — method call
const invoice = {
  total() {
    return 100;
  },
};
invoice.total(); // this === invoice

// 3. Explicit — call/apply
show.call({ label: 'explicit' });

// 4. new — constructor (skip for now)
Pattern Example this is
Default fn() undefined in strict mode
Implicit obj.fn() obj
Explicit fn.call(ctx) ctx
new new Fn() new object

The PHP developer footgun

const invoice = { total() { return 100; } };
const fn = invoice.total;
fn(); // not 100 — lost implicit binding

Extracting a method drops the object. Fix with bind, wrapper arrow, or call as invoice.total().

const bound = invoice.total.bind(invoice);
bound(); // 100

Try it

Use the amber Try it panel below. Click Run.

const cart = {
  items: [10, 20],
  sum() {
    return this.items.reduce((a, b) => a + b, 0);
  },
};

console.log('method call', cart.sum());

const loose = cart.sum;
try {
  console.log('detached', loose());
} catch (e) {
  console.log('detached failed:', e.message);
}

console.log('bound', cart.sum.bind(cart)());

Method call works; detached fails or returns wrong; bind restores the object.

From PHP to JavaScript — Control Flow, Functions, and Scope