Note: In addition to “runtime”, “execution environment” is also used to refer to the same concept, but these two terms are completely different. To avoid confusion, we will use the term “runtime” throughout this article.
Also, “runtime” has many meanings, but in this context, it refers to the runtime environment.
The Function That Exists and Doesn’t Exist
Our protagonist, Xiao Ming, received a requirement at work to encode a string in base64.
console.log(btoa('hello')) // aGVsbG8=
To convert a string from base64, simply change the function name to
console.log(atob('aGVsbG8=')) // hello
Some people may be curious, like me, why the functions are named
btoa. I initially misunderstood that the “b” in
atob stood for “base64”, so it was converting something to base64. But in fact, it is the opposite.
atob converts a string from base64.
btoa() named like that?, “a” stands for ASCII and “b” stands for binary, not Base64. Therefore,
atob means converting ASCII data (i.e., strings) to binary, which is to convert a base64-encoded string back to its original form.
For example, base64 can convert any binary data to a string, which is its most valuable feature. For example, you may have used data URI, which is a way to encode images as base64 strings.
btoa stands for binary to ASCII, which means encoding anything using base64. The output will be a base64-encoded string.
atob, on the other hand, means ASCII to binary, which is to convert a base64-encoded string back to its original form.
Okay, after talking so much about base64, let’s get back to the point.
Xiao Ming found out that he needed to use
btoa to complete the task and successfully implemented the feature on the webpage. Two months later, his supervisor asked him to implement the same feature on a server running Node.js.
Xiao Ming thought, “What’s so difficult about this?” and used
btoa as before. However, this time, a different result appeared, and an error was thrown:
Uncaught ReferenceError: btoa is not defined
This happens because Xiao Ming did not have the concept of runtime in mind.
What is Runtime?
As shown in the figure below, the left is the Node.js runtime, the middle is the things of JS itself, and the right is the browser runtime, each with its own things:
Therefore, you may have had a similar experience of not being able to execute the same code in Node.js. Now you know that this is because Node.js does not provide these things, such as
atob, and you cannot use them directly in Node.js (if you can, it means you are using other libraries or polyfills).
fs, but you cannot do this on the browser. Different runtimes provide different things, and you need to be very clear about which runtime you are in.
How to distinguish whether a feature is provided by the runtime or built into JS?
By following a principle, you can have a probability of about 80% to distinguish correctly, that is: “Is this feature related to the runtime itself?”
For example, the DOM and BOM APIs are closely related to the browser. When using the Node.js runtime, we don’t have a document because there is no such thing as a page, and we don’t have localStorage because that is something only the browser has. Therefore, things like
process, which can read a lot of information about threads, the browser cannot allow you to do this, so obviously it cannot be used on the browser, it is something exclusive to the
The other 20% are some exceptions that appear to be unrelated to the runtime, but are actually related. For example,
btoa just converts to Base64, what does it have to do with the runtime? But coincidentally, it is provided by the runtime.
console is also provided by the runtime, and there is a feature to note, that is, sometimes different runtimes will provide the same things. For example,
But although they look the same, the internal implementation is completely different, and the way they behave may also be different. For example, the
console.log in the browser will output to the console of the devtool, while Node.js will output to your terminal.
setInterval are also like this, although they are available in both the browser and Node.js, the implementation behind them is completely different.
If you want to confirm whether an API is provided by the runtime, there is a simple and correct way, which is to look at the ECMAScript specification or MDN. For example, for
atob, in the Specifications section of MDN, you can see that its source is the HTML Standard, not ECMAScript, which means it is not part of ECMAScript:
In short, if you cannot find it in the ECMAScript specification, it means it is provided by the runtime.
On MDN, these are not provided natively by ECMAScript, but are provided by the browser’s API, called Web API: https://developer.mozilla.org/en-US/docs/Web/API
fs, to write some small toys.
When you are familiar with different runtimes, you will find that the runtime is not only providing more APIs, but also a limiter.
When your runtime is a browser, the functions you can perform will naturally be subject to browser restrictions. For example, you cannot “actively read” files on your computer because the browser does not allow you to do so for security reasons. You also cannot restart the computer because the browser does not allow you to do so. When performing network-related operations, you will also be subject to the restrictions of the same-origin policy and CORS, which are unique to the browser environment.
Once you switch to a different runtime, all these restrictions will be lifted. When using Node.js to execute code, you can read files, restart the computer, and there are no restrictions on the same-origin policy and CORS. You can do whatever you want, send requests to anyone you want, and the response will not be intercepted.
Promise, and you can find their descriptions in the ECMAScript specification.
Some APIs are provided by the runtime, such as
document, which are APIs provided by the browser. Once you leave the browser runtime, you will not have these APIs available.
But this does not mean that APIs that can be used on both the browser and Node.js runtimes are built-in APIs of the language. For example,
setTimeout, and the recently natively supported
fetch in Node.js (https://github.com/nodejs/node/pull/41749) can be used on both the browser and Node.js, but they are all provided by the runtime.
In other words, the browser implements the APIs of
With the concept of runtime, if you encounter a function that can be used in the browser but not in Node.js in the future, you will know why.