https://github.com/tc39/proposal-extensions
Extensions proposal for ECMAScript
https://github.com/tc39/proposal-extensions
Last synced: 9 months ago
JSON representation
Extensions proposal for ECMAScript
- Host: GitHub
- URL: https://github.com/tc39/proposal-extensions
- Owner: tc39
- License: mit
- Created: 2019-11-04T08:15:12.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-18T06:45:39.000Z (almost 3 years ago)
- Last Synced: 2025-04-15T11:07:51.091Z (9 months ago)
- Language: HTML
- Homepage:
- Size: 82 KB
- Stars: 160
- Watchers: 38
- Forks: 6
- Open Issues: 14
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Extensions and `::` operator
## Proposal status
This is an ECMAScript (JavaScript) proposal in stage 1.
Note: The proposal could be seen as the reshape of the "virtual method" part of old [bind operator proposal](https://github.com/tc39/proposal-bind-operator), see https://github.com/tc39/proposal-bind-operator/issues/56.
## Simple examples
### Example of ad-hoc extension methods and accessors
```js
// define two extension methods
const ::toArray = function () { return [...this] }
const ::toSet = function () { return new Set(this) }
// define a extension accessor
const ::allDivs = {
get() { return this.querySelectorAll('div') }
}
// reuse built-in prototype methods and accessors
const ::flatMap = Array.prototype.flatMap
const ::size = Object.getOwnPropertyDescriptor(Set.prototype, 'size')
// Use extension methods and accesors to calculate
// the count of all classes of div element.
let classCount = document::allDivs
::flatMap(e => e.classList::toArray())
::toSet()::size
```
roughly equals to:
```js
// define two extension methods
const $toArray = function () { return [...this] }
const $toSet = function () { return new Set(this) }
// define a extension accessor
const $allDivs = {
get() { return this.querySelectorAll('div') }
}
// reuse built-in prototype method and accessor
const $flatMap = Array.prototype.flatMap
const $size = Object.getOwnPropertyDescriptor(Set.prototype, 'size')
// Use extension methods and accesors to calculate
// the count of all classes of div element.
let $
$ = $allDivs.get.call(document)
$ = $flatMap.call($, e => $toArray.call(e.classList))
$ = $toSet.call($)
$ = $size.get.call($)
let classCount = $
```
### Example of using constructors or namespace object as extensions
```js
// util.js
export const toArray = iterable => [...iterable]
export const toSet = iterable => new Set(iterable)
```
```js
import * as util from './util.js'
const ::allDivs = {
get() { return this.querySelectorAll('div') }
}
let classCount = document::allDivs
::Array:flatMap(
e => e.classList::util:toArray())
::util:toSet()
::Set:size
```
roughly equals to:
```js
import * as util from './util.js'
const $allDivs = {
get() { return this.querySelectorAll('div') }
}
let $
$ = $allDivs.get.call(document)
$ = Array.prototype.flatMap.call($,
e => util.toArray(e.classList))
$ = util.toSet($)
$ = Object.getOwnPropertyDescriptor(Set.prototype, 'size').get.call($)
let classCount = $
```
## Changes of the old bind operator proposal
- keep `obj::foo()` syntax for extension methods
- repurpose `obj::foo` as extension getters and add `obj::foo =` as extension setters
- separate namespace for ad-hoc extension methods and accessors, do not pollute normal binding names
- add `obj::ext:name` syntax
- change operator precedence to same as `.`
- remove `::obj.foo` (use cases can be solved by custom extension + library, or other proposals)
## Other matrials
- [design details](docs/design.md)
- [slide for stage 1](https://johnhax.net/2020/tc39-nov-ext/slide)
- [experimental implementation](experimental)