What does it mean to "contextify" an object?
History
All JavaScript executed within Node.js runs within the scope of a "context". According to the V8 Embedder's Guide:
In V8, a context is an execution environment that allows separate, unrelated, JavaScript applications to run in a single instance of V8. You must explicitly specify the context in which you want any JavaScript code to be run.
When the method vm.createContext() is called with an object, the contextObject argument
will be used to wrap the global object of a new instance of a V8 Context
(if contextObject is undefined, a new object will be created from the current context
before its contextified). This V8 Context provides the code run using the node:vm
module's methods with an isolated global environment within which it can operate.
The process of creating the V8 Context and associating it with the contextObject
in the outer context is what this document refers to as "contextifying" the object.
The contextifying would introduce some quirks to the globalThis value in the context.
For example, it cannot be frozen, and it is not reference equal to the contextObject
in the outer context.
import { createContext, runInContext } from 'node:vm'; // An undefined `contextObject` option makes the global object contextified. const context = createContext(); console.log(runInContext('globalThis', context) === context); // false // A contextified global object cannot be frozen. try { runInContext('Object.freeze(globalThis);', context); } catch (e) { console.log(`${e.constructor.name}: ${e.message}`); // TypeError: Cannot freeze } console.log(runInContext('globalThis.foo = 1; foo;', context)); // 1
const { createContext, runInContext } = require('node:vm'); // An undefined `contextObject` option makes the global object contextified. const context = createContext(); console.log(runInContext('globalThis', context) === context); // false // A contextified global object cannot be frozen. try { runInContext('Object.freeze(globalThis);', context); } catch (e) { console.log(`${e.constructor.name}: ${e.message}`); // TypeError: Cannot freeze } console.log(runInContext('globalThis.foo = 1; foo;', context)); // 1
To create a context with an ordinary global object and get access to a global proxy in
the outer context with fewer quirks, specify vm.constants.DONT_CONTEXTIFY as the
contextObject argument.
This constant, when used as the contextObject argument in vm APIs, instructs Node.js to create
a context without wrapping its global object with another object in a Node.js-specific manner.
As a result, the globalThis value inside the new context would behave more closely to an ordinary
one.
import { createContext, runInContext, constants } from 'node:vm'; // Use vm.constants.DONT_CONTEXTIFY to freeze the global object. const context = createContext(constants.DONT_CONTEXTIFY); runInContext('Object.freeze(globalThis);', context); try { runInContext('bar = 1; bar;', context); } catch (e) { console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: bar is not defined }
const { createContext, runInContext, constants } = require('node:vm'); // Use vm.constants.DONT_CONTEXTIFY to freeze the global object. const context = createContext(constants.DONT_CONTEXTIFY); runInContext('Object.freeze(globalThis);', context); try { runInContext('bar = 1; bar;', context); } catch (e) { console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: bar is not defined }
When vm.constants.DONT_CONTEXTIFY is used as the contextObject argument to vm.createContext(),
the returned object is a proxy-like object to the global object in the newly created context with
fewer Node.js-specific quirks. It is reference equal to the globalThis value in the new context,
can be modified from outside the context, and can be used to access built-ins in the new context directly.
import { createContext, runInContext, constants } from 'node:vm'; const context = createContext(constants.DONT_CONTEXTIFY); // Returned object is reference equal to globalThis in the new context. console.log(runInContext('globalThis', context) === context); // true // Can be used to access globals in the new context directly. console.log(context.Array); // [Function: Array] runInContext('foo = 1;', context); console.log(context.foo); // 1 context.bar = 1; console.log(runInContext('bar;', context)); // 1 // Can be frozen and it affects the inner context. Object.freeze(context); try { runInContext('baz = 1; baz;', context); } catch (e) { console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: baz is not defined }
const { createContext, runInContext, constants } = require('node:vm'); const context = createContext(constants.DONT_CONTEXTIFY); // Returned object is reference equal to globalThis in the new context. console.log(runInContext('globalThis', context) === context); // true // Can be used to access globals in the new context directly. console.log(context.Array); // [Function: Array] runInContext('foo = 1;', context); console.log(context.foo); // 1 context.bar = 1; console.log(runInContext('bar;', context)); // 1 // Can be frozen and it affects the inner context. Object.freeze(context); try { runInContext('baz = 1; baz;', context); } catch (e) { console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: baz is not defined }