简体   繁体   中英

Accessing object values within anonymous function

I have an object (Thing) that keeps track of some value, as well as a set of conditions:

var Thing = function(currentValue) {
    this.currentValue = currentValue;
    this.conditions = [];
};

Conditions can be added to this list:

Thing.prototype.addCondition = function(condition) {
    this.conditions.push(condition);
}

I want conditions to take the form of some function such that I could do

thing.addCondition(thing.exampleCondition(valueToCheckFor));

and these conditions could all be checked via

Thing.prototype.evaluateConditions = function() {
    this.conditions.forEach(function(condition) {
        if (condition()) {
            //Do something
        }
    });
};

Currently I have one such condition function:

Thing.prototype.exampleCondition = function(value) {
    return function() {
        return this.currentValue === value;
    };
};

This obviously doesn't work - this.currentValue is undefined within the anonymous function. My problem is that I need the value passed into exampleCondition to be evaluated against currentValue's value at the time that evaluateConditions() is invoked - therefore I can't do

Thing.prototype.exampleCondition = function(value) {
    var c = this.currentValue;
    return function() {
        return c === value;
    };
};

I'm kind of a javascript noob, but hopefully you brilliant people can point me in the right direction here.

In javascript, each function is always evaluated against a context, which defines the value for the this .

A function's context could be set explicitly or implicitly at the moment of calling the function. For setting the context implicitly you have to call a function like this:

//z will be the context, in other words, inside method: this = z
a.b.c.d...z.method();

For setting the context explicitly there are two functions in the prototype of the Function object that you can use: apply and call . These two are compatible with every browser so you won't have problems. The thing is that, with these two, you are setting the context each time you call the function so it wouldn't help you with your problem if you use them directly.

You must define a function that always evaluates against the same context every time you call it. For this you could use the bind function, defined in the Function object prototype, the problem is that it is not compatible with old browsers because is a recent addition to ECMA-262, 5th edition. Nevertheless the solution could be bind every condition function to your object in the addCondition function:

Thing.prototype.exampleCondition = function(value) {
    return function() {
        return this.currentValue === value;
    };
};

Thing.prototype.addCondition = function(condition) {
    //The bind function will return a function that will always execute with your object as context
    this.conditions.push(condition.bind(this));
}

For the browser compatibility thing you could try this code . Put it at the beginning of your script and you can be sure that you will have the bind function for every browser.

Another possible solution could be:

Thing.prototype.exampleCondition = function(value) {
    var self = this;
    return function() {
        return self.currentValue === value;
    };
};

The thing is that, in this case, you are not using the context, you are ignoring it inside the returned function. Instead of using the context you are replacing it with some variable defined in the scope. For doing this you are forcing every function defined as a condition function to do that little trick. I think that the first solution is better.

The problem is that this changes inside each function.

Then, inside the function returned by exampleCondition , this won't be your Thing instance, it will be window in non-strict mode and undefined in strict mode.

Therefore, you can do

Thing.prototype.exampleCondition = function(value) {
    var that = this;
    return function() {
        return that.currentValue === value;
    };
};

Or, if you prefer, you can use ES5 bind :

Thing.prototype.exampleCondition = function(value) {
    return (function() {
        return this.currentValue === value;
    }).bind(this);
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM