2

I was reading this page just to brush up on .apply syntax, and I realized I had a gap in my JS knowledge:

You can change the value of this in a function by using .bind and also .apply (I assume .call follows same rules as .apply so I'll not talk about that separately). So I wanted to know, if I use .bind and then call it with .apply, which will take precedence?

So I just yoinked the example from w3schools and modified it:

var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName: "John",
  lastName: "Doe"
}

var person2 = {
  firstName: "Mary",
  lastName: "Anne"
}

fn = person.fullName.bind(person2);

console.log(fn.apply(person1, ["Oslo", "Norway"]));

So if it prints Mary Anne, the value of this given by .bind takes precedence over the value given by .apply.

And it did! Marry Anne was printed. So that leaves me wondering if there are any other rules regarding this that I don't quite understand. For example, can you rebind after calling .bind?

Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
TKoL
  • 13,158
  • 3
  • 39
  • 73
  • 1
    Seems pretty broad. But yes, `.call` and `.apply` are identical, except in how they handle the arguments after the *thisArg*. – ziggy wiggy May 24 '19 at 16:36
  • I mean, how do we know what you do and don't understand? We could talk about arrow functions and calling with the `new` operator. But maybe you're already aware of those? – ziggy wiggy May 24 '19 at 16:39
  • What is your question? You have asked 3 of them – Ivar May 24 '19 at 16:40
  • Well... can you rebind an arrow function after declaring it? `fn = () => {}; fn = fn.bind(something)`; -- does that work? – TKoL May 24 '19 at 16:40
  • These are all things you can test or likely find out from docs. Arrow functions have no `this` identifier in their variable environment. – ziggy wiggy May 24 '19 at 16:59

4 Answers4

2

.bind returns a bound function. Bound functions and arrow functions will never change their context again, so you can't re.bind them or .apply another context.

It might become more clear if we assume .bind would be written in JavaScript itself ... then it would be written as:

  function bind(context, ...args) {
    const fn = this;
    return function(...args2) {
      // Note: "this" does not get accessed inside this function, so .bind ing it or .apply ing another context doesnt change anything
      return fn.call(context, ...args, ...args2);
    }
 }
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • your helper function is good, but it also pre-binds the context, mine does not, which is what OP is looking for –  May 24 '19 at 16:54
  • @mrCholo not sure if he really needs that. This example was meant to illustrate the observable behaviour of .bind. – Jonas Wilms May 24 '19 at 16:55
  • Many rational people including OP are looking for a way to pass the context later not earlier. I know the solution b/c obviously I had the same question 2 years ago or whatever. –  May 24 '19 at 16:55
  • @MrCholo It's not about what many rational people use. OP asked for an explanation of the behaviour. Writing out what `bind` does in plain JavaScript might help this cause. – 3limin4t0r May 24 '19 at 16:59
  • "most people use" I rarely use bind & apply at all – Jonas Wilms May 24 '19 at 17:00
  • I use closures instead of bind/apply much more often. However bind is more generic, more performant, but less pretty and less explicit though. bind is also harder for type systems like TypeScript to handle. –  May 28 '19 at 19:53
2

Per the specification:

NOTE 2: If func is an arrow function or a bound function then the thisArg will be ignored by the function [[Call]] in step X.

This applies to all three binding functions.

bind creates a bound function which is not a normal function (it is an exotic object) and does not contain a prototype.

https://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply

Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
1

There's nothing preventing a re-bind, except in the way that Function.prototype.bind is implemented. And you're right that once you call bind the this value is fixed and Function.prototype.apply can't override it.

Instead of using Function.prototype.bind, you can use this to bind arguments:

Function.prototype.create = function(){
  const args = arguments;
  const original = this;
  return function() {
    return original.call(this, ...args, ...arguments);
  }
};

however, modifying prototypes can be problematic, so I would avoid and create a helper function. I am sure utility libraries like lodash can do this, or ramda.

You can use it like this:

const fn = function (one, two, three) {
    return this + one + two + three;
};


const boundfn = fn.create(1, 2);
const val = boundfn.call(-6, 3);

console.log({val});

that's right, we passed -6 as the value for this. :)

-6 + 6 = 0

Note: Avoid doing this! Playing with contexts is the wrong approach and definitely not functional. I would avoid passing this around because it's an invisible parameter and "harder to reason about".

  • Yes `Function.prototype.bind` is also currying, but it just pre-binds the `this` value, whereas this technique does not pre-bind `this`. –  May 24 '19 at 16:52
0

.bind creates a new function with added this context and whatever arguments you want to curry and it is not possible to overwrite this with apply, call (unless your bind method is not native) there is an answer for that already https://stackoverflow.com/a/31656748/906265 which mentions ES2015 spec for bind

From MDN

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

.apply and .call allows you to override this value of a function at the time of calling it but will not work for arrow or bound functions as written in ES2015 spec note 2

If func is an arrow function or a bound function then the thisArg will be ignored by the function

Ivar
  • 4,350
  • 2
  • 27
  • 29