SRCP
SRCP

Reputation: 242

Why does this function with "this" not work? About "this" and its scope

I know that adding a method to the prototype this way is not the best, but I'm just testing.

Array.prototype.maap = function (transform) {
  let mapped = [];
  for (let element of this) {
        mapped.push(transform(element));
  }
  return mapped;
}

console.log([0, 2, 3].maap(n => n / this.length));

I am getting:

[NaN, Infinity, Infinity]. I think the problem is "this.length".

Upvotes: 2

Views: 87

Answers (3)

Rey Wang
Rey Wang

Reputation: 107

I think the problem is on the arrow function (the param transform), Yes, this.length is the directly related questions, go deeper, it's a problem about arrow function,

An arrow function does not have its own this. The this value of the enclosing lexical scope is used;

Simply put, where the arrow function defined where the this point.

So, for your code, The parameters you passed in is n => n / this.length, and it is defined in function console.log in the environment of window. So the real problem is:

transf = (n) => {
  console.log(this);    // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
  return n / this.length
}
console.log([0, 2, 3].maap(transf));

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370659

this inside an arrow function references the same this in its containing block. Here, the containing block is the top level, where this refers to window, and window.length is 0.

console.log(this === window);
console.log(window.length);

So, your code is equivalent to:

Array.prototype.maap = function(transform) {
  let mapped = [];
  for (let element of this) {
    mapped.push(transform(element));
  }
  return mapped;
}

console.log(this.length);
console.log([0, 2, 3].maap(n => n / 0));

0 / 0 is undefined, while most any other number / 0 is Infinity (or -Infinity).

If you want to emulate Array.prototype.map's behavior with this, the second argument passed to maap should be the this value the callback is invoked with:

Array.prototype.maap = function(transform, thisVal) {
  let mapped = [];
  for (let element of this) {
    mapped.push(transform.call(thisVal, element));
  }
  return mapped;
}

const arr = [0, 2, 3];
console.log(arr.maap(
  function(n){ return n / this.length; },
  arr
));

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385098

You're right, the problem is this.length. The trouble is that it's not in the function! It's in a lambda, whose scope is not that of the array by which it's later invoked. As such, this is not the array, and this.length is a solid 0 (0/0 being NaN, 2/0 being infinity and 3/0 also being infinity).

You can either hardcode the real value 3, or move your logic into the function itself. Or you could have the lambda (actually an "arrow function" in JavaScript) take another parameter: that of the denominator.

Array.prototype.maap = function (transform) {
  let mapped = [];
  for (let element of this) {
        mapped.push(transform(element, this.length));
  }
  return mapped;
}

console.log([0, 2, 3].maap((n, m) => n / m));

Upvotes: 3

Related Questions