Indirect Function Call in JavaScript

Indirect function call in JavaScript

You are just using The Comma Operator.

This operator only evaluates its operands from left to right, and returns the value from the second one, for example:

(0, 1); // 1
('foo', 'bar'); // 'bar'

In the context of calling a function, the evaluation of the operand will simply get a value, not a reference, this causes that the this value inside the invoked function point to the global object (or it will be undefined in the new ECMAScript 5 Strict Mode).

For example:

var foo = 'global.foo';

var obj = {
foo: 'obj.foo',
method: function () {
return this.foo;
}
};

obj.method(); // "obj.foo"
(1, obj.method)(); // "global.foo"

As you can see, the first call, which is a direct call, the this value inside the method will properly refer to obj (returning "obj.foo"), the second call, the evaluation made by the comma operator will make the this value to point to the global object (yielding "global.foo").

That pattern has been getting quite popular these days, to make indirect calls to eval, this can be useful under ES5 strict mode, to get a reference to the global object, for example, (imagine you are in a non-browser environment, window is not available):

(function () {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
})();

In the above code, the inner anonymous function will execute within a strict mode code unit, that will result on having the this value as undefined.

The || operator will now take the second operand, the eval call, which is an indirect call, and it will evaluate the code on the global lexical and variable environment.

But personally, in this case, under strict mode I prefer using the Function constructor to get the global object:

(function () {
"use strict";
var global = Function('return this')();
})();

Functions that are created with the Function constructor are strict only if they start with a Use Strict Directive, they don't "inherit" the strictness of the current context as Function Declarations or Function Expressions do.

Indirect references to functions in javascript

The final line is passing along the reference to the foo function and then executing in the global scope, just as you see. It is equivalent to this

var f = p.foo = o.foo;
f();

JavaScript: Passing parameters in to an indirect, anonymous function

Maybe I'm not understanding your question correctly, but it seems like you want to assign a function to pokerObjects.getPokerCard which would allow you to later call pokerObjects.getPokerCard(f, s) which returns the object with getFace, getSuit, and to_s.

pokerObjects.getPokerCard = function(f, s) {
// These are immutable, so hide them in the function.
var face = (typeof f === 'number' ? pokerObjects.faces[f] : f);
var suit = (typeof s === 'number' ? pokerObjects.suits[s] : s);

return {
getFace: function() { return face; },
getSuit: function() { return suit; },
to_s: function() { return face + " of " + suit; }
};
};

This accomplishes the same thing, while still ensuring that face and suit remain hidden. They are scoped variables within the function.

Indirect eval call in strict mode

tl;dr

The second (0, eval)('var a = 1;'); case is in fact not a direct call.

You can see this more prevalently in:

(function(){ "use strict"
var x = eval;
x("var y = 10"); // look at me all indirect
window.y;// 10
eval("var y = 11");
window.y;// still 10, direct call in strict mode gets a new context
})();

The issue can be seen in:

If the eval code is strict code, then (me: fix context)

But strict eval code is defined as:

Eval code is strict eval code if it begins with a Directive Prologue that contains a Use Strict Directive or if the call to eval is a direct call.

Since the call is not direct, the eval code is not strict eval code - and the execution is on global scope.


First of all great question.

"Eval Code" is more general than direct or indirect call to eval.

Let's check the exact specification for the eval function

15.1.2.1 eval (x)

When the eval function is called with one argument x, the following steps are taken:

  1. If Type(x) is not String, return x.

  2. Let prog be the ECMAScript code that is the result of parsing x as a Program. If the parse fails, throw a SyntaxError exception (but see also clause 16).

  3. Let evalCtx be the result of establishing a new execution context (10.4.2) for the eval code prog.

  4. Let result be the result of evaluating the program prog.

  5. Exit the running execution context evalCtx, restoring the previous execution context.
    ...

So, let's explore what 10.4.2 tells us, you cited that - in specific let's look at the first clause:

If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then ... Initialise the execution context as if it was a global execution context

So what is a direct call?

A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:

The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".

The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.

So, what's the MemberExpression in both cases?

In eval('var a = 1;'); indeed the result of evaluating it has a reference name eval and calling GetValue resolution on it returns the built in function.

In (0, eval)('var a = 1;'); the result of evaluating the member expression does not have a reference name eval. (It does resolve to the built in function on GetValue though).

What are reference names anyway?

Section 8.7 in the spec tells us:

A Reference is a resolved name binding. A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1). A base value of undefined indicates that the reference could not be resolved to a binding. The referenced name is a String.

This requires us to look into the GetReferencedName:

GetReferencedName(V). Returns the referenced name component of the reference V.

So, while the expression (0,eval) === eval is true, when evaluating the function, this is actually an indirect call because of naming.

May I offer the Function constructor instead :)?

cloud function callable [indirect call] test.wrapped(function) vs [direct call] function.run()

The difference between the direct & indirect (using wrap) call is that the later approach of using wrapped function utilizes the capabilities from Firebase Test SDK for Cloud Functions - "firebase-functions-test". Some of those capabilities are -

  1. Offers methods for constructing custom data or example data within data parameters.

  2. Generation of default eventContextOptions (2nd) parameter to wrapped function in case this parameter is not passed in explicitly.

You may read more around this topic on Cloud Functions public docs for unit testing & corresponding API reference for Firebase Test SDK.

What is the meaning of using a comma expression in a method call, such as `var proc = (0, _postcss2.default)();`

There is a subtle difference, in that the this value is different in default based on the two different calls. Consider the following code:

var _postcss2 = {    default: function() {        return this;    }};
var proc = (0, _postcss2.default)();console.log(proc); // proc === window (or undefined in strict mode)
var proc = _postcss2.default();console.log(proc); // proc === _postcss2

In Javascript / Jquery, is it possible to indirectly call an object method using a callback parameter?

Yes. The methods are keys in a Javascript Object. You can use the methods by name with a dot or within a string in square brackets:

$(selector).keyup()

or

$(selector)['keyup']()

So you could create a function like this:

function foobar(callback){
$("#input")[callback](function(){
//do something
})
}

Although, it seems silly.



Related Topics



Leave a reply



Submit