In a browser window, window
and self
are both accessor properties on the global object and both return the globalThis
value of the relevant realm. I.e window==self//true
However running Object.getOwnPropertyDescriptor()
on them yields different results. As you can see below, Object.getOwnPropertyDescriptor(window, 'window')
returns {configurable: false, get: get(), set: undefined}
, while Object.getOwnPropertyDescriptor(window, 'self')
returns {configurable: true, get: get(), set: set()}
where set()
is a setter that overwrites self
with the given value.
console.log({
window: Object.getOwnPropertyDescriptor(globalThis, 'window'),
self: Object.getOwnPropertyDescriptor(globalThis, 'self')
});
According to the spec, as properties of the main global object, window
is [LegacyUnforgeable]
and self
is [Replaceable]
In practice what that means is that unlike window
, self
can be deleted or overwritten. It doesn't make any sense then that self
is a "read-only property" of the window object as it says in the spec.
Workers
In workers window
is undefined and will throw a ReferenceError, while self
is a local variable rather than a property on the global object and cannot be overwritten as it can in a browser window.
Conclusion
Both self
and window
return the same object in a browser window however unlike window
, self
can be deleted or replaced.
Only self
can be safely referenced in both a browser window and a worker.
globalThis
was added to ES2020 to create a uniform way of getting the global object. But as it still doesn't work in many browsers, you're better off using self
in browser environments.