Javascript 'this' value changing, but can't figure out why

后端 未结 6 1868
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-14 08:52

I\'m a total Javascript newb, and I\'m trying to wrap my head around OLN. What I\'m encountering is that, when calling an object method from another method on the same objec

相关标签:
6条回答
  • 2021-01-14 09:01

    First of all, I would encourage you to avoid eval where you don't need it, for example, in your fist function:

    //...
    generateForLevelSkillAndCount : function(level, skill, count) {
        var functionCall = this['generate_' + level + '_' + skill];
        return functionCall(count);
    },
    //...
    

    You can use the bracket notation property accessor instead eval, it's unnecessary in this case.

    Now, I guess you are trying your code on the Chrome's Console, and eval is failing because the console has a bug, when eval is invoked from a FunctionExpression (such as generateForLevelSkillAndCount), the evaluated code uses the Global context for its Variable Environment and Lexical Environment.

    See this answer for more information on this bug.

    Edit: After re-reading your code, the problem happens because you lose the base object reference when you assign the function to your functionCall variable, you can either:

    Invoke the function directly, without using that variable:

    //...
    generateForLevelSkillAndCount : function(level, skill, count) {
        this['generate_' + level + '_' + skill](count);
    },
    //...
    

    Or still use your variable, but persist the this value:

    //...
    generateForLevelSkillAndCount : function(level, skill, count) {
        var functionCall = this['generate_' + level + '_' + skill];
        return functionCall.call(this, count);
    },
    //...
    

    More info on this...

    0 讨论(0)
  • 2021-01-14 09:02

    When you call generate_0_4 dynamically (using an implicit to_string()) it is returned to generateForLevelSkillAndCount as an ad-hoc function. Because it's in Window scope rather than Object scope, it can't reference this, and the internal call fails because this doesn't exist in that context.

    Here's how to see what's happening:

    generate_0_4 : function(count) {
        throw(this);
        return this.generate_generic_dots(count, 3);
    },
    

    With generator.generateForLevelSkillAndCount(0, 4, 1); you get [object Window] or [object DOMWindow].

    With generator.generate_0_4(1); you get what you're expecting (and what works): [object Object] or #<an Object>.

    0 讨论(0)
  • 2021-01-14 09:03

    In JavaScript, the context object (this) is set to the "global object" (window, in browsers) unless the method is accessed as an object property. Therefore:

    var foo = { bar: function() { alert(this.baz); }, baz: 5 };
    var bar = foo.bar;
    var baz = 3;
    
    foo.bar();    // alerts 5, from foo
    foo["bar"](); // alerts 5, from foo
    bar();        // alerts 3, from the global object
    

    Note that all three function calls are to the exact same function!

    So, in your code, you're assigning the desired method to functionCall and calling it directly, which causes the function to use window as its context object. There are two ways around this: access the method as an object property or use .call() or .apply():

    function generateForLevelSkillAndCount1(level, skill, count) {
        return this['generate_' + level + '_' + skill](count);
    }
    
    function generateForLevelSkillAndCount2(level, skill, count) {
        var functionCall = this['generate_' + level + '_' + skill];
        return functionCall.call(this, count);
    }
    
    0 讨论(0)
  • 2021-01-14 09:16

    I'm as baffled as you are, but have you considered checking out what happens if you call generate_0_4 explicitly, instead of parsing it through eval()?

    0 讨论(0)
  • 2021-01-14 09:19

    You can control the execution context of the method call by using call():

    var generator = {
      generateForLevelSkillAndCount : function(level, skill, count) {
        return this['generate_' + level + '_' + skill].call(this, count);
      },
      generate_0_4 : function(count) {
        return this.generate_generic_dots.call(this, count, 3);
      },
      generate_generic_dots : function(count, maxDots) {
        /* do cool stuff and return it */
      }
    };
    
    0 讨论(0)
  • 2021-01-14 09:26

    This is a feature of Javascript: the value of this will depend on the object from which the function was called, not where it was defined. (Which makes sense when functions are first-class objects themselves.) this in most other contexts refers to the window object.

    There are two common workarounds, using a wrapper function:

    function bind(func, obj) {
        return function() {
            func.apply(obj, arguments);
        }
    }
    

    or using a closure:

    var self = this;
    function generate_blah() {
        // use self instead of this here
    }
    

    In your case, though, simply replacing

    var functionCall = this['generate_' + level + '_' + skill];
    return functionCall(count);
    

    with

    this['generate_' + level + '_' + skill](count);
    

    would do the trick.

    0 讨论(0)
提交回复
热议问题