Bind function without thisArg

# Sultan (5 years ago)

Consider the following example:

var foo = (function(a) { console.assert(this === obj) }).bind(undefined, 1) var obj = {foo: foo}

Calling foo from obj:

obj.foo(1)

Would result in an assertion. How does one go about preserving the this reference of the caller. That is i want to use .bind to only bind "arguments" and not "thisArg".

# Ranando King (5 years ago)

Bind is just a wrapper function provided by the engine. You can always create your own:

function myBind(fn, ...args) {
  let retval = Object.defineProperties(function(...a) {
    console.assert(typeof(fn) == "function");
    return fn(...args, ...a)
  }, {
    name: {
      configurable: true,
      value: fn.name
    },
    length: {
      configurable: true,
      value: fn.length
    },
    prototype: {
      configurable: true,
      writable: true,
      value: fn.prototype
    }
  });
}

This should apply your bound arguments before any arguments supplied by the caller without affecting the context object.

# Ranando King (5 years ago)

oops.... forgot to return retval!

# Jordan Harband (5 years ago)
var foo = function(a) { console.assert(this === obj) };
var obj = { foo() { return foo.apply(this, arguments); } };

?

# Sultan (5 years ago)

The use case was to use bind to avoid closures such that "foo" is attached to an object on demand.

var obj = {} // some place else obj.foo = fn.bind(, 1, 2, 3)

Where the function "fn" relies on this being the "obj" it is attached to, this considering that calling a bound functions with:

fn.call(thisArg)

Doesn't change the thisArg.

# T.J. Crowder (5 years ago)

Although this is easy enough to implement in userland, frankly so was bind. I'd say it comes up often enough to consider. PrototypeJS (remember PrototypeJS?) called it curry.

I'd think the specification changes for it would be minimal as well, basically A) Allowing Empty as boundThis in BoundFunctionCreate and [[BoundThis]] on functions; B) Updating [[Call]] on Bound Functions to use thisArgument when boundThis is Empty; creating a new Function.prototype method (curry would be the obvious -- and almost certainly not websafe -- name) to call BoundFunctionCreate with Empty for boundThis.

But I'd be interested to know how difficult it is for JavaScript engines to implement, since Empty is largely a spec-level concept and it's valid (in strict mode) to use null and undefined as boundThis...

-- T.J. Crowder

# Andrea Giammarchi (5 years ago)

It's not so easy to implement in user land, not because it's difficult to do so:

Function.prototype.bindArgs = (...args) => {
  const fn = this;
  return function (...moar) {
    return fn.apply(this, args.concat(moar));
  };
};

rather because of the bloat it produces once transpiled: bit.ly/2SULtFZ

To avoid bloat and arguments leaking here and there, one must write something like:

function slice() {
  const arr = [];
  for (let {length} = arguments, i = +this; i < length; i++)
    arr.push(arguments[i]);
  return arr;
}

Function.prototype.bindArgs = function () {
  const args = slice.apply(0, arguments);
  return function () {
    const moar = slice.apply(0, arguments);
    return fn.apply(this, args.concat(moar));
  };
};

which is still a hell of a boilerplate compared to a native const ba = fn.bindArgs.apply(fn, arguments); or fn.bindArgs(...arguments) for short.

+1 for having this in core

# Boris Zbarsky (5 years ago)

On 1/14/19 9:47 AM, Andrea Giammarchi wrote:

rather because of the bloat it produces once transpiled:

Hold on.

The transpiling is necessary because of lack of what features in implementations?

And what is the set of implementations that will lack those features but support bindArgs?

(None of that is an argument against having bindArgs in core per se; I'm just trying to understand the transpiling issue here.)

# Andrea Giammarchi (5 years ago)

The transpilation is what everyone publishing Web Apps/pages do these days, it's not needed but most developers don't care, they mostly use whatever bundler default is there.

The main lack of ECMAScript feature is a method that is not based on new syntax, doesn't leak arguments, and transform these into an Array.

My proposed slice.apply(index, arguments) is still not used by transopilers to solve forever in one place the issue so that production code is full of unnecessary boilerplate every single time some dev uses rest parameters for any callback.

If there was a method, not based on new syntax, able to be called through fn.apply(fn, arguments) so that arguments wouldn't leak, and transpilers can use a single polyfill as entry point to provide that ability, no extra boilerplate would be ever need in production code.

I hope what I've said makes sense 👋