JS: Function Form vs Operator

By Xah Lee. Date: .

in JavaScript since ECMAScript 2015, many functional forms are added that does the same thing an operator does.

example:

Advantage of function form

Function form lets you manipulate expressions at run-time.

JavaScript has functional form of operator Array.of Reflect.construct Reflect.deleteProperty Reflect.get Reflect.has Reflect.set show a example where function form is useful but operator cannot do it.

(ai answer)

Operators aren’t first-class. The function forms fix that, and they also expose extra arguments (receiver, newTarget) that syntax can’t.

Array.of vs new Array / []

Array(3) is length 3 (holes). Array.of(3) is [3]. You can also pass it as a callback:

[1, 2, 3].map(Array.of);   // [[1], [2], [3]]
// no operator equivalent: you can’t pass `[` around

Reflect.construct vs new

new Ctor(...args) always sets new.target to Ctor. Reflect.construct lets you pick a different prototype / new.target:

function Base() { console.log(new.target.name); }
class Extra {}
Reflect.construct(Base, [], Extra); // logs "Extra"
// obj instanceof Extra === true
// `new Base()` cannot do this

Also: Reflect.construct(Ctor, argsArray) — no need to write new Ctor(...args).

Reflect.deleteProperty vs delete

Returns success (boolean). delete is an expression, not a function.

const frozen = Object.freeze({ a: 1 });
Reflect.deleteProperty(frozen, "a"); // false — failed
// delete frozen.a;  // still false, but you can’t pass `delete` to .every()
["a", "b"].every((k) => Reflect.deleteProperty(obj, k));

Needed inside a Proxy deleteProperty trap to forward the real operation.

Reflect.get / Reflect.set vs . / =

You can choose the receiver (this for getters/setters). Property syntax always uses the object you wrote.

const proto = {
  get x() { return this._x; },
  set x(v) { this._x = v; },
};
const a = Object.create(proto);
const b = {};
Reflect.set(proto, "x", 42, b); // setter runs with this === b
Reflect.get(proto, "x", b);     // 42
// a.x = 42 always uses `a` as this — you cannot retarget it

Same reason Proxy get/set traps take receiver and should call Reflect.get/set(..., receiver).

Reflect.has vs in

Same check, but callable:

["id", "name"].every((k) => Reflect.has(user, k));
// you cannot write `.every(in)` — `in` is not a value

Shared theme: pass as a callback, apply to a dynamic key list, return success, or supply receiver / new.target. Operators can’t do those.