JavaScript is a high-level, dynamically typed programming language that became the core language of the World Wide Web. It is a multi-paradigm language, supporting object-oriented, prototype-based and functional styles, and it is standardized as ECMAScript by Ecma International. Every major web browser runs it natively, and through runtimes such as Node.js and Deno it also runs on servers, desktops, mobile devices and embedded systems. Because it is the only language that browsers execute directly for scripting, it is central to internet applications and to modern programming language practice.
The language was created in about ten days in May 1995 by Brendan Eich at Netscape, originally to add interactivity to web pages. Its design reflects that origin: a C-like syntax, objects built from prototypes rather than classes, first-class functions borrowed from Lisp-family languages, and a loose type system with automatic type coercion. Its later history is a story of standardization, of a long period of stagnation followed by rapid growth after 2008, and of an expanding role as a general-purpose language. Its engines are among the most optimized runtimes in computing, and its ecosystem is one of the largest software package collections in existence.
This entry covers the language's origins and standardization, its core semantics (types, objects, functions, scope, asynchronous execution and modules), the browser and non-browser runtimes that host it, the engines that execute it, its security model and criticisms, the ecosystem of frameworks and tooling, its evolution through the ECMAScript editions up to ECMAScript 2026, and its relations to other fields and technologies.
Origins at Netscape (1995)
In the early 1990s, the web was largely a system of static documents. HTML could arrange text and images, but it could not respond to a user's actions without a round trip to the server. Netscape Communications, whose Navigator browser led the market, wanted a way for page authors to add small amounts of behavior directly in the browser. In April 1995 the company hired Brendan Eich, who was then asked to bring a lightweight scripting language to Navigator. Eich produced the first working version in about ten days, in May 1995, so that it could fit the release schedule of the Navigator 2.0 beta.
Eich's original goal was closer to Scheme, a Lisp dialect, but Netscape's management wanted the syntax to resemble Java, which was then attracting wide attention as a serious language for the web. Eich judged that Java was too complex for the people Netscape hoped to reach, namely designers and hobbyist scripters rather than professional programmers. The resulting design was a compromise: it borrowed C-style punctuation and surface conventions from Java, first-class functions in the manner of Scheme, and a prototype-based object model from the Self language. The partnership with Sun Microsystems, which was developing Java, reinforced this positioning. The new language was presented as a companion to Java, a complementary tool for the browser rather than a competitor.
The language went through three names. It was first called Mocha, then renamed LiveScript when it appeared in Netscape's beta releases in the fall of 1995. In December 1995, in a joint announcement with Sun, it was given the name JavaScript. The choice was openly commercial, since the name borrowed the attention that Java was receiving. Many developers later criticized the name as misleading, because the two languages share little beyond part of their syntax and their names. The confusion has persisted for decades, and it is one of the most frequently cited consequences of the language's marketing-driven origins.
The first implementation was also written under heavy constraints. Eich built the first engine, which would later be known as SpiderMonkey, in C, and it shipped in Navigator to run the new language. The core of that code stayed close to the original ten-day prototype through 1996, so many early design decisions, including loose typing, implicit conversions and an informal approach to scoping, were effectively fixed before the language had been tested at scale. When Mozilla inherited the Netscape codebase in 1998, the engine came with it, and it was later revised to match the ECMA-262 standard.
Within a short period the language had moved from a single company's product to a subject of industry-wide standardization, which is discussed in the next section. The origins at Netscape explain both the rapid success of the language on the web and the design compromises that later editions have had to work around.
Standardization by ECMA and TC39
Netscape's decision to hand JavaScript to a standards body was what turned it from one vendor's scripting language into a shared specification. In November 1996 Netscape announced that it had submitted the language to Ecma International, and Ecma formed Technical Committee 39 (TC39) to produce the standard. The committee chose the name "ECMAScript" as a compromise, so that the specification would not be associated with any single company, and Microsoft's JScript, which had shipped in Internet Explorer 3.0 in 1996, was among the implementations the standard needed to describe. The first edition, published as ECMA-262, was adopted by the Ecma General Assembly in June 1997 and was based on the JavaScript 1.1 behavior of Netscape Navigator 3.0. That edition is known as ECMAScript 1. A second edition in 1998 made only editorial changes so that the text stayed aligned with the parallel ISO/IEC 16262 standard. The third edition, in December 1999, added regular expressions, better string handling, try/catch exception handling and a stricter definition of errors, and it set the baseline that browsers would implement for most of the following decade.
After 1999 the work stalled for almost a decade. The fourth edition, ECMAScript 4, was intended to be the first major revision since 1999 and proposed substantial additions, including classes, modules and optional static typing. Its drafts were disputed inside TC39, with some members, including major browser vendors, considering the design too large and too complex to implement safely in the existing web. The disagreement split the committee. In mid-2008 the parties reached a compromise: the ambitious ES4 work was dropped, most of its features were scaled back, and the remaining incremental work became ECMAScript 3.1. That project was later renamed ECMAScript 5th Edition. The committee aimed for at least two interoperable implementations by early 2009, and the edition was published in December 2009. ES5 added strict mode, property descriptors, getters and setters, and native JSON support. The larger features that had been proposed for ES4 were postponed to a project that the committee called Harmony.
Harmony became ECMAScript 2015, also known as ES6, which the committee published in June 2015. It was the largest single update in the language's history. It introduced classes, arrow functions, block-scoped let and const, modules, promises, generators, and collections such as Map and Set. Because the edition had taken years to finish, the committee concluded that large, infrequent releases were a poor way to evolve the language. From then on, TC39 moved toward annual editions, each containing the proposals that had reached a sufficiently mature stage since the previous release. Changes are developed through a staged proposal process with six levels: a strawperson stage (Stage 0) and five maturity stages (Stage 1 to Stage 4). Each advance requires committee approval. Stage 3 requires reviewers' sign-off on a complete specification, and Stage 4 requires acceptance tests (in the Test262 suite), at least two compatible implementations, and a pull request that has been merged into the specification draft. Ratification of each annual specification is planned for the Ecma General Assembly in the summer, and the annual edition is named after its publication year.
Underlying this process is the committee's compatibility principle: existing web content must not break when the language changes. Under this principle, new syntax and built-ins are generally added in ways that do not alter the meaning of code already on the web, and the committee avoids removing behavior that sites may depend on. This is why the language can add features such as let and const alongside older rules, and why some early design choices, including the rules for automatic semicolon insertion and the typeof result for null, remain in the specification. Compatibility has therefore been one of the main reasons that the language has grown incrementally rather than through a clean redesign.
Syntax and Lexical Rules
JavaScript's surface syntax belongs to the C family. Statements such as if, for, while and switch, brace-delimited blocks, and operators with C-style precedence will be familiar to programmers who know C, Java or C#. The grammar is specified formally in the ECMAScript standard, and an engine's parser converts source text into a syntax tree before any code runs. The rules are split between a lexical layer, which groups characters into tokens, and a syntactic layer, which arranges tokens into statements and expressions. Both layers are part of the syntax of the programming language.
Semicolons are formally statement terminators, but the grammar lets programmers omit most of them through automatic semicolon insertion (ASI). The parser inserts a semicolon at a line break when the next token cannot continue the current statement. ASI also contains restricted productions: a line break directly after return, break, continue or throw, or before a postfix ++ or --, ends the statement. The best-known consequence is that a return followed by a line break and then an object literal returns undefined. Teams therefore either always write semicolons or adopt a no-semicolon style enforced by linters.
The lexical grammar divides source text into input elements: white space, line terminators, comments, and tokens. Tokens include identifiers, reserved words, punctuators, numeric literals, string literals, template literals and regular expression literals. Identifiers may use Unicode characters, with the permitted code points defined by the ID_Start and ID_Continue properties of Unicode. Numeric literals accept a n suffix for BigInt values and underscore separators such as 1_000_000. Template literals use backticks, support ${…} interpolation and span multiple lines, and tagged templates pass their parts to a function. The lexer cannot decide alone whether a slash begins a regular expression or is a division operator; the decision depends on the syntactic context, which is a useful example of why lexing and parsing are interleaved.
Variable declarations differ in scope and in initialization timing. var is function-scoped, may be redeclared, and its declarations are hoisted and initialized to undefined, so reading it before its line yields undefined rather than an error. let and const are block-scoped. Their bindings are hoisted but not initialized, and the span from the start of the block until the declaration is evaluated is the temporal dead zone, in which any access throws a ReferenceError. A const declaration must have an initializer and cannot be rebound, but the object it refers to remains mutable. Function declarations are hoisted together with their bodies, whereas function expressions and arrow functions are bound like variables.
Several syntactic features were added in later editions to reduce verbose code. ES2015 introduced destructuring for objects and arrays in declarations, parameters and assignments, along with spread and rest syntax, default parameters and arrow functions. ES2018 extended spread and rest to object literals. ES2020 added optional chaining (user?.address?.city), which replaces chains of explicit null checks, and nullish coalescing (??), which falls back only when the left operand is null or undefined. Thus 0 ?? 5 evaluates to 0, while 0 || 5 evaluates to 5. Because new keywords must not break existing code, some words such as let and yield are treated as reserved only in particular contexts, a constraint that shapes how the grammar grows.
Values, Types and Coercion
JavaScript defines seven primitive types: undefined, null, boolean, number, string, symbol and bigint. Every other value is an object, including arrays, functions and dates. Primitives are immutable and compared by value, while objects are mutable and compared by reference. Symbol arrived with ES2015 and bigint with ES2020, so the set of primitives grew after the language was first released. The typing discipline is dynamic: a value carries its type at runtime, while a variable has no fixed type and can hold a number at one moment and a string the next. The typeof operator reports the type of a value at runtime, although it has well-known exceptions.
All ordinary numbers are IEEE 754 double-precision floating-point values, the same binary64 format used by many other languages, so there is no separate integer type. Integers are exact only up to , which is the value of Number.MAX_SAFE_INTEGER. Beyond that range, distinct integers can collapse to the same value: evaluates to . Ordinary decimal fractions are also approximated in binary, so the expression 0.1 + 0.2 yields 0.30000000000000004. Values of NaN are not equal to themselves, which is a consequence of the same floating-point rules. These effects are the reason the floating-point arithmetic and numerical stability concerns that apply to other languages also appear in everyday JavaScript code.
The BigInt type was added to address integers that exceed this safe range. A bigint literal is written by appending n to an integer, as in 9007199254740993n, and bigint values support arithmetic operators such as +, -, * and %. Mixing a bigint with a number in arithmetic throws a TypeError rather than silently converting, and the unsigned right-shift operator >>> is not available for bigints. These restrictions keep exact integer arithmetic from being mixed with the approximate floating-point numbers by accident.
Implicit coercion is the most frequently criticized part of the value model. When operators such as + or == receive operands of different types, the engine applies conversion rules defined by the abstract operations ToPrimitive and the abstract equality comparison. These rules produce results such as [] == false evaluating to true, "" == 0 evaluating to true, and null == undefined evaluating to true while null == 0 evaluates to false. To avoid these surprises, the strict equality operator === compares values without converting their types, and it was part of the language from the third edition in 1999 onward. Most style guides recommend using it by default, and the loose operator is usually reserved for deliberate, well-understood conversions.
The typeof operator has its own long-standing inconsistencies. typeof null returns "object", although null is a primitive and not an object. This comes from the first implementation in 1995, and attempts to correct it were rejected because they would have broken existing sites. Functions are also objects, yet typeof returns "function" for them, and typeof applied to an undeclared identifier returns "undefined" instead of throwing an error. Developers therefore test for null with === null, and use Array.isArray to distinguish arrays from other objects. Because these results remain part of the specification, code that relies on typeof must account for them explicitly.
Sources: MDN Web Docs, "JavaScript data types and data structures" | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Data_structures JavaScript Tutorial, "JavaScript Data Types" | https://www.javascripttutorial.net/javascript-data-types/ 33 JavaScript Concepts, "Primitive Types" | https://33jsconcepts.com/concepts/primitive-types Tutorial Reference, "JavaScript Data Types: Primitive Types and Objects" | https://tutorialreference.com/javascript/fundamentals/javascript-data-types
Objects and Prototype Inheritance
In JavaScript an object is a collection of properties, and each property key is either a string or a symbol. Array indices are stored as string keys, so an array is also an object. Every object has an internal Prototype link that points either to another object or to null. When code reads a property, the engine first checks the object's own properties. If the key is missing, it follows the link to the prototype and checks there, then continues up the chain until it finds the property or reaches null, at which point the result is undefined. Writes behave differently: an assignment normally creates or updates an own property on the object being written to, although an inherited accessor (a setter) or an inherited read-only property can intercept or block the assignment.
This model is the main conceptual difference from the class-based inheritance of languages such as Java or C++. In those languages a class describes the layout and methods of its instances at declaration time, and subclasses extend that description. In JavaScript, objects delegate to other objects directly. A new object can be created with a chosen prototype using Object.create, which was introduced in ES5, and a prototype can be replaced at runtime through Object.setPrototypeOf, although the specification and engines discourage doing so on hot paths because it invalidates optimizations. Because delegation happens through ordinary object links, there is no separate class hierarchy that must exist before instances can share behavior.
The original way to create many similar objects was the constructor function. Calling a function with new creates an object whose Prototype is set to the function's prototype property, and methods placed on that shared prototype object are then available to every instance without being copied into each one. The ES2015 class syntax, with its extends and super keywords, provides a more familiar notation for the same mechanism: methods are still stored on a prototype object, and typeof a class still reports "function". Nevertheless, the syntax is not a complete disguise. Class bodies always run in strict mode, class declarations are not hoisted in the usual way, and private fields written with # were added as a genuinely new feature rather than as a rewrite of prototype behavior.
Each property has attributes that the language defines through property descriptors. A data property has a value, a writable flag, an enumerable flag and a configurable flag, while an accessor property has get and set functions instead of a value. The Object.defineProperty and Object.getOwnPropertyDescriptor methods read and write these attributes. Building on them, Object.preventExtensions stops new properties from being added, Object.seal also makes all existing properties non-configurable, and Object.freeze further makes data properties non-writable. These operations are shallow: nested objects remain mutable unless they are frozen separately. Proxies go further by allowing a program to intercept fundamental operations such as property reads, writes and deletions through handler traps. Together with the Reflect API, proxies make it possible to implement validation, logging or virtual objects without changing the code that uses them.
Functions, Scope and Closures
In JavaScript a function is an ordinary value. It can be assigned to a variable, stored in an array or object property, passed to another function as an argument, or returned as a result. This is what makes callback-based code and higher-order functions, which take or produce other functions, natural in the language. The idea comes from the lambda calculus and from Lisp-family languages such as Scheme, whose first-class procedures Brendan Eich drew on when designing the language in 1995. Passing a comparison function to a sort routine or a handler to an event listener is therefore a routine operation rather than an unusual technique.
Scope in JavaScript is lexical. When the engine resolves a name that the current function does not declare, it searches the scopes that surround the place where the function was written, not the place from which it is called. The structure of the source code thus fixes which binding a name refers to before the program runs. This is the opposite of dynamic scoping, in which the chain of callers would decide. Because lookup is static, a reader can determine the meaning of a free variable by reading the code around it.
A closure is formed when a function keeps access to variables from an enclosing scope after that scope has finished executing. The function does not receive copies of those variables; it retains the environment in which it was created, so the bindings survive for as long as the function remains reachable. A simple counter shows the effect: a factory function declares a local variable, returns an inner function that increments it, and each call to the inner function sees the same stored value. Every call to the factory creates a new environment, so two counters made separately keep independent state. Closures underlie private state without classes, event handlers that remember their configuration, and many asynchronous callbacks. They also explain a classic pitfall. Callbacks created inside a loop with var all share one binding of the loop variable, whereas let creates a fresh binding for each iteration. Because a closure keeps its environment alive, it can also retain large objects longer than the programmer intended.
The keyword this follows a different rule from scope. Its value depends on how a function is invoked: a method call binds it to the receiver, an ordinary call leaves it undefined in strict mode, and call, apply and bind can set it explicitly. Arrow functions, introduced in ES2015, do not have a this of their own. Instead, they take the value that is in effect in the surrounding code where they are written, and neither call, apply nor bind can change it. This removed the older habit of saving this in a variable such as self, or of binding callbacks by hand, and it made callbacks inside methods far simpler to write. The same design makes arrow functions a poor choice for object methods that rely on their own receiver, and they also lack their own arguments object.
ES2015 also extended what functions can express. Default parameters are evaluated when an argument is missing, rest parameters gather the remaining arguments into a real array, and generator functions, declared with function*, can pause at yield expressions and resume later. Generators are built on the iterator protocol, in which any object with a next method that returns a value and a done flag can be consumed by for...of loops and spread syntax. Together these features let libraries describe lazy sequences and cooperative control flow using ordinary functions, rather than relying on hand-written state machines or external tooling.
Event Loop and Asynchronous Programming
JavaScript runs code on a single thread for each agent, so one script occupies that thread until it returns. A long synchronous computation in a browser therefore freezes rendering and input handling. To avoid this, the language hands waiting work, such as timers, network requests and user input, to the host environment, which later returns results to the program as callbacks. The event loop is the mechanism that schedules these callbacks.
The loop works with a call stack, a macrotask queue and a microtask queue. The call stack holds the functions currently executing. Host APIs enqueue macrotasks such as setTimeout callbacks, I/O completions and DOM event handlers. Promise reactions, queueMicrotask callbacks and MutationObserver callbacks go to the microtask queue. A common description of the cycle is: when the stack is empty, drain the whole microtask queue, including microtasks added during the drain; then run one macrotask; then repeat. Browsers may render between macrotasks, but not between microtasks. This explains a classic ordering result. In console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);, the output is 1, 4, 3, 2, because the promise reaction runs before the timer callback even though the timer was scheduled first. Node.js uses a different macrotask structure, organized into phases by the libuv library, and it has its own priority for process.nextTick, which runs before promise microtasks.
Asynchronous code has been written in three successive styles. Early code nested callbacks inside callbacks, which made error handling and control flow hard to follow. Promises, standardized in ES2015, represent a result that will arrive later and let code chain then and catch handlers. The async and await syntax, added in ES2017, lets this logic be written in a sequential style. It is still built on promises: when an await resumes, its continuation is scheduled as a microtask. Each layer changes how the code is expressed, but none of them creates real parallelism, because all of the callbacks still run on the same thread.
Real parallelism comes from Web Workers, which run scripts in separate global contexts, usually on separate threads. By default a worker shares no objects with the page. Data moves through postMessage, which copies values using the structured clone algorithm, and transferable objects can be moved to the receiver instead of copied. Shared memory is available through SharedArrayBuffer together with the Atomics object. Because threads can then read and write the same memory at once, programs must use atomic operations and synchronization to avoid data races. In browsers, SharedArrayBuffer is available only when the page is cross-origin isolated.
These design choices have practical consequences for developers. A function that runs for a long time blocks every other callback, so heavy work is often split into smaller chunks that yield back to the loop, or moved to a worker. Recursive microtask chains can also starve the macrotask queue, because the loop must finish all microtasks before it runs the next macrotask. Understanding the queues is therefore less about memorizing an ordering table and more about knowing when a piece of code gets to run.
Modules and Code Organization
For most of its first decade JavaScript had no language-level way to split a program into files. Scripts were loaded with <script> tags, and their order in the HTML determined which definitions existed when. Everything shared a single global scope, so developers protected their code with conventions: immediately invoked function expressions, the "module pattern" that returned a public API object from a closure, and manually built namespace objects such as MyApp.utils. These techniques kept names from colliding, but dependencies stayed implicit, and a missing or misordered script failed only at runtime. As pages grew into applications, this became hard to maintain, and browser-side loaders such as AMD-style libraries appeared to fill the gap.
The server side took a different path. When Node.js appeared in 2009, it had no standard module system to build on, so it adopted the CommonJS format, which was designed for JavaScript outside the browser. A CommonJS file imports dependencies with a require() call and publishes values through module.exports. Loading is synchronous: require() reads, evaluates and returns a file before the next line runs. That fits a server that reads from a local filesystem, and it made it easy to load modules conditionally, since require() is an ordinary function call that can appear inside if blocks. Npm packages were built on this model for many years.
ECMAScript modules (ESM) were standardized in ES2015 with import and export declarations. Their key property is that these declarations are static: the set of imports and exports can be determined from the source text alone, without running it. Browsers load module scripts asynchronously, defer their execution, and run them in strict mode. Node.js added ESM support later, and by version 13.2 it no longer required an experimental flag. Dynamic import() complements the static form by returning a promise, which allows a module to be fetched only when a feature is first needed; this is the basis of code splitting in single-page applications.
The two systems differ in more than syntax. CommonJS loads synchronously at runtime, while ESM is resolved and linked before evaluation, and ESM bindings are live references rather than copied values. Because ESM imports are analyzable at build time, bundlers can perform tree-shaking, removing exports that nothing uses; CommonJS is harder to analyze, so such pruning is less reliable. The two also interoperate unevenly. ESM code can import a CommonJS module, which typically exposes its module.exports object as the default export, but moving in the other direction has historically been restricted, and mixed projects often need extra configuration. Node.js decides which format a file uses from the type field in the nearest package.json, or from the .mjs and .cjs extensions. Module specifiers in ESM generally must be complete paths, including file extensions, whereas CommonJS resolution can infer them.
In production, most application code is not served as separate files. Bundlers such as webpack, Rollup, esbuild and Vite read the dependency graph, apply resolution rules from package.json fields such as main, module and exports, transform the code, and emit a smaller set of output chunks. Resolution rules therefore have practical consequences: a package that ships both formats, or resolves differently under different conditions, can load twice or pick the wrong build. Because the two module systems still coexist in many codebases, developers often work with configuration files and build tools that paper over these differences, and the boundary between language semantics and tooling remains an active source of friction.
Browser Environment and Web APIs
In a browser, JavaScript is one part of a larger system. The language defined by ECMAScript has no notion of a page, a window, a timer or a network connection. Scripts reach those things through the web platform: the Document Object Model (DOM), which represents a document as a tree of objects, and a large collection of Web APIs for events, networking, storage, graphics and device access. These interfaces are specified in standards separate from the language, so the same syntax can run in a browser with DOM access, or in a server runtime that offers file and process APIs instead.
Browsers constrain what a script may touch. The same-origin policy was introduced in Netscape Navigator 2.02 in 1995, shortly after JavaScript itself appeared. Browsers decide whether two pages share an origin by comparing their scheme, domain and port. A script on one origin cannot read the DOM or the data of a page on another origin. The restriction applies to scripts rather than to every resource: images, stylesheets and script files can still be embedded across origins, and the page's JavaScript simply cannot inspect their contents. When a server wants to permit cross-origin access, it signals this through Cross-Origin Resource Sharing headers. Because cookies accompany requests to other sites, this boundary is a central element of browser security: it stops a script running in one tab from acting with a user's authenticated session in another.
Asynchronous network access grew out of the same environment. Microsoft developed the underlying mechanism for Outlook Web Access in Exchange Server 2000, and it later appeared in Internet Explorer 5 as the XMLHttpRequest object. Mozilla implemented an equivalent JavaScript object in Gecko in 2000, and Safari and Opera adopted it in the following years, making it a cross-browser capability. Once a page could fetch data in the background and update only parts of its content, the Ajax pattern became practical, and it underpinned the rise of single-page applications. Early XHR implementations were uneven, and developers often wrapped the API in libraries such as jQuery to hide the differences. XHR relied on callbacks and manual state tracking, which made larger codebases hard to maintain. The Fetch API, introduced in 2015 as part of the modern standards effort, replaced those patterns with Promises and cleaner Request and Response objects. Fetch resolves for HTTP error statuses such as 404 and rejects only on network failure, a behavior that often surprises newcomers. Both mechanisms, by default, observe the same-origin rules.
Cross-browser differences have never fully disappeared. Engines have implemented individual Web APIs at different times, and some features remain experimental, prefixed or unavailable on certain devices. Developers have therefore depended on feature detection, which checks whether an API exists before using it, and on polyfills that supply missing functionality in JavaScript. Tools that transpile new syntax help with the language itself, but they cannot supply an entire browser interface, so host-environment differences remain a permanent part of web development.
Server-Side and Non-Browser Runtimes
The best-known way to run JavaScript outside a browser is Node.js. Ryan Dahl, who was frustrated with how servers such as Apache handled many simultaneous connections, released the project on May 27, 2009. Its first version combined Google's V8 engine, an event loop and a low-level I/O layer, so that a slow disk or network request would not stall the whole process. The initial release ran only on Linux and macOS, and Windows support came later. Because Node.js exposes file system, networking, child-process and process-level APIs, a program written for it can do things that a browser script cannot, such as read local files, open sockets and manage its own environment. The project is now maintained by the OpenJS Foundation under an MIT license, and its APIs are the reference point that many other runtimes and tools aim to match. See Node.js for the project's history and release structure.
Node.js is not the only runtime. Dahl himself later created Deno, which he announced in 2018 as a JavaScript and TypeScript runtime built on V8. Deno was designed to correct choices he regarded as mistakes in Node.js. It treats TypeScript as a first-class language, denies file, network and environment access unless the program is granted explicit permissions, and imports modules by URL rather than through a package manager. Later versions added compatibility with npm packages and Node.js APIs, which blurred some of the original differences. Bun, first released in 2022, takes a different route. It is written in Zig, uses Apple's JavaScriptCore engine instead of V8, and ships a bundler, test runner, package manager and SQLite support in a single executable. Its developers emphasize startup time and throughput. Published benchmarks differ widely depending on the workload and the author, so performance claims for these runtimes should be checked against a specific use case. Their security models also differ: Bun does not restrict the program by default, while Deno does.
JavaScript also reaches the desktop through application frameworks. Electron, an open-source framework originally developed at GitHub, combines the Chromium rendering engine with Node.js so that a single HTML, CSS and JavaScript codebase can produce applications for Windows, macOS and Linux. Visual Studio Code, Slack and Discord are among the widely used programs built this way. The approach brings a trade-off, since each application ships its own copy of both Chromium and Node.js, which makes the downloads larger and the memory footprint heavier than those of native applications. Mobile and cross-platform frameworks follow a similar logic. React Native, for example, lets developers describe user interfaces in JavaScript while the framework maps them onto native platform components, so the language sits between the application logic and the operating system's widgets rather than replacing them.
Outside these major platforms, JavaScript runs in embedded and command-line settings. Lightweight engines such as QuickJS are designed for constrained devices, plug-in systems and tools that need to embed a scripting language inside another program. Command-line utilities, build tools and code formatters commonly run on Node.js or a comparable runtime, and many cloud and edge platforms execute JavaScript functions in isolated environments that start quickly and run with restricted host access. In each case the language core, meaning its syntax, types and semantics, stays largely the same, while the host-provided APIs change: a server runtime offers file and socket access, a browser offers the DOM, and an embedded device might offer only a handful of functions supplied by the application. Much of the practical difficulty in moving code between these environments therefore lies in host APIs, module resolution and packaging rather than in the language itself.
Engines, JIT Compilation and Garbage Collection
Modern engines do not read JavaScript text line by line. Engines such as V8 (used in Chrome and Node.js), SpiderMonkey (Firefox) and JavaScriptCore (Safari) first parse source code into an abstract syntax tree, then translate it into a compact intermediate form called bytecode that an interpreter can start running at once. The three major engines are written mainly in C++, with SpiderMonkey also using Rust.
Tiered Execution and Just-in-Time Compilation
Interpreting bytecode is slower than running native machine code, so engines watch which functions and loops execute often and pass these "hot" paths to a just-in-time compiler that emits machine code during execution. Each engine uses several tiers. V8 starts with the Ignition interpreter and moves code through the Sparkplug and Maglev compilers to the optimizing TurboFan compiler. SpiderMonkey combines a baseline interpreter, a baseline JIT and the optimizing WarpMonkey tier, which succeeded IonMonkey. JavaScriptCore has four tiers: its low-level interpreter, a baseline JIT, the DFG JIT and the FTL JIT. The logic is a trade-off. Cheap compilers produce code quickly, and expensive compilers are reserved for code that runs long enough to repay the effort.
Much of the optimization is speculative. JavaScript variables can hold values of any type, and objects can gain or lose properties at any time, so the compiler guesses from observed behavior and generates code specialized for those guesses. If a later operation breaks an assumption, the engine discards the optimized code and falls back to a slower tier. This process, called deoptimization, keeps programs correct while allowing aggressive optimization in the common case.
Hidden Classes and Inline Caches
Two mechanisms make property access fast despite the dynamic model. A hidden class (also called a shape) is an engine-internal description of an object's layout, recording which properties exist and at what offsets. Objects created the same way share one hidden class, and adding a property moves the object to a new one along a transition chain. An inline cache attached to each property access site remembers which hidden class it last saw and where the property sat. A site that always sees one shape is called monomorphic and is fastest. Polymorphic sites see a handful of shapes, and megamorphic sites see many and fall back to slower general lookup. This is why code that builds objects with consistent property names in a consistent order tends to run faster in every major engine.
Garbage Collection
Memory is reclaimed by automatic garbage collection, and programs do not free objects explicitly. Engines typically use generational collectors based on the observation that most objects die shortly after creation. A frequently run minor collector scans a small young region, while long-lived objects are promoted to an older region that is collected less often. Developers influence memory use mainly through reference retention: an object survives as long as something reachable still points to it. Forgotten event listeners, growing caches and closures that hold large data are therefore common sources of memory leaks, even though no manual free operation is involved.
Performance Since 2008
Chrome's 2008 release brought V8 to the public, and at that time JavaScript was mostly a page-scripting language run by simple interpreters. Since then, tiered JIT compilation, speculative optimization and shape-based property access have produced large speed gains across all major engines. Benchmarking has changed in response. Early measurements were often micro-tests of single operations, which engines could optimize in ways unrelated to real applications. Current evaluations increasingly examine startup time, memory consumption and realistic workloads, because these better reflect what users experience.
Security Model and Common Vulnerabilities
JavaScript's security problems come mostly from how web pages are built, not from the language's core semantics. Browsers run scripts from many origins inside one page, so the main concern is whether untrusted data can change what a trusted page does. The browser's security model separates origins, restricts what scripts can reach, and sets rules for requests, but application code can still break these boundaries.
The most common web vulnerability involving JavaScript is cross-site scripting (XSS). In XSS, an attacker gets script into a page that a trusted site serves, so the injected code runs with that site's privileges. It can read the page's DOM, session data and form contents, and send requests as the logged-in user. XSS usually comes from careless handling of untrusted input: a server embeds user text in HTML without escaping it, or client code writes input into the page through properties such as innerHTML. Reflected, stored and DOM-based XSS differ in where the malicious data travels, but they share this root cause.
Cross-site request forgery (CSRF) works differently. The attacker cannot read responses, but tricks the victim's browser into sending a state-changing request to another site, where the browser automatically attaches cookies. Defenses include anti-CSRF tokens checked on the server and the SameSite cookie attribute, which limits when cookies are sent on cross-site requests. Prototype pollution is a JavaScript-specific risk. When a merge or path-setting function copies attacker-controlled keys such as __proto__ into objects, the attacker can add or change properties on Object.prototype, and every ordinary object then inherits the injected values. Such flaws have appeared in widely used utility libraries and can turn into XSS, authorization bypass or denial of service, depending on how the application reads properties.
Supply-chain attacks extend the threat beyond the application's own code. Modern projects pull in deep dependency trees from the npm registry, and a compromised package can run code during installation or ship malicious logic that is later executed in production. A well-known 2018 incident involved a popular package whose maintainership was transferred to a stranger who added code aimed at stealing cryptocurrency wallets. Because the same runtime executes both the application and its dependencies, a single malicious package can reach everything the application can reach. Lockfiles, dependency auditing and reviewing new maintainers are the usual mitigations, though none is complete. The npm ecosystem itself is covered in open-source practice.
The main defenses are layered. Content Security Policy (CSP), delivered as an HTTP header, lets a site declare which sources may supply scripts and can forbid inline scripts unless they carry a nonce or a hash. Sanitization libraries such as DOMPurify strip dangerous markup before it is inserted into the page. Framework escaping, as in React or Vue's default template behavior, encodes values automatically, but explicit escape hatches such as React's dangerouslySetInnerHTML or Vue's v-html reopen the risk when they receive untrusted content. Subresource Integrity (SRI) helps when third-party scripts are loaded from a CDN, because the browser verifies a hash before running the file.
Two practices draw particular concern. Executing third-party code in the page context gives an analytics, advertising or widget vendor the same access as the host application, and a compromise at the vendor can affect every site that loads it. Eval-like functionality, including eval, new Function and string arguments to setTimeout, compiles text as code at runtime. This makes input validation much harder and often conflicts with CSP, which is why many security guides treat it as a last resort. The browser's isolation model still limits what a compromised page can reach, but it does not protect a site from flaws in its own code.
Language Criticism and Design Quirks
Criticism of JavaScript tends to cluster around a few recurring areas. Implicit type coercion is the most cited. The + operator concatenates when either operand is a string or an object that converts to one, so two empty arrays added together produce an empty string, and an array added to a plain object produces the text "[object Object]". These results follow from documented conversion rules, but they routinely surprise newcomers. Other frequent complaints are the this keyword, whose value depends on how a function is invoked rather than where it is written; hoisting, which makes a var declaration visible throughout its function before the line that declares it runs, with the value undefined until then; and the global object, which in non-strict code receives properties whenever an undeclared name is assigned.
The shorthand "WAT" grew out of a roughly four-minute lightning talk given by Gary Bernhardt at the CodeMash conference in January 2012. The talk covered Ruby as well as JavaScript. It presented a run of puzzling expressions, each followed by a picture captioned with that single word, and it became a widely shared piece of programmer humor. The joke has outlasted the specific examples, and "WAT" is now used for any unexpected behavior in a dynamic language.
Many of the quirks are also linked to the conditions of the first implementation. Brendan Eich wrote the initial version in roughly ten days in 1995, and critics argue that the speed of that work, together with a name chosen for marketing reasons, left a design that was never fully revisited. The best-known example is typeof null, which returns "object". In the early engine, values carried a small type tag, the tag for objects was zero, and null was represented as a null pointer, which also consisted of zero bits, so the engine confused the two. By the time the mistake was recognized, large amounts of web code depended on the result, and changing it would have broken those pages. It therefore remains in the specification, along with a number of other early choices.
Defenders respond that the language is internally coherent once its semantics are learned. The coercion rules are deterministic and documented, so the "strange" results are predictable rather than random. They also argue that many notorious examples rarely appear in modern code. Strict mode removes accidental globals and changes how this behaves in plain function calls, let and const replace most uses of var, the strict equality operator === avoids most loose-comparison surprises, and linters and static type checkers such as TypeScript flag many risky constructs before they run. From this perspective, the deliberate policy of preserving backward compatibility is a choice with costs, and the quirks are the price of keeping existing content working.
Debates continue on several fronts. One concerns the size and composition of the standard library: for many years the built-in library was thin, so developers depended on third-party packages, while supporters of a small core argue that it keeps the platform flexible. A second concerns numbers and dates. Every ordinary number is a double-precision float, so integers above 2^53 cannot all be represented exactly, a limitation that BigInt addresses for large integers. The Date object is criticized for zero-based month indexing, mutable instances and date-string parsing that varies between engines, and the Temporal API was designed to replace it. A third debate concerns the dynamic type system itself, which favors rapid experimentation but offers fewer compile-time guarantees, a trade-off that has driven the growth of typed supersets. These questions remain open, and the community continues to weigh them against the need to keep the web stable.
Typed Supersets, Transpilers and Build Tooling
Most JavaScript is not shipped in the exact form in which it is written. Source files usually pass through a chain of tools (type checkers, compilers, bundlers and package managers) before they reach a browser or a server, and this chain now shapes everyday practice as much as the language itself.
TypeScript and the typed superset
TypeScript was first released by Microsoft in October 2012, and development was led by Anders Hejlsberg. It adds optional static types to JavaScript. Its compiler checks the annotations and then removes them, so the output is ordinary JavaScript that any engine can run. Because every valid JavaScript file is valid TypeScript, a codebase can be migrated gradually. Type checking happens at compile time, so mismatched argument types or misspelled property names are reported before the program runs, and type inference lets developers annotate only where it helps. The term "superset" is itself disputed: some critics argue that the claim holds only loosely, because TypeScript's type system is not part of the JavaScript standard. Since the emitted code is plain JavaScript, runtime speed is generally the same, and the costs fall mainly on build time and on the extra compilation step.
Transpilers and polyfills
A transpiler, sometimes called a source-to-source compiler, rewrites code from one version of a language into another at roughly the same level of abstraction. Babel is the best-known example. It converts newer syntax, such as arrow functions, classes and JSX, into forms that older environments accept, and the output is steered by a list of target browsers or Node.js versions. Syntax alone is not enough, however. Built-in objects and methods such as Promise or Array.from are provided by the runtime rather than by the grammar, so a transpiler cannot add them. Babel can automatically inject polyfills provided by core-js to fill this gap. The division of labor is therefore clear: the transpiler changes syntax, while a polyfill library supplies the missing functions. The older all-in-one @babel/polyfill package was deprecated, and developers are now expected to import core-js and regenerator-runtime separately.
Build tooling
Production code is typically assembled by a toolchain with several distinct jobs. Bundlers merge many modules into a small number of files for delivery. Linters flag suspicious or error-prone code, formatters enforce a consistent style, and test runners execute automated checks. Most of these tools are themselves JavaScript programs that run on Node.js, and their behavior is usually declared in a package manifest together with tool-specific configuration files. Newer compilers and bundlers written in faster native languages have started to replace parts of this pipeline, which has improved build times for large projects.
npm and the dependency graph
npm is the default package manager for Node.js. It consists of a command line client, also called npm, and an online database of public and paid-for private packages, called the npm registry. Its size has grown rapidly, and counts vary by date and by source, with recent figures over 3.1 million packages. Because small, reusable packages are cheap to publish and install, a single application often depends on a large tree of indirect dependencies, and this tree is a defining feature of modern JavaScript development. The same openness has consequences. The registry does not have any vetting process for submission, so dependency management is closely tied to questions of maintenance, versioning and security, and projects increasingly rely on lock files and automated audits to keep their dependency trees under control.
Frameworks, Libraries and Web Application Architecture
Early libraries such as jQuery addressed a practical problem: in the years when browsers implemented the DOM and event model inconsistently, the same script often behaved differently from one engine to another. jQuery wrapped element selection, DOM manipulation, event binding and AJAX calls in a single consistent API, and for a decade it was the default way to build interactive pages. Its approach was imperative. The developer described each change to the page step by step, which became harder to manage as interfaces grew more stateful.
From about 2013 onward, component-based frameworks changed that model. React, released by Facebook, asked developers to describe what the interface should look like for a given state, and it handled the transition between states itself. It did this with a virtual DOM: a lightweight in-memory tree of JavaScript objects that is compared with the previous version (a process called reconciliation), so that only the differences are written to the real DOM. Vue, first released in 2014, offered a progressive alternative with a template syntax and, in its later versions, a reactivity system built on JavaScript Proxies that tracks which values each part of the interface depends on. Angular, rewritten as a full framework from its predecessor AngularJS, brought its own template compiler, dependency injection and tooling for large applications. The three projects have since moved closer together. Angular introduced signals as a fine-grained reactivity primitive, and Vue's experimental Vapor Mode compiles templates to direct DOM operations without a virtual DOM. Several other libraries, including Svelte and Solid, compile or track updates with little or no runtime diffing.
Many of these frameworks were designed around single-page applications. In that architecture, the server returns a minimal shell and the browser handles routing between views without full page reloads. Client-side routing maps URLs to components and updates the address bar through the History API. This model improved responsiveness, but it made the first page load slower and made search engine indexing harder, which led to server-side rendering. The server generates the HTML for the initial view, and the client then performs hydration, attaching event handlers and component state to the existing markup. Meta-frameworks such as Next.js and Nuxt package these patterns, along with routing, data loading and build configuration, into a single structure. Server components and similar mechanisms let some code run only on the server, so the boundary between client and server code is no longer fixed by the file or the framework.
These arrangements raise recurring engineering trade-offs. Rendering on the client keeps the server simple and makes interactions quick, but it increases the amount of script the browser must download, parse and execute before the page becomes usable. Server rendering reduces that cost for the first view but adds server load and requires code to run in two environments. Bundle size, state management and the choice between centralized and local state are debated in every major framework community, and each project's answer reflects its priorities. Teams building content-heavy sites, internal dashboards and real-time tools often reach different conclusions, and the ecosystem continues to revise its defaults as browsers, build tools and the language itself evolve.
Evolution from ES5 to ECMAScript 2026
ES5, finalized in 2009, added strict mode, property descriptors, native JSON support and a set of array methods. These changes made the language more reliable and filled gaps in its standard library. Its successor is where the naming scheme changed. Once releases were tied to their publication year, ES6 became ES2015 in formal usage, a move that also signalled a language expected to keep changing The renaming of ES6 to ES2015 was done to reflect the year of its release and to move away from version numbers.. ES2015 was the first edition whose features were vetted through the TC39 proposal workflow ES2015 was the first offering to follow the TC39 process, a proposal-based model for discussing and adopting elements.. Its additions included classes, promises, arrow functions, modules, generators and iterators classes, promises, arrow functions, ES modules, generators and iterators, along with block-scoped let and const and the Map and Set collections the ability to create promises for asynchronous programming, and the new data structures: Map and Set.
After 2015 the committee shifted to a yearly cadence, so each release carries a smaller batch of finished proposals. Later editions brought async functions, object rest and spread, optional chaining, nullish coalescing, BigInt, private class fields and top-level await. Several of these are summarized in the sections on syntax and asynchronous programming.
ECMAScript 2026 is the 17th edition. Final sign-off came on 30 June 2026, when the 131st General Assembly of Ecma International met in Geneva and approved the ECMA-262 17th edition The 131st General Assembly held in Geneva on 30 June 2026 approved the following documents: ECMA-262 17th edition – ECMAScript® 2026 language specification.. Ratification mostly fixes the exact behavior in the specification text. Many of the new features had already shipped in engines beforehand Many of these features were already available before the June 30 ratification, which lets toolchains such as TypeScript definitions, Babel and bundlers settle their support lock the exact behavior into the specification text, so implementers and tooling.
The library additions are mostly small. Their list covers Math.sumPrecise for summing an iterable of numbers of varying magnitude while minimizing precision loss; Iterator.concat for sequencing iterators; Array.fromAsync for constructing arrays from async iterables and other async sources; Error.isError for identifying error objects, along with default-value lookups for Map and WeakMap, hex and Base64 conversion for Uint8Array, and new JSON source-access features. Math.sumPrecise targets a well-known problem with the floating-point arithmetic used for every ordinary number, since precision is lost on summing decimal numbers one after another. Error.isError offers a dependable check on caught values, even when they originate in a separate runtime sandbox. Not every highlight is a library function: some summaries point to Explicit Resource Management, which adds a new pair of keywords for deterministic cleanup.
The most anticipated addition is Temporal, a replacement for the Date object. Date has been criticized because it mixes timestamps, local time, UTC behavior, and mutation in ways that regularly create bugs. Temporal instead offers separate types, and It offers immutable, purpose-built types such as PlainDate, ZonedDateTime, Instant, and Duration, along with first-class IANA time-zone support and DST-safe arithmetic. Most sources date its advancement to the final TC39 stage to March 2026, when Temporal reached TC39 Stage 4 and became part of the ES2026 specification. One account instead places this milestone in late 2024 Temporal hit Stage 4 in late 2024 and browsers began shipping immediately after., so the dates in secondary sources differ. Browser support is also uneven: Firefox shipped Temporal by default in Firefox 139 on 27 May 2025, Chrome added it in Chrome 144 on 13 January 2026, and Safari hasn't shipped it yet, so pair it with the official polyfill for public-facing work.
Relations to Other Fields and Technologies
JavaScript's connections to other fields run in two directions. Some technologies were built to work next to it in the browser, and others were built on top of it as a general-purpose platform. The most direct example of the first kind is WebAssembly. Browsers execute it alongside JavaScript, and it is typically used as a compilation target for languages such as C, C++, Rust and Go. Its lineage runs through asm.js, a strict subset of JavaScript that engines could optimize aggressively. Because asm.js still had to pass through the ordinary JavaScript parsing pipeline, compilation was slow, and a binary format that skips text parsing was the response. In practice the two technologies divide the work. WebAssembly handles compute-heavy kernels such as video decoding, games and cryptography, while JavaScript handles the user interface and event-driven logic. The boundary between them has a cost. The two use different memory models, and WebAssembly has no direct access to the DOM, so developers route DOM operations through JavaScript.
Machine learning is a second area of overlap. TensorFlow.js is an open-source library that lets developers define, train and run models in JavaScript, either in the browser or under Node.js. Its APIs are designed to be compatible with the Python version of TensorFlow, so models can move between the two ecosystems, and the project includes a converter for models built in Python. In browsers it can use WebGL to run computation on the GPU. The browser model has limits. Small models are practical to train on the client, but training large models is much slower than on native hardware, and the performance gap is one of the main reasons server-side or native training remains common. Running inference locally, however, avoids sending user data to a server, which is one reason developers adopt on-device models.
Scientific computing, data visualization and education have adopted JavaScript largely because of its position in the browser. Interactive notebook environments let researchers combine prose, code and live output in a page that needs no local installation. Charting and visualization libraries turn data into graphics that respond to user input, and interactive learning materials can run code examples directly in the reader's browser. These uses connect JavaScript to data science and to education more broadly, including higher education courses that teach programming through the web. The common thread is accessibility: a browser tab is a platform that most learners and researchers already have.
The language's governance also links it to the wider world of computer science and open-source software. The specification is developed in the open. The TC39 committee publishes its work on GitHub and meets roughly every two months to discuss proposals. New features move through numbered stages from 0 to 4, and the committee's site notes that the process includes additional intermediate steps. Reaching the final stage has concrete requirements, including acceptance tests written for a shared test suite and two compatible implementations that pass them. This makes standardization a feedback loop between academic-style specification work and industrial engine development. Engines such as V8, SpiderMonkey and JavaScriptCore are open-source projects maintained by competing vendors, who must agree on the same specification while racing on performance. The result is a model in which research ideas, implementation experience and community review shape one another, a pattern that the language shares with other open standards.
Adoption, Economics and Labor Market
For the most used programming languages, JavaScript is at the top of the list for the 12th year in a row, according to the 2026 edition of the Stack Overflow Developer Survey. Exact shares depend on which respondents are counted. One summary of the 2025 survey gives 66% for ranked #1 across developers, while the professional subset reached 68.9% of professional developers. Usage surveys also differ from other popularity measures. The TIOBE index counts search results rather than developers, and both indexes still rank Python first. On GitHub, contributor counts have put TypeScript ahead of JavaScript since 2025.
Hiring data points the same way. Recruiter figures from CoderPad's 2025 report show that JavaScript is the second most in-demand programming language, with 41.5% of recruiters actively looking for JavaScript developers, second only to Python. A June 2026 analysis of 6,138 frontend listings on one job board found JavaScript named in 48.7% of postings, with no single skill passing half. The same dataset places the US median for postings that explicitly require TypeScript at $147,200, against $120,000 for postings listing plain JavaScript, which suggests that the typed superset now commands much of the pay premium.
Counts vary sharply between trackers. One index reports 542 active listings in August 2026, ranking JavaScript #5 of 16 tracked languages, while another lists 273 active jobs at a given time. These figures use different methods and cannot be compared directly. Opinion is also divided on the trend. Upwork's assessment holds that JavaScript is more in demand than ever, but the way we use it (and the skills required to succeed with it) have changed completely. Other industry reporting argues that demand for junior developers has softened as AI tools absorb routine coding, and breaking in is harder than it was in 2021.
Economically, JavaScript sits underneath much of the commercial web. Product pages, checkout flows, personalization and advertising scripts run in the browser, and many software-as-a-service products are built as single-page applications that depend on it. Through Node.js, the same language can serve both client and server, which reduces the number of distinct skill sets a team needs. This is why JavaScript appears in so many e-commerce and software engineering job descriptions, even as the work itself shifts toward TypeScript, frameworks and tooling.
Corporate sponsorship shapes the language from several directions. Browser vendors fund the engines that execute it: Google's V8 powers Chrome and Node.js, Apple develops JavaScriptCore for Safari, and Mozilla maintains SpiderMonkey for Firefox. Large firms also drive major frameworks and tools. React began at Meta, Angular at Google, TypeScript at Microsoft, and Next.js is maintained by Vercel. Much of this work is released as open-source software under foundations, but commercial priorities still influence roadmaps, and representatives of browser vendors and large companies participate in the standards committee that defines the language.
Dependence on scripting also raises accessibility questions. Pages that render only after script execution can fail on older or low-powered smartphones, on slow networks, or where scripts are blocked. Custom interface widgets can be unusable with assistive technologies if developers do not build them carefully. Progressive enhancement, server-side rendering and routine accessibility testing are common responses. These concerns connect the language's commercial success to the digital divide, since access to many online services now assumes a modern, JavaScript-enabled browser on a reasonably capable device.
References
- History of JavaScript - read our article to find out!softteco.com
- Brendan Eichen.wikipedia.org
- Brendan Eich: JavaScript, the Language That Made the Web Interactiveblog.immunitynetworks.com
- Brendan Eichebsco.com
- A Brief History of JavaScript: Everything You Need to Know in 2024nestify.io
- DEV Communitydev.to
- ECMAScript version historyen.wikipedia.org
- The TC39 Processtc39.es
- ECMAScript Language (ECMA-262), including JavaScriptloc.gov
- ECMAScript version history explainedeverything.explained.today
- History of ECMAScriptbenmvp.com
- JavaScript data types and data structures - JavaScriptdeveloper.mozilla.org
- JavaScript Data Typesjavascripttutorial.net
- Primitive Types - 33 JavaScript Concepts33jsconcepts.com
- JavaScript Data Types: Primitive Types and Objectstutorialreference.com
- this - JavaScriptdeveloper.mozilla.org
- Closures & Lexical Scope: How JavaScript Functions "Remember" Where They Were Born - DEV Communitydev.to
- javascript fundamentals 92bjavascriptcorner.substack.com
- Event loop: microtasks and macrotasksjavascript.info
- JavaScript Event Loop Explained: Microtasks & Macrotasks - DEV Communitydev.to
- The JavaScript Event Loop: Macrotasks vs Microtasks - AlexWebLab in Bangkok, Thailand now, before in Hong Kong 香港alexweblab.com
- JavaScript Event Loop Explained (With Examples) 2026crosscheck.cloud
- Understanding the Event Loop in JavaScript/TypeScript - NashTech Blogblog.nashtechglobal.com
- Exploring Node.js Modules: CommonJS vs. ES6 Modulesmedium.com
- CommonJS vs. ES Modulesbetterstack.com
- Comparing ECMAScript Modules and CommonJSwanago.io
- ES Modules and CommonJS: An Overview - DEV Communitydev.to
- which one is better commonjs or ecma script syntax for a nodejs application b80e935f6b55zainsadaqat.medium.com
- Same-origin policyen.wikipedia.org
- Same-origin policy (SOP)portswigger.net
- Cross-Origin Resource Sharing (CORS) - HTTPdeveloper.mozilla.org
- Understanding CORS and Same Origin Policy in Web Security🚨 - DEV Communitydev.to
- The history of XMLHttpRequest (XHR)medium.com
- DEV Communitydev.to
- The Fetch API is finally stable in Node.js - LogRocket Blogblog.logrocket.com
- Javascript Fetch API: The XMLHttpRequest evolution - Developer How-todeveloperhowto.com
- XMLHttpRequest (XHR)developer.mozilla.org
- Node.jsen.wikipedia.org
- Ryan Dahlen.wikipedia.org
- Node.js Introduced at JSConf.eu (2009)centerconsulting.com
- Brief history and evolution of Node.jsluisllamas.es
- Node.js — Historynodejs.ximg.app
- Node.js vs Deno vs Bun: Comparing JavaScript Runtimesbetterstack.com
- Deno 2 vs Bun: which JavaScript runtime is right for you? · Appwriteappwrite.io
- Node.js vs Bun vs Deno: Best JavaScript Runtime in 2025sevensquaretech.com
- Deno vs Bun: Comparing Modern JavaScript Runtimesdev.to
- GitHub - electron/electron: :electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS · GitHubgithub.com
- Electron App Development: The Complete 2026 Guidecoderio.com
- Electron Software Framework: The Best Way to Build Desktop Apps?pangea.ai
- Build cross-platform desktop apps with JavaScript, HTML, and CSSelectronjs.org
- V8 vs SpiderMonkey vs JavaScriptCorelanguagelineage.org
- Meet the JavaScript Engines Powering the Webblog.openreplay.com
- NOJITSU: Locking Down JavaScript Enginesndss-symposium.org
- Two-level Just-in-Time Compilation with One Interpreter and One Enginearxiv.org
- JavaScript Engines Explained—Comparing V8, SpiderMonkey, JavaScriptCore, and More · Frontend Dogmafrontenddogma.com
- Hidden V8 optimizations : hidden classes and inline cachingmedium.com
- Fast Property Accesschromium.googlesource.com
- JavaScript Engines - 33 JavaScript Concepts33jsconcepts.com
- What is JavaScript written in? V8, SpiderMonkey, and JSClanguagelineage.org
- Analyzing the JavaScript Examples in Gary Bernhardt's "Wat" Talk — Marius Schulzmariusschulz.com
- JavaScript Says WAT!yassinemoumen.com
- Gary Bernhardt - De Programmatica Ipsumdeprogrammaticaipsum.com
- typeof null is "object" — a bug from 1995 nobody can fix - DEV Communitydev.to
- typeof null: investigating a classic JavaScript bug · Caffeinspirationalexanderell.is
- History behind typeof null equals object.webdioxide.com
- TypeScript vs JavaScript in 2026sencha.com
- Babel (transcompiler)en.wikipedia.org
- npmen.wikipedia.org
- TypeScript Overviewtutorialsteacher.com
- Types without TypeScriptdepth-first.com
- What is Babel Transpiler?educative.io
- core-js/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md at master · zloirock/core-jsgithub.com
- Reactivity Models Compared: React, Vue, Angular, Svelteblog.openreplay.com
- Virtual DOMen.wikipedia.org
- React vs Angular vs Vue (2026): Which to Pick, With Datahashbyt.com
- The Reactivity Wars: Virtual DOM, Signals, and the React Compiler in 2026daily.dev
- ES6 (ES2015) and Beyond: Understanding JavaScript Versioning — SitePointsitepoint.com
- What is ES6 in JavaScript? A Comprehensive Guide to ECMAScript 2015talent500.com
- Ecma International approves new standards - Ecma Internationalecma-international.org
- ECMAScript 2026 Is Official: Everything New in JavaScript's 17th Editionnatanaelrusli.com
- ECMAScript 2026 specification approvedinfoworld.com
- ECMA 2026 Spec Approvedi-programmer.info
- JavaScript 2026w3schools.com
- JavaScript Temporal API: Replacing Date, With Exampleskvassiliou.com
- JavaScript Temporal API: The Modern Replacement for Datematthewswong.com
- Temporal API: The Modern Replacement for JavaScript Datethingsaboutweb.dev
- JavaScript's Date Problem Is Finally Fixed: The Temporal API After 9 Years · Groundygroundy.com
- The Temporal API Is Here. Here's How to Actually Start Using Itdainemawer.com
- WebAssemblytechtarget.com
- Next-generation Web Applications with WebAssembly and TruffleWasmarxiv.org
- WebAssembly enables low latency interoperable augmented and virtual reality softwarearxiv.org
- WebAssembly: a binary format for the web2ality.com
- TENSORFLOW.JS: MACHINE LEARNING FOR THE WEB AND BEYONDproceedings.mlsys.org
- TensorFlow.jstensorflow.org
- TensorFlow.js: Deploying and Training Machine Learning Models Right in a Browseraltoros.com
- TC39 - Specifying JavaScript.tc39.es
- TC39 Standards Processdeepwiki.com
- Technology 2026survey.stackoverflow.co
- Top Programming Languages 2026: Developer Usage Rankings And Datacommandlinux.com
- Top 15 Programming Languages by Usage Statistics [2026] - Second Talentsecondtalent.com
- Is JavaScript Still in Demand in 2026? - Upworkupwork.com
- Frontend Developer Skills in 2026: 1 in 5 Openings Is Entry-Levelmedium.com
- JavaScript Developer Jobs — Demand, Roles and Skills in 2026datamatastudios.com
- JavaScript Developer Demand 2026 — Is It Worth Learning?devskopehq.com
- Is JavaScript Still Worth Learning in 2026? An Honest Look at the Job Marketuselearnai.com