admin管理员组

文章数量:1417420

Is it possible to create an alternate of Array.forEach that automatically sets the context "this" to be the same context as when the method was invoked?

For example (not working, not sure why):

Array.prototype.each = function(fn) {
    return this.forEach(fn, arguments.callee.caller);
}


function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].each(function() {
        console.log(this.myVar); // logs 'myVar'
    });
}

Is it possible to create an alternate of Array.forEach that automatically sets the context "this" to be the same context as when the method was invoked?

For example (not working, not sure why):

Array.prototype.each = function(fn) {
    return this.forEach(fn, arguments.callee.caller);
}


function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].each(function() {
        console.log(this.myVar); // logs 'myVar'
    });
}
Share Improve this question asked Jun 5, 2014 at 4:43 Kirk OuimetKirk Ouimet 28.4k44 gold badges130 silver badges183 bronze badges 5
  • Why would you want to do this? Seems like an XY problem to me... – elclanrs Commented Jun 5, 2014 at 4:51
  • So I don't have to remember to bind or pass "this" everytime I loop over an array – Kirk Ouimet Commented Jun 5, 2014 at 18:41
  • 1 The answer is clearly "no", a JavaScript function cannot determine the value of this in the caller. That's why Function.bind() and arrow functions exist. – Wladimir Palant Commented Jul 17, 2014 at 10:15
  • @WladimirPalant you should make that an answer and I'll accept it – Kirk Ouimet Commented Jul 24, 2014 at 3:23
  • @Kirk: There are plenty of answers already, I'm not going to repeat what other people said. For example, thefourtheye said pretty much the same thing as me. – Wladimir Palant Commented Jul 24, 2014 at 5:57
Add a ment  | 

5 Answers 5

Reset to default 9

Array.forEach already takes a context argument as the optional last parameter,

(function() {
    this.myvar = "myvar";

    [1,2,3,4].forEach(function(v) {
        console.log("v:"+v);
        console.log("myvar="+this.myvar);
    }, this);
})();

See MDN forEach

Also, the above examples (if we're not dealing with methods on instances regarding this) work without using bind or the optional context argument for forEach, the following also works correctly:

function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].forEach(function() {
        console.log(this.myVar); // logs 'myVar'
    });
}
myFunction();

Because javascript is functionally scoped, so the anonymous function can access the parent function's scope using this and it logs correctly. this only really bees problematic as a context when dealing with instance methods.

The answer is no, a JavaScript function cannot determine the value of this in the caller.

You can bind the function passed with the current object, like this

function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].forEach(function() {
        console.log(this.myVar); // logs 'myVar'
    }.bind(this));
}

In ECMA Script 6, you can use an Arrow function, like this

[1,2,3].forEach(() => {
    console.log(this.myVar); // logs 'myVar'
});

An alternative to messing with the this variable when passing around callbacks, you could always just assign this to a new variable so child scoped functions can access it:

Array.prototype.each = function(fn) {
    return this.forEach(fn, arguments.callee.caller);
}


function myFunction() {
    var me = this;
    me.myVar = 'myVar';

    [1,2,3].each(function() {
        console.log(me.myVar); // logs 'myVar'
    });
}

now you don't have to remember to pass this as a second parameter

Firstly, it must be pointed out that myFunction is a constructor. Yet, the first letter in the identifier is not capitalized. Please call it MyFunction.

If a constructor is called without the new operator, this is bound to the global object, i.e. window in browsers. This makes the capitalization convention our only way of spotting such mishaps.

The following lines of code demonstrate this:

// After the original code...
myFunction();
console.log(window.myVar); // logs "myVar"

Secondly, to be able to apply functions on any array, instead of changing Array.prototype, consider the following:

var utils = {array: {}};  // utils.array is a container for array utilities.
utils.array.each = function (array, func) {
    var i;
    for (i = 0; i < array.length; i += 1) { func(array[i]); }
};
utils.write = function (s) { 
    console.log(s); // Alternatively, document.write(s);
};
utils.array.each([1, 2, 3], utils.write); // prints 1 2 and 3 (on separate lines)

Notice that we didn't use this and new. They make JavaScript look like Java, apart from that, they rarely serve a useful purpose.

While libraries may modify Object.prototype and Array.prototype, end-developers shouldn't.

Also, we should (ideally) be able to do something like:

  • utils.array.each([1, 2, 3], console.log); or
  • utils.array.each([1, 2, 3], document.write);.

But most browsers won't allow it.

Hope this helped.

If I understand your requirement correctly, then you are trying to override the "this". I think this can help you.

本文标签: javascriptModifying ArrayforEach to Automatically Set the Context to be the CallerStack Overflow