Nature of JS bound functions and function invocation operator

前端 未结 3 1968
遇见更好的自我
遇见更好的自我 2020-12-11 08:17
var obj = {};

var r1 = (obj[\'toString\'])();
var m1 = obj[\'toString\'];
var r2 = m1();

var r3 = (obj.toString)();
var m2 = obj.toString;
var r4 = m2();


        
相关标签:
3条回答
  • 2020-12-11 08:42

    The meaning of "this" always seems to cause confusion in JavaScript, especially in scenarios such as callbacks.

    Modifying your example slightly, as you are using toString() and we can't see its implementation, I've introduced a function that can be seen to depend on the context, that is the meaning of this. The coder is expecting to access the object attribute x, but he gets surprised when the function is unbound.

    window.x = "some global value";
    var obj = { 
        wibble : function() { console.log("wibble " + this.x); }, 
        x : 7
    };
    
    obj.wibble();
    var f = obj.wibble;
    f();
    

    This results in:

    wibble 7 
    wibble some global value
    

    That is when we explictly specify a context in the form obj.wibble() or indeed obj["wibble"], this behaves as most developers expect.

    However when we call the "naked" function, we are effectively getting a default context, and in my example I happen to have a value for x in that context and so get that value, which happens to be a string.

    0 讨论(0)
  • 2020-12-11 08:49

    This is actually a "special" behavior of the grouping operator (...):

    1. Return the result of evaluating Expression. This may be of type Reference.

    NOTE This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions.

    So, this operator specifically does not call GetValue and thus does not return the function object itself but rather the whole reference, so that operations which expect a reference still work.


    A Reference is basically an encapsulation of a value with an optional "base value" which is the object in case of a property access.

    0 讨论(0)
  • 2020-12-11 08:53

    Turns out that function invocation operator looks back on what is the context. I would expect operator to not know where operands come from.

    In fact, it does know.

    In the EcmaScript specification this is described by the property accessor operators (and a few similar operations) to return a "Reference object" that holds exactly this information: the context on which the property was accessed. Any "normal" operations will usually just get the reference's value - including the assignment operators, which do dissolve the reference in your case.

    The call operator uses this to make method calls special:

    1. Let ref be the result of evaluating MemberExpression. which may return a Reference
    2. Let func be GetValue(ref). which fetches the actual function object - you see this operation a lot in the spec
    3. fetch arguments, do some type assertions
    4. If Type(ref) is Reference, then
      • If IsPropertyReference(ref) is true, then
        Let thisValue be GetBase(ref). <- here the method context is fetched
      • Else, the base of ref is an Environment Record
        which basically describes variables inside a with statement
    5. Else, Type(ref) is not Reference.
      Let thisValue be undefined.
    0 讨论(0)
提交回复
热议问题