0

Cannot reproduce MDN's example («Using an object in an array-like fashion») with arrow-function-based method.

> let obj = {
... length: 0,
... addEl: el => [].push.call(this, el={}) //default argument
... };

It counts something, but… what? It definitely stores the incrementing value somewhere, but where?

> obj.addEl();
1
> obj.addEl({});
2
> obj
{ length: 0, addEl: [Function: addEl] } // array function «this» problem?

The original variant increments the length the right way, but it also creates new properties. There was nothing about it in the example.

addEl: function (el) { [].push.call(this, el) }
...
// the function in work
> anotherObj.addEl();
> anotherObj.addEl('new');
> anotherObj
{ '0': undefined,
  '1': 'new',
  length: 2,
  addEl: [Function: addEl] }

Is it ok? If so, I guess, it should be called «creating array-like object», meaning not only the length property, but the numeric keys too. Related, already answered question is here.

JulyMorning
  • 511
  • 4
  • 8

2 Answers2

2

The incrementing value is stored in window because arrow functions don’t bind this. Their this value is that of the enclosing scope, which in this case is the global scope. For example:

let obj = {
  length: 0,
  addEl: el => [].push.call(this, el={}) //default argument
};

console.log(obj.addEl());
console.log(window.length);

The window.length is the incrementing value. The reason obj.addEl() returns the incrementing value is because Array#push returns the new length. And if you log window[0], you’ll get the default argument that was pushed to this, which was window:

> window[0]
< {}

The reason why regular function expressions behave differently is because they are bound to the object obj so this refers to obj in a regular function expression opposed to window in an arrow function.

Andrew Li
  • 47,104
  • 10
  • 106
  • 132
  • I've expected this answer (see my other comment here, for the answer by @marvel308). But why does the function from the original example adds new properties to the object? Is that correct and why? – JulyMorning Oct 08 '17 at 20:14
  • @JulyMorning The very bottom of my answer explains this. Yes that is correct behavior because when you use a regular function expression, `this` is explicitly bound to the calling object. So when you do `obj.addEl()`, `obj` is the `this` value in a regular function expression. Thus, a new numeric property is added to `obj` and the length is increased. – Andrew Li Oct 08 '17 at 20:16
  • so, I shouldn't use arrow functions as methods, right? – JulyMorning Oct 08 '17 at 20:18
  • 1
    @JulyMorning You shouldn’t. The term ‘method’ specifically applies to regular function expressions because regular function expressions actually *bind `this`* thus are bound to objects, which is the definition of a method. (aside from class property arrow functions in ES2017/18-ish). – Andrew Li Oct 08 '17 at 20:19
0

It is because you are using arrow functions which does not bind this, using a normal function would give you the result.

addEl: el => [].push.call(this, el={})

this in your case refers to the global window object

marvel308
  • 9,593
  • 1
  • 16
  • 31
  • explain the downvote – marvel308 Oct 08 '17 at 20:07
  • I didn't do it. However, I've suspected that the problem is connected with `this` context, and arrow functions don't have it. But still I do not know why it's not bound to the object itself. What does this function count? Where is that value? – JulyMorning Oct 08 '17 at 20:10
  • 1
    it is incrementing windows.length since it calls to increase the value of this, which currently is windows. If you console.log(windows.length) you will see the value – marvel308 Oct 08 '17 at 20:14