0

While I was working with the bind() function, I came across a situation I am currently not aware of. Can someone give me and explanation why this example works like this? Apparently the inline object passed to the bind function is initialised only in the first iteration and then a reference is kept. I was not able to find any documentation about this, if you can point me to the right direction I will be veeeery grateful :-)

class test {

 addLetter(element) {
  console.log('addLetter', this.str);
  this.str += element + ',';
 }

 foo() {
  let arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

  arr.forEach(this.addLetter.bind({
   str: ''
  }));
 }
}

let a = new test();
a.foo();

OUTPUT:
addLetter 
addLetter a,
addLetter a,b,
addLetter a,b,c,
addLetter a,b,c,d,
addLetter a,b,c,d,e,
addLetter a,b,c,d,e,f,
  • Possible duplicate of [How does the "this" keyword work?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – Erazihel Aug 28 '17 at 09:20
  • 1
    Your code can be deconstructed to `let cb = this.addLetter.bind({...}); arr.forEach(cb);`. – Yes, you're only ever creating *one* callback with one bound object. – deceze Aug 28 '17 at 09:22
  • Indeed, my error was considering the forEach as a normal loop, thanks. – Nicola Meneghetti Aug 28 '17 at 11:48

2 Answers2

1

the first argument to .bind() is the context. In this case it is the

{
    str: ''
}

Now, you are referencing it and using its str property within. Now, we know .bind() returns a function. That function happens to be called within .forEach().

This bind generated function receives as its first argument an element (depending on the iteration).

For the first time iteration index is zero and the bind generated function function receives 'a' as its argument.

Statement

this.str += element + ',';

enhances str as '' + 'a'. In the second iteration, the argument is 'b' and that gets appended to 'a,' + 'b' and so on.

Hence you see the result you see. Let me know if this clarifies your question.

tyskr
  • 1,532
  • 9
  • 15
  • why does `bind` pass the same anonymous object in all iterations but does not create an object for each iteration? – Dmitry Aug 28 '17 at 09:34
  • That is a good question. So, what is bound is a reference to an anonymous object and in the loop you happen to only change its `str` property. There is no new object created with each loop iteration that is why the `str` property of the same object continues to grow (through concatenation). – tyskr Aug 28 '17 at 09:39
  • @Dmitry Because `bind` isn't aware of any loops. It only binds the object once, because it's only called once. – deceze Aug 28 '17 at 09:42
  • Ah, now I see. I was perceiving the `forEach` as a normal loop and that confused me. – Dmitry Aug 28 '17 at 09:58
  • Me too, instead of treating it as a callback I was simply considering it as a normal execution inside a loop/ :) – Nicola Meneghetti Aug 28 '17 at 11:33
1

It's easier to see if you separate the argument from the function call. A function call like:

someFunc(<expression>);

is equivalent to:

var tempVar = <expression>;
someFunc(tempVar);

So in your case, it's like this:

var tempVar = this.addLetter.bind({
   str: ''
});
arr.forEach(tempVar);

This makes it clearthat we're only calling bind() once, when we set tempVar. It creates a function that's bound to a specific object. Then forEach calls that function repeatedly. We can further break it down to:

var tempObj = { str: '' };
var tempFun = this.addLetter.bind(tempObj);
arr.forEach(tempFun);

Now it should really be clear why there's just a single object that gets reused each time the function is called.

Barmar
  • 596,455
  • 48
  • 393
  • 495