Search code examples
javascriptrequire

Function.apply(x)/.bind(x) results in `this` being `undefined`


I'm working on getting https://github.com/donmccurdy/expression-eval to properly support expressions containing this.

The module defines a function:

function evaluate ( node, context ) {
  ...
}

It exports it as eval:

module.exports = {
  parse: jsep,
  eval: evaluate,
  compile: compile
};

In my code, I define a local context for this and call expr.eval:

const expr = require( 'expression-eval' );

function test() {

  console.log( this ); // outputs the right thing

  var context = { baz: 'blah' };
  var ast = expr.parse( 'this.A + baz' );
  var val = expr.eval( ast, context );

  console.log( val ); // outputs "undefinedbaz"

}
test.apply( { A: 'aay', B: 'bee } );

Inside of evaluate() I inserted console.log( this ). Initially, it was the global object. I added 'use strict'; and it changed to undefined.

I've tried everything I can think of to get the value of this to have the right value inside of the evaluate function:

var fn = expr.eval.bind( this );
fn( ast, context );

and

expr.eval.apply( this, [ ast, context ] );

Nothing works. It's almost as if require is doing something BAD which is breaking the ability to use .apply

How do I fix this?


Solution

  • It turns out that in JavaScript, whenever you call a function (as opposed to an object method), unless you use .bind(), .call(), or .apply(), the value of this is always lost. Who knew?

    The solutions include:

    1) in your recursive function, recurse using .call()

    function silly(i) {
      if ( i < 1 ) // do something with this
      else return silly.call( this, i-1 );
    }
    

    2) wrap your function and save this for reference

    function silly(i) {
      var localThis = this;
      function s(i) {
        if ( i < 1 ) // do something with localThis
        else return s( i-1 );
      }
      return s(i);
    }