Question presented to candidate: "Two separate customization hooks: Symbol.hasInstance lets a class define its own instanceof logic instead of the default prototype-chain walk, and Symbol.toPrimitive lets a class control exactly how it coerces to a number, string, or generic primitive value. Implement both on a couple of small example classes, and explain: what are the three coercion hints, and which JavaScript operators actually use each one?"
What a strong answer should cover:
Symbol.hasInstanceis a static method that, when defined, COMPLETELY REPLACES whatinstanceofdoes for that class — it does not run in addition to the prototype-chain check, it overrides it entirely.Symbol.toPrimitiveis an instance method taking a singlehintargument, which is always one of exactly three string values:"number","string", or"default".- Without a
Symbol.toPrimitive, coercion falls back to the older two-method protocol:valueOf()is tried first for hint"number"/"default",toString()is tried first for hint"string". - The specific hint-to-operator mapping is easy to get wrong from memory:
Number(x)and unary+xuse"number";String(x)and template literals use"string"; and — the narrowest bucket, not the widest — ONLY binary+and loose equality (==/!=) use"default". Every other arithmetic operator (-,*,/,%,**) and every relational operator (<,>,<=,>=) uses"number"directly, not"default". - A
Symbol.toPrimitiveimplementation must return an actual primitive value; returning an object throws a realTypeError.
Clarifying questions expected:
- "Should Symbol.hasInstance validate the value's type strictly, or is duck-typing acceptable?" — a real design question, since a permissive check can make instanceof misleadingly pass for unrelated values.
- "Does the class need value equality (two instances with the same underlying value being ==) in addition to coercion, or is coercion to a primitive enough on its own?" — clarifies whether Symbol.toPrimitive alone covers the requirement, since == on two objects still compares by reference unless both sides coerce to the same primitive.
Code / implementation expected: Yes — two small classes plus a runnable test that logs which hint is actually passed for each real operator, since that mapping is exactly the kind of claim that must be verified, not recalled.