Javascript ShadowRealms
📣 Sponsor
It sounds dark and mysterious - but it's just another future Javascript feature. The ShadowRealm is a new feature coming to Javascript, which will let us create a separate global context from which to execute Javascript. In this article, we'll look at what the ShadowRealm is, and how it works.
Support for ShadowRealms in Javascript
ShadowRealm
s are a Javascript proposal, currently at Stage 3. As such, ShadowRealm
s do not have support in browsers or natively in server side languages like Node.JS, and given it has had many changes over the years, there is no stable babel or npm plugin to polyfill the functionality. However, given it has reached Stage 3, this means there won't be very many changes going forward, and we can expect ShadowRealm
s to have native support at some point in the future.
How ShadowRealms in Javascript work
A ShadowRealm
is ultimately a way to set up a totally new environment with a different global object, separating the code off from other realms. When we talk about a global object in Javascript, we are referring to the concept of window
or globalThis
. The problem that ShadowRealm
ultimately tries to solve, is to reduce conflict between different sets of code, and provide a safe environment for executing and running code that needs to be run in isolation. It means less pollution in the global object from other pieces of code or packages. As such, code within a ShadowRealm
cannot interact with objects in different realms.
ShadowRealm Use Cases:
- Code editors where the user can edit code, and which we don't want to interact with the main webpage.
- Plugins that can be executed independently.
- Emulating the DOM in a separated environment, i.e. if we need to know the scroll position in certain scenarios, we could emulate it within a
ShadowRealm
so that the user scrolling on the main webpage would not affect thewindow.top
variable in our emulation.
ShadowRealm
s run on the same thread as all other Javascript - so if you want to multi-thread your Javascript, you still have to use Web Workers. As such, a ShadowRealm
can exist within a worker, as well as within a regular Javascript file. ShadowRealm
s can even exist within other ShadowRealm
s.
Creating a ShadowRealm in Javascript
Let's look at how a ShadowRealm
actually looks in code. The first thing we have to do is call a new ShadowRealm
instance. We can then import some Javascript into our Realm, which will run within it. For this, we use a function called importValue
, which works effectively in the same way as import
.
let myRealm = new ShadowRealm();
let myFunction = await myRealm.importValue('./function-script.js', 'analyseFiles');
// Now we can run our function within our ShadowRealm
let fileAnalysis = myFunctions();
In the above example, analyseFiles
is the export name we are importing from function-script.js. We then capture and store this export within myFunction
. Significantly, the export we import into our realm must be callable, so it must effectively be a function we can run.
Our function-script.js file is just a normal Javascript file with an export. It may look something like this:
export function analyseFiles() {
console.log('hello');
}
The ShadowRealm
is totally separate from other global objects we may have, such as window
or globalThis
.
Similar to other imports, we can use the curly bracket import notation:
let myRealm = new ShadowRealm();
const { runFunction, testFunction, createFunction } = await myRealm.importValue('./function-script.js');
let fileAnalysis = runFunction();
Or, we can create multiple promises that all translate into an array if we want to use named importValue
s.
let myRealm = new ShadowRealm();
const [ runFunction, testFunction, createFunction ] = await Promise.all([
myRealm.importValue('./file-one.js', 'runFunction'),
myRealm.importValue('./file-two.js', 'testFunction'),
myRealm.importValue('./file-three.js', 'createFunction'),
]);
let fileAnalysis = runFunction();
Executing Code with evaluate in ShadowRealms
Should we want to execute code directly in a ShadowRealm, which does not come from another file, we can use the evaluate
method on our ShadowRealm, to execute a string of Javascript. This works in much the same way as eval()
:
let myRealm = new ShadowRealm();
myRealm.evaluate(`console.log('hello')`);
ShadowRealm importValue is thennable
Since importValue
returns a promise, its value is thennable. That means we can use then()
on it, and then do something with the output function that it returns. For example:
window.myVariable = 'hello';
let myRealm = new ShadowRealm();
myRealm.importValue('someFile.js', 'createFunction').then((createFunction) => {
// Do something with createFunction();
console.log(window.myVariable); // Returns undefined
})
The cool thing about this is that the global object does not leak into the then()
statement. So window.myVariable
is undefined. This provides us with a totally isolated area of our code where we don't have to worry about interference from the global object!
We can also use this methodology to access global variables defined in someFile.js
. For example, let's say we changed someFile.js
to this:
globalThis.name = "fjolt";
export function returnGlobals(property) {
return globalThis[property];
}
Now, in our then
function, we could get the value of globalThis.name
:
window.myVariable = 'hello';
let myRealm = new ShadowRealm();
myRealm.importValue('someFile.js', 'returnGlobals').then((returnGlobals) => {
// Do something with returnGlobals();
console.log(returnGlobals("name")); // Returns fjolt
console.log(window.myVariable); // Returns undefined
})
Conclusion
Today, iframe
s are the way we usually separate out separate environments on the web. iframe
s are clunky, and can be quite annoying to work with. ShadowRealm
s on the other hand, are more efficient, allow us to easily integrate with our existing code base, and integrate well with modern Javascript technologies like Web Workers.
Given their unique value proposition of providing a separated area for code execution, which does not interact at all with the rest of the code base, ShadowRealm
s will likely become a staple in writing Javascript code. They could become an important way for packages and modules to export their contents without worrying about interferance from other parts of the code base. As such, expect to see them popping up in the future.
Read about the ShadowRealm specification here.
More Tips and Tricks for Javascript
- How to validate an email with Javascript
- Web Components and the Shadow DOM
- How to install nvm, the Node Version Manager
- Javascript Logical Statements and Loops
- Making your own Express Middleware
- Javascript on Click Confetti Effect
- Javascript Map, and How it is Different from forEach
- Javascript Errors
- A Guide to Heaps, Stacks, References and Values in Javascript
- Javascript: Check if an Array is a Subset of Another Array