Skip to content
academia.sh

Lesson 02 / 21

Ways to Run Code

Running from a file, the interactive shell, the browser console, and the script tag; the script–module distinction and reading an error message.

Contents

The previous lesson showed that which names a script can see depends on the host environment. This lesson looks at how code is handed to those environments: a file from the command line, an interactive shell session, the browser console, and a script tag attached to a page. The four paths are not merely “ways to start code”; each also determines the rules under which the code will be interpreted.

The same measurement script will be run through all of these paths. Where the results match and where they diverge draws the boundary between the language’s core and the environment’s contribution a second time.

Running a File from the Command Line

The server-side runtime takes its source from a file. The examples in this course have been run with the node command; the name and extension given to the file determine how the source is interpreted.

A file named measurement.mjs:

const temperatures = [21.5, 19.75, 23, 18.25];
let total = 0;
for (const value of temperatures) {
  total += value;
}
console.log("reading count:", temperatures.length);
console.log("average:", total / temperatures.length);
node measurement.mjs
reading count: 4
average: 20.625

The file is read start to finish, parsed, and executed. The process ends once no pending work is left after the code’s last line finishes.

For one-line experiments, there is no need to open a file; the source can be given directly as an argument:

node -e 'console.log(Math.max(21.5, 19.75, 23))'
23

The Interactive Shell

When called with no arguments, the runtime opens an interactive shell (REPL). This shell reads each line, evaluates it, prints the result, and loops back. Its name comes from these four steps.

> const temperatures = [21.5, 19.75, 23, 18.25];
undefined
> temperatures.length
4
> Math.max(...temperatures)
23
> typeof temperatures
'object'

Two observations are enough to understand this shell.

First: the shell prints each line’s value. A declaration statement produces no value; this is why the first line’s result is undefined. This does not mean the declaration failed — the next line reads the variable without any problem. The statement–expression distinction from the Programming Fundamentals course becomes directly visible here: const is a statement, temperatures.length is an expression.

Second: the format the shell prints is not the same as the console.log format. The result of typeof was written in the shell as 'object', with quotes; printed with console.log, the same value would appear as object, without quotes. The shell prints a value in an inspection format: strings are quoted, so the number 4 and the text '4' can be told apart. This format’s details are specific to the runtime and are not defined in the standard.

The same difference shows up with objects too:

const record = { station: "A1", temperature: 21.5 };
console.log(record);
console.log(String(record));
{ station: 'A1', temperature: 21.5 }
[object Object]

The readable form on the first line is the environment’s inspection format. The second line is the result of the language’s own to-string rule and is the same in every environment. The browser console shows a collapsible tree for the first line; the second line still produces [object Object] there too.

The Browser Console and the Script Tag

In a document-based environment, code enters two ways. The console is an interactive shell opened in the context of the page; the same rules above apply exactly. The script tag binds code to the page’s loading process:

<script src="measurement.js"></script>

The tag’s position and attributes determine the moment of execution. If no attribute is given, document parsing stops, the source is downloaded and run, then parsing resumes. The defer attribute delays execution to the end of document parsing; the type="module" attribute both applies the same delay and loads the source as a module rather than a script.

This course’s examples can be verified in the console and from the command line; since they do not touch document objects, they do not require a page context. Code that accesses the document is the subject of the host environment, not the language.

Script or Module

ECMAScript parses a source text according to one of two separate targets: script and module. This is a decision derived from the file extension or the tag’s attribute, and it produces three concrete differences.

The first difference is strict mode. Source loaded as a module always runs in strict mode; source loaded as a script does not, unless requested separately. Strict mode turns several behaviors that would silently produce a wrong result into an error. Its most visible example is assigning to an undeclared name:

try {
  undeclaredName = 5;
} catch (error) {
  console.log(error.name + ": " + error.message);
}

When run as a module (with an .mjs extension):

ReferenceError: undeclaredName is not defined

The same assignment throws no error in a non-strict script; it silently creates a global name. This is why a name created by a typo can go on living throughout the program.

The second difference is the value of this in the outermost scope. In a module, this value is undefined; in a script, it is an object chosen by the environment.

The third difference is whether names declared in the outermost scope get written onto the global object. In a module, the outermost scope is private to the module; no name declared there becomes a property of the global object:

var x = 1;
console.log(typeof this);
console.log(typeof globalThis.x);

When run as a module:

undefined
undefined

In a source loaded as a classic script, on the other hand, outermost-scope names declared with var do become properties of the global object. Two files accidentally overwriting each other’s names is a consequence of this behavior; the module model removes this sharing.

Throughout this course, examples have been run as modules. If an example behaves differently only in script mode, this will be noted separately in the text.

Reading an Error Message

An uncaught error carries three pieces of information: the error’s name, message, and stack trace. All three can be read separately:

const records = [{ station: "A1", temperature: 21.5 }];
try {
  console.log(records[1].temperature);
} catch (error) {
  console.log("name   :", error.name);
  console.log("message:", error.message);
}
name   : TypeError
message: Cannot read properties of undefined (reading 'temperature')

Name gives the error’s class. TypeError reports that a value was not of the expected kind, ReferenceError that a name could not be found, SyntaxError that the source could not be parsed.

Message is the event’s detail. Reading the message above step by step directly says where the error is: the value of records[1] came out undefined, and a temperature property was then looked up on undefined. So the real problem is that the array’s second element does not exist — not the property’s name. An error message shows the symptom of the error; the cause is usually a line above it.

Stack trace lists, in lines starting with at, the call chain the error passed through: the topmost line is where it occurred, the lines below give who called from where. Because its format, file paths, and the runtime’s internal frames vary by machine, only the name and message sections will be shown in this course.

An uncaught error terminates the process with a nonzero exit code in a server-side runtime. In a browser, the page keeps running; the error is only written to the console. The same error’s outcome differs across the two environments, but its message is the same.

Summary

  • Code enters four ways: a file, a command-line argument, an interactive shell, and a script tag in a document context.
  • The interactive shell prints each line’s value in an inspection format; because declarations produce no value, undefined appears. This format comes from the environment, not the standard.
  • ECMAScript parses a source as either a script or a module; a module always runs in strict mode, its outermost this value is undefined, and its names are not written onto the global object.
  • Strict mode turns assignment to an undeclared name into a ReferenceError instead of silently creating a global name.
  • An error message is made up of name, message, and stack trace; the message describes the symptom, and the cause is usually in the step before it.

Next Step

The script being loaded as a module, the presence of const and let declarations, the for...of loop — all of these are abilities the language gained at a specific point. The next lesson takes up how a feature enters the language: how the standard is versioned, the stages a proposal passes through, and how the question “does this feature exist here” gets answered without looking at a version name.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close