core-js Documentation

JavaScript ecmascript-proposals & es2015 repository by @zloirock. AI agent rules & system prompts with 26k stars.

25,516 stars JavaScript JavaScript 1 file · ~57,202 tokens #ecmascript-proposals#es2015#es6#es7#esnext#javascript#js#polyfill
RAW Doc

File: README.md

**Welcome to our new website, [core-js.io](https://core-js.io), where our documentation is moving!**

**I highly recommend reading this: [So, what's next?](https://core-js.io/blog/2023-02-14-so-whats-next)**

Modular standard library for JavaScript. Includes polyfills for ECMAScript up to 2025: promises, symbols, collections, iterators, typed arrays, many other features, ECMAScript proposals, some cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.

[core-js@3, babel and a look into the future](https://core-js.io/blog/2019-03-19-core-js-3-babel-and-a-look-into-the-future)

Raising funds

core-js isn't backed by a company, so the future of this project depends on you. Become a sponsor or a backer if you are interested in core-js: Open Collective, Patreon, Boosty, Bitcoin ( bc1qlea7544qtsmj2rayg0lthvza9fau63ux0fstcz ), Alipay.




Example of usage:

js
import 'core-js/actual';

Promise.try(() => 42).then(it => console.log(it)); // => 42

Array.from(new Set([1, 2, 3]).union(new Set([3, 4, 5]))); // => [1, 2, 3, 4, 5]

[1, 2].flatMap(it => [it, it]); // => [1, 1, 2, 2]

Iterator.concat([1, 2], function * (i) { while (true) yield i++; }(3))
  .drop(1).take(5)
  .filter(it => it % 2)
  .map(it => it ** 2)
  .toArray(); // => [9, 25]

structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3])

You can load only required features:

js
import 'core-js/actual/promise';
import 'core-js/actual/set';
import 'core-js/actual/iterator';
import 'core-js/actual/array/from';
import 'core-js/actual/array/flat-map';
import 'core-js/actual/structured-clone';

Promise.try(() => 42).then(it => console.log(it)); // => 42

Array.from(new Set([1, 2, 3]).union(new Set([3, 4, 5]))); // => [1, 2, 3, 4, 5]

[1, 2].flatMap(it => [it, it]); // => [1, 1, 2, 2]

Iterator.concat([1, 2], function * (i) { while (true) yield i++; }(3))
  .drop(1).take(5)
  .filter(it => it % 2)
  .map(it => it ** 2)
  .toArray(); // => [9, 25]

structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3])

Or use it without global namespace pollution:

js
import Promise from 'core-js-pure/actual/promise';
import Set from 'core-js-pure/actual/set';
import Iterator from 'core-js-pure/actual/iterator';
import from from 'core-js-pure/actual/array/from';
import flatMap from 'core-js-pure/actual/array/flat-map';
import structuredClone from 'core-js-pure/actual/structured-clone';

Promise.try(() => 42).then(it => console.log(it)); // => 42

from(new Set([1, 2, 3]).union(new Set([3, 4, 5]))); // => [1, 2, 3, 4, 5]

flatMap([1, 2], it => [it, it]); // => [1, 1, 2, 2]

Iterator.concat([1, 2], function * (i) { while (true) yield i++; }(3))
  .drop(1).take(5)
  .filter(it => it % 2)
  .map(it => it ** 2)
  .toArray(); // => [9, 25]

structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3])

Index

