Understanding the behavior of .call and .apply in Javascript

I read from the MDN docs that the first argument of .apply and .call is the "this" object, but

var stringToEval = "console.log(this)";
eval.call(null, stringToEval);
eval.apply(null, [stringToEval]);

in the code above both lines end up logging the Window object (in a browser). Shouldn't it log null, that is what I passed as the first argument in both methods ?

Upvotes: 3

Views: 1709

Answers (2)

Paul
Paul

Reputation: 141839

From MDN:

The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.

The global object is window.

Edit since it doesn't matter whether you pass in null, undefined, or an object with eval. Inside eval, this will be the same as this right before the eval call.

As per section 15.1.2.1 of ECMA-262 5.1, eval takes in a string and parses it as ECMAscript. If it is valid ECMAscript then it runs it, in the context that it was called from. So in your case you can think of the calls to eval as if they replace themselves with console.log(this) at runtime. With that replacement you end up with the program:

var stringToEval = "console.log(this)";
console.log(this);
console.log(this);

Which makes it clear why it outputs the DOMWindow object twice. You can get around it like this:

var stringToEval = "console.log(this)";
(function(str){ eval(str); }).call({not: "empty"}, stringToEval);
(function(str){ eval(str); }).apply({not: "empty"}, [stringToEval]);

Since when eval replaces itself with console.log(this) it will be in the context of that anonymous function in which this will refer to the object passed in.

See this JSFiddle

Upvotes: 2

Esailija
Esailija

Reputation: 140220

You call eval with explicitly set this to null, this has nothing to do with what the this object will be inside the evaled code, nowhere is it said that eval evaluates the code with this set to the same this that you called eval with. In fact, doing so is calling eval indirectly and results in the eval code being run in global context for modern browsers.

Therefore, you want to test with normal function:

function test() {
    console.log(this);
}

test.call({});
test.apply({});

Upvotes: 0

Related Questions