Object legacy accessors (__defineGetter__ …)

Netscape shipped these before the language had a property-descriptor model. They still work everywhere, they are normatively optional, and defineProperty does all four jobs properly.

Object methods (Annex B)Annex B (legacy)Live demo
Common call
Object.defineProperty(o, k, {get})
Returns
undefined, or the accessor function
Replaces
nothing; defineProperty replaces THEM
Watch out
they create enumerable properties, unlike defineProperty
__defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__
→ varies

Demo

Live evaluation
Try:
Inputs
vnumberthe value the getter returns
Output
(o => { o.__defineGetter__('a', () => 7); return [Object.keys(o), o.a]; })({})
[['a'], 7]

The pair is the object own keys and the value read back. Note that the key IS listed — these legacy methods create an enumerable property, which is the one behavioural difference from Object.defineProperty, whose enumerable flag defaults to false. Compare with the defineProperty demo, where the equivalent call produces an empty key list. That difference is the only practical reason to know these exist beyond recognising them in old code.

Parameters

NameTypeRequiredDescription
keystring | symbolyesThe property key. All four take this as their first argument.
accessorFunctionno (none)Only the define pair take a second argument — the getter or setter function. A non-callable value is a TypeError.

Return value

varies — The define pair return undefined and modify the object. The lookup pair return the accessor function, or undefined if there is none.

Common patterns

Use defineProperty
The full descriptor model, explicitly.
Object.defineProperty(o, "a", {
  get: () => 7,
  enumerable: true,
  configurable: true,
});
Or a literal getter
Clearest when the object is being created.
const o = { get a() { return 7; } };
Look one up
The descriptor carries the getter.
Object.getOwnPropertyDescriptor(o, "a")?.get;

Examples

1. Defines a getter
const o = {}; o.__defineGetter__("a", () => 7); o.a
Returns
7
2. And it is enumerable
Object.keys(o)
Returns
['a']
3. defineProperty is not
const p = {}; Object.defineProperty(p, "a", {get: () => 7}); Object.keys(p)
Returns
[]
4. A setter
const o = {}; let seen; o.__defineSetter__("a", v => { seen = v; }); o.a = 5; seen
Returns
5
5. Looking one up
typeof ({get a() { return 1; }}).__lookupGetter__("a")
Returns
'function'
6. None defined
({}).__lookupSetter__("a")
Returns
undefined

Pitfalls

1. They are Annex B, not the core language
Annex B is normatively optional — required only of web browsers. A conforming non-browser runtime may omit them entirely, so code using them is not portable even though every engine happens to ship them today.
May be absent
o.__defineGetter__("a", f)
TypeError in a conforming non-browser host
Standard form
Object.defineProperty(o, "a", {get: f})
portable
2. They differ from defineProperty on enumerability
Not a bug in either — a genuine behavioural difference. These create enumerable, configurable properties; defineProperty defaults both to false. Porting old code across without specifying the flags silently changes what appears in Object.keys and JSON.
Now hidden
Object.defineProperty(o, "a", {get: f})
absent from Object.keys
Match the old behaviour
Object.defineProperty(o, "a", {get: f, enumerable: true, configurable: true})
listed again
3. They are inherited methods, so they can be shadowed
The same weakness as hasOwnProperty. An object created with Object.create(null) does not have them, and untrusted data can replace them.
Missing
Object.create(null).__defineGetter__("a", f)
TypeError: ...__defineGetter__ is not a function
Static form works
Object.defineProperty(Object.create(null), "a", {get: f})
fine
4. The lookup pair search the prototype chain
Unlike getOwnPropertyDescriptor, which is own-only, __lookupGetter__ walks up the chain to find an inherited accessor. That is occasionally useful and frequently surprising when you expected an own-property answer.
Finds inherited
const p = {get a() { return 1; }};
Object.create(p).__lookupGetter__("a")
the function
Own only
Object.getOwnPropertyDescriptor(Object.create(p), "a")
undefined

When to use

Use it
  • Never in new code
  • Recognising them while reading something written before 2009
Reach for something else
  • Defining an accessor → Object.defineProperty, or a get/set literal
  • Reading an accessor → Object.getOwnPropertyDescriptor
  • Portable code → Annex B is optional outside browsers
  • Null-prototype objects → the methods are not there

Notes

Complexity
O(1) to define; O(d) for the lookup pair, which walk the chain
Return
undefined from the define pair; the accessor function or undefined from the lookup pair
CPython impl
V8: Builtins-object — the Annex B section
Memory
No allocation beyond the property slot
Thread-safe
Single-threaded

FAQ

__defineGetter__ and __defineSetter__ attach an accessor to a property; __lookupGetter__ and __lookupSetter__ retrieve one. Object.defineProperty covers the first two and getOwnPropertyDescriptor covers the last two.

o.__defineGetter__("a", () => 1);
Object.defineProperty(o, "a", {get: () => 1});   // the same thing

History

Netscape 4
Added in the late 1990s, years before the language had any notion of property descriptors.
ES5
Object.defineProperty introduced the descriptor model and made them redundant.
ES2015
Formally documented in Annex B as normatively optional legacy features.