Usage[⬆](#index)

Installation:[⬆](#index)

sh
// global version
npm install --save [email protected]
// version without global namespace pollution
npm install --save [email protected]
// bundled global version
npm install --save [email protected]

`postinstall` message[⬆](#index)

The core-js project needs your help, so the package shows a message about it after installation. If it causes problems for you, you can disable it:

sh
ADBLOCK=true npm install
// or
DISABLE_OPENCOLLECTIVE=true npm install
// or
npm install --loglevel silent

CommonJS API[⬆](#index)

You can import only-required-for-you polyfills, like in the examples at the top of README.md. Available CommonJS entry points for all polyfilled methods / constructors and namespaces. Just some examples:

ts
// polyfill all `core-js` features, including early-stage proposals:
import "core-js";
// or:
import "core-js/full";
// polyfill all actual features - stable ES, web standards and stage 3 ES proposals:
import "core-js/actual";
// polyfill only stable features - ES and web standards:
import "core-js/stable";
// polyfill only stable ES features:
import "core-js/es";

// if you want to polyfill `Set`:
// all `Set`-related features, with early-stage ES proposals:
import "core-js/full/set";
// stable required for `Set` ES features, features from web standards and stage 3 ES proposals:
import "core-js/actual/set";
// stable required for `Set` ES features and features from web standards
// (DOM collections iterator in this case):
import "core-js/stable/set";
// only stable ES features required for `Set`:
import "core-js/es/set";
// the same without global namespace pollution:
import Set from "core-js-pure/full/set";
import Set from "core-js-pure/actual/set";
import Set from "core-js-pure/stable/set";
import Set from "core-js-pure/es/set";

// if you want to polyfill just the required methods:
import "core-js/full/set/intersection";
import "core-js/actual/array/find-last";
import "core-js/stable/queue-microtask";
import "core-js/es/array/from";

// polyfill iterator helpers proposal:
import "core-js/proposals/iterator-helpers";
// polyfill all stage 2+ proposals:
import "core-js/stage/2";

[!TIP]
The usage of the /actual/ namespace is recommended since it includes all actual JavaScript features and does not include unstable early-stage proposals that are available mainly for experiments.

[!WARNING]

  • The modules path is an internal API, does not inject all required dependencies and can be changed in minor or patch releases. Use it only for a custom build and/or if you know what are you doing.
  • If you use core-js with the extension of native objects, recommended to load all core-js modules at the top of the entry point of your application, otherwise, you can have conflicts.
    • For example, Google Maps use their own Symbol.iterator, conflicting with Array.from, URLSearchParams and / or something else from core-js, see related issues.
    • Such conflicts are also resolvable by discovering and manually adding each conflicting entry from core-js.
  • core-js is extremely modular and uses a lot of very tiny modules, because of that for usage in browsers bundle up core-js instead of a usage loader for each file, otherwise, you will have hundreds of requests.

CommonJS and prototype methods without global namespace pollution[⬆](#index)

In the pure version, we can't pollute prototypes of native constructors. Because of that, prototype methods transformed into static methods like in examples above. But with transpilers, we can use one more trick - bind operator and virtual methods. Special for that, available /virtual/ entry points. Example:

ts
import fill from 'core-js-pure/actual/array/virtual/fill';
import findIndex from 'core-js-pure/actual/array/virtual/find-index';

Array(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4

[!WARNING]
The bind operator is an early-stage ECMAScript proposal and usage of this syntax can be dangerous.

Babel[⬆](#index)

core-js is integrated with babel and is the base for polyfilling-related babel features:

`@babel/polyfill`[⬆](#index)

@babel/polyfill IS just the import of stable core-js features and regenerator-runtime for generators and async functions, so loading @babel/polyfill means loading the global version of core-js without ES proposals.

Now it's deprecated in favor of separate inclusion of required parts of core-js and regenerator-runtime and, for backward compatibility, @babel/polyfill is still based on core-js@2.

As a full equal of @babel/polyfill, you can use the following:

js
import 'core-js/stable';
import 'regenerator-runtime/runtime';

`@babel/preset-env`[⬆](#index)

@babel/preset-env has useBuiltIns option, which optimizes the use of the global version of core-js. With useBuiltIns option, you should also set corejs option to the used version of core-js, like corejs: '3.50'.

[!IMPORTANT]
It is recommended to specify the used minor core-js version, like corejs: '3.50', instead of corejs: 3, since with corejs: 3 will not be injected modules which were added in minor core-js releases.


  • useBuiltIns: 'entry' replaces imports of core-js to import only required for a target environment modules. So, for example,
js
import 'core-js/stable';

with chrome 71 target will be replaced just to:

js
import 'core-js/modules/es.array.unscopables.flat';
import 'core-js/modules/es.array.unscopables.flat-map';
import 'core-js/modules/es.object.from-entries';
import 'core-js/modules/web.immediate';

It works for all entry points of global version of core-js and their combinations, for example for

js
import 'core-js/es';
import 'core-js/proposals/set-methods';
import 'core-js/full/set/map';

with chrome 71 target you will have as the result:

js
import 'core-js/modules/es.array.unscopables.flat';
import 'core-js/modules/es.array.unscopables.flat-map';
import 'core-js/modules/es.object.from-entries';
import 'core-js/modules/esnext.set.difference';
import 'core-js/modules/esnext.set.intersection';
import 'core-js/modules/esnext.set.is-disjoint-from';
import 'core-js/modules/esnext.set.is-subset-of';
import 'core-js/modules/esnext.set.is-superset-of';
import 'core-js/modules/esnext.set.map';
import 'core-js/modules/esnext.set.symmetric-difference';
import 'core-js/modules/esnext.set.union';
  • useBuiltIns: 'usage' adds to the top of each file import of polyfills for features used in this file and not supported by target environments, so for:
js
// first file:
let set = new Set([1, 2, 3]);
js
// second file:
let array = Array.of(1, 2, 3);

if the target contains an old environment like IE 11 we will have something like:

js
// first file:
import 'core-js/modules/es.array.iterator';
import 'core-js/modules/es.object.to-string';
import 'core-js/modules/es.set';

var set = new Set([1, 2, 3]);
js
// second file:
import 'core-js/modules/es.array.of';

var array = Array.of(1, 2, 3);

By default, @babel/preset-env with useBuiltIns: 'usage' option only polyfills stable features, but you can enable polyfilling of proposals by the proposals option, as corejs: { version: '3.50', proposals: true }.

[!IMPORTANT]
In the case of useBuiltIns: 'usage', you should not add core-js imports by yourself, they will be added automatically.

`@babel/runtime`[⬆](#index)

@babel/runtime with corejs: 3 option simplifies work with the core-js-pure. It automatically replaces the usage of modern features from the JS standard library to imports from the version of core-js without global namespace pollution, so instead of:

js
import from from 'core-js-pure/stable/array/from';
import flat from 'core-js-pure/stable/array/flat';
import Set from 'core-js-pure/stable/set';
import Promise from 'core-js-pure/stable/promise';

from(new Set([1, 2, 3, 2, 1]));
flat([1, [2, 3], [4, [5]]], 2);
Promise.resolve(32).then(x => console.log(x));

you can write just:

js
Array.from(new Set([1, 2, 3, 2, 1]));
[1, [2, 3], [4, [5]]].flat(2);
Promise.resolve(32).then(x => console.log(x));

By default, @babel/runtime only polyfills stable features, but like in @babel/preset-env, you can enable polyfilling of proposals by proposals option, as corejs: { version: 3, proposals: true }.

[!WARNING]
If you use @babel/preset-env and @babel/runtime together, use corejs option only in one place since it's duplicate functionality and will cause conflicts.

swc[⬆](#index)

Fast JavaScript transpiler swc contains integration with core-js, that optimizes work with the global version of core-js. Like @babel/preset-env, it has 2 modes: usage and entry, but usage mode still works not so well as in babel. Example of configuration in .swcrc:

json
{
  "env": {
    "targets": "> 0.25%, not dead",
    "mode": "entry",
    "coreJs": "3.50"
  }
}

Configurable level of aggressiveness[⬆](#index)

By default, core-js sets polyfills only when they are required. That means that core-js checks if a feature is available and works correctly or not and if it has no problems, core-js uses native implementation.

But sometimes core-js feature detection could be too strict for your case. For example, Promise constructor requires the support of unhandled rejection tracking and @@species.

Sometimes we could have an inverse problem - a knowingly broken environment with problems not covered by core-js feature detection.

For those cases, we could redefine this behavior for certain polyfills:

js
const configurator = require('core-js/configurator');

configurator({
  useNative: ['Promise'],                                 // polyfills will be used only if natives are completely unavailable
  usePolyfill: ['Array.from', 'String.prototype.padEnd'], // polyfills will be used anyway
  useFeatureDetection: ['Map', 'Set'],                    // default behavior
});

require('core-js/actual');

It does not work with some features. Also, if you change the default behavior, even core-js internals may not work correctly.

Custom build[⬆](#index)

For some cases could be useful to exclude some core-js features or generate a polyfill for target engines. You could use core-js-builder package for that.

Supported engines and compatibility data[⬆](#index)

core-js tries to support all possible JS engines and environments with ES3 support. Some features have a higher lower bar - for example, some accessors can properly work only from ES5, promises require a way to set a microtask or a task, etc.

However, I have no possibility to test core-js absolutely everywhere - for example, testing in IE7- and some other ancient was stopped. The list of definitely supported engines you can see in the compatibility table by the link below. Write if you have issues or questions with the support of any engine.

core-js project provides (as core-js-compat package) all required data about the necessity of core-js modules, entry points, and tools for work with it - it's useful for integration with tools like babel or swc. If you wanna help, you could take a look at the related section of CONTRIBUTING.md. The visualization of compatibility data and the browser tests runner is available here, the example:

Features:[⬆](#index)

CommonJS entry points:

text
core-js(-pure)

ECMAScript[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/es

ECMAScript: Object[⬆](#index)

Modules es.object.assign, es.object.create, es.object.define-getter, es.object.define-property, es.object.define-properties, es.object.define-setter, es.object.entries, es.object.freeze, es.object.from-entries, es.object.get-own-property-descriptor, es.object.get-own-property-descriptors, es.object.get-own-property-names, es.object.get-prototype-of, es.object.group-by, es.object.has-own, es.object.is, es.object.is-extensible, es.object.is-frozen, es.object.is-sealed, es.object.keys, es.object.lookup-setter, es.object.lookup-getter, es.object.prevent-extensions, es.object.proto, es.object.to-string, es.object.seal, es.object.set-prototype-of, es.object.values.

text
/* Detailed source-code truncated for AI context efficiency. */

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/object
core-js(-pure)/es|stable|actual|full/object/assign
core-js(-pure)/es|stable|actual|full/object/is
core-js(-pure)/es|stable|actual|full/object/set-prototype-of
core-js(-pure)/es|stable|actual|full/object/get-prototype-of
core-js(-pure)/es|stable|actual|full/object/create
core-js(-pure)/es|stable|actual|full/object/define-property
core-js(-pure)/es|stable|actual|full/object/define-properties
core-js(-pure)/es|stable|actual|full/object/get-own-property-descriptor
core-js(-pure)/es|stable|actual|full/object/get-own-property-descriptors
core-js(-pure)/es|stable|actual|full/object/group-by
core-js(-pure)/es|stable|actual|full/object/has-own
core-js(-pure)/es|stable|actual|full/object/keys
core-js(-pure)/es|stable|actual|full/object/values
core-js(-pure)/es|stable|actual|full/object/entries
core-js(-pure)/es|stable|actual|full/object/get-own-property-names
core-js(-pure)/es|stable|actual|full/object/freeze
core-js(-pure)/es|stable|actual|full/object/from-entries
core-js(-pure)/es|stable|actual|full/object/seal
core-js(-pure)/es|stable|actual|full/object/prevent-extensions
core-js/es|stable|actual|full/object/proto
core-js(-pure)/es|stable|actual|full/object/is-frozen
core-js(-pure)/es|stable|actual|full/object/is-sealed
core-js(-pure)/es|stable|actual|full/object/is-extensible
core-js/es|stable|actual|full/object/to-string
core-js(-pure)/es|stable|actual|full/object/define-getter
core-js(-pure)/es|stable|actual|full/object/define-setter
core-js(-pure)/es|stable|actual|full/object/lookup-getter
core-js(-pure)/es|stable|actual|full/object/lookup-setter

Examples:

js
let foo = { q: 1, w: 2 };
let bar = { e: 3, r: 4 };
let baz = { t: 5, y: 6 };
Object.assign(foo, bar, baz); // => foo = { q: 1, w: 2, e: 3, r: 4, t: 5, y: 6 }

Object.is(NaN, NaN); // => true
Object.is(0, -0);    // => false
Object.is(42, 42);   // => true
Object.is(42, '42'); // => false

function Parent() { /* empty */ }
function Child() { /* empty */ }
Object.setPrototypeOf(Child.prototype, Parent.prototype);
new Child() instanceof Child;  // => true
new Child() instanceof Parent; // => true

({
  [Symbol.toStringTag]: 'Foo',
}).toString(); // => '[object Foo]'

Object.keys('qwe'); // => ['0', '1', '2']
Object.getPrototypeOf('qwe') === String.prototype; // => true

Object.values({ a: 1, b: 2, c: 3 });  // => [1, 2, 3]
Object.entries({ a: 1, b: 2, c: 3 }); // => [['a', 1], ['b', 2], ['c', 3]]

for (let [key, value] of Object.entries({ a: 1, b: 2, c: 3 })) {
  console.log(key);   // => 'a', 'b', 'c'
  console.log(value); // => 1, 2, 3
}

// Shallow object cloning with prototype and descriptors:
let copy = Object.create(Object.getPrototypeOf(object), Object.getOwnPropertyDescriptors(object));
// Mixin:
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));

const map = new Map([['a', 1], ['b', 2]]);
Object.fromEntries(map); // => { a: 1, b: 2 }

class Unit {
  constructor(id) {
    this.id = id;
  }
  toString() {
    return `unit${ this.id }`;
  }
}

const units = new Set([new Unit(101), new Unit(102)]);

Object.fromEntries(units.entries()); // => { unit101: Unit { id: 101 }, unit102: Unit { id: 102 } }

Object.hasOwn({ foo: 42 }, 'foo'); // => true
Object.hasOwn({ foo: 42 }, 'bar'); // => false
Object.hasOwn({}, 'toString');     // => false

Object.groupBy([1, 2, 3, 4, 5], it => it % 2); // => { 1: [1, 3, 5], 0: [2, 4] }

ECMAScript: Function[⬆](#index)

Modules es.function.name, es.function.has-instance. Just ES5: es.function.bind.

ts
class Function {
  name: string;
  bind(thisArg: any, ...args: Array<mixed>): Function;
  @@hasInstance(value: any): boolean;
}

CommonJS entry points:

text
core-js/es|stable|actual|full/function
core-js/es|stable|actual|full/function/name
core-js/es|stable|actual|full/function/has-instance
core-js(-pure)/es|stable|actual|full/function/bind
core-js(-pure)/es|stable|actual|full/function/virtual/bind

Example:

js
(function foo() { /* empty */ }).name; // => 'foo'

console.log.bind(console, 42)(43); // => 42 43

ECMAScript: Error[⬆](#index)

Modules es.aggregate-error, es.aggregate-error.cause, es.error.cause, es.error.is-error, es.suppressed-error.constructor, es.error.to-string.

ts
class Error {
  static isError(value: any): boolean;
  constructor(message: string, { cause: any }): %Error%;
  toString(): string; // different fixes
}

class [
  EvalError,
  RangeError,
  ReferenceError,
  SyntaxError,
  TypeError,
  URIError,
  WebAssembly.CompileError,
  WebAssembly.LinkError,
  WebAssembly.RuntimeError,
] extends Error {
  constructor(message: string, { cause: any }): %Error%;
}

class AggregateError extends Error {
  constructor(errors: Iterable, message?: string, { cause: any }?): AggregateError;
  errors: Array<any>;
  message: string;
  cause: any;
}

class SuppressedError extends Error {
  constructor(error: any, suppressed: any, message?: string): SuppressedError;
  error: any;
  suppressed: any;
  message: string;
}

CommonJS entry points:

text
core-js/es|stable|actual|full/error
core-js/es|stable|actual|full/error/constructor
core-js(-pure)/es|stable|actual|full/error/is-error
core-js/es|stable|actual|full/error/to-string
core-js(-pure)/es|stable|actual|full/aggregate-error
core-js(-pure)/es|stable|actual|full/suppressed-error

Example:

js
const error1 = new TypeError('Error 1');
const error2 = new TypeError('Error 2');
const aggregate = new AggregateError([error1, error2], 'Collected errors');
aggregate.errors[0] === error1; // => true
aggregate.errors[1] === error2; // => true

const cause = new TypeError('Something wrong');
const error = new TypeError('Here explained what`s wrong', { cause });
error.cause === cause; // => true

Error.prototype.toString.call({ message: 1, name: 2 }) === '2: 1'; // => true

Example:

js
Error.isError(new Error('error')); // => true
Error.isError(new TypeError('error')); // => true
Error.isError(new DOMException('error')); // => true

Error.isError(null); // => false
Error.isError({}); // => false
Error.isError(Object.create(Error.prototype)); // => false

[!WARNING]
We have no bulletproof way to polyfill this Error.isError / check if the object is an error, so it's an enough naive implementation.

ECMAScript: Array[⬆](#index)

Modules es.array.from, es.array.from-async, es.array.is-array, es.array.of, es.array.copy-within, es.array.fill, es.array.find, es.array.find-index, es.array.find-last, es.array.find-last-index, es.array.iterator, es.array.includes, es.array.push, es.array.slice, es.array.join, es.array.unshift, es.array.index-of, es.array.last-index-of, es.array.every, es.array.some, es.array.for-each, es.array.map, es.array.filter, es.array.reduce, es.array.reduce-right, es.array.reverse, es.array.sort, es.array.flat, es.array.flat-map, es.array.unscopables.flat, es.array.unscopables.flat-map, es.array.at, es.array.to-reversed, es.array.to-sorted, es.array.to-spliced, es.array.with.

text
/* Detailed source-code truncated for AI context efficiency. */

CommonJS entry points:

text
/* Detailed source-code truncated for AI context efficiency. */

Examples:

text
/* Detailed source-code truncated for AI context efficiency. */

Array.fromAsync example:

js
await Array.fromAsync((async function * () { yield * [1, 2, 3]; })(), i => i ** 2); // => [1, 4, 9]

ECMAScript: Iterator[⬆](#index)

Modules es.iterator.constructor, es.iterator.concat, es.iterator.dispose, es.iterator.drop, es.iterator.every, es.iterator.filter, es.iterator.find, es.iterator.flat-map, es.iterator.for-each, es.iterator.from, es.iterator.map, es.iterator.reduce, es.iterator.some, es.iterator.take, es.iterator.to-array, es.iterator.zip, es.iterator.zip-keyed

ts
class Iterator {
  static concat(...items: Array): Iterator<any>;
  static from(iterable: Iterable<any> | Iterator<any>): Iterator<any>;
  static zip<T extends readonly Iterable[]>(
    iterables: T,
    options?: {
      mode?: 'shortest' | 'longest' | 'strict';
      padding?: { [K in keyof T]?: T[K] extends Iterable ? U : never };
    }
  ): Iterator<{ [K in keyof T]: T[K] extends Iterable ? U : never }>;
  static zipKeyed<K extends PropertyKey, V extends Record<K, Iterable>>(
    iterables: V,
    options?: {
      mode?: 'shortest' | 'longest' | 'strict';
      padding?: { [P in keyof V]?: V[P] extends Iterable ? U : never };
    }
  ): Iterator<{ [P in keyof V]: V[P] extends Iterable ? U : never }>;
  drop(limit: uint): Iterator<any>;
  every(callbackfn: (value: any, counter: uint) => boolean): boolean;
  filter(callbackfn: (value: any, counter: uint) => boolean): Iterator<any>;
  find(callbackfn: (value: any, counter: uint) => boolean)): any;
  flatMap(callbackfn: (value: any, counter: uint) => Iterable<any> | Iterator<any>): Iterator<any>;
  forEach(callbackfn: (value: any, counter: uint) => void): void;
  map(callbackfn: (value: any, counter: uint) => any): Iterator<any>;
  reduce(callbackfn: (memo: any, value: any, counter: uint) => any, initialValue: any): any;
  some(callbackfn: (value: any, counter: uint) => boolean): boolean;
  take(limit: uint): Iterator<any>;
  toArray(): Array<any>;
  @@dispose(): undefined;
  @@toStringTag: 'Iterator'
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/iterator
core-js(-pure)/es|stable|actual|full/iterator/concat
core-js(-pure)/es|stable|actual|full/iterator/dispose
core-js(-pure)/es|stable|actual|full/iterator/drop
core-js(-pure)/es|stable|actual|full/iterator/every
core-js(-pure)/es|stable|actual|full/iterator/filter
core-js(-pure)/es|stable|actual|full/iterator/find
core-js(-pure)/es|stable|actual|full/iterator/flat-map
core-js(-pure)/es|stable|actual|full/iterator/for-each
core-js(-pure)/es|stable|actual|full/iterator/from
core-js(-pure)/es|stable|actual|full/iterator/map
core-js(-pure)/es|stable|actual|full/iterator/reduce
core-js(-pure)/es|stable|actual|full/iterator/some
core-js(-pure)/es|stable|actual|full/iterator/take
core-js(-pure)/es|stable|actual|full/iterator/to-array
core-js(-pure)/es|stable|actual|full/iterator/zip
core-js(-pure)/es|stable|actual|full/iterator/zip-keyed

Examples:

js
[1, 2, 3, 4, 5, 6, 7].values()
  .drop(1)
  .take(5)
  .filter(it => it % 2)
  .map(it => it ** 2)
  .toArray(); // => [9, 25]

Iterator.from({
  next: () => ({ done: Math.random() > 0.9, value: Math.random() * 10 | 0 }),
}).toArray(); // => [7, 6, 3, 0, 2, 8]

Iterator.concat([0, 1].values(), [2, 3], function * () {
  yield 4;
  yield 5;
}()).toArray(); // => [0, 1, 2, 3, 4, 5]

Iterator.zip([
  [0, 1, 2],
  [3, 4, 5],
]).toArray();  // => [[0, 3], [1, 4], [2, 5]]

Iterator.zipKeyed({
  a: [0, 1, 2],
  b: [3, 4, 5, 6],
  c: [7, 8, 9],
}, {
  mode: 'longest',
  padding: { c: 10 },
}).toArray(); // =>
/*
[
  { a: 0,         b: 3, c: 7  },
  { a: 1,         b: 4, c: 8  },
  { a: 2,         b: 5, c: 9  },
  { a: undefined, b: 6, c: 10 },
];
 */

[!WARNING]

  • For preventing prototype pollution, in the pure version, new %IteratorPrototype% methods are not added to the real %IteratorPrototype%, they are available only on wrappers - instead of [].values().map(fn) use Iterator.from([]).map(fn).

ECMAScript: String and RegExp[⬆](#index)

The main part of String features: modules es.string.from-code-point, es.string.raw, es.string.iterator, es.string.split, es.string.code-point-at, es.string.ends-with, es.string.includes, es.string.repeat, es.string.pad-start, es.string.pad-end, es.string.starts-with, es.string.trim, es.string.trim-start, es.string.trim-end, es.string.match-all, es.string.replace-all, es.string.at-alternative, es.string.is-well-formed, es.string.to-well-formed.

Adding support of well-known symbols @@match, @@replace, @@search and @@split and direct .exec calls to related String methods, modules es.string.match, es.string.replace, es.string.search and es.string.split.

Annex B methods. Modules es.string.anchor, es.string.big, es.string.blink, es.string.bold, es.string.fixed, es.string.fontcolor, es.string.fontsize, es.string.italics, es.string.link, es.string.small, es.string.strike, es.string.sub, es.string.sup, es.string.substr, es.escape and es.unescape.

RegExp features: modules es.regexp.constructor, es.regexp.escape, es.regexp.dot-all, es.regexp.flags, es.regexp.sticky and es.regexp.test.

text
/* Detailed source-code truncated for AI context efficiency. */

CommonJS entry points:

text
/* Detailed source-code truncated for AI context efficiency. */

Examples:

text
/* Detailed source-code truncated for AI context efficiency. */

Example:

js
console.log(RegExp.escape('10$')); // => '\\x310\\$'
console.log(RegExp.escape('abcdefg_123456')); // => '\\x61bcdefg_123456'
console.log(RegExp.escape('Привет')); // => 'Привет'
console.log(RegExp.escape('(){}[]|,.?*+-^$=<>\\/#&!%:;@~\'"`'));
// => '\\(\\)\\{\\}\\[\\]\\|\\x2c\\.\\?\\*\\+\\x2d\\^\\$\\x3d\\x3c\\x3e\\\\\\/\\x23\\x26\\x21\\x25\\x3a\\x3b\\x40\\x7e\\x27\\x22\\x60'
console.log(RegExp.escape('\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF'));
// => '\\\t\\\n\\\v\\\f\\\r\\x20\\xa0\\u1680\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029\\ufeff'
console.log(RegExp.escape('💩')); // => '💩'
console.log(RegExp.escape('\uD83D')); // => '\\ud83d'

ECMAScript: Number[⬆](#index)

Module es.number.constructor. Number constructor support binary and octal literals, example:

js
Number('0b1010101'); // => 85
Number('0o7654321'); // => 2054353

Modules es.number.epsilon, es.number.is-finite, es.number.is-integer, es.number.is-nan, es.number.is-safe-integer, es.number.max-safe-integer, es.number.min-safe-integer, es.number.parse-float, es.number.parse-int, es.number.to-exponential, es.number.to-fixed, es.number.to-precision, es.parse-int, es.parse-float.

ts
class Number {
  constructor(value: any): number;
  toExponential(digits: number): string;
  toFixed(digits: number): string;
  toPrecision(precision: number): string;
  static isFinite(number: any): boolean;
  static isNaN(number: any): boolean;
  static isInteger(number: any): boolean;
  static isSafeInteger(number: any): boolean;
  static parseFloat(string: string): number;
  static parseInt(string: string, radix?: number = 10): number;
  static EPSILON: number;
  static MAX_SAFE_INTEGER: number;
  static MIN_SAFE_INTEGER: number;
}

function parseFloat(string: string): number;
function parseInt(string: string, radix?: number = 10): number;

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/number
core-js(-pure)/es|stable|actual|full/number/constructor
core-js(-pure)/es|stable|actual|full/number/is-finite
core-js(-pure)/es|stable|actual|full/number/is-nan
core-js(-pure)/es|stable|actual|full/number/is-integer
core-js(-pure)/es|stable|actual|full/number/is-safe-integer
core-js(-pure)/es|stable|actual|full/number/parse-float
core-js(-pure)/es|stable|actual|full/number/parse-int
core-js(-pure)/es|stable|actual|full/number/epsilon
core-js(-pure)/es|stable|actual|full/number/max-safe-integer
core-js(-pure)/es|stable|actual|full/number/min-safe-integer
core-js(-pure)/es|stable|actual|full/number(/virtual)/to-exponential
core-js(-pure)/es|stable|actual|full/number(/virtual)/to-fixed
core-js(-pure)/es|stable|actual|full/number(/virtual)/to-precision
core-js(-pure)/es|stable|actual|full/parse-float
core-js(-pure)/es|stable|actual|full/parse-int

ECMAScript: Math[⬆](#index)

Modules es.math.acosh, es.math.asinh, es.math.atanh, es.math.cbrt, es.math.clz32, es.math.cosh, es.math.expm1, es.math.fround, es.math.f16round, es.math.hypot, es.math.imul, es.math.log10, es.math.log1p, es.math.log2, es.math.sign, es.math.sinh, esnext.math.sum-precise, es.math.tanh, es.math.trunc.

ts
namespace Math {
  acosh(number: number): number;
  asinh(number: number): number;
  atanh(number: number): number;
  cbrt(number: number): number;
  clz32(number: number): number;
  cosh(number: number): number;
  expm1(number: number): number;
  fround(number: number): number;
  f16round(number: any): number;
  hypot(...args: Array<number>): number;
  imul(number1: number, number2: number): number;
  log1p(number: number): number;
  log10(number: number): number;
  log2(number: number): number;
  sign(number: number): 1 | -1 | 0 | -0 | NaN;
  sinh(number: number): number;
  sumPrecise(items: Iterable<number>): Number;
  tanh(number: number): number;
  trunc(number: number): number;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/math
core-js(-pure)/es|stable|actual|full/math/acosh
core-js(-pure)/es|stable|actual|full/math/asinh
core-js(-pure)/es|stable|actual|full/math/atanh
core-js(-pure)/es|stable|actual|full/math/cbrt
core-js(-pure)/es|stable|actual|full/math/clz32
core-js(-pure)/es|stable|actual|full/math/cosh
core-js(-pure)/es|stable|actual|full/math/expm1
core-js(-pure)/es|stable|actual|full/math/fround
core-js(-pure)/es|stable|actual|full/math/f16round
core-js(-pure)/es|stable|actual|full/math/hypot
core-js(-pure)/es|stable|actual|full/math/imul
core-js(-pure)/es|stable|actual|full/math/log1p
core-js(-pure)/es|stable|actual|full/math/log10
core-js(-pure)/es|stable|actual|full/math/log2
core-js(-pure)/es|stable|actual|full/math/sign
core-js(-pure)/es|stable|actual|full/math/sinh
core-js(-pure)/es|stable|actual|full/math/sum-precise
core-js(-pure)/es|stable|actual|full/math/tanh
core-js(-pure)/es|stable|actual|full/math/trunc

Examples:

js
1e20 + 0.1 + -1e20; // => 0
Math.sumPrecise([1e20, 0.1, -1e20]); // => 0.1

ECMAScript: Date[⬆](#index)

Modules es.date.to-string, ES5 features with fixes: es.date.now, es.date.to-iso-string, es.date.to-json and es.date.to-primitive.

Annex B methods. Modules es.date.get-year, es.date.set-year and es.date.to-gmt-string.

ts
class Date {
  getYear(): int;
  setYear(year: int): number;
  toGMTString(): string;
  toISOString(): string;
  toJSON(): string;
  toString(): string;
  @@toPrimitive(hint: 'default' | 'number' | 'string'): string | number;
  static now(): number;
}

CommonJS entry points:

text
core-js/es|stable|actual|full/date
core-js/es|stable|actual|full/date/to-string
core-js(-pure)/es|stable|actual|full/date/now
core-js(-pure)/es|stable|actual|full/date/get-year
core-js(-pure)/es|stable|actual|full/date/set-year
core-js(-pure)/es|stable|actual|full/date/to-gmt-string
core-js(-pure)/es|stable|actual|full/date/to-iso-string
core-js(-pure)/es|stable|actual|full/date/to-json
core-js(-pure)/es|stable|actual|full/date/to-primitive

Example:

js
new Date(NaN).toString(); // => 'Invalid Date'

ECMAScript: Promise[⬆](#index)

Modules es.promise, es.promise.all-settled, es.promise.any, es.promise.finally, es.promise.try and es.promise.with-resolvers.

ts
class Promise {
  constructor(executor: (resolve: Function, reject: Function) => void): Promise;
  then(onFulfilled: Function, onRejected: Function): Promise;
  catch(onRejected: Function): Promise;
  finally(onFinally: Function): Promise;
  static all(iterable: Iterable): Promise;
  static allSettled(iterable: Iterable): Promise;
  static any(promises: Iterable): Promise;
  static race(iterable: Iterable): Promise;
  static reject(r: any): Promise;
  static resolve(x: any): Promise;
  static try(callbackfn: Function, ...args?: Array<mixed>): Promise;
  static withResolvers(): { promise: Promise, resolve: function, reject: function };
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/promise
core-js(-pure)/es|stable|actual|full/promise/all-settled
core-js(-pure)/es|stable|actual|full/promise/any
core-js(-pure)/es|stable|actual|full/promise/finally
core-js(-pure)/es|stable|actual|full/promise/try
core-js(-pure)/es|stable|actual|full/promise/with-resolvers

Basic example:

js
/* eslint-disable promise/prefer-await-to-callbacks -- example */
function sleepRandom(time) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, time * 1e3, 0 | Math.random() * 1e3);
  });
}

console.log('Run');                    // => Run
sleepRandom(5).then(result => {
  console.log(result);                 // => 869, after 5 sec.
  return sleepRandom(10);
}).then(result => {
  console.log(result);                 // => 202, after 10 sec.
}).then(() => {
  console.log('immediately after');    // => immediately after
  throw new Error('Irror!');
}).then(() => {
  console.log('will not be displayed');
}).catch(error => console.log(error)); // => => Error: Irror!

Promise.resolve and Promise.reject example:

js
/* eslint-disable promise/prefer-await-to-callbacks -- example */
Promise.resolve(42).then(x => console.log(x));         // => 42
Promise.reject(42).catch(error => console.log(error)); // => 42

Promise.resolve($.getJSON('/data.json')); // => ES promise

Promise#finally example:

js
Promise.resolve(42).finally(() => console.log('You will see it anyway'));

Promise.reject(42).finally(() => console.log('You will see it anyway'));

Promise.all example:

js
Promise.all([
  'foo',
  sleepRandom(5),
  sleepRandom(15),
  sleepRandom(10),            // after 15 sec:
]).then(x => console.log(x)); // => ['foo', 956, 85, 382]

Promise.race example:

js
/* eslint-disable promise/prefer-await-to-callbacks -- example */
function timeLimit(promise, time) {
  return Promise.race([promise, new Promise((resolve, reject) => {
    setTimeout(reject, time * 1e3, new Error(`Await > ${ time } sec`));
  })]);
}

timeLimit(sleepRandom(5), 10).then(x => console.log(x));           // => 853, after 5 sec.
timeLimit(sleepRandom(15), 10).catch(error => console.log(error)); // Error: Await > 10 sec

Promise.allSettled example:

js
Promise.allSettled([
  Promise.resolve(1),
  Promise.reject(2),
  Promise.resolve(3),
]).then(console.log); // => [{ value: 1, status: 'fulfilled' }, { reason: 2, status: 'rejected' }, { value: 3, status: 'fulfilled' }]

Promise.any example:

js
Promise.any([
  Promise.resolve(1),
  Promise.reject(2),
  Promise.resolve(3),
]).then(console.log); // => 1

Promise.any([
  Promise.reject(1),
  Promise.reject(2),
  Promise.reject(3),
]).catch(({ errors }) => console.log(errors)); // => [1, 2, 3]

Promise.try examples:

js
/* eslint-disable promise/prefer-await-to-callbacks -- example */
Promise.try(() => 42).then(it => console.log(`Promise, resolved as ${ it }`));

Promise.try(() => { throw new Error('42'); }).catch(error => console.log(`Promise, rejected as ${ error }`));

Promise.try(async () => 42).then(it => console.log(`Promise, resolved as ${ it }`));

Promise.try(async () => { throw new Error('42'); }).catch(error => console.log(`Promise, rejected as ${ error }`));

Promise.try(it => it, 42).then(it => console.log(`Promise, resolved as ${ it }`));

Promise.withResolvers examples:

js
const d = Promise.withResolvers();
d.resolve(42);
d.promise.then(console.log); // => 42

Example with async functions:

js
let delay = time => new Promise(resolve => setTimeout(resolve, time));

async function sleepRandom(time) {
  await delay(time * 1e3);
  return 0 | Math.random() * 1e3;
}

async function sleepError(time, msg) {
  await delay(time * 1e3);
  throw new Error(msg);
}

(async () => {
  try {
    console.log('Run');                // => Run
    console.log(await sleepRandom(5)); // => 936, after 5 sec.
    let [a, b, c] = await Promise.all([
      sleepRandom(5),
      sleepRandom(15),
      sleepRandom(10),
    ]);
    console.log(a, b, c);              // => 210 445 71, after 15 sec.
    await sleepError(5, 'Error!');
    console.log('Will not be displayed');
  } catch (error) {
    console.log(error);                // => Error: 'Error!', after 5 sec.
  }
})();

Unhandled rejection tracking[⬆](#index)

In Node.js, like in native implementation, available events unhandledRejection and rejectionHandled:

js
process.on('unhandledRejection', (reason, promise) => console.log('unhandled', reason, promise));
process.on('rejectionHandled', promise => console.log('handled', promise));

let promise = Promise.reject(42);
// unhandled 42 [object Promise]

// eslint-disable-next-line promise/prefer-await-to-then -- example
setTimeout(() => promise.catch(() => { /* empty */ }), 1e3);
// handled [object Promise]

In a browser on rejection, by default, you will see notify in the console, or you can add a custom handler and a handler on handling unhandled, example:

js
globalThis.addEventListener('unhandledrejection', e => console.log('unhandled', e.reason, e.promise));
globalThis.addEventListener('rejectionhandled', e => console.log('handled', e.reason, e.promise));
// or
globalThis.onunhandledrejection = e => console.log('unhandled', e.reason, e.promise);
globalThis.onrejectionhandled = e => console.log('handled', e.reason, e.promise);

let promise = Promise.reject(42);
// => unhandled 42 [object Promise]

// eslint-disable-next-line promise/prefer-await-to-then -- example
setTimeout(() => promise.catch(() => { /* empty */ }), 1e3);
// => handled 42 [object Promise]

ECMAScript: Symbol[⬆](#index)

Modules es.symbol, es.symbol.async-dispose, es.symbol.async-iterator, es.symbol.description, es.symbol.dispose, es.symbol.has-instance, es.symbol.is-concat-spreadable, es.symbol.iterator, es.symbol.match, es.symbol.replace, es.symbol.search, es.symbol.species, es.symbol.split, es.symbol.to-primitive, es.symbol.to-string-tag, es.symbol.unscopables, es.math.to-string-tag.

ts
class Symbol {
  constructor(description?): symbol;
  readonly attribute description: string | void;
  static asyncDispose: @@asyncDispose;
  static asyncIterator: @@asyncIterator;
  static dispose: @@dispose;
  static hasInstance: @@hasInstance;
  static isConcatSpreadable: @@isConcatSpreadable;
  static iterator: @@iterator;
  static match: @@match;
  static replace: @@replace;
  static search: @@search;
  static species: @@species;
  static split: @@split;
  static toPrimitive: @@toPrimitive;
  static toStringTag: @@toStringTag;
  static unscopables: @@unscopables;
  static for(key: string): symbol;
  static keyFor(sym: symbol): string;
  static useSimple(): void;
  static useSetter(): void;
}

class Object {
  static getOwnPropertySymbols(object: any): Array<symbol>;
}

Also wrapped some methods for correct work with Symbol polyfill.

ts
class Object {
  static create(prototype: Object | null, properties?: { [property: PropertyKey]: PropertyDescriptor }): Object;
  static defineProperties(object: Object, properties: { [property: PropertyKey]: PropertyDescriptor })): Object;
  static defineProperty(object: Object, property: PropertyKey, attributes: PropertyDescriptor): Object;
  static getOwnPropertyDescriptor(object: any, property: PropertyKey): PropertyDescriptor | void;
  static getOwnPropertyNames(object: any): Array<string>;
  propertyIsEnumerable(key: PropertyKey): boolean;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/symbol
core-js(-pure)/es|stable|actual|full/symbol/async-dispose
core-js(-pure)/es|stable|actual|full/symbol/async-iterator
core-js/es|stable|actual|full/symbol/description
core-js(-pure)/es|stable|actual|full/symbol/dispose
core-js(-pure)/es|stable|actual|full/symbol/has-instance
core-js(-pure)/es|stable|actual|full/symbol/is-concat-spreadable
core-js(-pure)/es|stable|actual|full/symbol/iterator
core-js(-pure)/es|stable|actual|full/symbol/match
core-js(-pure)/es|stable|actual|full/symbol/replace
core-js(-pure)/es|stable|actual|full/symbol/search
core-js(-pure)/es|stable|actual|full/symbol/species
core-js(-pure)/es|stable|actual|full/symbol/split
core-js(-pure)/es|stable|actual|full/symbol/to-primitive
core-js(-pure)/es|stable|actual|full/symbol/to-string-tag
core-js(-pure)/es|stable|actual|full/symbol/unscopables
core-js(-pure)/es|stable|actual|full/symbol/for
core-js(-pure)/es|stable|actual|full/symbol/key-for
core-js(-pure)/es|stable|actual|full/object/get-own-property-symbols
core-js(-pure)/es|stable|actual|full/math/to-string-tag

Basic example:

js
let Person = (() => {
  let NAME = Symbol('name');
  return class {
    constructor(name) {
      this[NAME] = name;
    }
    getName() {
      return this[NAME];
    }
  };
})();

let person = new Person('Vasya');
console.log(person.getName());            // => 'Vasya'
console.log(person.name);                 // => undefined
console.log(person[Symbol('name')]);      // => undefined, symbols are uniq
for (let key in person) console.log(key); // => nothing, symbols are not enumerable

Symbol.for & Symbol.keyFor example:

js
let symbol = Symbol.for('key');
symbol === Symbol.for('key'); // true
Symbol.keyFor(symbol);        // 'key'

Example with methods for getting own object keys:

js
let object = { a: 1 };
Object.defineProperty(object, 'b', { value: 2 });
object[Symbol('c')] = 3;
Object.keys(object);                  // => ['a']
Object.getOwnPropertyNames(object);   // => ['a', 'b']
Object.getOwnPropertySymbols(object); // => [Symbol(c)]
Reflect.ownKeys(object);              // => ['a', 'b', Symbol(c)]

Symbol#description getter:

js
Symbol('foo').description; // => 'foo'
// eslint-disable-next-line symbol-description -- example
Symbol().description;      // => undefined

Caveats when using `Symbol` polyfill:[⬆](#index)

  • We can't add a new primitive type, Symbol returns an object.
  • Symbol.for and Symbol.keyFor can't be polyfilled cross-realm.
  • By default, to hide the keys, Symbol polyfill defines a setter in Object.prototype. For this reason, an uncontrolled creation of symbols can cause a memory leak and the in operator is not working correctly with Symbol polyfill: Symbol() in {} // => true.

You can disable defining setters in Object.prototype. Example:

js
Symbol.useSimple();
let object1 = { [Symbol('symbol1')]: true };
for (let key in object1) console.log(key); // => 'Symbol(symbol1)_t.qamkg9f3q', w/o native Symbol

Symbol.useSetter();
let object2 = { [Symbol('symbol2')]: true };
for (let key in object2) console.log(key); // nothing
  • Currently, core-js does not add setters to Object.prototype for well-known symbols for correct work something like Symbol.iterator in foo. It can cause problems with their enumerability.
  • Some problems are possible with environment exotic objects (for example, IE localStorage).

ECMAScript: Collections[⬆](#index)

core-js uses native collections in most cases, just fixes methods / constructor, if it's required, and in the old environment uses fast polyfill (O(1) lookup).

Map[⬆](#index)

Modules es.map, es.map.group-by, es.map.get-or-insert and es.map.get-or-insert-computed.

ts
class Map {
  constructor(iterable?: Iterable<[key, value]>): Map;
  clear(): void;
  delete(key: any): boolean;
  forEach(callbackfn: (value: any, key: any, target: any) => void, thisArg: any): void;
  get(key: any): any;
  getOrInsert(key: any, value: any): any;
  getOrInsertComputed(key: any, (key: any) => value: any): any;
  has(key: any): boolean;
  set(key: any, val: any): this;
  values(): Iterator<value>;
  keys(): Iterator<key>;
  entries(): Iterator<[key, value]>;
  @@iterator(): Iterator<[key, value]>;
  readonly attribute size: number;
  static groupBy(items: Iterable, callbackfn: (value: any, index: number) => key): Map<key, Array<mixed>>;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/map
core-js(-pure)/es|stable|actual|full/map/group-by
core-js(-pure)/es|stable|actual|full/map/get-or-insert
core-js(-pure)/es|stable|actual|full/map/get-or-insert-computed

Examples:

js
let array = [1];

let map = new Map([['a', 1], [42, 2]]);
map.set(array, 3).set(true, 4);

console.log(map.size);        // => 4
console.log(map.has(array));  // => true
console.log(map.has([1]));    // => false
console.log(map.get(array));  // => 3
map.forEach((val, key) => {
  console.log(val);           // => 1, 2, 3, 4
  console.log(key);           // => 'a', 42, [1], true
});
map.delete(array);
console.log(map.size);        // => 3
console.log(map.get(array));  // => undefined
console.log(Array.from(map)); // => [['a', 1], [42, 2], [true, 4]]

map = new Map([['a', 1], ['b', 2], ['c', 3]]);

for (let [key, value] of map) {
  console.log(key);                                 // => 'a', 'b', 'c'
  console.log(value);                               // => 1, 2, 3
}
for (let value of map.values()) console.log(value); // => 1, 2, 3
for (let key of map.keys()) console.log(key);       // => 'a', 'b', 'c'
for (let [key, value] of map.entries()) {
  console.log(key);                                 // => 'a', 'b', 'c'
  console.log(value);                               // => 1, 2, 3
}

map = Map.groupBy([1, 2, 3, 4, 5], it => it % 2);
map.get(1); // => [1, 3, 5]
map.get(0); // => [2, 4]

map = new Map([['a', 1]]);

map.getOrInsert('a', 2); // => 1

map.getOrInsert('b', 3); // => 3

map.getOrInsertComputed('a', key => key); // => 1

map.getOrInsertComputed('c', key => key); // => 'c'

console.log(map); // => Map { 'a': 1, 'b': 3, 'c': 'c' }

Set[⬆](#index)

Modules es.set, es.set.difference.v2, es.set.intersection.v2, es.set.is-disjoint-from.v2, es.set.is-subset-of.v2, es.set.is-superset-of.v2, es.set.symmetric-difference.v2, es.set.union.v2

ts
class Set {
  constructor(iterable?: Iterable<value>): Set;
  add(key: any): this;
  clear(): void;
  delete(key: any): boolean;
  forEach((value: any, key: any, target: any) => void, thisArg: any): void;
  has(key: any): boolean;
  values(): Iterator<value>;
  keys(): Iterator<value>;
  entries(): Iterator<[value, value]>;
  difference(other: SetLike<mixed>): Set;
  intersection(other: SetLike<mixed>): Set;
  isDisjointFrom(other: SetLike<mixed>): boolean;
  isSubsetOf(other: SetLike<mixed>): boolean;
  isSupersetOf(other: SetLike<mixed>): boolean;
  symmetricDifference(other: SetLike<mixed>): Set;
  union(other: SetLike<mixed>): Set;
  @@iterator(): Iterator<value>;
  readonly attribute size: number;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/set
core-js(-pure)/es|stable|actual|full/set/difference
core-js(-pure)/es|stable|actual|full/set/intersection
core-js(-pure)/es|stable|actual|full/set/is-disjoint-from
core-js(-pure)/es|stable|actual|full/set/is-subset-of
core-js(-pure)/es|stable|actual|full/set/is-superset-of
core-js(-pure)/es|stable|actual|full/set/symmetric-difference
core-js(-pure)/es|stable|actual|full/set/union

Examples:

js
let set = new Set(['a', 'b', 'a', 'c']);
set.add('d').add('b').add('e');
console.log(set.size);        // => 5
console.log(set.has('b'));    // => true
set.forEach(it => {
  console.log(it);            // => 'a', 'b', 'c', 'd', 'e'
});
set.delete('b');
console.log(set.size);        // => 4
console.log(set.has('b'));    // => false
console.log(Array.from(set)); // => ['a', 'c', 'd', 'e']

set = new Set([1, 2, 3, 2, 1]);

for (let value of set) console.log(value);          // => 1, 2, 3
for (let value of set.values()) console.log(value); // => 1, 2, 3
for (let key of set.keys()) console.log(key);       // => 1, 2, 3
for (let [key, value] of set.entries()) {
  console.log(key);                                 // => 1, 2, 3
  console.log(value);                               // => 1, 2, 3
}

new Set([1, 2, 3]).union(new Set([3, 4, 5]));               // => Set {1, 2, 3, 4, 5}
new Set([1, 2, 3]).intersection(new Set([3, 4, 5]));        // => Set {3}
new Set([1, 2, 3]).difference(new Set([3, 4, 5]));          // => Set {1, 2}
new Set([1, 2, 3]).symmetricDifference(new Set([3, 4, 5])); // => Set {1, 2, 4, 5}
new Set([1, 2, 3]).isDisjointFrom(new Set([4, 5, 6]));      // => true
new Set([1, 2, 3]).isSubsetOf(new Set([5, 4, 3, 2, 1]));    // => true
new Set([5, 4, 3, 2, 1]).isSupersetOf(new Set([1, 2, 3]));  // => true

WeakMap[⬆](#index)

Module es.weak-map, es.weak-map.get-or-insert and es.weak-map.get-or-insert-computed.

ts
class WeakMap {
  constructor(iterable?: Iterable<[key, value]>): WeakMap;
  delete(key: object | symbol): boolean;
  get(key: object | symbol): any;
  getOrInsert(key: object | symbol, value: any): any;
  getOrInsertComputed(key: object | symbol, (key: any) => value: any): any;
  has(key: object | symbol): boolean;
  set(key: object | symbol, val: any): this;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/weak-map
core-js(-pure)/es|stable|actual|full/weak-map/get-or-insert
core-js(-pure)/es|stable|actual|full/weak-map/get-or-insert-computed

Examples:

js
let a = [1];
let b = [2];
let c = [3];

let weakmap = new WeakMap([[a, 1], [b, 2]]);
weakmap.set(c, 3).set(b, 4);
console.log(weakmap.has(a));   // => true
console.log(weakmap.has([1])); // => false
console.log(weakmap.get(a));   // => 1
weakmap.delete(a);
console.log(weakmap.get(a));   // => undefined

// Private properties store:
let Person = (() => {
  let names = new WeakMap();
  return class {
    constructor(name) {
      names.set(this, name);
    }
    getName() {
      return names.get(this);
    }
  };
})();

let person = new Person('Vasya');
console.log(person.getName());            // => 'Vasya'
for (let key in person) console.log(key); // => only 'getName'

WeakSet[⬆](#index)

Module es.weak-set.

ts
class WeakSet {
  constructor(iterable?: Iterable<value>): WeakSet;
  add(key: object | symbol): this;
  delete(key: object | symbol): boolean;
  has(key: object | symbol): boolean;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/weak-set

Examples:

js
let a = [1];
let b = [2];
let c = [3];

let weakset = new WeakSet([a, b, a]);
weakset.add(c).add(b).add(c);
console.log(weakset.has(b));   // => true
console.log(weakset.has([2])); // => false
weakset.delete(b);
console.log(weakset.has(b));   // => false

[!WARNING]

  • Weak-collections polyfill stores values as hidden properties of keys. It works correctly and does not leak in most cases. However, it is desirable to store a collection longer than its keys.
  • Native symbols as WeakMap keys can't be properly polyfilled without memory leaks.

ECMAScript: Explicit Resource Management[⬆](#index)

[!NOTE]
This is only built-ins for this Explicit Resource Management, using syntax support requires transpiler support.

Modules es.disposable-stack.constructor, es.iterator.dispose, es.async-disposable-stack.constructor, es.async-iterator.async-dispose.

ts
class Symbol {
  static asyncDispose: @@asyncDispose;
  static dispose: @@dispose;
}

class DisposableStack {
  constructor(): DisposableStack;
  dispose(): undefined;
  use(value: Disposable): value;
  adopt(value: object, onDispose: Function): value;
  defer(onDispose: Function): undefined;
  move(): DisposableStack;
  @@dispose(): undefined;
  @@toStringTag: 'DisposableStack';
}

class AsyncDisposableStack {
  constructor(): AsyncDisposableStack;
  disposeAsync(): Promise;
  use(value: AsyncDisposable | Disposable): value;
  adopt(value: object, onDispose: Function): value;
  defer(onDispose: Function): undefined;
  move(): AsyncDisposableStack;
  @@asyncDispose(): Promise;
  @@toStringTag: 'AsyncDisposableStack';
}

class SuppressedError extends Error {
  constructor(error: any, suppressed: any, message?: string): SuppressedError;
  error: any;
  suppressed: any;
  message: string;
  cause: any;
}

class Iterator {
  @@dispose(): undefined;
}

class AsyncIterator {
  @@asyncDispose(): Promise;
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/disposable-stack
core-js(-pure)/es|stable|actual|full/async-disposable-stack
core-js(-pure)/es|stable|actual|full/iterator/dispose
core-js(-pure)/es|stable|actual|full/async-iterator/async-dispose

ECMAScript: Typed Arrays[⬆](#index)

Implementations and fixes for ArrayBuffer, DataView, Typed Arrays constructors, static and prototype methods. Typed arrays work only in environments with support descriptors (IE9+), ArrayBuffer and DataView should work anywhere.

Modules es.array-buffer.constructor, es.array-buffer.is-view, esnext.array-buffer.detached, es.array-buffer.slice, esnext.array-buffer.transfer, esnext.array-buffer.transfer-to-fixed-length es.data-view, es.data-view.get-float16, es.data-view.set-float16, es.typed-array.int8-array, es.typed-array.uint8-array, es.typed-array.uint8-clamped-array, es.typed-array.int16-array, es.typed-array.uint16-array, es.typed-array.int32-array, es.typed-array.uint32-array, es.typed-array.float32-array, es.typed-array.float64-array, es.typed-array.copy-within, es.typed-array.every, es.typed-array.fill, es.typed-array.filter, es.typed-array.find, es.typed-array.find-index, es.typed-array.find-last, es.typed-array.find-last-index, es.typed-array.for-each, es.typed-array.from, es.typed-array.includes, es.typed-array.index-of, es.typed-array.iterator, es.typed-array.last-index-of, es.typed-array.map, es.typed-array.of, es.typed-array.reduce, es.typed-array.reduce-right, es.typed-array.reverse, es.typed-array.set, es.typed-array.slice, es.typed-array.some, es.typed-array.sort, es.typed-array.subarray, es.typed-array.to-locale-string, es.typed-array.to-string, es.typed-array.at, es.typed-array.to-reversed, es.typed-array.to-sorted, es.typed-array.with, es.uint8-array.from-base64, es.uint8-array.from-hex, es.uint8-array.set-from-hex, es.uint8-array.to-base64, es.uint8-array.to-hex.

text
/* Detailed source-code truncated for AI context efficiency. */

CommonJS entry points:

text
/* Detailed source-code truncated for AI context efficiency. */

Examples:

js
new Int32Array(4);                          // => [0, 0, 0, 0]
new Uint8ClampedArray([1, 2, 3, 666]);      // => [1, 2, 3, 255]
new Float32Array(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]

let buffer = new ArrayBuffer(8);
let view = new DataView(buffer);
view.setFloat64(0, 123.456, true);
new Uint8Array(buffer.slice(4)); // => [47, 221, 94, 64]

Int8Array.of(1, 1.5, 5.7, 745);      // => [1, 1, 5, -23]
Uint8Array.from([1, 1.5, 5.7, 745]); // => [1, 1, 5, 233]

let typed = new Uint8Array([1, 2, 3]);

let a = typed.slice(1);    // => [2, 3]
typed.buffer === a.buffer; // => false
let b = typed.subarray(1); // => [2, 3]
typed.buffer === b.buffer; // => true

typed.filter(it => it % 2); // => [1, 3]
typed.map(it => it * 1.5);  // => [1, 3, 4]

for (let value of typed) console.log(value);          // => 1, 2, 3
for (let value of typed.values()) console.log(value); // => 1, 2, 3
for (let key of typed.keys()) console.log(key);       // => 0, 1, 2
for (let [key, value] of typed.entries()) {
  console.log(key);                                   // => 0, 1, 2
  console.log(value);                                 // => 1, 2, 3
}

new Int32Array([1, 2, 3]).at(1);  // => 2
new Int32Array([1, 2, 3]).at(-1); // => 3

buffer = Int8Array.of(1, 2, 3, 4, 5, 6, 7, 8).buffer;
console.log(buffer.byteLength); // => 8
console.log(buffer.detached); // => false
const newBuffer = buffer.transfer(4);
console.log(buffer.byteLength); // => 0
console.log(buffer.detached); // => true
console.log(newBuffer.byteLength); // => 4
console.log(newBuffer.detached); // => false
console.log([...new Int8Array(newBuffer)]); // => [1, 2, 3, 4]

Base64 / Hex examples:

js
let arr = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);
console.log(arr.toBase64()); // => 'SGVsbG8gV29ybGQ='
console.log(arr.toBase64({ omitPadding: true })); // => 'SGVsbG8gV29ybGQ'
console.log(arr.toHex()); // => '48656c6c6f20576f726c64'
console.log(Uint8Array.fromBase64('SGVsbG8gV29ybGQ=')); // => Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])
console.log(Uint8Array.fromHex('48656c6c6f20576f726c64')); // => Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])

[!WARNING]

  • Polyfills of Typed Arrays constructors work completely how they should work by the spec. Still, because of the internal usage of getters / setters on each instance, they are slow and consume significant memory. However, polyfills of Typed Arrays constructors are required mainly for old IE, all modern engines have native Typed Arrays constructors and require only fixes of constructors and polyfills of methods.
  • ArrayBuffer.prototype.{ transfer, transferToFixedLength } polyfilled only in runtime with native structuredClone with ArrayBuffer transfer or MessageChannel support.

ECMAScript: Reflect[⬆](#index)

Modules es.reflect.apply, es.reflect.construct, es.reflect.define-property, es.reflect.delete-property, es.reflect.get, es.reflect.get-own-property-descriptor, es.reflect.get-prototype-of, es.reflect.has, es.reflect.is-extensible, es.reflect.own-keys, es.reflect.prevent-extensions, es.reflect.set, es.reflect.set-prototype-of.

ts
namespace Reflect {
  apply(target: Function, thisArgument: any, argumentsList: Array<mixed>): any;
  construct(target: Function, argumentsList: Array<mixed>, newTarget?: Function): Object;
  defineProperty(target: Object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
  deleteProperty(target: Object, propertyKey: PropertyKey): boolean;
  get(target: Object, propertyKey: PropertyKey, receiver?: any): any;
  getOwnPropertyDescriptor(target: Object, propertyKey: PropertyKey): PropertyDescriptor | void;
  getPrototypeOf(target: Object): Object | null;
  has(target: Object, propertyKey: PropertyKey): boolean;
  isExtensible(target: Object): boolean;
  ownKeys(target: Object): Array<string | symbol>;
  preventExtensions(target: Object): boolean;
  set(target: Object, propertyKey: PropertyKey, V: any, receiver?: any): boolean;
  setPrototypeOf(target: Object, proto: Object | null): boolean; // required __proto__ - IE11+
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/reflect
core-js(-pure)/es|stable|actual|full/reflect/apply
core-js(-pure)/es|stable|actual|full/reflect/construct
core-js(-pure)/es|stable|actual|full/reflect/define-property
core-js(-pure)/es|stable|actual|full/reflect/delete-property
core-js(-pure)/es|stable|actual|full/reflect/get
core-js(-pure)/es|stable|actual|full/reflect/get-own-property-descriptor
core-js(-pure)/es|stable|actual|full/reflect/get-prototype-of
core-js(-pure)/es|stable|actual|full/reflect/has
core-js(-pure)/es|stable|actual|full/reflect/is-extensible
core-js(-pure)/es|stable|actual|full/reflect/own-keys
core-js(-pure)/es|stable|actual|full/reflect/prevent-extensions
core-js(-pure)/es|stable|actual|full/reflect/set
core-js(-pure)/es|stable|actual|full/reflect/set-prototype-of

Examples:

js
let object = { a: 1 };
Object.defineProperty(object, 'b', { value: 2 });
object[Symbol('c')] = 3;
Reflect.ownKeys(object); // => ['a', 'b', Symbol(c)]

function C(a, b) {
  this.c = a + b;
}

let instance = Reflect.construct(C, [20, 22]);
instance.c; // => 42

ECMAScript: JSON[⬆](#index)

Since JSON object is missed only in very old engines like IE7-, core-js does not provide a full JSON.{ parse, stringify } polyfill, however, fix already existing implementations by the current standard.

Modules es.json.is-raw-json, es.json.parse, es.json.raw-json, es.json.stringify and es.json.to-string-tag .

ts
namespace JSON {
  isRawJSON(O: any): boolean;
  parse(text: string, reviver?: (this: any, key: string, value: any, context: { source?: string }) => any): any;
  rawJSON(text: any): RawJSON;
  stringify(value: any, replacer?: Array<string | number> | (this: any, key: string, value: any) => any, space?: string | number): string | void;
  @@toStringTag: 'JSON';
}

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/json/is-raw-json
core-js(-pure)/es|stable|actual|full/json/parse
core-js(-pure)/es|stable|actual|full/json/raw-json
core-js(-pure)/es|stable|actual|full/json/stringify
core-js(-pure)/es|stable|actual|full/json/stringify
core-js(-pure)/es|stable|actual|full/json/to-string-tag

Examples:

js
JSON.stringify({ '𠮷': ['\uDF06\uD834'] }); // => '{"𠮷":["\\udf06\\ud834"]}'

function digitsToBigInt(key, val, { source }) {
  return /^\d+$/.test(source) ? BigInt(source) : val;
}

function bigIntToRawJSON(key, val) {
  return typeof val === 'bigint' ? JSON.rawJSON(String(val)) : val;
}

const tooBigForNumber = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
JSON.parse(String(tooBigForNumber), digitsToBigInt) === tooBigForNumber; // true

const wayTooBig = BigInt(`1${ '0'.repeat(1000) }`);
JSON.parse(String(wayTooBig), digitsToBigInt) === wayTooBig; // true

const embedded = JSON.stringify({ tooBigForNumber }, bigIntToRawJSON);
embedded === '{"tooBigForNumber":9007199254740993}'; // true

ECMAScript: globalThis[⬆](#index)

Module es.global-this.

ts
let globalThis: GlobalThisValue;

CommonJS entry points:

text
core-js(-pure)/es|stable|actual|full/global-this

Examples:

js
globalThis.Array === Array; // => true

ECMAScript proposals[⬆](#index)

The TC39 process.

core-js/stage/3 entry point contains only stage 3 proposals, core-js/stage/2.7 - stage 2.7 and stage 3, etc.

Finished proposals[⬆](#index)

Finished (stage 4) proposals already marked in core-js as stable ECMAScript, they are available in core-js/stable and core-js/es namespace, you can find them in related sections of the README. However, even for finished proposals, core-js provides a way to include only features for a specific proposal like core-js/proposals/proposal-name.

[`globalThis`](https://github.com/tc39/proposal-global)[⬆](#index)

ts
let globalThis: GlobalThisValue;

CommonJS entry points:

text
core-js/proposals/global-this

[Relative indexing method](https://github.com/tc39/proposal-relative-indexing-method)[⬆](#index)

ts
class Array {
  at(index: int): any;
}

class String {
  at(index: int): string;
}

class %TypedArray% {
  at(index: int): number;
}

CommonJS entry points:

text
core-js/proposals/relative-indexing-method

[`Array.prototype.includes`](https://github.com/tc39/proposal-Array.prototype.includes)[⬆](#index)

ts
class Array {
  includes(searchElement: any, from?: number): boolean;
}

class %TypedArray% {
  includes(searchElement: any, from?: number): boolean;
}

CommonJS entry points:

text
core-js/proposals/array-includes

[`Array.prototype.flat` / `Array.prototype.flatMap`](https://github.com/tc39/proposal-flatMap)[⬆](#index)

ts
class Array {
  flat(depthArg?: number = 1): Array<mixed>;
  flatMap(mapFn: (value: any, index: number, target: any) => any, thisArg: any): Array<mixed>;
}

CommonJS entry points:

text
core-js/proposals/array-flat-map

[Array find from last](https://github.com/tc39/proposal-array-find-from-last)[⬆](#index)

ts
class Array {
  findLast(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): any;
  findLastIndex(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): uint;
}

class %TypedArray% {
  findLast(callbackfn: (value: any, index: number, target: %TypedArray%) => boolean, thisArg?: any): any;
  findLastIndex(callbackfn: (value: any, index: number, target: %TypedArray%) => boolean, thisArg?: any): uint;
}

CommonJS entry points:

text
core-js/proposals/array-find-from-last

[Change `Array` by copy](https://github.com/tc39/proposal-change-array-by-copy)[⬆](#index)

ts
class Array {
  toReversed(): Array<mixed>;
  toSpliced(start?: number, deleteCount?: number, ...items: Array<mixed>): Array<mixed>;
  toSorted(comparefn?: (a: any, b: any) => number): Array<mixed>;
  with(index: includes, value: any): Array<mixed>;
}

class %TypedArray% {
  toReversed(): %TypedArray%;
  toSorted(comparefn?: (a: any, b: any) => number): %TypedArray%;
  with(index: includes, value: any): %TypedArray%;
}

CommonJS entry points:

text
core-js/proposals/change-array-by-copy-stage-4
core-js(-pure)/es|stable|actual|full/array(/virtual)/to-reversed
core-js(-pure)/es|stable|actual|full/array(/virtual)/to-sorted
core-js(-pure)/es|stable|actual|full/array(/virtual)/to-spliced
core-js(-pure)/es|stable|actual|full/array(/virtual)/with
core-js/es|stable|actual|full/typed-array/to-reversed
core-js/es|stable|actual|full/typed-array/to-sorted
core-js/es|stable|actual|full/typed-array/with

[`Array` grouping](https://github.com/tc39/proposal-array-grouping)[⬆](#index)

ts
class Object {
  static groupBy(items: Iterable, callbackfn: (value: any, index: number) => key): { [key]: Array<mixed> };
}

class Map {
  static groupBy(items: Iterable, callbackfn: (value: any, index: number) => key): Map<key, Array<mixed>>;
}

CommonJS entry points:

text
core-js/proposals/array-grouping-v2

[`Array.fromAsync`](https://github.com/tc39/proposal-array-from-async)[⬆](#index)

ts
class Array {
  static fromAsync(asyncItems: AsyncIterable | Iterable | ArrayLike, mapfn?: (value: any, index: number) => any, thisArg?: any): Array;
}

CommonJS entry points:

text
core-js/proposals/array-from-async-stage-2

[`ArrayBuffer.prototype.transfer` and friends](https://github.com/tc39/proposal-arraybuffer-transfer)[⬆](#index)

ts
class ArrayBuffer {
  readonly attribute detached: boolean;
  transfer(newLength?: number): ArrayBuffer;
  transferToFixedLength(newLength?: number): ArrayBuffer;
}

CommonJS entry points:

text
core-js/proposals/array-buffer-transfer

[`Uint8Array` to / from base64 and hex](https://github.com/tc39/proposal-arraybuffer-base64)[⬆](#index)

ts
class Uint8Array {
  static fromBase64(string: string, options?: { alphabet?: 'base64' | 'base64url', lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial' }): Uint8Array;
  static fromHex(string: string): Uint8Array;
  setFromBase64(string: string, options?: { alphabet?: 'base64' | 'base64url', lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial' }): { read: uint, written: uint };
  setFromHex(string: string): { read: uint, written: uint };
  toBase64(options?: { alphabet?: 'base64' | 'base64url', omitPadding?: boolean }): string;
  toHex(): string;
}

CommonJS entry points:

text
core-js/proposals/array-buffer-base64

[`Error.isError`](https://github.com/tc39/proposal-is-error)[⬆](#index)

ts
class Error {
  static isError(value: any): boolean;
}

CommonJS entry points:

text
core-js/proposals/is-error

[!WARNING]
We have no bulletproof way to polyfill this Error.isError / check if the object is an error, so it's an enough naive implementation.

[Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management)[⬆](#index)

[!NOTE]
This is only built-ins for this Explicit Resource Management, using syntax support requires transpiler support.

ts
class Symbol {
  static asyncDispose: @@asyncDispose;
  static dispose: @@dispose;
}

class DisposableStack {
  constructor(): DisposableStack;
  dispose(): undefined;
  use(value: Disposable): value;
  adopt(value: object, onDispose: Function): value;
  defer(onDispose: Function): undefined;
  move(): DisposableStack;
  @@dispose(): undefined;
  @@toStringTag: 'DisposableStack';
}

class AsyncDisposableStack {
  constructor(): AsyncDisposableStack;
  disposeAsync(): Promise;
  use(value: AsyncDisposable | Disposable): value;
  adopt(value: object, onDispose: Function): value;
  defer(onDispose: Function): undefined;
  move(): AsyncDisposableStack;
  @@asyncDispose(): Promise;
  @@toStringTag: 'AsyncDisposableStack';
}

class SuppressedError extends Error {
  constructor(error: any, suppressed: any, message?: string): SuppressedError;
  error: any;
  suppressed: any;
  message: string;
  cause: any;
}

class Iterator {
  @@dispose(): undefined;
}

class AsyncIterator {
  @@asyncDispose(): Promise;
}

CommonJS entry points:

text
core-js/proposals/explicit-resource-management

[`Float16` methods](https://github.com/tc39/proposal-float16array)[⬆](#index)

ts
class DataView {
  getFloat16(offset: any, littleEndian?: boolean = false): float16
  setFloat16(offset: any, value: any, littleEndian?: boolean = false): void;
}

namespace Math {
  fround(number: any): number;
}

CommonJS entry points:

text
core-js/proposals/float16

[`Iterator` helpers](https://github.com/tc39/proposal-iterator-helpers)[⬆](#index)

ts
class Iterator {
  static from(iterable: Iterable<any> | Iterator<any>): Iterator<any>;
  drop(limit: uint): Iterator<any>;
  every(callbackfn: (value: any, counter: uint) => boolean): boolean;
  filter(callbackfn: (value: any, counter: uint) => boolean): Iterator<any>;
  find(callbackfn: (value: any, counter: uint) => boolean)): any;
  flatMap(callbackfn: (value: any, counter: uint) => Iterable<any> | Iterator<any>): Iterator<any>;
  forEach(callbackfn: (value: any, counter: uint) => void): void;
  map(callbackfn: (value: any, counter: uint) => any): Iterator<any>;
  reduce(callbackfn: (memo: any, value: any, counter: uint) => any, initialValue: any): any;
  some(callbackfn: (value: any, counter: uint) => boolean): boolean;
  take(limit: uint): Iterator<any>;
  toArray(): Array<any>;
  @@toStringTag: 'Iterator'
}

CommonJS entry points:

text
core-js/proposals/iterator-helpers-stage-3-2

[`Iterator` sequencing](https://github.com/tc39/proposal-iterator-sequencing)[⬆](#index)

ts
class Iterator {
  static concat(...items: Array): Iterator<any>;
}

CommonJS entry points:

text
core-js/proposals/iterator-sequencing

[Joint iteration](https://github.com/tc39/proposal-joint-iteration)[⬆](#index)

Modules es.iterator.zip, es.iterator.zip-keyed

ts
class Iterator {
  zip<T extends readonly Iterable[]>(
    iterables: T,
    options?: {
      mode?: 'shortest' | 'longest' | 'strict';
      padding?: { [K in keyof T]?: T[K] extends Iterable ? U : never };
    }
  ): IterableIterator<{ [K in keyof T]: T[K] extends Iterable ? U : never }>;
  zipKeyed<K extends PropertyKey, V extends Record<K, Iterable>>(
    iterables: V,
    options?: {
      mode?: 'shortest' | 'longest' | 'strict';
      padding?: { [P in keyof V]?: V[P] extends Iterable ? U : never };
    }
  ): IterableIterator<{ [P in keyof V]: V[P] extends Iterable ? U : never }>;
}

CommonJS entry points:

text
core-js/proposals/joint-iteration
core-js(-pure)/es|stable|actual|full/iterator/zip
core-js(-pure)/es|stable|actual|full/iterator/zip-keyed

Example:

js
Iterator.zip([
  [0, 1, 2],
  [3, 4, 5],
]).toArray();  // => [[0, 3], [1, 4], [2, 5]]

Iterator.zipKeyed({
  a: [0, 1, 2],
  b: [3, 4, 5, 6],
  c: [7, 8, 9],
}, {
  mode: 'longest',
  padding: { c: 10 },
}).toArray();
/*
[
  { a: 0,         b: 3, c: 7  },
  { a: 1,         b: 4, c: 8  },
  { a: 2,         b: 5, c: 9  },
  { a: undefined, b: 6, c: 10 },
];
 */

[`Object.values` / `Object.entries`](https://github.com/tc39/proposal-object-values-entries)[⬆](#index)

ts
class Object {
  static entries(object: Object): Array<[string, mixed]>;
  static values(object: any): Array<mixed>;
}

CommonJS entry points:

text
core-js/proposals/object-values-entries

[`Object.fromEntries`](https://github.com/tc39/proposal-object-from-entries)[⬆](#index)

ts
class Object {
  static fromEntries(iterable: Iterable<[key, value]>): Object;
}

CommonJS entry points:

text
core-js/proposals/object-from-entries

[`Object.getOwnPropertyDescriptors`](https://github.com/tc39/proposal-object-getownpropertydescriptors)[⬆](#index)

ts
class Object {
  static getOwnPropertyDescriptors(object: any): { [property: PropertyKey]: PropertyDescriptor };
}

CommonJS entry points:

text
core-js/proposals/object-getownpropertydescriptors

[Accessible `Object.prototype.hasOwnProperty`](https://github.com/tc39/proposal-accessible-object-hasownproperty)[⬆](#index)

ts
class Object {
  static hasOwn(object: object, key: PropertyKey): boolean;
}

CommonJS entry points:

text
core-js/proposals/accessible-object-hasownproperty

[`String` padding](https://github.com/tc39/proposal-string-pad-start-end)[⬆](#index)

ts
class String {
  padStart(length: number, fillStr?: string = ' '): string;
  padEnd(length: number, fillStr?: string = ' '): string;
}

CommonJS entry points:

text
core-js/proposals/string-padding

[`String#matchAll`](https://github.com/tc39/proposal-string-matchall)[⬆](#index).

ts
class String {
  matchAll(regexp: RegExp): Iterator;
}

CommonJS entry points:

text
core-js/proposals/string-match-all

[`String#replaceAll`](https://github.com/tc39/proposal-string-replace-all)[⬆](#index)

ts
class String {
  replaceAll(searchValue: string | RegExp, replaceString: string | (searchValue, index, this) => string): string;
}

CommonJS entry points:

text
core-js/proposals/string-replace-all-stage-4

[`String.prototype.trimStart` / `String.prototype.trimEnd`](https://github.com/tc39/proposal-string-left-right-trim)[⬆](#index)

ts
class String {
  trimLeft(): string;
  trimRight(): string;
  trimStart(): string;
  trimEnd(): string;
}

CommonJS entry points:

text
core-js/proposals/string-left-right-trim

[`RegExp` `s` (`dotAll`) flag](https://github.com/tc39/proposal-regexp-dotall-flag)[⬆](#index)

ts
// patched for support `RegExp` dotAll (`s`) flag:
class RegExp {
  constructor(pattern: RegExp | string, flags?: string): RegExp;
  exec(): Array<string | undefined> | null;
  readonly attribute dotAll: boolean;
  readonly attribute flags: string;
}

CommonJS entry points:

text
core-js/proposals/regexp-dotall-flag

[`RegExp` named capture groups](https://github.com/tc39/proposal-regexp-named-groups)[⬆](#index)

ts
// patched for support `RegExp` named capture groups:
class RegExp {
  constructor(pattern: RegExp | string, flags?: string): RegExp;
  exec(): Array<string | undefined> | null;
  @@replace(string: string, replaceValue: Function | string): string;
}

CommonJS entry points:

text
core-js/proposals/regexp-named-groups

[`RegExp` escaping](https://github.com/tc39/proposal-regex-escaping)[⬆](#index)

ts
class RegExp {
  static escape(value: string): string
}

CommonJS entry points:

text
core-js/proposals/regexp-escaping

[`Promise.allSettled`](https://github.com/tc39/proposal-promise-allSettled)[⬆](#index)

ts
class Promise {
  static allSettled(iterable: Iterable): Promise;
}

CommonJS entry points:

text
core-js/proposals/promise-all-settled

[`Promise.any`](https://github.com/tc39/proposal-promise-any)[⬆](#index)

ts
class AggregateError {
  constructor(errors: Iterable, message: string): AggregateError;
  errors: Array<any>;
  message: string;
}

class Promise {
  static any(promises: Iterable): Promise<any>;
}

CommonJS entry points:

text
core-js/proposals/promise-any

[`Promise.prototype.finally`](https://github.com/tc39/proposal-promise-finally)[⬆](#index)

ts
class Promise {
  finally(onFinally: Function): Promise;
}

CommonJS entry points:

text
core-js/proposals/promise-finally

[`Promise.try`](https://github.com/tc39/proposal-promise-try)

ts
class Promise {
  static try(callbackfn: Function, ...args?: Array<mixed>): Promise;
}

CommonJS entry points:

text
core-js/proposals/promise-try

[`Promise.withResolvers`](https://github.com/tc39/proposal-promise-with-resolvers)[⬆](#index)

ts
class Promise {
  static withResolvers(): { promise: Promise, resolve: function, reject: function };
}

CommonJS entry points:

text
core-js/proposals/promise-with-resolvers

[`Symbol.asyncIterator` for asynchronous iteration](https://github.com/tc39/proposal-async-iteration)[⬆](#index)

ts
class Symbol {
  static asyncIterator: @@asyncIterator;
}

CommonJS entry points:

text
core-js/proposals/async-iteration

[`Symbol.prototype.description`](https://github.com/tc39/proposal-Symbol-description)[⬆](#index)

ts
class Symbol {
  readonly attribute description: string | void;
}

CommonJS entry points:

text
core-js/proposals/symbol-description

[`JSON.parse` source text access](https://github.com/tc39/proposal-json-parse-with-source)[⬆](#index)

ts
namespace JSON {
  isRawJSON(O: any): boolean;
  // patched for source support
  parse(text: string, reviver?: (this: any, key: string, value: any, context: { source?: string }) => any): any;
  rawJSON(text: any): RawJSON;
  // patched for `JSON.rawJSON` support
  stringify(value: any, replacer?: Array<string | number> | (this: any, key: string, value: any) => any, space?: string | number): string | void;
}

CommonJS entry points:

text
core-js/proposals/json-parse-with-source

[Well-formed `JSON.stringify`](https://github.com/tc39/proposal-well-formed-stringify)[⬆](#index)

ts
namespace JSON {
  stringify(target: any, replacer?: Function | Array, space?: string | number): string | void;
}

CommonJS entry points:

text
core-js/proposals/well-formed-stringify

[Well-formed unicode strings](https://github.com/tc39/proposal-is-usv-string)[⬆](#index)

ts
class String {
  isWellFormed(): boolean;
  toWellFormed(): string;
}

CommonJS entry points:

text
core-js/proposals/well-formed-unicode-strings

[New `Set` methods](https://github.com/tc39/proposal-set-methods)[⬆](#index)

ts
class Set {
  difference(other: SetLike<mixed>): Set;
  intersection(other: SetLike<mixed>): Set;
  isDisjointFrom(other: SetLike<mixed>): boolean;
  isSubsetOf(other: SetLike<mixed>): boolean;
  isSupersetOf(other: SetLike<mixed>): boolean;
  symmetricDifference(other: SetLike<mixed>): Set;
  union(other: SetLike<mixed>): Set;
}

CommonJS entry points:

text
core-js/proposals/set-methods-v2

[`Map` upsert](https://github.com/thumbsupep/proposal-upsert)[⬆](#index)

ts
class Map {
  getOrInsert(key: any, value: any): any;
  getOrInsertComputed(key: any, (key: any) => value: any): any;
}

class WeakMap {
  getOrInsert(key: object | symbol, value: any): any;
  getOrInsertComputed(key: object | symbol, (key: any) => value: any): any;
}

CommonJS entry points:

text
core-js/proposals/map-upsert-v4

[`Math.sumPrecise`](https://github.com/tc39/proposal-math-sum)

ts
namespace Math {
  sumPrecise(items: Iterable<number>): Number;
}

CommonJS entry points:

text
core-js/proposals/math-sum

Stage 3 proposals[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/stage/3

[`Iterator` chunking](https://github.com/tc39/proposal-iterator-chunking)[⬆](#index)

Modules esnext.iterator.chunks
and esnext.iterator.windows

ts
class Iterator {
  chunks(chunkSize: number): Iterator<any>;
  windows(windowSize: number, undersized?: 'only-full' | 'allow-partial' | undefined): Iterator<any>;
}

CommonJS entry points:

text
core-js/proposals/iterator-chunking-v2
core-js(-pure)/actual|full/iterator/chunks
core-js(-pure)/actual|full/iterator/windows

Examples

js
const digits = () => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].values();

let chunks = Array.from(digits().chunks(2));  // [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]

let windows = Array.from(digits().windows(2));  // [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9]]

let windowsPartial = Array.from([0, 1].values().windows(3, 'allow-partial'));  // [[0, 1]]

let windowsFull = Array.from([0, 1].values().windows(3));  // []

[`Iterator` includes](https://github.com/tc39/proposal-iterator-includes)[⬆](#index)

Module esnext.iterator.includes

ts
class Iterator {
  includes(searchElement: any, skippedElements?: number): boolean;
}

CommonJS entry points:

text
core-js/proposals/iterator-includes
core-js(-pure)/actual|full/iterator/includes
js
[1, 2, 3].values().includes(2);     // => true
[1, 2, 3].values().includes(4);     // => false
[NaN].values().includes(NaN);       // => true
[1, 2, 3].values().includes(3, 2);  // => true
[1, 2, 3].values().includes(1, 1);  // => false

[`Iterator` join](https://github.com/tc39/proposal-iterator-join)[⬆](#index)

Module esnext.iterator.join

ts
class Iterator {
  join(separator?: string): string;
}

CommonJS entry points:

text
core-js/proposals/iterator-join
core-js(-pure)/actual|full/iterator/join
js
[1, 2, 3].values().join();     // => '1,2,3'
[1, 2, 3].values().join('-');  // => '1-2-3'
[1, null, 3].values().join();  // => '1,,3'

[Await dictionary](https://github.com/tc39/proposal-await-dictionary)[⬆](#index)

Modules esnext.promise.all-keyed
and esnext.promise.all-settled-keyed

ts
class Promise {
  allKeyed(obj: Record<string | symbol, any>): Promise<Record<string | symbol, any>>;
  allSettledKeyed(obj: Record<string | symbol, any>): Promise<Record<string | symbol, { status: 'fulfilled', value: any } | { status: 'rejected', reason: any }>>;
}

CommonJS entry points:

text
core-js/proposals/await-dictionary
core-js(-pure)/actual|full/promise/all-keyed
core-js(-pure)/actual|full/promise/all-settled-keyed
js
await Promise.allKeyed({
  a: Promise.resolve(1),
  b: Promise.resolve(2),
  c: 3,
}); // => { a: 1, b: 2, c: 3 }

await Promise.allSettledKeyed({
  a: Promise.resolve(1),
  b: Promise.reject(2),
  c: 3,
}); // => { a: { status: "fulfilled", value: 1 }, b: { status: "rejected", reason: 2 }, c: { status: "fulfilled", value: 3 } }

Stage 2.7 proposals[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/stage/2.7

[`Symbol.metadata` for decorators metadata proposal](https://github.com/tc39/proposal-decorator-metadata)[⬆](#index)

Modules esnext.symbol.metadata and esnext.function.metadata.

ts
class Symbol {
  static metadata: @@metadata;
}

class Function {
  @@metadata: null;
}

CommonJS entry points:

text
core-js/proposals/decorator-metadata-v2
core-js(-pure)/actual|full/symbol/metadata
core-js(-pure)/actual|full/function/metadata

Stage 2 proposals[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/stage/2

[`AsyncIterator` helpers](https://github.com/tc39/proposal-async-iterator-helpers)[⬆](#index)

Modules esnext.async-iterator.constructor, esnext.async-iterator.drop, esnext.async-iterator.every, esnext.async-iterator.filter, esnext.async-iterator.find, esnext.async-iterator.flat-map, esnext.async-iterator.for-each, esnext.async-iterator.from, esnext.async-iterator.map, esnext.async-iterator.reduce, esnext.async-iterator.some, esnext.async-iterator.take, esnext.async-iterator.to-array, , esnext.iterator.to-async

ts
class Iterator {
  toAsync(): AsyncIterator<any>;
}

class AsyncIterator {
  static from(iterable: AsyncIterable<any> | Iterable<any> | AsyncIterator<any>): AsyncIterator<any>;
  drop(limit: uint): AsyncIterator<any>;
  every(async callbackfn: (value: any, counter: uint) => boolean): Promise;
  filter(async callbackfn: (value: any, counter: uint) => boolean): AsyncIterator<any>;
  find(async callbackfn: (value: any, counter: uint) => boolean)): Promise<any>;
  flatMap(async callbackfn: (value: any, counter: uint) => AsyncIterable<any> | Iterable<any> | AsyncIterator<any>): AsyncIterator<any>;
  forEach(async callbackfn: (value: any, counter: uint) => void): Promise<void>;
  map(async callbackfn: (value: any, counter: uint) => any): AsyncIterator<any>;
  reduce(async callbackfn: (memo: any, value: any, counter: uint) => any, initialValue: any): Promise<any>;
  some(async callbackfn: (value: any, counter: uint) => boolean): Promise;
  take(limit: uint): AsyncIterator<any>;
  toArray(): Promise<Array>;
  @@toStringTag: 'AsyncIterator'
}

CommonJS entry points:

text
core-js/proposals/async-iterator-helpers
core-js(-pure)/actual|full/async-iterator
core-js(-pure)/actual|full/async-iterator/drop
core-js(-pure)/actual|full/async-iterator/every
core-js(-pure)/actual|full/async-iterator/filter
core-js(-pure)/actual|full/async-iterator/find
core-js(-pure)/actual|full/async-iterator/flat-map
core-js(-pure)/actual|full/async-iterator/for-each
core-js(-pure)/actual|full/async-iterator/from
core-js(-pure)/actual|full/async-iterator/map
core-js(-pure)/actual|full/async-iterator/reduce
core-js(-pure)/actual|full/async-iterator/some
core-js(-pure)/actual|full/async-iterator/take
core-js(-pure)/actual|full/async-iterator/to-array
core-js(-pure)/actual|full/iterator/to-async

Examples:

js
await AsyncIterator.from([1, 2, 3, 4, 5, 6, 7])
  .drop(1)
  .take(5)
  .filter(it => it % 2)
  .map(it => it ** 2)
  .toArray(); // => [9, 25]

await [1, 2, 3].values().toAsync().map(async it => it ** 2).toArray(); // => [1, 4, 9]

Caveats:[⬆](#index)

  • For preventing prototypes pollution, in the pure version, new %AsyncIteratorPrototype% methods are not added to the real %AsyncIteratorPrototype%, they available only on wrappers - instead of [].values().toAsync().map(fn) use AsyncIterator.from([]).map(fn).
  • Now, we have access to the real %AsyncIteratorPrototype% only with usage async generators syntax. So, for compatibility of the library with old browsers, we should use Function constructor. However, that breaks compatibility with CSP. So, if you wanna use the real %AsyncIteratorPrototype%, you should set USE_FUNCTION_CONSTRUCTOR option in the core-js/configurator to true:
js
const configurator = require('core-js/configurator');

configurator({ USE_FUNCTION_CONSTRUCTOR: true });

require('core-js/actual/async-iterator');

(async function * () { /* empty */ })() instanceof AsyncIterator; // => true
  • As an alternative, you could pass to the core-js/configurator an object that will be considered as %AsyncIteratorPrototype%:
js
const configurator = require('core-js/configurator');

const { getPrototypeOf } = Object;

configurator({ AsyncIteratorPrototype: getPrototypeOf(getPrototypeOf(getPrototypeOf(async function * () { /* empty */ }()))) });

require('core-js/actual/async-iterator');

(async function * () { /* empty */ })() instanceof AsyncIterator; // => true

[`Iterator.range`](https://github.com/tc39/proposal-Number.range)[⬆](#index)

Module esnext.iterator.range

ts
class Iterator {
  range(start: number, end: number, options: { step: number = 1, inclusive: boolean = false } | step: number = 1): NumericRangeIterator;
  range(start: bigint, end: bigint | Infinity | -Infinity, options: { step: bigint = 1n, inclusive: boolean = false } | step: bigint = 1n): NumericRangeIterator;
}

CommonJS entry points:

text
core-js/proposals/number-range
core-js(-pure)/full/iterator/range

Example:

js
for (const i of Iterator.range(1, 10)) {
  console.log(i); // => 1, 2, 3, 4, 5, 6, 7, 8, 9
}

for (const i of Iterator.range(1, 10, { step: 3, inclusive: true })) {
  console.log(i); // => 1, 4, 7, 10
}

[`Array.isTemplateObject`](https://github.com/tc39/proposal-array-is-template-object)[⬆](#index)

Module esnext.array.is-template-object

ts
class Array {
  static isTemplateObject(value: any): boolean
}

CommonJS entry points:

text
core-js/proposals/array-is-template-object
core-js(-pure)/full/array/is-template-object

Example:

js
console.log(Array.isTemplateObject((it => it)`qwe${ 123 }asd`)); // => true

[`Number.prototype.clamp`](https://github.com/tc39/proposal-math-clamp)[⬆](#index)

Module esnext.number.clamp

ts
class Number {
  clamp(min: number, max: number): number;
}

CommonJS entry points:

text
core-js/proposals/math-clamp-v2
core-js(-pure)/full/number/clamp

Example:

js
5.0.clamp(0, 10); // => 5
-5.0.clamp(0, 10); // => 0
15.0.clamp(0, 10); // => 10

[`String.dedent`](https://github.com/tc39/proposal-string-dedent)[⬆](#index)

Module esnext.string.dedent

ts
class String {
  static dedent(templateOrTag: { raw: Array<string> } | function, ...substitutions: Array<string>): string | function;
}

CommonJS entry points:

text
core-js/proposals/string-dedent
core-js(-pure)/full/string/dedent

Example:

js
const message = 42;

console.log(String.dedent`
  print('${ message }')
`); // => print('42')

String.dedent(console.log)`
  print('${ message }')
`; // => ["print('", "')", raw: Array(2)], 42

[`Symbol` predicates](https://github.com/tc39/proposal-symbol-predicates)[⬆](#index)

Modules esnext.symbol.is-registered-symbol, esnext.symbol.is-well-known-symbol.

ts
class Symbol {
  static isRegisteredSymbol(value: any): boolean;
  static isWellKnownSymbol(value: any): boolean;
}

CommonJS entry points:

text
core-js/proposals/symbol-predicates-v2
core-js(-pure)/full/symbol/is-registered-symbol
core-js(-pure)/full/symbol/is-well-known-symbol

Example:

js
Symbol.isRegisteredSymbol(Symbol.for('key')); // => true
Symbol.isRegisteredSymbol(Symbol('key')); // => false

Symbol.isWellKnownSymbol(Symbol.iterator); // => true
Symbol.isWellKnownSymbol(Symbol('key')); // => false

[`Symbol.customMatcher` for extractors](https://github.com/tc39/proposal-extractors)[⬆](#index)

Module esnext.symbol.custom-matcher.

ts
class Symbol {
  static customMatcher: @@customMatcher;
}

CommonJS entry points:

text
core-js/proposals/pattern-extractors
core-js(-pure)/full/symbol/custom-matcher

Stage 1 proposals[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/stage/1

[`Observable`](https://github.com/zenparsing/es-observable)[⬆](#index)

Modules esnext.observable and esnext.symbol.observable

ts
class Observable {
  constructor(subscriber: Function): Observable;
  subscribe(observer: Function | { next?: Function, error?: Function, complete?: Function }): Subscription;
  @@observable(): this;
  static of(...items: Array<mixed>): Observable;
  static from(x: Observable | Iterable): Observable;
  static readonly attribute @@species: this;
}

class Symbol {
  static observable: @@observable;
}

CommonJS entry points:

text
core-js/proposals/observable
core-js(-pure)/full/observable
core-js(-pure)/full/symbol/observable

Example:

js
new Observable(observer => {
  observer.next('hello');
  observer.next('world');
  observer.complete();
}).subscribe({
  next(it) { console.log(it); },
  complete() { console.log('!'); },
});

[New collections methods](https://github.com/tc39/proposal-collection-methods)[⬆](#index)

Modules esnext.set.add-all, esnext.set.delete-all, esnext.set.every, esnext.set.filter, esnext.set.find, esnext.set.join, esnext.set.map, esnext.set.reduce, esnext.set.some, esnext.map.delete-all, esnext.map.every, esnext.map.filter, esnext.map.find, esnext.map.find-key, esnext.map.includes, esnext.map.key-by, esnext.map.key-of, esnext.map.map-keys, esnext.map.map-values, esnext.map.merge, esnext.map.reduce, esnext.map.some, esnext.map.update, esnext.weak-set.add-all, esnext.weak-set.delete-all, esnext.weak-map.delete-all

[`.of` and `.from` methods on collection constructors](https://github.com/tc39/proposal-setmap-offrom)[⬆](#index)

Modules esnext.set.of, esnext.set.from, esnext.map.of, esnext.map.from, esnext.weak-set.of, esnext.weak-set.from, esnext.weak-map.of, esnext.weak-map.from

text
/* Detailed source-code truncated for AI context efficiency. */

CommonJS entry points:

text
core-js/proposals/collection-methods
core-js/proposals/collection-of-from
core-js(-pure)/full/set/add-all
core-js(-pure)/full/set/delete-all
core-js(-pure)/full/set/every
core-js(-pure)/full/set/filter
core-js(-pure)/full/set/find
core-js(-pure)/full/set/from
core-js(-pure)/full/set/join
core-js(-pure)/full/set/map
core-js(-pure)/full/set/of
core-js(-pure)/full/set/reduce
core-js(-pure)/full/set/some
core-js(-pure)/full/map/delete-all
core-js(-pure)/full/map/every
core-js(-pure)/full/map/filter
core-js(-pure)/full/map/find
core-js(-pure)/full/map/find-key
core-js(-pure)/full/map/from
core-js(-pure)/full/map/includes
core-js(-pure)/full/map/key-by
core-js(-pure)/full/map/key-of
core-js(-pure)/full/map/map-keys
core-js(-pure)/full/map/map-values
core-js(-pure)/full/map/merge
core-js(-pure)/full/map/of
core-js(-pure)/full/map/reduce
core-js(-pure)/full/map/some
core-js(-pure)/full/map/update
core-js(-pure)/full/weak-set/add-all
core-js(-pure)/full/weak-set/delete-all
core-js(-pure)/full/weak-set/of
core-js(-pure)/full/weak-set/from
core-js(-pure)/full/weak-map/delete-all
core-js(-pure)/full/weak-map/of
core-js(-pure)/full/weak-map/from

.of / .from examples:

js
Set.of(1, 2, 3, 2, 1); // => Set {1, 2, 3}

Map.from([[1, 2], [3, 4]], ([key, value]) => [key ** 2, value ** 2]); // => Map { 1: 4, 9: 16 }

[`compositeKey` and `compositeSymbol`](https://github.com/tc39/proposal-richer-keys/tree/master/compositeKey)[⬆](#index)

Modules esnext.composite-key and esnext.composite-symbol

ts
function compositeKey(...args: Array<mixed>): object;
function compositeSymbol(...args: Array<mixed>): symbol;

CommonJS entry points:

text
core-js/proposals/keys-composition
core-js(-pure)/full/composite-key
core-js(-pure)/full/composite-symbol

Examples:

js
// returns a symbol
const symbol = compositeSymbol({});
console.log(typeof symbol); // => 'symbol'

// works the same, but returns a plain frozen object without a prototype
const key = compositeKey({});
console.log(typeof key); // => 'object'
console.log({}.toString.call(key)); // => '[object Object]'
console.log(Object.getPrototypeOf(key)); // => null
console.log(Object.isFrozen(key)); // => true

const a = ['a'];
const b = ['b'];
const c = ['c'];

/* eslint-disable no-self-compare -- example */
console.log(compositeSymbol(a) === compositeSymbol(a)); // => true
console.log(compositeSymbol(a) !== compositeSymbol(['a'])); // => true
console.log(compositeSymbol(a, 1) === compositeSymbol(a, 1)); // => true
console.log(compositeSymbol(a, b) !== compositeSymbol(b, a)); // => true
console.log(compositeSymbol(a, b, c) === compositeSymbol(a, b, c)); // => true
console.log(compositeSymbol(1, a) === compositeSymbol(1, a)); // => true
console.log(compositeSymbol(1, a, 2, b) === compositeSymbol(1, a, 2, b)); // => true
console.log(compositeSymbol(a, a) === compositeSymbol(a, a)); // => true

[Array filtering](https://github.com/tc39/proposal-array-filtering)[⬆](#index)

Modules esnext.array.filter-reject and esnext.typed-array.filter-reject.

ts
class Array {
  filterReject(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): Array<mixed>;
}

class %TypedArray% {
  filterReject(callbackfn: (value: number, index: number, target: %TypedArray%) => boolean, thisArg?: any): %TypedArray%;
}

CommonJS entry points:

text
core-js/proposals/array-filtering-stage-1
core-js(-pure)/full/array(/virtual)/filter-reject
core-js/full/typed-array/filter-reject

Examples:

js
[1, 2, 3, 4, 5].filterReject(it => it % 2); // => [2, 4]

[Array deduplication](https://github.com/tc39/proposal-array-unique)[⬆](#index)

Modules esnext.array.unique-by and esnext.typed-array.unique-by

ts
class Array {
  uniqueBy(resolver?: (item: any) => any): Array<mixed>;
}

class %TypedArray% {
  uniqueBy(resolver?: (item: any) => any): %TypedArray%;;
}

CommonJS entry points:

text
core-js/proposals/array-unique
core-js(-pure)/full/array(/virtual)/unique-by
core-js/full/typed-array/unique-by

Examples:

js
[1, 2, 3, 2, 1].uniqueBy(); // [1, 2, 3]

[
  { id: 1, uid: 10000 },
  { id: 2, uid: 10000 },
  { id: 3, uid: 10001 },
].uniqueBy(it => it.uid);    // => [{ id: 1, uid: 10000 }, { id: 3, uid: 10001 }]

[`DataView` get / set `Uint8Clamped` methods](https://github.com/tc39/proposal-dataview-get-set-uint8clamped)[⬆](#index)

Modules esnext.data-view.get-uint8-clamped and esnext.data-view.set-uint8-clamped

ts
class DataView {
  getUint8Clamped(offset: any): uint8
  setUint8Clamped(offset: any, value: any): void;
}

CommonJS entry points:

text
core-js/proposals/data-view-get-set-uint8-clamped
core-js/full/dataview/get-uint8-clamped
core-js/full/dataview/set-uint8-clamped

Examples:

js
const view = new DataView(new ArrayBuffer(1));
view.setUint8Clamped(0, 100500);
console.log(view.getUint8Clamped(0)); // => 255

[`Number.fromString`](https://github.com/tc39/proposal-number-fromstring)[⬆](#index)

Module esnext.number.from-string

ts
class Number {
  fromString(string: string, radix: number): number;
}

CommonJS entry points:

text
core-js/proposals/number-from-string
core-js(-pure)/full/number/from-string

[`String.cooked`](https://github.com/tc39/proposal-string-cooked)[⬆](#index)

Module esnext.string.cooked

ts
class String {
  static cooked(template: Array<string>, ...substitutions: Array<string>): string;
}

CommonJS entry points:

text
core-js/proposals/string-cooked
core-js(-pure)/full/string/cooked

Example:

js
function safePath(strings, ...subs) {
  return String.cooked(strings, ...subs.map(sub => encodeURIComponent(sub)));
}

let id = 'spottie?';

safePath`/cats/${ id }`; // => /cats/spottie%3F

[`String.prototype.codePoints`](https://github.com/tc39/proposal-string-prototype-codepoints)[⬆](#index)

Module esnext.string.code-points

ts
class String {
  codePoints(): Iterator<{ codePoint, position }>;
}

CommonJS entry points:

text
core-js/proposals/string-code-points
core-js(-pure)/full/string/code-points

Example:

js
for (let { codePoint, position } of 'qwe'.codePoints()) {
  console.log(codePoint); // => 113, 119, 101
  console.log(position);  // => 0, 1, 2
}

[`Symbol.customMatcher` for pattern matching](https://github.com/tc39/proposal-pattern-matching)[⬆](#index)

Module esnext.symbol.custom-matcher.

ts
class Symbol {
  static customMatcher: @@customMatcher;
}

CommonJS entry points:

text
core-js/proposals/pattern-matching-v2
core-js(-pure)/full/symbol/custom-matcher

Stage 0 proposals[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/stage/0

[`Function.prototype.demethodize`](https://github.com/js-choi/proposal-function-demethodize)[⬆](#index)

Module esnext.function.demethodize

ts
class Function {
  demethodize(): Function;
}

CommonJS entry points:

text
core-js/proposals/function-demethodize
core-js(-pure)/full/function/demethodize
core-js(-pure)/full/function/virtual/demethodize

Examples:

js
const slice = Array.prototype.slice.demethodize();

slice([1, 2, 3], 1); // => [2, 3]

[`Function.{ isCallable, isConstructor }`](https://github.com/caitp/TC39-Proposals/blob/trunk/tc39-reflect-isconstructor-iscallable.md)[⬆](#index)

Modules esnext.function.is-callable, esnext.function.is-constructor

ts
class Function {
  static isCallable(value: any): boolean;
  static isConstructor(value: any): boolean;
}

CommonJS entry points:

text
core-js/proposals/function-is-callable-is-constructor
core-js(-pure)/full/function/is-callable
core-js(-pure)/full/function/is-constructor

Examples:

js
/* eslint-disable prefer-arrow-callback -- example */
Function.isCallable(null);                        // => false
Function.isCallable({});                          // => false
Function.isCallable(function () { /* empty */ }); // => true
Function.isCallable(() => { /* empty */ });       // => true
Function.isCallable(class { /* empty */ });       // => false

Function.isConstructor(null);                        // => false
Function.isConstructor({});                          // => false
Function.isConstructor(function () { /* empty */ }); // => true
Function.isConstructor(() => { /* empty */ });       // => false
Function.isConstructor(class { /* empty */ });       // => true

Pre-stage 0 proposals[⬆](#index)

CommonJS entry points:

text
core-js(-pure)/stage/pre

[`Reflect` metadata](https://github.com/rbuckton/reflect-metadata)[⬆](#index)

Modules esnext.reflect.define-metadata, esnext.reflect.delete-metadata, esnext.reflect.get-metadata, esnext.reflect.get-metadata-keys, esnext.reflect.get-own-metadata, esnext.reflect.get-own-metadata-keys, esnext.reflect.has-metadata, esnext.reflect.has-own-metadata and esnext.reflect.metadata.

ts
namespace Reflect {
  defineMetadata(metadataKey: any, metadataValue: any, target: Object, propertyKey?: PropertyKey): void;
  getMetadata(metadataKey: any, target: Object, propertyKey?: PropertyKey): any;
  getOwnMetadata(metadataKey: any, target: Object, propertyKey?: PropertyKey): any;
  hasMetadata(metadataKey: any, target: Object, propertyKey?: PropertyKey): boolean;
  hasOwnMetadata(metadataKey: any, target: Object, propertyKey?: PropertyKey): boolean;
  deleteMetadata(metadataKey: any, target: Object, propertyKey?: PropertyKey): boolean;
  getMetadataKeys(target: Object, propertyKey?: PropertyKey): Array<mixed>;
  getOwnMetadataKeys(target: Object, propertyKey?: PropertyKey): Array<mixed>;
  metadata(metadataKey: any, metadataValue: any): decorator(target: Object, targetKey?: PropertyKey) => void;
}

CommonJS entry points:

text
core-js/proposals/reflect-metadata
core-js(-pure)/full/reflect/define-metadata
core-js(-pure)/full/reflect/delete-metadata
core-js(-pure)/full/reflect/get-metadata
core-js(-pure)/full/reflect/get-metadata-keys
core-js(-pure)/full/reflect/get-own-metadata
core-js(-pure)/full/reflect/get-own-metadata-keys
core-js(-pure)/full/reflect/has-metadata
core-js(-pure)/full/reflect/has-own-metadata
core-js(-pure)/full/reflect/metadata

Examples:

js
let object = {};
Reflect.defineMetadata('foo', 'bar', object);
Reflect.ownKeys(object);               // => []
Reflect.getOwnMetadataKeys(object);    // => ['foo']
Reflect.getOwnMetadata('foo', object); // => 'bar'

Web standards[⬆](#index)

`self`[⬆](#index)

Spec, module web.self

ts
getter self: GlobalThisValue;

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/self

Examples:

js
// eslint-disable-next-line no-restricted-globals -- example
self.Array === Array; // => true

`structuredClone`[⬆](#index)

Spec, module web.structured-clone

ts
function structuredClone(value: Serializable, { transfer?: Sequence }): any;

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/structured-clone

Examples:

js
const structured = [{ a: 42 }];
const sclone = structuredClone(structured);
console.log(sclone);                      // => [{ a: 42 }]
console.log(structured !== sclone);       // => true
console.log(structured[0] !== sclone[0]); // => true

const circular = {};
circular.circular = circular;
const cclone = structuredClone(circular);
console.log(cclone.circular === cclone);  // => true

structuredClone(42);                                            // => 42
structuredClone({ x: 42 });                                     // => { x: 42 }
structuredClone([1, 2, 3]);                                     // => [1, 2, 3]
structuredClone(new Set([1, 2, 3]));                            // => Set{ 1, 2, 3 }
structuredClone(new Map([['a', 1], ['b', 2]]));                 // => Map{ a: 1, b: 2 }
structuredClone(new Int8Array([1, 2, 3]));                      // => new Int8Array([1, 2, 3])
structuredClone(new AggregateError([1, 2, 3], 'message'));      // => new AggregateError([1, 2, 3], 'message'))
structuredClone(new TypeError('message', { cause: 42 }));       // => new TypeError('message', { cause: 42 })
structuredClone(new DOMException('message', 'DataCloneError')); // => new DOMException('message', 'DataCloneError')
structuredClone(document.getElementById('myfileinput'));        // => new FileList
structuredClone(new DOMPoint(1, 2, 3, 4));                      // => new DOMPoint(1, 2, 3, 4)
structuredClone(new Blob(['test']));                            // => new Blob(['test'])
structuredClone(new ImageData(8, 8));                           // => new ImageData(8, 8)
// etc.

structuredClone(new WeakMap()); // => DataCloneError on non-serializable types

[!WARNING]

  • Many platform types cannot be transferred in most engines since we have no way to polyfill this behavior, however .transfer option works for some platform types. I recommend avoiding this option.
  • Some specific platform types can't be cloned in old engines. Mainly it's very specific types or very old engines, but here are some exceptions. For example, we have no sync way to clone ImageBitmap in Safari 14.0- or Firefox 83-, so it's recommended to look to the polyfill source if you wanna clone something specific.

Base64 utility methods[⬆](#index)

Specification, MDN. Modules web.atob, web.btoa.

ts
function atob(data: string): string;
function btoa(data: string): string;

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/atob
core-js(-pure)/stable|actual|full/btoa

Examples:

js
btoa('hi, core-js');      // => 'aGksIGNvcmUtanM='
atob('aGksIGNvcmUtanM='); // => 'hi, core-js'

`setTimeout` and `setInterval`[⬆](#index)

Module web.timers. Additional arguments fix for IE9-.

ts
function setTimeout(callback: any, time: any, ...args: Array<mixed>): number;
function setInterval(callback: any, time: any, ...args: Array<mixed>): number;

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/set-timeout
core-js(-pure)/stable|actual|full/set-interval
js
// Before:
setTimeout(log.bind(null, 42), 1000);
// After:
setTimeout(log, 1000, 42);

`setImmediate`[⬆](#index)

Module web.immediate. setImmediate polyfill.

ts
function setImmediate(callback: any, ...args: Array<mixed>): number;
function clearImmediate(id: number): void;

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/set-immediate
core-js(-pure)/stable|actual|full/clear-immediate

Examples:

js
setImmediate((arg1, arg2) => {
  console.log(arg1, arg2); // => Message will be displayed with minimum delay
}, 'Message will be displayed', 'with minimum delay');

clearImmediate(setImmediate(() => {
  console.log('Message will not be displayed');
}));

`queueMicrotask`[⬆](#index)

Spec, module web.queue-microtask

ts
function queueMicrotask(fn: Function): void;

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/queue-microtask

Examples:

js
queueMicrotask(() => console.log('called as microtask'));

`URL` and `URLSearchParams`[⬆](#index)

URL standard implementation. Modules web.url, web.url.can-parse, web.url.parse, web.url.to-json, web.url-search-params, web.url-search-params.delete, web.url-search-params.has, web.url-search-params.size.

ts
class URL {
  constructor(url: string, base?: string);
  attribute href: string;
  readonly attribute origin: string;
  attribute protocol: string;
  attribute username: string;
  attribute password: string;
  attribute host: string;
  attribute hostname: string;
  attribute port: string;
  attribute pathname: string;
  attribute search: string;
  readonly attribute searchParams: URLSearchParams;
  attribute hash: string;
  toJSON(): string;
  toString(): string;
  static canParse(url: string, base?: string): boolean;
  static parse(url: string, base?: string): URL | null;
}

class URLSearchParams {
  constructor(params?: string | Iterable<[key, value]> | Object);
  append(name: string, value: string): void;
  delete(name: string, value?: string): void;
  get(name: string): string | void;
  getAll(name: string): Array<string>;
  has(name: string, value?: string): boolean;
  set(name: string, value: string): void;
  sort(): void;
  toString(): string;
  forEach(callbackfn: (value: any, index: number, target: any) => void, thisArg: any): void;
  entries(): Iterator<[key, value]>;
  keys(): Iterator<key>;
  values(): Iterator<value>;
  @@iterator(): Iterator<[key, value]>;
  readonly attribute size: number;
}

CommonJS entry points:

text
core-js/proposals/url
core-js(-pure)/stable|actual|full/url
core-js(-pure)/stable|actual|full/url/can-parse
core-js/stable|actual|full/url/to-json
core-js(-pure)/stable|actual|full/url-search-params

Examples:

text
/* Detailed source-code truncated for AI context efficiency. */

[!WARNING]

  • IE8 does not support setters, so they do not work on URL instances. However, URL constructor can be used for basic URL parsing.
  • Legacy encodings in a search query are not supported. Also, core-js implementation has some other encoding-related issues.
  • URL implementations from all of the popular browsers have significantly more problems than core-js, however, replacing all of them does not look like a good idea. You can customize the aggressiveness of polyfill by your requirements.

`DOMException`:[⬆](#index)

The specification. Modules web.dom-exception.constructor, web.dom-exception.stack, web.dom-exception.to-string-tag.

ts
class DOMException {
  constructor(message: string, name?: string);
  readonly attribute name: string;
  readonly attribute message: string;
  readonly attribute code: string;
  attribute stack: string; // in engines that should have it
  @@toStringTag: 'DOMException';
}

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/dom-exception
core-js(-pure)/stable|actual|full/dom-exception/constructor
core-js/stable|actual|full/dom-exception/to-string-tag

Examples:

js
const exception = new DOMException('error', 'DataCloneError');
console.log(exception.name);                            // => 'DataCloneError'
console.log(exception.message);                         // => 'error'
console.log(exception.code);                            // => 25
console.log(typeof exception.stack);                    // => 'string'
console.log(exception instanceof DOMException);         // => true
console.log(exception instanceof Error);                // => true
console.log(exception.toString());                      // => 'DataCloneError: error'
console.log(Object.prototype.toString.call(exception)); // => '[object DOMException]'

Iterable DOM collections[⬆](#index)

Some DOM collections should have iterable interface or should be inherited from Array. That means they should have forEach, keys, values, entries and @@iterator methods for iteration. So add them. Modules web.dom-collections.iterator and web.dom-collections.for-each.

ts
class [
  CSSRuleList,
  CSSStyleDeclaration,
  CSSValueList,
  ClientRectList,
  DOMRectList,
  DOMStringList,
  DataTransferItemList,
  FileList,
  HTMLAllCollection,
  HTMLCollection,
  HTMLFormElement,
  HTMLSelectElement,
  MediaList,
  MimeTypeArray,
  NamedNodeMap,
  PaintRequestList,
  Plugin,
  PluginArray,
  SVGLengthList,
  SVGNumberList,
  SVGPathSegList,
  SVGPointList,
  SVGStringList,
  SVGTransformList,
  SourceBufferList,
  StyleSheetList,
  TextTrackCueList,
  TextTrackList,
  TouchList,
] {
  @@iterator(): Iterator<value>;
}

class [DOMTokenList, NodeList] {
  forEach(callbackfn: (value: any, index: number, target: any) => void, thisArg: any): void;
  entries(): Iterator<[key, value]>;
  keys(): Iterator<key>;
  values(): Iterator<value>;
  @@iterator(): Iterator<value>;
}

CommonJS entry points:

text
core-js(-pure)/stable|actual|full/dom-collections/iterator
core-js/stable|actual|full/dom-collections/for-each

Examples:

js
for (let { id } of document.querySelectorAll('*')) {
  if (id) console.log(id);
}

for (let [index, { id }] of document.querySelectorAll('*').entries()) {
  if (id) console.log(index, id);
}

document.querySelectorAll('*').forEach(it => console.log(it.id));

Iteration helpers[⬆](#index)

Helpers for checking iterability / get iterator in the pure version or, for example, for arguments object:

ts
function isIterable(value: any): boolean;
function getIterator(value: any): Object;
function getIteratorMethod(value: any): Function | void;

CommonJS entry points:

text
core-js-pure/es|stable|actual|full/is-iterable
core-js-pure/es|stable|actual|full/get-iterator
core-js-pure/es|stable|actual|full/get-iterator-method

Examples:

js
import isIterable from 'core-js-pure/actual/is-iterable';
import getIterator from 'core-js-pure/actual/get-iterator';
import getIteratorMethod from 'core-js-pure/actual/get-iterator-method';

let list = (function () {
  // eslint-disable-next-line prefer-rest-params -- example
  return arguments;
})(1, 2, 3);

console.log(isIterable(list)); // true;

let iterator = getIterator(list);
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
console.log(iterator.next().value); // undefined

getIterator({}); // TypeError: [object Object] is not iterable!

let method = getIteratorMethod(list);
console.log(typeof method);         // 'function'
iterator = method.call(list);
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
console.log(iterator.next().value); // undefined

console.log(getIteratorMethod({})); // undefined

Missing polyfills[⬆](#index)

  • ES BigInt can't be polyfilled since it requires changes in the behavior of operators, you can find more info here. You could try to use JSBI.
  • ES Proxy can't be polyfilled, you can try to use proxy-polyfill which provides a very small subset of features.
  • ES String#normalize is not a very useful feature, but this polyfill will be very large. If you need it, you can use unorm.
  • ECMA-402 Intl is missed because of the size. You can use those polyfills.
  • window.fetch is not a cross-platform feature, in some environments, it makes no sense. For this reason, I don't think it should be in core-js. Looking at a large number of requests it might be added in the future. Now you can use, for example, this polyfill.

File: deno/corejs/README.md

**I highly recommend reading this: [So, what's next?](https://github.com/zloirock/core-js/blob/master/docs/2023-02-14-so-whats-next.md)**

Modular standard library for JavaScript. Includes polyfills for ECMAScript up to 2021: promises, symbols, collections, iterators, typed arrays, many other features, ECMAScript proposals, some cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.

[core-js@3, babel and a look into the future](https://github.com/zloirock/core-js/tree/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md)

Raising funds

core-js isn't backed by a company, so the future of this project depends on you. Become a sponsor or a backer if you are interested in core-js: Open Collective, Patreon, Boosty, Bitcoin ( bc1qlea7544qtsmj2rayg0lthvza9fau63ux0fstcz ), Alipay.




Example:

js
import 'https://deno.land/x/[email protected]/index.js'; // <- at the top of your entry point

Object.hasOwn({ foo: 42 }, 'foo');   // => true

[1, 2, 3, 4, 5, 6, 7].at(-3);        // => 5

[1, 2, 3, 4, 5].group(it => it % 2); // => { 1: [1, 3, 5], 0: [2, 4] }

Promise.any([
  Promise.resolve(1),
  Promise.reject(2),
  Promise.resolve(3),
]).then(console.log);                // => 1

(function * (i) { while (true) yield i++; })(1)
  .drop(1).take(5)
  .filter(it => it % 2)
  .map(it => it ** 2)
  .toArray();                        // => [9, 25]

It's a bundled global version for Deno 1.0+, for more info see core-js documentation.


File: docs/zh_CN/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md

core-js@3, Babel 展望未来

经过一年半的开发,数十个版本,许多不眠之夜,core-js@3 终于发布了。这是 core-js 和 babel 补丁相关的功能的最大的一次变化。

什么是 core-js?

  • 它是JavaScript标准库的 polyfill,它支持
    • 最新的 ECMAScript 标准
    • ECMAScript 标准库提案
    • 一些 WHATWG / W3C 标准(跨平台或者 ECMAScript 相关)
  • 它最大限度的模块化:你能仅仅加载你想要使用的功能
  • 它能够不污染全局命名空间
  • 它和babel紧密集成:这能够优化core-js的导入

它是最普遍、最流行 的给 JavaScript 标准库打补丁的方式,但是有很大一部分开发者并不知道他们间接的使用了core-js🙂

贡献

core-js 是我自己爱好的项目,没有给我带来任何利润。它花了我很长的时间,真的很昂贵:为了完成 core-js@3,我在几个月之前已经离开我的工作。这个项目对许多人和公司起到了促进作用。因为这些,筹集资金去支持 core-js 的维护是说得通的。

如果你对 core-js 感兴趣或者在你每天的工作中有使用到,你可以在 Open Collective 或者 Patreon 成为赞助者。

你可以给我提供一个好的工作,和我现在做的相关的。

或者你可以以另一种方式贡献,你可以帮助去改进代码、测试或者文档(现在,core-js 的文档还很糟糕!)。

`core-js@3` 有哪些变化?

JavaScript 标准库中变化的内容

由于以下两个原因,这个版本包含丰富的、新的 JavaScript 补丁:

  • core-js 只在 major(主)版本更新时才有 break changes,即使需要和提案的内容对齐。
  • core-js@2 在一年半前已经进入功能冻结阶段了;所有新的功能只能够添加到 core-js@3 这个分支。

稳定的 ECMAScript 功能

稳定的 ECMAScript 功能在 core-js 中已经几乎完全支持有很长一段时间了,除此之外,core-js@3 引进了一些新功能:

一些在 ES2016-ES2019 中作为提案被接受且已经使用很长时间的功能,现在被标记为稳定:

修复了针对浏览器的许多问题,例如,Safari 12.0 Array.prototype.reverse bug 已经被修复了。

ECMAScript 提案

除了上文提到的支持内容,core-js@3 现在还支持下面的 ECMAScript 提案:

  • globalThis stage 3( 现在是 stage 4 )的提案 - 之前,已经有了 global 和 System.global
  • Promise.allSettled stage 2( 现在是 stage 4 )提案
  • 新 Set 方法 stage 2 提案:
    • Set.prototype.difference
    • Set.prototype.intersection
    • Set.prototype.isDisjoinFrom
    • Set.prototype.isSubsetOf
    • Set.prototype.isSupersetOf
    • Set.prototype.symmetricDifference
    • Set.prototype.union
  • 新 collections 方法 stage 1 提案,包函许多新的有用的方法:
    • Map.groupBy
    • Map.keyBy
    • Map.prototype.deleteAll
    • Map.prototype.every
    • Map.prototype.filter
    • Map.prototype.find
    • Map.prototype.findKey
    • Map.prototype.includes
    • Map.prototype.keyOf
    • Map.prototype.mapKeys
    • Map.prototype.mapValues
    • Map.prototype.merge
    • Map.prototype.reduce
    • Map.prototype.some
    • Map.prototype.update
    • Set.prototype.addAll
    • Set.prototype.deleteAll
    • Set.prototype.every
    • Set.prototype.filter
    • Set.prototype.find
    • Set.prototype.join
    • Set.prototype.map
    • Set.prototype.reduce
    • Set.prototype.some
    • WeakMap.prototype.deleteAll
    • WeakSet.prototype.addAll
    • WeakSet.prototype.deleteAll
  • String.prototype.replaceAll stage 1( 现在是 stage 3 ) 提案
  • String.prototype.codePoints stage 1 提案
  • Array.prototype.last(Item|Index) stage 1 提案
  • compositeKey 和 compositeSymbol 方法 stage 1 提案
  • Number.fromString stage 1 提案
  • Math.seededPRNG stage 1 提案
  • Promise.any (合并的错误) stage 0( 现在是 stage 3 )提案

一些提案的变化很大,core-js 也将相应的更新:

web 标准

许多有用的功能被添加到这个类别中。

最重要的一个是 URL 和 URLSearchParams。他是最受欢迎的功能请求之一。增加 URL 和 URLSearchParams,并保证他们最大限度的符合规范,保持源代码足够紧凑来支撑任何环境是 core-js@3 开发中最困难的任务之一。

core-js@3 包函在 JavaScript 中创建微任务( microtask )的标准方法:queueMicrotask 。core-js@2 提供了 asap 函数,提供了同样功能的老的提案。queueMicrotask 被定义在 HTML 标准中,它已经能够在现代浏览器比如 Chromium 或者 NodeJS 中使用。

另一个受欢迎的功能请求是支持 DOM 集合的 .forEach 方法。由于 core-js 已经针对 DOM 集合迭代器做了 polyfill,为什么不给 节点列表 和 DOMTokenList 也增加 .forEach 呢?

移除过时的功能:

  • Reflect.enumerate 因为他已经从标准中移除了
  • System.global 和 global 现在他们已经被 globalThis 代替
  • Array.prototype.flatten 现在被 Array.prototype.flat 代替
  • asap 被 queueMicrotask 代替
  • Error.isError 被撤销很长时间了
  • RegExp.escape 很久之前被拒绝了
  • Map.prototype.toJSON 和 Set.prototype.toJSON 也是很久前被拒绝了
  • 不必要并且被错误添加的迭代器方法:CSSRuleList,MediaList,StyleSheetList。

不再有非标准、非提案的功能

许多年前,我开始写一个库,他是我的 JavaScript 程序的核心:这个库包函 polyfills 和一些我需要的工具函数。一段时间后,这个库以 core-js 命名发布。我认为现在大多数 core-js 用户不需要非标准的 core-js 功能,他们大多已经在早期版本移除了,现在是时候将剩余部分从 core-js 中移除。从这个版本开始,core-js 可以被称为 polyfill 了。

包、入口和模块名字

一个 issue 里提了 core-js 包的很大( ~2MB ),有很多重复文件。因为这个原因,core-js 分成了 3 个包:

core-js 的早期版本中,稳定的 ECMAScript 功能和 ECMAScript 提案的 polyfill 模块化需要分别加 es6. 和 es7. 前缀。这是在 2014 年做的决定,那时将 ES6 之后的所有功能都视为 ES7。在 core-js@3 中所有稳定的 ECMAScript 功能都增加 es. 前缀,ECMAScript 提案增加 esnext. 前缀。

几乎所有的 CommonJS 入口都改变了。core-js@3 相比于 core-js@2 有更多的入口:这带来的最大限度的灵活性,使你能够仅仅引入你的应用需要的依赖。

这里是一些例子关于如何使用新的入口:

js
// 使用 `core-js` 全部功能打补丁:
import "core-js";
// 仅仅使用稳定的 `core-js` 功能 - ES 和 web 标准:
import "core-js/stable";
// 仅仅使用稳定的 ES 功能
import "core-js/es";

// 如果你想用 `Set` 的补丁
// 所有 `Set`- ES 提案中,相关的功能:
import "core-js/features/set";
// 稳定的 `Set` ES 功能和来自web标准的功能
// (DOM 集合迭代器)
import "core-js/stable/set";
// 只有 `Set` 所需的稳定的 ES 功能
import "core-js/es/set";
// 与上面一致,但不会污染全局命名空间
import Set from "core-js-pure/features/set";
import Set from "core-js-pure/stable/set";
import Set from "core-js-pure/es/set";


// 仅仅为需要的方法打补丁
import "core-js/feature/set/intersection";
import "core-js/stable/queque-microtask";
import "core-js/es/array/from";

// 为 reflect metadata 提案打补丁
import "core-js/proposals/reflect-metadata";
// 为所有 stage 2+ 的提案打补丁
import "core-js/stage/2";

其他重要的变化

core-js polyfill 能够 配置侵入等级。如果你认为有些情境 core-js 功能检测侵入性太强,原生实现对你来说已经足够,或者一个错误的实现没有被 core-js 检测到,你可以修改 core-js 的默认行为。

如果无法安装规范的每个细节实现某个功能,core-js 增加了一个 .sham 属性,例如,IE11中 Symbol.sham 是 true。

不再有 LiveScript! 当我开始写 core-js 时,我主要使用的是 LiveScript ;一段时间后,我用 JavaScript 重写了全部的 polyfills 。在 core-js@2 中测试和帮助的工具函数仍然使用 LiveScript :它是非常有趣的像 CoffeeScript 一样的语言,有强大的语法糖使你能够写非常紧凑的代码,但是它几乎已经死了。除此之外,它也是为 core-js 贡献的屏障,因为大多数 core-js 用户不知道这个语言。core-js@3 测试和工具函数使用现代 ES 语法:它将成为为 core-js 贡献的好时机🙂。

对于大多数用户,为了优化 core-js 导入,我建议使用 babel。当然,有些情况下 core-js-builder 仍然有用。现在它支持 target 参数,使用带有目标引擎的browserslist 查询 - 你能够创建一个 bundle,仅仅包含目标引擎需要的 polyfills。对于这种情况,我做了 core-js-compat,更多关于它的信息,你能够从 这篇文章的 @babel/preset-env 部分了解到。


这仅仅是冰山一角,更多的变化在内部。更多关于 core-js 变化可以在 changelog 中找到。

Babel

正如上文提到的,babel 和 core-js 是紧密集成的:babel 提供了优化 core-js 优化导入的可能性。core-js@3 开发中很重要的一部分是改进 core-js 相关的 babel 功能(看这个PR)。这些变化在 Babel 7.4.0 发布了。

babel/polyfill

@babel/polyfill 是一个包裹的包,里面仅仅包含 core-js 稳定版的引入(在Babel 6 中也包含提案)和 regenerator-runtime/runtime,用来转译 generators 和 async 函数。这个包没有提供从 core-js@2 到 core-js@3 平滑升级路径:因为这个原因,决定弃用 @babel/polyfill 代之以分别引入需要的 core-js 和 regenerator-runtime 。

原来

js
import "@babel/polyfill";

现在使用两行代替:

js
import "core-js/stable";
import "regenerator-runtime/runtime";

别忘记直接安装这两个依赖!

js
npm i --save core-js regenerator-runtime

@babe/preset-env

@babel/preset-env 有两种不同的模式,通过 useBuiltIns 选项:entry 和 usage 优化 core-js的导入。

Babel 7.4.0 引入了两种模式的共同更改,以及每种模式的特定的修改。

由于现在 @babel/preset-env 支持 core-js@2 和 core-js@3,因此 useBuiltIns 需要新的选项 -- corejs,这个选项用来定义使用 core-js 的版本(corejs: 2 或者 corejs: 3)。如果没有设置,corejs: 2 是默认值并且会有警告提示。

为了使 babel 支持将来的次要版本中引入的 core-js 的新功能,你可以在项目中定义明确的次要版本号。例如,你想使用 [email protected] 使用这个版本的新特性,你可以设置 corejs 选项为 3.1:corejs: '3.1' 或者 corejs: {version: '3.1'}。

@babel/preset-env 最重要的一个功能就是提供不同浏览器支持特性的数据来源,用来确定是否需要 core-js 填充某些内容。 caniuse,mdn 和 compat-table 是很好的教育资源,但是并不意味着他们能够作为数据源被开发者使用:只有 compat-table 包函好的 ES 相关数据集,它被 @babel/preset-env 使用,但是仍有些限制:

  • 它包含的数据仅仅关于 ECMAScript 特性和提案,和 web 平台特性例如 setImmediate 或者 DOM 集合迭代器没有关系。所以直到现在,@babel/preset-env 仍然通过 core-js 添加全部的 web 平台特性即使他们已经支持了。

  • 它不包含任何浏览器(甚至是严重的)bug 信息:例如,上文提到的在 Safari 12 中 Array#reverse,但是 compat-table 并没有将它标记为不支持。另一方面,core-js 已经修复了这个错误实现,但是因为 compat-table 关系,并不能使用它。

  • 它仅包函一些基础的、简单的测试,没有检查功能在真实环境下是否可以正常工作。例如,老版本 Safari 的破坏的迭代器没有 .next 方法,但是 compat-table 表明 Safari 支持,因为它用 typeof 方法检测迭代器方法返回了 "function"。一些像 typed arrays 的功能几乎没有覆盖。

  • compat-table 不是为了向工具提供数据而设计的。我是 compat-table 的维护者之一,但是其他的维护者反对为维护这个功能。

因为这个原因,我创建了 core-js-compat:它提供了对于不同浏览器 core-js 模块的必要性数据。当使用 core-js@3 时,@babel/preset-env 将使用新的包取代 compat-table。请帮助我们测试并提供缺少的引擎的数据的映射关系!😊。

在 Babel 7.3 之前,@babel/preset-env 有一些与 polyfills 注入顺序有关的问题。从 7.4.0开始,@babel/preset-env 只按推荐顺序增加需要的 polyfills 。

`useBuiltIns: entry` with `corejs: 3`

当使用这个选项时,@babel/preset-env 代替直接引用 core-js 而是引入目标环境特定需要的模块。

在这个变化前,@babel/preset 仅替换 import '@babel/polyfill' 和 import 'core-js',他们是同义词用来 polyfill 所有稳定的 JavaScript 特性。

现在 @babel/polyfill 弃用了,当 corejs 设置为 3 时 @babel/preset-env 不会转译他。

core-js@3 中等价替换 @babel/polyfill 是

js
import "core-js/stable";
import "regenerator-runtime/runtime";

当目标浏览器是 chrome 72 时,上面的内容将被 @babel/preset-env 转换为

js
import "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopaables.flat-map";
import "core-js/modules/es.object.from-entries";
import "core-js/modules/web.immediate";

当目标浏览器是 chrome 73(它完全支持 ES2019 标准库),他将变为很少的引入:

js
import "core-js/modules/web.immediate";

自从 @babel/polyfill 被弃用,转而使用分开的 core-js 和 regenerator-runtime,我们能够优化 regenerator-runtime 的导入。因为这个原因,如果目标浏览器原生支持 generators ,那么 regenerator-runtime 的导入将从源代码中移除。

现在,设置 useBuiltIns: entry 模式的 @babel/preset-env 编译所有能够获得的 core-js 入口和他们的组合。这意味着你能够自定义,通过使用不同的 core-js 入口,它将根据的目标环境优化。

例如,目标环境是 chrome 72,

js
import "core-js/es";
import "core-js/proposals/set-methods";
import "core-js/features/set/map";

将被替换为

js
import "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopables.flat-map";
import "core-js/modules/es.object.from-entries";
import "core-js/modules/esnext.set.difference";
import "core-js/modules/esnext.set.intersection";
import "core-js/modules/esnext.set.is-disjoint-from";
import "core-js/modules/esnext.set.is-subset-of";
import "core-js/modules/esnext.set.is-superset-of";
import "core-js/modules/esnext.set.map";
import "core-js/modules/esnext.set.symmetric-difference";
import "core-js/modules/esnext.set.union";

`useBuiltIns: usage` with `corejs: 3`

当使用这个选项时,@babel/preset-env 在每个文件的开头引入目标环境不支持、仅在当前文件中使用的 polyfills。

例如,

js
const set = new Set([1, 2, 3]);
[1, 2, 3].includes(2);

当目标环境是老的浏览器例如 ie 11,将转换为

js
import "core-js/modules/es.array.includes";
import "core-js/modules/es.array.iterator";
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.set";

const set = new Set([1, 2, 3]);
[1, 2, 3].includes(2);

当目标是 chrome 72 时不需要导入,因为这个环境需要 polyfills:

js
const set = new Set([1, 2, 3]);
[1, 2, 3].includes(2);

Babel 7.3 之前,useBuiltIns: usage 不稳定且不是足够可靠:许多 polyfills 不包函,并且添加了许多不是必须依赖的 polyfills。在 Babel 7.4 中,我尝试使它理解每种可能的使用模式。

在属性访问器、对象解构、in 操作符、全局对象属性访问方面,我改进了确定使用哪个 polyfills 的技术。

@babel/preset-env 现在注入语法特性所需的 polyfills:使用 for-of 时的迭代器,解构、扩展运算符和 yield 委托;使用动态 import 时的 promises,异步函数和 generators,等。

Babel 7.4 支持注入提案 polyfills。默认,@babel/preset-env 不会注入他们,但是你能够通过 proposals 标志设置:corejs: { version: 3, proposals: true }。

@babel/runtime

当使用 core-js@3 时, @babel/transform-runtime 现在通过 core-js-pure(core-js的一个版本,不会污染全局变量) 注入 polyfills。

通过将 @babel/transform-runtime 设置 corejs: 3 选项和创建 @babel/runtime-corejs3 包,已经将 core-js@3 和 @babel/runtime 集成在一起。但是这将带来什么好处呢?

@babel/runtime 的一个受欢迎的 issue 是:不支持实例方法。从 @babel/runtime-corejs3 开始,这个问题已经解决。例如,

js
array.includes(something);

将被编译为

js
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";

_includesInstanceProperty(array).call(array, something);

另一个值得关注的变化是支持 ECMAScript 提案。默认情况下的,@babel/plugin-transform-runtime 不会为提案注入 polyfills 并使用不包含提案的入口。但是正如你在 @babel/preset-env 中做的那样,你可以设置 proposals 标志去开启:corejs: { version: 3, proposals: true }。

没有 proposals 标志,

js
new Set([1, 2, 3, 2, 1]);
string.matchAll(/something/g);

将被编译为:

js
import _Set from "@babel/runtime-corejs/core-js-stable/set";

new _Set([1, 2, 3, 2, 1]);
string.matchAll(/something/g);

当设置 proposals 后,将变为:

js
import _Set from "@babel/runtime-corejs3/core-js/set";
import _matchAllInstanceProperty from "@babel/runtime-corejs/core-js/instance/match-all";

new _Set([1, 2, 3, 2, 1]);
_matchAllInstanceProperty(string).call(string, /something/g);

有些老的问题已经被修复了。例如,下面这种流行的模式在 @babel/runtime-corejs2 不工作,但是在 @babel/runtime-corejs3 被支持。

js
myArrayLikeObject[Symbol.iterator] = Array.prototype[Symbol.iterator];

尽管 @babel/runtime 早期版本不支持实例方法,但是使用一些自定义的帮助函数能够支持迭代([Symbol.iterator]() 和他的presence)。之前不支持提取 [Symbol.iterator] 方法,但是现在支持了。

作为意外收获,@babel/runtime 现在支持IE8-,但是有些限制,例如,IE8- 不支持访问器、模块转换应该用松散的方式,regenerator-runtime(内部使用 ES5+ 实现)需要通过这个插件转译。

畅享未来

做了许多工作,但是 core-js 距离完美还很远。这个库和工具将来应该如何改进?语言的变化将会如何影响它?

老的引擎支持

现在,core-js 试图去支持所有可能的引擎或者我们能够测试到的平台:甚至是IE8-,或者例如,早期版本的 Firefox。虽然它对某些用户有用,但是仅有一小部分使用 core-js 的开发者需要它。对于大多数用户,它将引起像包体积过大或者执行缓慢的问题。

主要的问题源自于支持 ES3 引擎(首先是 IE8- ):多数现代 ES 特性是基于 ES5,这些功能在老版本浏览器中均不可用。

最大的缺失特性是属性描述符:当它缺失时,一些功能不能 polyfill,因为他们要么是访问器(像 RegExp.prototype.flags 或 URL 属性的 setters )要么就是基于访问器(像 typed array polyfill)。为了解决这个不足,我们需要使用不同的解决方法(例如,保持 Set.prototype.size 更新)。维护这些解决方法有时很痛苦,移除他们将极大的简化许多 polyfills。

然而,描述符仅仅是问题的一部分。ES5 标准库包含了很多其他特性,他们被认为是现代 JavaScript 的基础:Object.create,Object.getPrototypeOf,Array.prototype.forEach,Function.prototype.bind,等等。和多数现代特性不同,core-js 内部依赖他们并且为了实现一个简单的现代函数,core-js 需要加载其中一些"建筑模块"的实现。对于想要创建一个足够小的构建包和仅仅想要引入部分 core-js 的用户来说,这是个问题。

在一些国家 IE8 仍很流行,但是为了让 web 向前发展,浏览器到了某些时候就应该消失了。 IE8 在 2009 年 3 月 19 日发布,到今天已经 10 年了。IE6 已经 18 岁了:几个月前新版的 core-js 已经不再测试 IE6 了。

在 core-js@4 我们应该舍弃 IE8- 和其他不知道 ES5 的引擎。

ECMAScript 模块

core-js 使用 CommonJS 模块规范。长期以来,他是最受欢迎的 JavaScript 模块规范,但是现在 ECMAScript 提供了他自己的模块规范。许多引擎已经支持它了。一些构建工具(像 rollup )基于它,其他的构建工具提供它作为 CommonJS 的替代。这意味提供了一个可选择的使用 ESMAScript 模块规范版本的 core-js 行得通。

支持 web 标准扩展?

core-js 当前专注在 ECMAScript 支持,但是也支持少量的跨平台以及和 ECMAScript 紧密联系的 web 标准功能。为 web 标准添加像 fetch 的这种的 polyfill 是受欢迎的功能请求。

core-js 没有增加他们的主要原因是,他们将严重的增加构建包大小并且将强制 core-js 用户载入他们可能用不到的功能。现在 core-js 是最大限度的模块化,用户能够仅选择他们需要的功能,这就像 @babel/preset-env 和 @babel/runtime 能够帮助用户去减少没用到和不必要的 polyfills。

现在是时候重新审视这个决定了?

针对目标环境的 `@babel/runtime`

目前,我们不能像对 @babel/preset-env 那样为 @babel/runtime 设置目标加环境。这意味即使目标是现代浏览器, @babel/runtime 也将注所有可能的 polyfills:这不必要的增加了最终构建包的大小。

现在 core-js-compat 包函全部必要数据,将来,可以在 @babel/runtime 中添加对目标环境的编译支持,并且在 @babel/preset-env 中添加 useBuiltIns: runtime 选项。

更好的优化 polyfill 加载

正如上面解释的,Babel 插件给了我们不同的方式去优化 core-js 的使用,但是他并不完美:我们可以改进他们。

通过 useBuiltIns: usage 选项,@babe/preset-env 能够做的比之前更好,但是针对一些不寻常的例子他们仍然会失败:当代码不能被静态分析。针对这个问题,我们需要为库开发者寻找一个方式去确定哪种 polyfill 是他们的库需要的,而不是直接载入他们:某种元数据 -- 将在创建最终构建包时注入 polyfill。

另一个针对 useBuiltIns: usage 的问题是重复的 polyfills 导入。useBuiltIns: usage 能够在每个文件中注入许多 core-js 的导入。但如果我们的项目有数千个文件或者即使十分之一会怎么样呢?这种情况下,与导入 core-js 自身相比,导入 core-js/... 将有更多代码行:我们需要一种方式去收集所有的导入到一个文件中,这样才能够删除重复的。

几乎每一个需要支持像 IE11 浏览器的 @babel/preset-env 用户都为每个浏览器使用同一个构建包。这意味着完全支持 ES2019 的现代浏览器将加载不必要的、仅仅是 IE11 需要的 polyfills。当然,我们可以为不同的浏览器创建不同的构建包来使用,例如,type=module /
nomodules 属性:一个构建包给支持模块化的现代浏览器,另一个给传统浏览器。不幸的是,这不是针对这个问题的完整的解决方案:基于用户代理打包目标浏览器需要的 polyfill 的服务非常有用。我们已经有了一个 - polyfill-service。尽管很有趣也很流行,但是 polyfill 的质量还有很多不足。它不像几年前那么差:项目团队积极工作去改变它,但是如果你想用他们匹配原生实现,我不建议你通过这个项目使用 polyfill。许多年前我尝试通过这个项目将 core-js 作为 polyfill 的源,但是这不可能。因为 polyfill-service 依赖文件嵌套而不是模块化(就像 core-js 发布后的前几个月 😊)。

像这样一个集成了一个很棒的 polyfill 源 -- core-js 的服务,通过像 Babel 的 useBuiltIns: usage 选项,静态分析源代码真的能够引起我们对于 polyfill 思考方式的革命。

来自 TC39 的新功能预案和 `core-js` 可能的问题

TC39 一直在努力工作去改进 ECMAScript:你可以通过查看 core-js 中实现所有新提案查看进度。然而,我认为有些新的提案功能在 polyfill 或者转译时可能引起严重的问题。关于这个足够可以写一篇新的文章,但是我将尝试在这总结一下我的想法。

标准库提案,stage 1

现在,TC39 考虑给 ECMAScript 增加内置模块:一个模块化的标准库。它将成为 JavaScript 的最佳补充,而 core-js 是它可以被 polyfill 的最佳位置。根据 @babel/preset-env 和 @babel/runtime 用到的技术,理论上我们可以通过一种简单的方式注入内置模块需要的 polyfill。然而,这个提案的当前版本会导致一些严重问题,这些问题并没有使其简单明了。

内置模块的 polyfill,根据作者的提案,仅仅意味着退回到分层 API 或者 导入 maps。这表明如果原生模块缺失,它将能够通过提供的 url 载入一个polyfill。这绝对不是 polyfill 需要的,并且它与 core-js 的架构以及其他流行的 polyfill 都不兼容。导入 maps 不应该是 polyfill 内置模块的唯一方式。

我们通过一个特定前缀使用 ES 模块语法就能够得到内置模块。这个语法在语言的早期版本并没有对等的 - 转译模块不可能在现在浏览器中与未转译的交互 - 这会导致包分发的问题。

更进一步讲,他将异步工作。对于功能检测这是个严重的问题 - 当你要检测一个功能并且加载 polyfill 时脚本不会等待 - 功能检测应该同步的做。

在没有转译和 polyfill 的情况下第一次实现内置模块。如果没有修改,在当前的 core-js 格式下内置模块将不可能 polyfill。建议的 polyfill 方式将使开发变得严重复杂。

这个标准库的问题能够通过添加一个新的全局变量解决(这将是最后一个吗?):一个内置模块的注册表将允许异步的设置和获取,例如:

js
StandardLibraryRegistry.get(moduleName);
StandardLibraryRegistry.set(moduleName, value);

异步回调,比如分层API应该全局注册表之后使用。

值得一提的是,它将简化将本地模块导入到老的语法的转换。

装饰器提案,新的迭代器语法,stage 2

这个提案中的 新迭代器,他被很认真的重做了。装饰器定义不再是语法糖,就像内置模块,我们不能在老版本的语言中编写装饰器并将其用作原生装饰器。除此之外,装饰器不仅仅是普通的标识符 - 他们生活在平行的词汇范围内:这意味着已经编译的装饰器不能和原生装饰器交互。

提案作者建议使用未编译的装饰器发布包,让包的使用者选择去编译他们的依赖。然而,在不同的情况下是不可能的。当他们被添加到 JS 标准库时,这个方法将阻止 core-js polyfill 新的内置装饰器。

装饰器应该是在某些东西上应用功能的一种方法,他们应该仅仅是包裹的语法糖。为什么要复杂化呢?


如果引入的一个语言功能不是从根本上是新的,在语言的早期版本什么不应该实现是可以选择的,我们能够转译或者 polyfill 它,被转译或者 polyfill 的代码应该能够和支持这个功能的浏览器原生交互。

我希望根据提案作者和委员会的智慧,这些提案能够被采纳,这样才能够合理的转译或者 polyfill 他们。


如果你对 core-js 项目感兴趣,或者你在你日常工作中使用它,你可以成为 OpenCollective 或者 Patreon 捐赠者。core-js 的背后不是一个公司:他的将来要靠你。


这里 可以评论这篇文章。

Denis Pushkarev,2019年3月19日,感谢 Nicolò Ribaudo 编辑。


File: docs/zh_CN/2023-02-14-so-whats-next.md

那么,接下来是什么?(So, what's next?)

翻译:卫剑钒 (微信公众号:man-mind)

嗨,我是(@zloirock),一个全职开源开发者。我不喜欢写长帖子,但似乎是时候写了。

最初,这篇文章应该是一篇关于开发core-js最新主要版本和关于其路线图的帖子(它被移到了后半部分),然而,由于最近的事件,它变成了一篇关于许多不同事情的长篇帖子......我他妈的累了(I'm fucking tired)。自由和开源软件从根本上被玩坏了。我可以默默地停止做这件事,但我想给开源最后一次机会。

🔻 点击查看如何提供帮助 🔻

如果你或你的公司以这样或那样的方式使用core-js,并且你对你的供应链质量感兴趣,请支持这个项目:

如果你能在 Web 标准和开源方面提供一份好工作,请写信给我。

什么是[`core-js`](https://github.com/zloirock/core-js)?

  • 它是JavaScript标准库中最受欢迎和最通用的polyfill,它支持最新的ECMAScript标准和提案,从古老的ES5功能到iterator helpers)等前沿功能,以及与ECMAScript密切相关的Web平台功能,如structuredClone。

  • 它是最复杂、最全面的polyfill项目。在发布这篇文章时,core-js包含大约5000个具有不同复杂程度的polyfill模块,从Object.hasOwn或Array.prototype.at到URL、Promise或Symbol,这些模块旨在协同工作。使用不同的架构,它们每个都可以是一个单独的包——虽然,可能有人并不喜欢这样。

  • 它做到了最大程度的模块化——你可以轻松(甚至自动)选择仅加载你将使用的功能。它可以在不污染全局命名空间的情况下使用(有人称这种用例为“ponyfill”)。

  • 它专为与工具集成而设计,并为此提供了所需的一切。例如,@babel/preset-env,@babel/transform-runtime,以及类似的SWC功能,这些都是基于core-js的。

  • 它是开发人员多年来每天在开发过程中使用现代ECMAScript功能的主要原因之一,但大多数开发人员只是不知道他们之所以有这种可能性,是因为core-js,因为他们间接使用core-js,因为它是由他们的transpilers/框架/中间包(如babel-polyfill)提供的。

  • 它不是一个框架或库,开发人员需要了解框架和库的API,定期查看文档,或者至少记住他或她正在使用它。而对于core-js而言,即便开发人员直接使用它——也只是一些导入行或配置中的一些行(在大多数情况下——会配置错误,因为几乎没有人阅读文档),之后,他们忘记了core-js,只是使用由core-js提供的Web标准的功能——但有时这是他们使用最多的JS标准库。

译者注:为了理解本文,读者需要知道`polyfill`的含义。polyfill是填充物的意思,是指在一种材料中填充另一种材料,比如用玻璃胶填充混凝土表面的裂缝,达到光滑平整的效果;再如填充在玩具或沙发中,让被填充物更温暖舒适。在JavaScript 语境中,polyfill意味着用于向旧版浏览器添加其并不支持的JavaScript 新版标准的功能。通常,polyfill 是一种 JavaScript 库或代码,它可以检测当前环境的功能支持,并在缺少的情况下提供相应的实现。

core-js总共有90亿次NPM下载以及每月2.5亿次NPM下载, GitHub上有1900万个仓库依赖它(global和pure),这是很大的数字,但并不能直观展示core-js的真正传播。让我们用其他方式查看一下。

我写了个简单的脚本,通过Alexa顶级网站列表检查core-js的使用情况。我们可以知道core-js的应用情况(以及各网站所使用的版本)。

在TOP 1000网站上运行这个脚本,我检测到52%的测试网站对core-js的使用情况。由于每天情况并不太一样(列表、网站等不是常数),结果可能会有百分之几的差异。然而,这只是使用现代浏览器对网站主页的粗略检测,很多使用并没有测出来,如果手动检查,会发现使用量增加百分之几十。例如,上面截图中某些网站没有被脚本发现使用了core-js,但去他们主页手工看一下就会发现他们也用了(请耐心点,在下面的一系列屏幕截图之后,就没有这么多图片了):











通过这样的手动检查,你可以在前100个网站的75-80%的网站上找到core-js,而检测脚本只在55-60%的网站上找到它。当然,在较大的样本中,百分比会下降。

Wappalyzer使用浏览器插件检测一个网站使用的技术(包括core-js),在他们之前的统计数据中,能够看到很有趣的结果,但现在他们的网站上,所有最受欢迎的技术的公开结果,最多也就只显示500万之多。基于Wappalyzer结果的统计数据,在800万个移动页面和500万个桌面页面中,有41%和44%显示使用了core-js。Built With显示前10000个网站中有54%使用了core-js(我不确定其检测的完整性)。

无论如何,我们可以自信地说,大多数热门网站都在使用core-js。即使core-js没有在大公司的主要网站上使用,肯定也用在了他们的其他项目上。

还有什么JS库在网站更流行?不是React、Lodash或任何其他人们经常谈及的库或框架,我能确定的只有“又老又好”的jQuery。

core-js不仅仅是在网站的前端——它几乎在所有使用JavaScript的地方——我认为这从来没有被认真统计过。

然而,由于上述原因,几乎没有人记得他或她使用了core-js。

我为什么要发表此文?不是为了展示我有多酷,而是为了展示一切有多糟糕。请继续读。


让我们从一张流行的xkcd图片开始下一部分

译者注:图片来自:https://xkcd.com/2347/

缘起

2012年,我把我的开发栈切换到了全栈JavaScript。当时JavaScript仍然太原始——IE仍然比其他任何东西都更受欢迎,ES3时代的浏览器仍然占据了Web的主要部分,最新的NodeJS版本是0.7——它才刚刚开始。JavaScript 仍然不适用于编写严肃的应用程序,开发人员用 CoffeeScript 语言转译器,解决了JavaScript所缺乏的语法糖问题,用 Underscore 等解决了 Javascript 缺乏标准库的问题。 然而,他们并不是标准,随着时间的推移,这些语言和库连同使用它们的项目一起过时了。因此,我满怀希望地期待即将到来的ECMAScript Harmony 6标准。

旧 JavaScript 引擎仍然流行,用户并不着急,还没有什么机会放弃它们,即使新的 ECMAScript 标准有着快速和可靠的优点,想让 JavaScript 引擎支持这个新标准,也要等很多年。但可以使用一些工具尝试使用新标准,让转译器(这个词不像现在这么流行)和标准库来解决语法和 polyfill 的问题。当时,这些工具包才刚刚出现。

那时,ECMAScript 转译器开始流行并发展迅速。而与此同时,polyfill 几乎没有根据用户和实际项目的需求而发展。它们不是模块化的,并可能带来全局命名空间污染——它们不适合做库。它们不是单一复合体——而是需要用多个来自不同作者的不同的 polyfill 库,并以某种方式使它们工作在一起——在某些情况下,这几乎是不可能的。太多必要的基本语言功能都还没有实现。

为了解决这些问题,2012年底,我开始研究一个后来被称为core-js的项目,一开始仅仅是为了我自己的需要。为了让所有JS开发人员的生活更轻松,2014年11月,我开源发布了core-js。也许这是我一生中最大的错误。

由于我不是唯一一个面临这些问题的人,几个月后,core-js已经成为JavaScript标准库polyfill的事实标准。core-js被集成到Babel(当时叫6to5)中,它在core-js发布前几个月就出现了——上面谈及的一些问题也是该项目所致力解决的。core-js开始作为6to5/polyfill分发,后来更名为babel-polyfill。经过几个月的合作、品牌重塑和演化后,babel-runtime出现了,又几个月后,core-js被集成到其关键框架中。

保障整个Web的兼容性

我没有宣传自己,也没有宣传这个项目。这是第二个错误。 core-js没有网站或社交媒体帐户,只有GitHub。我没有在会议上谈论它。我几乎没有写任何关于它的帖子。我只是在制作一个非常有用的东西,并使之成为现代开发栈的一部分,对此我很高兴。我给了开发人员一个机会,让他们使用最现代和真正必要的JavaScript功能,而不需要等待多年(等它们在所有引擎中实现),并且无需考虑兼容性和错误。人们开始使用它,项目的传播呈指数级增长,很快,它已经在百分之几十的热门网站上使用。

`译者注:注意,上面这段是作者最后悔的部分。`

然而,这只是所需工作的开始,之后跟随的是常年的辛勤工作。我几乎每天都花几个小时在core-js和相关项目(主要是Babel和compat-table)的维护上。

core-js不是一个只有几十行代码的库,这种库你写完就可以忘掉它。与绝大多数库不同,它与Web的状态息息相关。它对JavaScript标准或提案的任何更改、任何JS引擎新发布、任何JS引擎中新的bug要做出反应。ECMAScript 6 2015之后跟着的是新提案、新版本的ECMAScript、新的非ECMAScript Web标准、新引擎和新工具等。项目的演变、改进以及对Web当前状态的适配从未停止过——几乎所有这些工作,对普通用户来说,仍然是看不见的。

越来越多的工作需要我做。

长期以来,我试图以不同的方式为core-js找到维护者或至少是持续的贡献者,但所有尝试都失败了。几乎每个JS开发人员都间接使用core-js,他们知道babel-polyfill、babel-runtime,他们知道自己所用的框架里已经polyfill了所必需的功能,但几乎没有人知道core-js。在一些提到core-js的关于polyfill的帖子中,它被称为“一个小库”。这不是一个时髦的被广泛讨论的项目,如果它做得很好,为什么要帮助维护它呢?随着时间的推移,我失去了希望,但我觉得我对社区有责任,所以我被迫继续独自工作。

几年后,全职工作和FOSS(译者注:FOSS即自由和开放源码软件)几乎变得不可能——没有人愿意为你在工作时间致力于FOSS而付钱,仅仅用非工作时间是不够的,有时,core-js需要完全沉浸式的开发,而且是几周的时间。我认为社区需要适当的polyfill,而钱并不是我的第一任务。

我辞去了一份高薪工作,并且拒绝了一些非常好的职位选择,因为在这些职位上,我不能投入足够的时间从事开源。我开始全职从事开源工作,没有人给我付钱。我希望或早或晚,我能找到一份可以完全致力于开源和Web标准的工作。我定期通过短期合同赚取在FOSS上生活和工作所需的钱。我回到了俄罗斯,在那里可以用相对较少的钱获得体面的生活水准。我又犯了一个错——正如你将在下面看到的,钱其实很重要。


直到2019年4月,大约一年半时间,我没有分心干任何别的,我把所有时间都致力于core-js@3,并从根本上改进了Babel的polyfill工具,这是工具集(toolkit)生成的基础,现在几乎到处都在使用。

事故

坏事发生在core-js@3发布3周后。一个四月的晚上,凌晨3点,我开车回家。两个穿着深色衣服的醉酒的18岁女孩决定以爬的方式穿过一条光线很差的高速公路——其中一个躺在路上,另一个坐着并拽着第一个,她们并不在人行道上——而是直接在我的车轮下。目击者就是这么说的。我绝对没有机会看到他们。还有一名目击者说,在事故发生之前,她们只是在路上开玩笑地打闹。这并没什么不寻常,这是俄罗斯。其中一个女孩死了,另一个女孩进了医院。然而,即使在这种情况下,根据俄罗斯的仲裁惯例,如果司机不是议员或类似什么人的儿子,他几乎总是被判有罪——他必须要看到并预测一切,行人不负任何责任。我可能会在监狱里呆很长时间,如果我没有记错,检察官要求判我7年。

不入狱的唯一方法是与“受害者”和解——这是此类事故后的标准做法——并且还要有一名好律师。在事故发生后的几周内,我收到了“受害者”亲属当时以汇率计算的总额约为8万美元的资金索赔。律师也需要一大笔钱。

对于一个好的软件工程师来说,也许这不是一笔不可思议的钱,但是,正如我上面所写的,我长期全职在core-js@3版本上工作。没有人为这项工作付钱给我,我之前就已经花尽了所有的财务储备,所以,我没有那么多钱,也没有办法找到所需的钱。我的时间不多了。

筹钱

那时,core-js的使用几乎和现在一样广泛。正如我上面所写,我为core-js寻找了很长时间的贡献者,但没有任何成功。然而,core-js是一个应该积极维护的项目,它不能一直冻结。我的长期监禁不仅会给我带来问题——而且也会给core-js带来死亡,给每个使用它的人带来问题——有一半的Web都在用它。考虑一下令人头疼的公交车因素。

译者注:`公交车因素`是指这样的问题:一个项目里的关键人员如果突然被公交车撞了,项目会怎么样?

在事件发生几个月前,我开始筹集资金来支持core-js开发(主要发布在GitHub的README中和NPM上)。结果是......57美元/月。这就是全职工作以确保整个Web兼容性的“合理回报”。

于是我决定做一个小实验——向core-js用户寻求帮助——如果没人维护core-js,他们将首先遭受痛苦。我在core-js安装后添加了一条消息:

译者注:上面这段提示主要是向用户请求资金捐助,给出了两个捐助网站,并在最后一句说:“core-js的作者正在找一个好工作。”

我知道我无法从捐款中获得所需的钱,但是,每一美元都很重要。我添加了一条求职消息,以便有机会获得捐赠之外的钱。我想,NPM安装日志中的几行帮助请求(如果需要,可以隐藏)是可以接受的。我最初想的是在几周内删除这条消息,但一切都偏离了计划。我对人的看法是有多错......

恨

当然,我知道有人不希望在他们的控制台中看到帮助请求,但我收到的恨意简直淹没了我的房顶,每天有数百条消息、帖子和评论,都在表达他们的恨意。所有这些都可以简化为:

译者注:标题翻译:让这个SB zloirock和他的core-js库去死吧

这远远不是我所见过的最搞笑的事——如果我愿意,我能收集到大量这种风格的“恨开源”评论——但我不会这样,我的生活中已经足够多负面东西了。

开发人员喜欢使用免费的开源软件——免费,效果好。他们对背后数千小时的开发不感兴趣,他们对项目背后那个真人的问题和需求不感兴趣。他们认为,提及这些就是对他们个人空间的侵犯,甚至是对他们个人的冒犯。对他们来说,这些开源项目,就是一些齿轮,应该自动耦合,不应该有任何噪音,也不应该要他们参与。

因此,成千上万的开发人员侮辱我,并声称我无权向他们寻求任何形式的帮助。我的帮助请求如此冒犯了他们,以至于他们开始要求限制我对仓库和包的访问,并要求将它们转移到其他人那里,就像曾经对left-pad那样。他们中几乎没有人了解core-js的作用和规模,当然,也没有人想维护它——它应该由“社区”和其他人来维护。我看到所有这些仇恨,为了不被他们影响,我没有删除安装包的请求帮助信息,本来我只想让它存在几周。

求助于那些用core-js赚大钱的大公司?那可几乎是每家大公司。让我们稍微改一下这条旧推文:

公司:“我们想使用SQL Server 企业版”

MS:“这需要25万美元+2万美元/月”

公司:“好的!”

...

公司:“我们想使用core-js”

core-js:“简单,执行npm i core-js 就可以了”

公司:“酷!”

core-js:“你想在经济上做出贡献吗?”

公司:“哈哈,不”

译者注:这条旧推文是@AdamRackis于2017年11月17日发布的,里面原先写的是Babel,在此文中改为core-js

几个月后,厌倦了用户的投诉,NPM推出了npm fund——这不是解决问题的办法,这只是摆脱这些投诉的一种方式。你多久会敲一下npm fund?你多久会向npm fund中的人捐款?你会先看到谁并支持他?是core-js还是维护着十几个单行库(并且相互依赖)的人?npm fund为NPM的未来步骤提供了完美的理由(请往下阅读)。

在9个月内,数千名开发人员,包括重度依赖core-js的项目开发人员,了解了我的状况——但没有人提出要维护core-js。几个月内,我与一些依赖core-js的重要项目的维护人员进行了交谈,但没有取得任何成功——他们没有必要的时间资源。因此,我不得不要求一些与FOSS社区无关的朋友(起初是@slowcheetah,感谢他的帮助)替代我,至少尝试解决那些比较重大的issue,在我重获自由之前。


有个别用户和小公司支持了core-js——我非常感谢他们。然而,9个月内筹集的资金仅为所需资金的1/4左右,你们知道,我需要8万美元解决困境,而且应该是在几周之内。

就在这段时间,不管怎么样,每天core-js的下载量几乎翻了一番。

2020年1月,我进了监狱。

出狱

我不想说太多关于监狱的事,我也不想记住那些。那是在一家化工厂的奴隶般劳动,在那里我的健康严重受损,我24/7和毒贩、小偷和杀手们在一起,共渡了难忘的时光,而且,还无法访问互联网和计算机。

大约10个月后,我被提前释放了。


我看到了数十篇文章、数百篇帖子和数千条评论,其中许多评论的本质,大概就是这样的:

译者注:上面这段话的翻译:这家伙是个大混蛋。他绝对是我在 Github 上遇到的最糟糕的维护者,无人能及。不知道他因什么进了监狱,但我很高兴看到他离开这里。

他们到底认为我做错了什么?是的,我犯了上面所说错。我看到一些人支持开发core-js,看到许多issue,问题和消息——但比那些恶评要少。与此同时,core-js变得更受欢迎,已经达到了和现在一样的使用比例。

继续,保障整个Web的兼容性

出狱后,我像以前一样回到了core-js维护。而且,我完全不再被合同和任何其他工作分散注意力,我只是在core-js上工作。core-js在融资平台上有一些钱——虽然不多,比我全职从事core-js之前收到的少很多倍——但对我来说,这足以维持生活。**这是一种降级。我全职开源,为了让世界变得更美好......**我不考虑事故遗留下来的数万美元的诉讼,我也不考虑我的未来,我只是想Web有更美好的未来。当然,我希望一些公司能给我提供一个职位,让我有机会从事Web标准工作,并赞助我在polyfill和FOSS方面的工作。

在接下来的两年里,我在core-js工作方面取得了很多成就,几乎和前8年一样多。仍然是core-js@3——但要好得多。然而,changelog以及之前的diff只反映了一小部分已完成的工作。几乎所有的工作都在暗处,普通用户看不到。

这些工作包括JS标准和建议方面的基本工作,作为它的连带后果,考虑到我的辛勤工作,以及我反馈和建议后的变化,我认为一些ECMAScript提案——许多已成为语言一部分——是我的成就,也是提案拥护者的成就;这些工作包括core-js和引擎及其错误跟踪器的调错工作;这些工作包括在数百乃至数千个环境中持续自动或手动/构建/测试以确保标准库在任何地方的正常运行并收集兼容数据。core-js兼容数据,从一开始仅仅是几天内制作的原型,变成了一个具有外部和内部工具的详尽数据集;这些工作包括对项目中正在开发的许多功能的设计和原型制作;这些工作还有更多,更多。


如上所述,core-js存在于大多数流行的网站中,它提供了一个几乎完整的JavaScript标准库,并修复了不正确的实现。使用core-js打开的网页多于Safari和Firefox打开的网页。因此,从某种角度来看,core-js可以被称为最受欢迎的JavaScript运行时之一。

在开发core-js时,我是几乎所有现代和未来JavaScript标准库功能的第一个实现者,几乎所有功能都有我的反馈,并根据这些功能进行了修复。core-js是实验ECMAScript提案的最佳场地。在非常多的情况下,提案收到的反馈,都是用户在尝试了提案的实验性core-js之后提交的。

JavaScript的最佳前进方式是TC39和core-js的合作。TC39邀请Babel等项目的成员担任专家,却不找我。我经常看到TC39成员忽视我或core-js,甚至故意制造障碍,他们甚至毫不避讳这点:

译者注:TC39则是ECMA为ES专门组织的技术委员会(Technical Committee),39这个数字用来标记旗下的技术委员会。TC39的成员由各个主流浏览器厂商的代表构成。

上图文字翻译:真正的困难是我现在拒绝与core-js的作者接触


上图文字翻译:polyfill从来没有也从来不会决定提案如何运作,所以我不知道为什么这个问题一直被提起。


一段时间后,NPM表达了它的“支持”。在2020年底发布的npm@7中,作为npm fund的逻辑延续,控制台禁用了安装后脚本(post-install scripts)的输出。结果是可以预期的,人们不再能看到资金请求,同时,几乎没有人使用npm fund,所以core-js赞助者的数量开始下降。NPM可真够“支持”我的,它不仅通过分发我的作品来赚钱,而且它自己也在用core-js :-)

译者注:这张图表明了NPM网站也在用core-js

此外,另一个因素也在发挥作用。“质量越高,支持越少”,这个库维护得很好吗?几乎没有什么处于打开状态的错误报告吧?当有错误时,会立即得到修复吗?这库给了我们几乎所有想要的东西了吧?是吧?那么,我们为什么要支持这个库的维护呢?支持维护者的成本不会停留在表面——对于大多数开发人员和公司来说,它仍然只是“一个小库”。许多以前还赞助core-js的人,后来都停止了。

core-js代码包含我的版权。正如你在这篇文章前面看到的,core-js出现在大约一半的网站上。经常性地,有人在有害网站或应用的源代码中发现它——他们不知道什么是core-js,他们的技术水平甚至不足以发现它。当这种情况发生时,警察会打电话威胁我,甚至有人试图勒索我。大多数时候,这一点都不好笑。

美国和加拿大记者多次联系我,因为他们在美国新闻和政府网站上发现了core-js。当他们弄明白的时候,他们非常失望,失望于我不是一个干涉美国选举的邪恶的俄罗斯黑客。

无休止的仇恨流随着时间的推移略有减少,但仍然有。大部分内容从GitHub issues或Twitter Threads转移到我的邮件或IM。今天,一位开发人员给我写了一条消息,他称我是开发人员社区的寄生虫,说我的core-js到处蔓延传播,没有一点屁用,但却赚了很多钱。他称我和Hans Reiser是同样的杀人犯,买通了法官,逃脱了惩罚。他希望我和我所有的亲戚都死。这没有什么不寻常的,我每个月都会收到几条这样的消息。去年,又补充了一种,说我是一个“俄罗斯法西斯主义者”。

关于战争说几句

开源应该脱离政治。

我不想在两种邪恶之间做出选择。我不会对此发表更详细的评论,边境两边都有我身边的人,他们可能会因此而受罪。

让我提醒你我上面写过的内容:我回到了俄罗斯,因为在那里,可以用相对较少的钱获得体面的生活水平,并专注于FOSS,而不是赚钱。现在我不能离开俄罗斯,因为在事故发生后,我有数万美元的未决诉讼,在还清它们之前,我被禁止离开这个国家。

你猜猜core-js每个月能收到多少钱?

当我开始全职维护core-js时,没有被合同和任何其他工作分心,**我每月收到的钱大约为2500美元——比我通常的全职工作少4~5倍。**记住,这是一种降级,为了让Web变得更好,为了让issues和bugs减少到零,为了制作最高质量的产品,这可是几乎每个人都在用的东西......项目将得到足够多的赞助,对吧?对吧?

几个月后,每月收入下降到约1700美元(至少我觉得是这么多),通过Tidelift是1000美元,通过Open Collective是600美元,通过Patreon是100美元。除了订阅式的每月捐款,还会有一些一次性捐款(平均每月可能为100美元)。

Crypto?通过加密钱包请求捐款是很流行的。然而,一直以来,加密钱包上只收到了2笔总额约为200美元的转账,最后一次是在一年多前。GitHub赞助商?它在俄罗斯不可用,所以从来没有过。PayPal?这是禁止俄罗斯人使用的,当它可用时,core-js在这段时间里收到了大约60美元。补助金?我申请了很多补助金——所有申请都被忽略了。

在这些捐赠中,Bower作为另一个FOSS社区,提供了主要部分:每月400美元。我也非常感谢所有其他赞助商:由于您的捐款,我仍在为这个项目工作。

然而,在这个列表中,没有一家大公司,或者至少没有一家是前1000名网站列表中的公司。老实说,目前支持者名单上主要是个人,少数是小公司,他们每月支付几美元。

如果有人说他们不知道core-js需要资金......拜托,我经常看到这样的表情包:

#####译者注:此图来自reddit网站的r/ programmerHumor版块,用来讽刺作者在core-js安装后请求捐款。


一年前,Tidelift不再给我寄钱了。他们说,由于政治局势,他们使用的Hyperwallet不再供俄罗斯人使用(但上个月我试图更新一些个人数据时,它又可以使用了),为了安全起见,他们会把我的钱存放在他们那边。在过去的几个月里,我试图把这笔钱存入银行或Hyperwallet账户,但收到回复说,他们会尝试做些事情(听起来很棒,不是吗?)。去年年底以来,他们干脆停止回复电子邮件。现在,我只有这个:

信件主要内容翻译:Denis你好,对于延误回复深表歉意。不幸的是,如果您在Hyperwallet中的账户被冻结,我们将无法向您付款,因此我们将终止您和我们之间的协议并立即生效。如果您能够解冻您的Hyperwallet帐户,请告诉我们,我们可以重新建立关系。(这是Tidelift给core-js作者Denis的邮件)

**Tidelift以如此有趣的方式,让我知道我的收入减少了,今年我的工作收入不是每月1800美元,而是每月800美元。**当然,没有对后续电子邮件的回复。然而,他们的网站显示,我仍然在收到钱。

译者注:从截图中可以看出,同样地,Tidelift网站也使用了core-js

我想知道,通过这个网站支持其供应链的公司,对这种骗局将如何反应。


同一天,在OpenCollective上,我看到每月订阅的捐赠从大约600美元减少到大约300美元。显然,Bower的财务储备已经耗尽了。这意味着这个月我总共会得到大约400美元。

在之前的几个月里,我测量了在core-js上工作需要多少时间。结果大约是......每月250小时——远远超过连休息日都没有的全职工作的时长,这使得我不可能有“真正的”全职工作或者为任何合同工作。250小时400美元......**每小时工作的报酬不到2美元,前一年多一点:每小时4美元。**是的,几个月来,我确实花了更少的时间在这个项目上,但没有太大变化。

这就是确保整个Web兼容性的当前价格。加上没有保险或社会保障。

收入增长和职业发展都很棒,对吧?

译者注:注意在本文中,denis经常使用反讽手法。

我想你很了解主要IT公司的高级软件工程师的报酬是多少。我收到了许多类似的报价,然而,它们与core-js的正确工作不兼容。

在经常受到的威胁、指责、命令和侮辱中,我经常会得到类似“停止乞讨,去工作,你这个懒惰的人。立即删除你的乞讨信息——我不想看到它们。”有趣的是,至少其中一些人每年获得超过30万美元(我很确信这点,因为我与他们的同事交谈过),而(由于他们的工作性质)core-js每月为他们节省了许多小时的工作。

一切都改变了

当我开始研究core-js时,我独自一人。现在我有一个家庭了。一年多前,我成了我儿子的父亲。现在我必须为他提供体面的生活水平。

我有一个妻子,有时她想要一双新鞋,或一个包,或一个新的iPhone,或一个Apple Watch。我的父母已经到了需要我有力支持他们的年龄。

很明显,我不可能用我从core-js维护中获得的钱来正常地支持一个家庭,我的财政状况走到头了。

我越来越经常听到这样的责备:“放弃你的开源,你这是纵容自己,请回到正常的工作。谁谁谁只做了一年程序员,他对开源几乎一无所知,他每天只工作几个小时,已经赚的是你的好几倍。”

没有了

我他妈的累了(I'm damn tired. )。我喜欢开源和core-js。但我这样做是为了谁,为了什么?让我们总结一下上述内容。

  • 自2014年以来,我一直在确保零兼容性问题,我为Web世界提供web平台的前沿功能;我大部分时间都在为此而工作,而我所赚的钱甚至不足以购买食物。
  • 我看不到任何感激之情,而是来自开发人员的巨大仇恨,我可简化了他们的生活啊。
  • 通过使用core-js而节省并赚取数百万美元的公司,所做的只是忽略core-js的资金请求。
  • 即便在我危急的情况下,在面对我的请求时,他们中的大多数,不是帮助,而是忽视或憎恨。
  • 那些标准开发人员和浏览器开发人员,不是和我合作以共同致力于JavaScript的美好未来,而是给我设置障碍,逼得我和他们斗争。

恨我的人,我并不在乎。如果我在乎,我早就离开开源了。

我可以容忍与标准开发人员缺乏正常的互动。这意味着用户将来会遇到问题,而且,当 Web 崩溃时,标准开发人员自己也会遇到问题。

**不管怎样,钱很重要。**我已经受够了以牺牲我和家人的福祉为代价而资助公司。我应该有能力确保我的家人、我的儿子有一个光明的未来。

core-js的工作几乎占据了我所有的时间,超过了全职工作日的时间。这项工作确保了大多数热门网站的正常运行,这项工作应该得到适当的报酬。我不会继续免费工作,也不会以每小时2美元的价格工作。我愿意继续以每小时至少80美元的价格为项目工作。这正是eslint团队成员的收费标准。如果开源工作需要,我准备还清我的诉讼并离开俄罗斯——虽然,这并不便宜。


我经常看到这样的评论:

图片内容翻译:Zach Leatherman说:“认真想想这个:如果有人试图勒索开源怎么办:‘这个项目需要每月____美元的捐款,否则将停止维护,没有更新,没有bug修复或安全补丁。这是个很好的项目——如果它发生了什么事,那真是太遗憾了。’”Matt Mink说:“听起来core-js就是这么干的。”

好的,伙计们,如果你们想要这个——我就给你这个。


根据你们的反馈,core-js将很快实施以下方式之一:

  • 给我适当的资金支持

    我希望,至少在阅读了这篇文章后,大企业、小公司和开发人员会考虑其开发栈的可持续性,并适当地支持core-js开发。在这种情况下,core-js将得到适当的维护,我将能够专注于添加新的功能。

    我现在的工作规模已经达到了顶峰,我一个人已经不能支撑了——我在体力上已经不能继续。一些工作,比如改善测试覆盖范围或文档,这不难,但需要很多时间,这不是志愿者想做的那种工作——我不记得有任何PR是关于改进现有功能的测试覆盖范围的。因此,在付费的基础上吸引至少一两个开发人员(至少是学生,当然最好有更高水平)是有意义的。

    考虑到其他维护人员的参与和其他费用,我认为目前每月大约3万美元就足够了。更多的钱,就会有更好的产品、更快的开发、更少的时间。我一个全职工作在core-js上当然可以,但不像团队那样有成效。

  • 我被一家公司雇用,在那里我将能够从事开源和Web标准的工作

    这将给我继续工作所需的资源。

  • core-js将会成为一个商业项目,如果得不到适当的用户支持

    以当前的core-js包创建商业模式是有问题的,因此新的core-js版本很可能会改变许可证。免费版本的功能将受到限制,所有额外的功能都将付费。core-js将继续发展,在该项目范围内,将创建许多新工具以确保Web兼容性。当然,这将大大减少core-js的传播,并将给许多开发人员带来问题,然而,即使是一些付费客户也足够了,我的家人将有钱支付账单。

  • core-js缓慢的死亡,如果你们并不需要它

    我对商业项目有很多想法,我有很多好的工作机会——所有这些都需要时间,而我把时间都给了core-js维护。这并不意味着我会立即完全停止维护core-js,我只会按捐款金额的多少,来决定我干多少。如果它们处于当前水平,那我每月只会干几个小时,而不是像现在这样数投入百个小时。该项目将停止增长——也许小错误将被修复,兼容性数据将被更新——但不会更多了。一段时间后,core-js将变得毫无用处,并会死亡。

我仍然希望是第一种结果,因为core-js是现代数字基础设施的关键组成部分之一,但看看现在和过去,我正在为其他选择做准备。

我提前回答一些我经常看到的愤怒的评论,这些评论肯定会在这篇文章之后出现:

  • 没问题,我们会固定依赖core-js的某个版本(pin the core-js dependency)。

    与大多数项目不同,core-js应该保持跟上最新前沿(bleeding edge),最新的core-js能让你使用最新的JavaScript技术,而不用考虑引擎的兼容性和错误。你可以固定在某个core-js版本,也许头一年或两年,你不会遇到严重的问题。之后,问题就出现了——你用的polyfill会变得过时,但仍然存在于你的捆绑包中,变成一个无用的压舱石。你将无法使用JS语言的新功能,并将在JS引擎中面对新错误。

  • 这是开源的,我们将分叉(fork)它,滚开。

    我经常看到这样的评论,有人甚至试图用分叉吓唬我。我已经说过太多次了,如果有人能分叉并正确维护core-js,我会很高兴——在没有人维护的情况下,分叉有什么意义呢。现在我根本没有看到任何人试图为core-js添加一些重要的东西,或者至少定期做出贡献。项目应该跟进每个新的JavaScript引擎版本,更新兼容性数据,修复或至少考虑每个引擎的每个新错误(无论多大的错误),查看并实现每个可能的新的JavaScript功能,最大限度地正确执行,测试并考虑每个现代引擎或老引擎的每个版本的具体细节。这是一项艰苦的工作,你准备好了吗,并且有所需的知识和时间吗?举个例子,当我在监狱里时,Babel说他们搞不定:

图片文字翻译:nicolo-ribaudo在2020年3月15日说:“我是Babel的维护者,我们大概率不会fork core-js,因为我们没有足够的资源维护它。”

  • 我们不需要core-js,有许多替代项目可用。

    我没有抱着你不放。你说的替代品在哪里?当然,core-js不是JavaScript标准库的唯一polyfill,但所有其他项目的使用率都比core-js少几十倍,这并不奇怪——所有这些项目都只提供了core-js功能的一小部分,它们不够合适和复杂,它们可使用的场景非常有限,它们不能以如此简单的方式正确集成到你的项目中,并且还存在很多严重问题。如果真的有合适的替代品,我早就停止在core-js上工作了。

  • 我们可以放弃IE支持,所以我们不再需要polyfill。

    正如我在上面写的那样,我没有抱着你。在某些情况下,真的不需要polyfill,你可以不用它们,但IE只是一小部分问题——即便在IE时代也是这样。当然,你不用IE的话,polyfill不会给你带来在IE8中支持ES6这样的功能。但即使是最现代的引擎,也没有实现最现代的JavaScript功能。即使是最现代的引擎,也有错误。你确定你和你的团队完全了解你们应用所支持的所有引擎的所有限制,并且可以绕过它们吗?我有时都会忘记一些很怪的地方和缺失的特性。

  • 你是个混蛋,我们会把你从FOSS社区中开除。

    是的,你是对的。我真是个混蛋,让你有机会在现实生活中使用现代JavaScript功能,我这个混蛋多年来一直在解决你的跨引擎兼容性问题,并且比任何人都为此做出了更多的牺牲。我真是个混蛋,只想让他的儿子吃饱,只希望他的家人有足够的钱来支付账单,我这个混蛋的家人不应该需要任何东西。上面我说的,可能真的会让我离开FOSS而拥抱商业,让我们拭目以待吧。


现在,让我们从负面因素转到这篇文章的后半部分,我们将讨论如何让core-js做的更好以及polyfill的一般性问题。

路线图

JavaScript、浏览器和Web开发正在以惊人的速度发展。所有浏览器需要所有core-js模块的时代已经一去不复返了。最新的浏览器具有良好的标准支持,在常见的用例中,它们只需要一定比例的core-js模块来提供最新的语言功能和错误修复。一些公司已经放弃了对最近再次“埋葬”的IE11的支持。然而,即使没有IE,旧浏览器也会一直存在,现代浏览器中也会发生错误,新的语言功能将定期出现,无论如何,它们都会延迟出现在浏览器中;因此,如果我们想在开发中使用现代JS并尽量减少可能的问题,polyfill会长期留在我们身边,它们应该继续发展。

在这里,我将(几乎)不写任何关于添加新的或改进现有特定polyfill的内容(当然,这是core-js开发的主要部分之一),让我们谈谈其他一些关键事情,而不关注小事。如果决定将core-js做成商业项目,路线图是应该讨论的。

我正试图保持core-js尽可能紧凑,它应该遵循的主要原则是在现代Web中发挥最大作用——客户端不应加载任何不必要的polyfill,polyfill应该最大限度地紧凑和优化。当前,最大的core-js(针对早期提案)捆绑大小约为220KB缩小,压缩后70KB——它不是一个小包,它挺大——它就像jQuery、LoDash、Axios加起来一样大——原因是该包几乎涵盖了该语言的整个标准库。core-js每个组件的大小比同类可用替代品的大小少若干倍。你可以只加载所使用的core-js功能,在最小情况下,捆绑大小可以减少到几K;正确使用时,通常是几十K——然而,还有一些东西需要考虑,大多数页面包含比整个core-js捆绑包更大的图片,大多数用户的互联网速度为几十Mbps,那么为什么还要这么关注core-js的大小?

我不想详细重复关于“JavaScript的成本”这种旧帖子,在那里你可以阅读为什么添加JS会增加用户开始与页面交互的时间,而不是添加类似大小的图片——它不仅是下载,它还解析、编译、评估脚本,它阻止了页面渲染。

在太多地方,移动互联网并不完美,它们还停留3G甚至2G。在3G的情况下,下载一个完整的core-js可能需要几秒钟。然而,页面经常包含多个core-js和许多其他重复的polyfill。一些(主要是移动互联网)互联网提供商的“无限”数据套餐非常有限,用户用了几G字节之后,网速就会降到几Kbps。连接速度还受很多其他因素受限。

页面加载速度就等于收入。

插图来自谷歌随机搜索的一个帖子

由于polyfill的新增或改进,core-js的大小不断增长。这也是一些大型polyfill所遇到的问题,在core-js中添加Intl、Temporal和其他一些功能,捆绑的大小将增加十几倍,达到几兆字节。

core-js的杀手级功能之一是,它可以通过使用Babel、SWC或手动方式进行优化,虽然,现在的方法只能解决部分问题。为了正确解决这些问题,现代Web需要新一代的工具包,该工具包可以简单地集成到当前的开发堆栈中。在某些情况下,正如你将在下面看到的,这个工具包可以帮助使你的网页变得甚至比没有core-js更小。

我已经在“关于core-js@3、Babel和对未来的展望”中写了其中一些内容,但这些只是原始的想法。现在他们正处于实验甚至实施阶段。
由于该项目的未来不确定,在这里写下任何具体日期是没有意义的,我不保证所有这些都会很快完成,但这值得我们努力去做。


新的主版本(major version)

core-js@3大约4年前发布——已经很久了。对我来说,添加一些突破性更改并将新版本标记为major版本并不是一个大问题(相比之下,确保向后兼容则是一个挑战)——但这对用户来说是个大问题。

目前,大约25%的core-js下载是严重过时的core-js@2。许多用户想将其更新到core-js@3,但由于他们的依赖项使用core-js@2,他们仍然使用过时的版本来避免多个副本(我在GitHub上看到了太多项目有此类问题)。太频繁的重大更新将使此类情况更加恶化。

然而,最好不要痴迷于与旧版本的兼容性。core-js包含太多没有删掉的东西,仅仅是出于保持兼容性。**而缺乏一些长期需要的突破性变化将对未来产生负面影响。**从标准、生态系统和Web的变化以及遗留物(legacy)的积累情况来看,最好每2-3年发布一个新的major版本。

如果需要好几年的开发,才能出来一个新版本让大家看到他们想要的一切,这对用户是不可接受的。core-js遵循SemVer,首先发布一个新的major版本,内含一些突破性变化(部分叙述如下),大多数新功能在minor版本中添加。在这种情况下,新版本可能需要大约2-3个月的全职工作,它会比上一个版本小,这将是core-js第一次变小 :-)

关于core-js包

放弃严重过时的引擎支持

IE死了。然而,还有人用——出于许多不同的原因,有人仍然被迫制作或维护在IE中工作的网站,core.js是让他们生活更轻松的主要工具之一。

现在,core-js试图支持所有可能的引擎和平台,甚至ES3和IE8-(译者注:IE8-代表IE8及更早的版本)。但只有一小部分使用core-js的开发人员需要这个,IE8-浏览器的占有率约为0.1%。对于更多其他用户来说,继续支持IE8-就意味着——更大的捆绑和更慢的执行。

主要问题来自对ES3引擎的支持:大多数现代ES功能都基于ES5,而ES5在旧引擎中不可用。一些功能(如getter/setter)不能被polyfill,因此一些polyfill(如类型数组)根本无法在IE8-中工作。另一些功能需要很复杂的变通办法。真正可以polyfill的只是一些简单的功能,捆绑包中core-js大小的主要部分是ES5方法的实现(在polyfill的大量功能中,它只占百分之几,所以这个问题与精简捆绑包有关)。

即便简单地将ES5功能的fallback替换为直接使用原生功能,也会将core-js捆绑的大小减少2倍以上。重新设计架构后,它将进一步减少。

IE9-10浏览器的占比也很小了——目前,同样的0.1%。但是,如果core-js还在支持其他一些过时的浏览器,考虑放弃对IE9-10的支持是没有意义的,他们的问题和限制是类似的,甚至前者还更多。例如Android 4.4.4——总共占比约为1%。将标准提高到ES5之上是一个困难的决定,因为还要对一些非浏览器引擎提供支持。然而,即使将来放弃对IE11支持,也不会像现在放弃IE8-带来那么多好处。

ECMAScript模块和现代语法

目前,core-js使用CommonJS模块。长期以来,这是最受欢迎的JavaScript模块格式,但现在ECMAScript提供了自己的模块格式,它已经非常受欢迎,几乎在任何地方都支持。例如,Deno,像浏览器一样,它不支持CommonJS,但支持ES。core-js应该在不久的将来有一个ECMAScript模块版本。但是,ECMAScript模块仅在NodeJS的现代版本中受支持,core-js 应该可以在较老的NodeJS版本中工作,而无需转译或打包。Electron仍然不支持它,因此现在立刻干掉core-js的CommonJS版本是有问题的。

现代语法的其他方面并不那么明显。目前,core-js使用ES3语法。最初,这是为了最大程度优化,因为它无论如何都应该预先转译为旧语法。但这只是最初的情况,现在,core-js 库无法在用户环境中正确转译,并且在转译器配置中应该被忽略。为什么?让我们以Babel转换为例看看:

  • 转换的很大一部分依赖于现代的内置,例如,使用@@iterator协议的转换——但Symbol.iterator、迭代器和所有其他相关内置都是在core-js中实现的,在core-js加载之前不存在。

  • 另一个问题是使用注入core-js polyfill的方式转译core-js。显然,我们无法将polyfill注入到它们实现的地方,因为这会导致循环依赖。

  • 对core-js的一些其他转换会破坏其内部结构 - 例如,typeof的转换(将有助于支持polyfill后的symbol)会破坏Symbol的polyfill。

然而,在polyfill代码中使用现代语法可以显著提高源代码的可读性,并且有助于减少大小,在某些情况下还可以提高性能(如果在现代引擎中捆绑polyfill)。所以,现在是时候考虑将core-js重写为现代语法了,为了让它能够正常转译,需要使用变通手法,并为不同用例发布不同语法的版本,以解决上述问题。

Web标准的polyfill

我已经考虑很长时间了,想为 core-js 添加尽可能多的 Web 标准支持(不仅仅是 ECMAScript 以及与其密切相关的特性)。首先,是关于最小通用 Web 平台 API (它是什么?)的尚未实现功能,但不仅限于此。最好是有一个牢靠的polyfill项目,可以用于尽可能多的开发案例,而不仅仅是ECMAScript。目前,浏览器中支持 Web 标准的情况比支持现代 ECMAScript 特性的情况要糟糕得多。

将Web标准polyfill添加到core-js的主要障碍之一,是捆绑包的大小显著增加,但我认为,使用目前仅加载所需polyfill的技术,以及下面我将描述的技术,我们可以将Web标准的polyfill添加到core-js中。

但主要问题是它不应该是一个幼稚的polyfill,如我之前所述,现在,在绝大多数情况下,ECMAScript 功能的正确性并不算太差,但对于Web 平台的功能就没有这么好了。例如,最近添加的structuredClone polyfill。在实现它时,考虑到依赖关系,我遇到了数百种不同的JavaScript引擎错误,但我不记得在添加新的ECMAScript功能时看到过这种情况。 因为这个原因,为了一个简单的方法,本应该可以在几个小时内完成的工作(包括解决所有issue和添加所需功能),却持续了几个月。对于polyfill,如果做得不好,还不如不做。适当的测试、polyfill填充和确保跨平台兼容性的 Web 平台功能,比我在 ECMAScript polyfill 上花费的资源更多。因此,仅在我有这样的资源时,才会开始向 core-js 添加尽可能多的 Web 标准支持。


更有趣的新方法

有人会问它为什么处于这个位置。像转译器这样的工具与core-js项目有什么关系?core-js只是一个polyfill,转译器那些工具由其他人编写和维护的。有一次我也认为,用一个好的API编写一个伟大的项目,解释它的可能性就足够了,当它流行起来时,它将获得一个生态,并有着适当的第三方工具。然而,多年来,我意识到,如果你不这样做,或者不控制你自己,这种情况就不会发生。

例如,多年来,实例方法无法通过Babel runtime进行polyfill,我解释了太多次如何操作。在实际的项目中,通过preset-env来polyfill是不可行的,因为所需的polyfill的检测不完整,以及兼容性数据来源不够好,我从一开始就解释了这一点。由于这些问题,我被迫在2018-2019年几乎完全重写这些工具,并发布在core-js@3版本中,之后我们获得了现在这种基于静态分析的polyfill注入工具。

我相信,如果没有在core-js范围内使用如下的方法,它根本无法正常工作。


为了避免与以下文本相关的一些问题:core-js的工具将挪动(move)到作用域包——像core-js-builder和core-js-compat等工具将分别成为@core-js/builder和@core-js/compat。

不仅仅是Babel:转译器和模块捆绑器的插件

目前,一些用户被迫使用Babel,只是因为需要自动注入及优化所需的polyfill。Babel的preset-env和runtime通过静态分析优化core-js的使用,这是唯一足够好且众所周知的方法。从历史上看,它之所以能这样,是因为我帮助Babel进行polyfill。但这并不意味着Babel是唯一或最好的干这事的地方。

Babel只是众多转译器之一。TypeScript是另一个流行的选项。其他转译器现在越来越受欢迎,例如SWC(它已经包含一个自动polyfill/core-js优化的工具,但仍然不够完美)。然而,我们为什么要谈论转译器?捆绑器和webpack或esbuild(也包含集成的转译器)等工具对polyfill的优化更有兴趣。rome已经发展了几年,还没有准备好,但它的概念看起来很有希望。

位于转译层的基于静态分析的自动polyfill有这么一个主要问题:不是捆绑包中的所有文件都会被转译——例如,依赖项。如果你的一些依赖项需要现代内置功能的polyfill,但你没有在用户空间代码中使用此内置功能,则polyfill不会被添加到捆绑包中。不必要的polyfill导入也不会从你的依赖项中删除(见下文)。将自动polyfill从转译层移动到捆绑器层,可以解决这个问题。

当然,与Babel相比,编写或使用此类插件在很多地方是比较困难的。例如,如果没有一些额外的工具,你无法在TypeScript中使用插件进行自定义转换。然而,有意愿的地方就有办法(where there's a will there's a way)。

core-js的自动polyfill/优化不应该仅在Babel中可用。虽然core-js项目也不可能为所有转译器和捆绑器编写和维护插件,但可以做这些事情:

  • 改进core-js(@core-js/compat)提供的数据以及用于第三方项目集成的工具,它们应该是全面的。例如,“内置定义”仍然在Babel一侧,导致它们在其他项目中的重用存在问题。

  • 由于一些工具已经提供了core-js集成,因此帮助他们也是有意义的,而不仅仅是Babel。

  • 为core-js项目范围内的一些重要工具编写和维护插件是有意义的。哪个?我们拭目以待。

polyfill收集器

上面解释了文件层上基于静态分析的自动polyfill(usage polyfilling mode of Babel preset-env)的问题,但这不是唯一的问题。让我们谈谈其他的。

你的依赖项可能有自己的core-js依赖,它们可能与你在项目根部使用的core-js版本不兼容,直接将core-js导入到你的依赖项可能会导致损坏。

项目通常包含多个入口点、多个捆绑包,在某些情况下,将所有core-js模块移动到一个块(chunk)可能会有问题,并可能导致每个捆绑包中core-js重复。

我已经在前面发布了core-js使用统计数据。在许多情况下,你可以看到core-js的重复——而且它只在应用程序的第一个加载页面上。有时它甚至像我们在彭博网站上看到的那样:

前段时间,这个数字甚至更高。当然,通常的网站不会拥有这么多不同版本的core-js,但有多个core-js确实很常见,大约一半的用了core-js的网站都这样。为了防止这种情况,需要一个新的解决方案,在一个地方从项目的所有入口点、捆绑包和依赖项收集所有的polyfill。

让我们称这个工具为 @core-js/collector。这个工具应该接收一个入口点或入口点列表,并使用 preset-env 中所使用的静态分析技术,但是,该工具不应该转换代码或注入任何内容,而是检查完整的依赖树,并返回所需的core-js模块的完整列表。作为一项需求,它应该可以很简单地集成到当前开发栈中。一种可能的方式是在插件中创建一个新的polyfill模式,称为“collected” - 它将允许在一个地方加载应用程序的所有收集的polyfill,并删除不必要的polyfill(见下文)。

删除不必要的第三方polyfill

作为一个例子,一个很典型的情况是:在一个网站上看到多个具有相同功能的Promise polyfill副本,例如你只加载了一个来自core-js的Promise polyfill,但是一些依赖项会自己加载Promise polyfill,例如来自另一个core-js副本,或是来自es6-promise、promise-polyfill、es6-promise-polyfill、native-promise-only等等。然而,对于这些ES6 Promise,core-js已经完全实现了,而且大多数浏览器都不需要对Promise进行polyfill。由于这种重复,使得捆绑包中所有polyfill的大小会膨胀到几兆字节。

译者注:Promise是一种用于处理异步操作的对象,它可以让异步的代码变得更加易于阅读和维护。

这可能不是一个很好的例证,其他例子可能会更好,但既然上面我们开始谈论彭博网站,让我们再看看这个网站。我们无法访问源代码,但是我们有像bundlescanner.com这样很棒的工具(我希望彭博团队能尽快修复它,那时数据就不会是这样了)。

从实践上讲,这种分析不是一件简单的工作,该工具只能检测到大约一半的库代码。然而,除了450K字节的core-js外,我们还看到数百K字节的其他polyfill——许多es6-promise、promise-polyfill、whatwg-fetch的副本(由于前面所述的原因,core-js仍然没有polyfill它)、string.prototype.codepointat、object-assign(这是ponyfill,下一节会讲到它们)、array-find-index等。

但是有多少polyfill没有检测到?这个网站加载的所有polyfill的大小是多少?看起来有几兆字节。然而,即使是非常旧的浏览器,最多一百K字节就足够了......这种情况并不特别——这太常见了。

由于许多polyfill仅包含core-js功能的子集,在@core-js/compat的范围内,我们能通过收集数据,判断一个第三方polyfill模块是否必要,如果此功能包含在core-js中,转译器(transpiler)或捆绑器(bundler)插件将删除此模块的导入或将其替换为适当core-js模块的导入。

同样的方法可以应用于摆脱对旧core-js版本的依赖。

纯版本polyfill/ponyfill的全球化

另一个更常见的问题是来自全局(global)和纯(pure)core-js版本的polyfill的重复。纯core-js / babel-runtime旨在用于库的代码中,因此如果你使用全局版本的core-js而且你的依赖项也加载了一些不带全局命名空间污染的core-js副本,那么这是一种正常情况。它们使用不同的内部结构,但它们之间共享类似的代码是有问题的。

我正考虑在转译器或捆绑器插件中解决这个问题,与前一个类似(但当然,更复杂一点)——我们可以将纯版本的导入替换为全局版本导入,并删除目标引擎中不必要的 polyfill。

这也可以应用于那些第三方 ponyfills或过时库,他们所实现的功能在JS标准库中已经可用了。例如,has包的使用可以替换为Object.hasOwn,left-pad替换为String.prototype.padStart,somelodash方法替换为相关的现代内置JS方法等。

服务

在IE11、iOS Safari 14.8以及最新的Firefox中加载相同的polyfill是错误的——在现代浏览器中加载了太多的死代码。一个流行的模式是使用2个捆绑包:为“现代”浏览器加载支持本地模块的 <script type="module"> ,而对于不支持本地模块的过时浏览器,则加载 <script nomodule> (实践中有些困难)。例如,Lighthouse可以检测到一些不需要的polyfill案例,让我们检查一下长期受苦的彭博网站:

Lighthouse检测出所有资源仅约200KB,0.56秒。但该网站包含大约几兆字节的polyfill。Lighthouse只检测出不到一半的应检测内容,但即把它能检测的都检测出来,它也只是所有加载的polyfill的一小部分。其余的在哪里?现代浏览器真的需要它们吗?问题是,本地模块支持的下限太低——在这种情况下,“现代”浏览器仍将需要旧版IE所需的大多数polyfill,以提供稳定的JS功能。因此,部分polyfill显示在“未使用JavaScript”部分中,需要6.41s,另外的部分根本没有显示......

从我开始写core-js的一开始,我就一直在考虑创建一个Web服务,为浏览器提供所需的polyfill服务。

未提供此类服务是core-js落后于另一个项目的唯一方面。polyfill-service就基于这一概念,这是一个很牛的服务。这个项目的主要问题是,服务很牛,但是用了糟糕的polyfill。这个项目只是polyfill了core-js提供的ECMAScript功能的一小部分,大多数polyfill是第三方的,他们并不是为一起工作而设计的,太多polyfill没有正确遵循规范,或者太粗糙,或者使用起来很危险(例如,WeakMap看样子是在一步步实现规范,但缺乏一些特别手段,使其导致内存泄漏和线性访问时间,这就不太好了,还有更多其他问题——它不是在IE11这类引擎中修补、修复和重用本地实现,而是不接受可迭代参数,这使得WeakMap将会被完全替换)。一些优秀的开发人员时不时尝试修复这个问题,但是polyfill本身拥有的时间实在是太少了,因此它还远远达不到可被推荐的地步。

以适当的形式创建这样的服务需要制作和维护许多新组件。我独自一人从事core-js开发,项目没有得到任何公司的必要支持,开发纯粹是建立在热情之上,我需要资金来养活自己和家人,所以我没有时间和其他资源去开发它。然而,在其他任务范围内,我已经制作了一些必需的组件,与一些用户的讨论让我相信,创建一个最大限度简化的服务,然后在你自己的服务器上运行它就足够了。

我们已经拥有了最好的polyfill集、适当的兼容性数据以及可以为目标浏览器创建捆绑包的构建器。前面提到的@core-js/collector可以用来做优化——仅获取所需的模块子集、转译器/捆绑器的插件——删除不必要的polyfill。仅仅是缺少一个用于用户代理规范化的工具和一个将这些组件绑定在一起的服务,让我们称它为@core-js/service。

在一个完美的世界里,它应该是什么样子?

  • 捆绑你的项目。捆绑器端的插件会从你的依赖项中删除所有polyfill导入(包括第三方的,没有全局污染的)。你的捆绑包将不包含任何polyfill。
  • 运行@core-js/service。当你运行它时,@core-js/collector会检查你的所有前端代码库、所有入口点,包括依赖项,并收集所有必需的polyfill的列表。
  • 用户加载一个页面,并向服务请求polyfill捆绑包。服务为客户端提供了一个为目标浏览器编译的捆绑包,其中包含所需的polyfill子集并使用所允许的语法。

这样,使用这种工具,现代浏览器如果自身够用,将根本不加载polyfill,旧浏览器将只加载所需的优化后的polyfill。


以上大部分内容都是关于如何将发送给客户端的polyfill最小化——这些只是core-js范围内想要实现东西的一小部分,如果你明白做到这一点需要大量的工作,而且它可以显著改善Web开发,那就足够了。它是否会真的被做出来,以及它是FOSS的还是商业的,取决于你。

结论

这是我最后一次尝试将core-js保留为具有适当质量和功能水平的免费开源项目。这是最后一次传递信息:在开源的另一边有真实的人,他有家庭需要养活,有问题需要解决。

如果你或你的公司以这样或那样的方式使用core-js,并且对你的供应链质量感兴趣,请支持本项目:

如果你能在Web标准和开源方面提供一份好工作,请联系我。


请随时在此issue帖子中添加评论:

Denis Pushkarev,2023年2月14日

翻译|卫剑钒 (微信公众号:man-mind)