8

I'm not asking how to use it. I know how to use it.

But when apply invokes the function that called it, how exactly does it pass an array of arguments into a function that was not written to take an array for an argument? Does it combine its given arguments array with the called functions "arguments"?

I looked at the latest ECMAscript specifications for .apply and .call, but I didn't really see anything about underlying logic.

Any explanation would be welcome. I'm new to JavaScript, and want to better understand what's going on under the hood. I'm currently trying to recreate some of the basic functions on my own, and this one is giving me a lot of trouble.

8
  • 1
    It is under the hood. It is implemented outside JavaScript; and JavaScript interpreter can do whatever it wants. You cannot implement apply without apply (barring eval siliness), as it is the only thing in JavaScript that provides this functionality. You might as well ask "how does console.log access the console", or "how does setTimeout schedule a timeout"; it's just what it does. Commented Jan 15, 2016 at 1:40
  • I think it depends on each engine. Commented Jan 15, 2016 at 1:40
  • Thanks @Amadan, that was exactly what I wanted to know. I'd argue it's different than something like console.log though. That is API based. The window of many environments has a console namespace. Apply doesn't have appear to have anything to do with outside environments. Though maybe I'm wrong there. Commented Jan 15, 2016 at 1:46
  • Whether it is "outside environment" or not, it is API: Function API, to be exact. The same way console.log does something to a console (i.e. write to it), Array.prototype.push.apply does something to Array.prototype.push (i.e. invoke it). Commented Jan 15, 2016 at 1:48
  • @Amadan, never thought of it like that. Huh. I did some googling after your comment. I didn't realize how much JavaScript was engine dependent. I didn't even realize there were such different engines. I now understand why people sometimes talk about ECMAScript requiring one thing, and "JavaScript" doing something different. Puts JS in a whole new light for me. So to know "how" apply works, I'd have to look at what a specific engine decided to do for it, and that wouldn't even be in JS, that would be in C/C++ or whatever? Commented Jan 15, 2016 at 1:57

3 Answers 3

5

From the spec.

We have to take the argArray and create what will be the arguments object pseudo array.

Essentially

Function.prototype.apply = function apply (thisArg, argArray) {
    var len = argArray.length;
    var n = ToUint32(len);
    var argList = []; // where this is a List not an array.
    var index = 0;
    var indexName;
    var nextArg;
    while (index < len) {
        indexName = ToString(index);
        nextArg = argArray[indexName];
        argList.push(nextArg);
        index++;
    }
    return this['[[Call]]'](func, thisArg, argList); // ignore the syntax however
                                                     // This is the line where the function
                                                     // will be called and provide 
                                                     // the thisArg and thisArray
}

I omitted some of the type checking that happens but this is essentially pretty close to what the spec dictates as how Function.prototype.apply is implemented. We craft our own object and build up the argList prior to calling the function.

Its important to note. that the internal method named [[Call]] is different than Function.prototype.call.

Sign up to request clarification or add additional context in comments.

2 Comments

And following the spec, List is an implementation specific type, right? It's not part of the JS I can use, it's a type under the hood used to store arguments? I think this is the answer. The specs do indeed say how apply is run. You wrote them out really clearly too, thank you.
Yes all the stuff wrapped in double square braces [[]] are implementation specific. And in this case the List is as well. See the spec about it es5.github.io/#x8.8
2

how exactly does it pass an array of arguments into a function that was not written to take an array for an argument

You are misunderstanding how this works. apply does not pass an array of arguments to the object. It takes an array and then uses that to dynamically build a function call, similarly to what you could do with an eval statement (but it does it natively).

For example, an eval statement could work like this:

function buildFromArray(funcName, arrayOfArgs)
{
    var functionCall = funcName + "(";

    for ( var i = 0; i < arrayOfArgs.length; i++ )
    {
        //Very simplified, only allows for string args
        functionCall += "\"" + arrayOfArgs + "\"";

        if ( i < arrayOfArgs.length - 1 ) functionCall += ",";
    }

    functionCall += ")";

    //Now we run the compiled call which will be something like:
    //myFunction("one","two")
    eval( functionCall );
}

buildFromArray( "myFunction", [ "one", "two" ] );

This is very simplified, but you can see how an array is never passed to the function myFunction.

Comments

0

Function.prototype.apply is implementation-specific: it is implemented in the JavaScript engine itself. There is really no way to replicate it, as when you call it it uses the C* API to tell the engine to call the function you want to call. User code can't manipulate the internal API, so there's no way to implement Function.prototype.apply without native code.

"Source code" of <code>Function.prototype.apply</code>
"Source code" of Function.prototype.apply according to Function.prototype.toString in V8

* Assuming a JS engine written in C. Replace with whatever language <insert JS engine here> is written in.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.