3

(Disclaimer: I'm in the process of learning JavaScript) I have an object constructor like so:

var Palette = function() {
    this.colors = ["red", "green", "blue"];
    this.getColorCombinations = function() {
        var combos = [];
        this.colors.forEach(function(a) {
            this.colors.forEach(function(b) {
                if(a !== b) {
                    combos.push([a, b]);
                }
            });
        });
        return combos;
    };
}

var p = new Palette();
alert(JSON.stringify(p.getColorCombinations()));

With the expected output:

[["red","green"],["red","blue"],["green","red"],["green","blue"],["blue","red"],["blue","green"]]

After some research I realize now that this doesn't work because in the inner anonymous functions, "this" points to the global object and not the Palette instance anymore.

My question is, what is the "JavaScript way" to deal with this? I see similar problems solved with Apply, Bind, Call or simply assigning this to a variable, but so far I've not found any examples of which way is consistently best practice for referencing this in inner anonymous functions.

here is the JsFiddle. (I modified it to output to a div for easier text copying)

Blackhawk
  • 5,984
  • 4
  • 27
  • 56
  • Depends on whether or not you transpile to support old browsers. Best solution is fat arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions second best is rossipedia's answer. Any professional js dev will likely be familiar with all of them. – Jared Smith Oct 22 '15 at 19:37
  • …and the third best is `Function.prototype.bind`. – eush77 Oct 22 '15 at 19:39

1 Answers1

5

Pass this as the second parameter to forEach

arr.forEach(callback, thisArg);

Per MDN Documentation:

thisArg:
Optional. Value to use as this when executing callback.

I've updated your fiddle to show it's usage, but the gist is change this call:

this.colors.forEach(function(a) {
    this.colors.forEach(function(b) {
        if(a !== b) {
            combos.push([a, b]);
        }
    });
});

To this:

this.colors.forEach(function(a) {
    this.colors.forEach(function(b) {
        if(a !== b) {
            combos.push([a, b]);
        }
    });
}, this); // <- pass this as second arg

Also of note is that many other Array.prototype methods that accept a callback also support this idiom, including:

  • forEach
  • map
  • every
  • some
  • filter

However, if you just need to specify what this is bound to when calling a function, and that function is not set as a property on the object, then probably the most idiomatic way would be with Function.prototype.call() or Function.prototype.apply().

And if ES6 is available to you, then an arrow function would be even better, as it inherits this from the calling context:

this.colors.forEach(a => {
    this.colors.forEach(b => {
        if(a !== b) {
            combos.push([a, b]);
        }
    });
});
rossipedia
  • 56,800
  • 10
  • 90
  • 93