Besides having no `this` of their own, what else do JavaScript arrow functions lack compared with ordinary functions, and what happens if you call `new` on one?
answer
- four bindings never created
- rest parameters instead of arguments
- no prototype object at all
- new fails before the body runs
- no generator arrow syntax exists
basics
~20 sArrow functions have no own arguments, no super, no new.target, and no prototype property, and they are not constructors: new on an arrow throws a TypeError. They also cannot be generators. All four missing bindings resolve lexically instead.
solid answer
~40 sAn arrow is deliberately a stripped-down function object. Four bindings that an ordinary function creates on every call — `this`, `arguments`, `super`, and `new.target` — are simply absent, so each one resolves outward to the enclosing ordinary function, method, or class field initializer. An arrow also has no `prototype` property (`typeof (() => {}).prototype` is `'undefined'`) and no `[[Construct]]` internal method, so `new (() => {})` throws `TypeError: ... is not a constructor`. It cannot be declared as a generator either — there is no `async *` or `*` arrow form — though `async () => {}` is fine. The practical consequences: use rest parameters `(...args) =>` when you need the argument list, and never reach for an arrow where a constructor, a prototype method, or a generator is wanted.
code
javascript · 18 linesconst arrow = () => {};
console.log(typeof arrow.prototype); // 'undefined'
try {
new arrow();
} catch (e) {
console.log(e.constructor.name); // 'TypeError'
}
function outer() {
const inner = () => arguments[0];
return inner();
}
console.log(outer('from outer')); // 'from outer'
const sum = (...nums) => nums.length;
console.log(sum(1, 2, 3)); // 3go deeper
Remember the short list — no own this, no arguments, no prototype, cannot be used with new — and reach for rest parameters when you need the argument list.
Explain that these bindings are never created rather than set to a default, so each resolves lexically to the enclosing ordinary function, and that new fails because the function object has no [[Construct]].
Predict concrete refactor damage: constructor functions rewritten as arrows, legacy code depending on arguments, and prototype augmentation that now throws — and say which failures are loud versus silent.
Frame the design rationale and the guardrails: arrows are the cheap callback form precisely because they allocate no prototype and no per-call bindings, and lint rules plus class syntax should keep constructor-shaped code out of arrow syntax entirely.
## What an ordinary function creates per call When an ordinary function is invoked, the engine builds a function environment record holding several bindings that only functions can supply: - `this` — the receiver, decided by the call form. - `arguments` — an array-like object of the actual arguments passed. - `super` — available in methods with a home object, for property and constructor access. - `new.target` — the constructor invoked with `new`, otherwise `undefined`. An arrow function's environment record creates **none** of these. The spec models this as the arrow having a `[[ThisMode]]` of `lexical`. Each of the four therefore resolves like an ordinary free variable, walking outward until it finds a scope that does define it. ## `arguments` ```javascript function outer() { const inner = () => arguments[0]; return inner(); } outer('from outer'); // 'from outer' ``` The arrow did not get its own `arguments`; it read `outer`'s. That is occasionally useful and frequently a trap — someone writes a standalone arrow, reaches for `arguments`, and gets a ReferenceError (no enclosing function) or, worse, a different function's arguments. The modern answer is rest parameters, which work identically in both function kinds and give a real array: ```javascript const sum = (...nums) => nums.reduce((a, b) => a + b, 0); ``` ## No `prototype`, no `[[Construct]]` An ordinary function declaration is born with a `prototype` object used to seed instances created via `new`. An arrow has no such property, and the function object lacks the `[[Construct]]` internal method entirely: ```javascript const Arrow = () => {}; typeof Arrow.prototype; // 'undefined' new Arrow(); // TypeError: Arrow is not a constructor ``` This is not a runtime check inside the body — the call fails before any body runs. Class methods and methods defined with shorthand syntax are also non-constructors, so the arrow is not unique here, but arrows are the case candidates meet first. ## No generator form There is no `*() => {}` syntax. If you need `yield`, you need `function*` or a shorthand generator method. `async () => {}` is legal and common; `async` arrows still lack all four bindings above. ## `super` and `new.target` Both resolve lexically too. Inside a class method, an arrow can use `super.method()` because it inherits the method's home object: ```javascript class Sub extends Base { run() { const go = () => super.run(); // legal: super comes from run() return go(); } } ``` The same arrow written at module top level is a syntax error, because there is no enclosing home object for `super` to bind against. `new.target` follows the same pattern: an arrow inside a constructor sees the constructor's `new.target`, which is how a helper arrow can tell whether the surrounding function was called with `new`. ## What arrows keep Arrows are still ordinary function *objects*. They have `length` and `name`, they are `typeof 'function'`, they inherit from `Function.prototype`, and you can call `call`, `apply`, and `bind` on them — those methods still forward arguments; only the thisArg is inert. They can close over variables like any function, and they can be `async`. ## Why the language was designed this way Every removed feature is a per-call allocation or a source of confusion in callback-heavy code. Dropping `this` and `arguments` means a nested arrow reads like the surrounding code rather than shadowing it; dropping `prototype` and `[[Construct]]` means the tiny throwaway function you passed to `map` cannot be mistaken for a class. Engines can also allocate arrows more cheaply because there is no `prototype` object to create. ## What an interviewer is testing Not memorization of a list. The signal is whether you can predict a failure: someone converts a constructor function to arrow syntax during a refactor and every `new` site breaks; someone converts a legacy function that uses `arguments` and it silently starts reading an outer function's arguments; someone tries `Arrow.prototype.helper = ...` and quietly attaches a property to `undefined`, throwing a TypeError. Name the mechanism and the failure together.
- A refactor converts `function Point(x, y) { this.x = x; }` to `const Point = (x, y) => { this.x = x; }`. What breaks and when?Every `new Point(...)` call site throws `TypeError: Point is not a constructor`, and it throws before the body runs, so no partially built object escapes. Anything hanging off `Point.prototype` also breaks, since arrows have no `prototype` property — the assignment itself throws. The failure is immediate and total rather than subtle, which is the one mercy here.
- If arrows have no `arguments`, how do you write a variadic arrow function?Use rest parameters: `const f = (...args) => args.length`. Rest gives a real `Array`, so `map`, `filter` and destructuring work directly, unlike the array-like `arguments` object. It also works identically in ordinary functions, which is why `arguments` is legacy surface in modern code.
- Can an arrow function use `super`?Yes, when it is written inside something that has a home object — a class method, a class field initializer, or an object-literal shorthand method. The arrow inherits that home object and `super.foo()` resolves against it. Written anywhere without an enclosing home object, `super` is a syntax error, because the arrow supplies none of its own.
saying these in an interview costs you the question
- Says arrows have their own arguments object
- Thinks new on an arrow returns a plain object
- Claims arrow.prototype exists but is empty
- Believes there is an async generator arrow form
- Says bind and call cannot be used on arrows at all