This proposal introduces a new operator :: which performs function binding and
method extraction.
It is a more detailed description of the bind operator strawman.
Using an iterator library implemented as a module of "virtual methods":
import { map, takeWhile, forEach } from "iterlib";
getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));Using a jquery-like library of virtual methods:
// Create bindings for just the methods that we need
let { find, html } = jake;
// Find all the divs with class="myClass", then get all of the "p"s and
// replace their content.
document.querySelectorAll("div.myClass")::find("p")::html("hahaha");Using method extraction to print the eventual value of a promise to the console:
Promise.resolve(123).then(::console.log);Using method extraction to call an object method when a DOM event occurs:
$(".some-link").on("click", ::view.reset);With the introduction of arrow functions in ECMAScript 6, the need for explicitly
binding closures to the lexical this value has been dramatically reduced, resulting
in a significant increase in language usability. However, there are still two use cases
where explicit this binding or injection is both common and awkward.
Calling a known function with a supplied this argument:
var hasOwnProp = Object.prototype.hasOwnProperty;
var obj = { x: 100 };
hasOwnProp.call(obj, "x");Extracting a method from an object:
Promise.resolve(123).then(console.log.bind(console));This proposal introduces a new operator :: which can be used as syntactic sugar
for these use cases.
In its binary form, the :: operator creates a bound function such that the left
hand side of the operator is bound as the this variable to the target function on
the right hand side.
In its unary prefix form, the :: operator creates a bound function such that
the base of the supplied reference is bound as the this variable to the target
function.
If the bound function is immediately called, and the target function is strict mode, then the bound function itself is not observable and the bind operator can be optimized to an efficient direct function call.
NOTE: If the target function is not strict, then it may use function.callee or
arguments.callee, which would result in an observable difference of behavior.
By providing syntactic sugar for these use cases we will enable a new class of "virtual method" library, which will have usability advantages over the standard adapter patterns in use today.
- Parser: esparse
- Transpiler: esdown
- Demo: esdown REPL
LeftHandSideExpression[Yield] :
NewExpression[?Yield]
CallExpression[?Yield]
BindExpression[?Yield]
BindExpression[Yield] :
LeftHandSideExpression[?Yield] :: NewExpression[?Yield]
:: NewExpression[?Yield]
CallExpression[Yield] :
MemberExpression[?Yield] Arguments[?Yield]
super Arguments[?Yield]
CallExpression[?Yield] Arguments[?Yield]
CallExpression[?Yield] [ Expression[In, ?Yield] ]
CallExpression[?Yield] . IdentifierName
CallExpression[?Yield] TemplateLiteral[?Yield]
BindExpression[?Yield] Arguments[?Yield]
BindExpression :
LeftHandSideExpression :: NewExpression
- Let baseReference be the result of evaluating LeftHandSideExpression.
- Let baseValue be GetValue(baseReference).
- ReturnIfAbrupt(baseValue).
- Let targetReference be the result of evaluating NewExpression.
- Let target be GetValue(targetReference).
- If IsCallable(target) is false, throw a TypeError exception.
- Let F be BoundFunctionCreate(target, baseValue, ()).
- Let targetHasLength be HasOwnProperty(target, "length").
- ReturnIfAbrupt(targetHasLength).
- If targetHasLength is true, then
- Let targetLen be Get(target, "length").
- ReturnIfAbrupt(targetLen).
- If Type(targetLen) is not Number, then let L be 0.
- Else, let L be ToInteger(targetLen).
- Else let L be 0.
- Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
- ReturnIfAbrupt(status).
- Let targetName be Get(target, "name").
- ReturnIfAbrupt(targetName).
- If Type(targetName) is not String, then let targetName be the empty string.
- Let status be SetFunctionName(F, targetName, "bound").
- ReturnIfAbrupt(status).
- Return F.
BindExpression :
:: NewExpression
- Let targetReference be the result of evaluating NewExpression.
- If Type(targetReference) is Reference, then let baseValue be GetBase(targetReference).
- Else let baseValue be undefined.
- Let target be GetValue(targetReference).
- If IsCallable(target) is false, throw a TypeError exception.
- Let F be BoundFunctionCreate(target, baseValue, ()).
- Let targetHasLength be HasOwnProperty(target, "length").
- ReturnIfAbrupt(targetHasLength).
- If targetHasLength is true, then
- Let targetLen be Get(target, "length").
- ReturnIfAbrupt(targetLen).
- If Type(targetLen) is not Number, then let L be 0.
- Else, let L be ToInteger(targetLen).
- Else let L be 0.
- Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
- ReturnIfAbrupt(status).
- Let targetName be Get(target, "name").
- ReturnIfAbrupt(targetName).
- If Type(targetName) is not String, then let targetName be the empty string.
- Let status be SetFunctionName(F, targetName, "bound").
- ReturnIfAbrupt(status).
- Return F.