Seriously, I will ☺

Should I call() or Should I apply()?

Dron Rathore
6 min readFeb 1, 2015

--

This question was always on my mind, but I never tried my hands on really getting into what actually makes the difference between two, or more precisely why these 2 happens to be in Javascript when we can do the same task with any of them? Also the biggest question is why they have a difference in their performance?

For a layman who is in hurry to read this blog, the answer is.

The difference between call and apply can be visualised as, you have a stick and you call your dog’s name and throw that stick towards him and dog grabs it, no hassle, simple task isn’t it? That’s call(). Another way could be, you call the name, let him come to you and then you can give him as many sticks as you want or none at all. Now here sticks refer to the 2nd arguments that you pass to the call() and apply() function i.e. list of arguments

Lets have a look at this small code snippet.

Now some might question about the GC cycles, but trust me you can explicitly call GC by either calling delete somObj which is the worst way of doing it, rather you can reuse the same variable by either assigning null or undefined but you will find an equal variation of time for both call() and apply().

Okay, lets get to the problem, you can try running the benchmarks and you will find out that call() is far more optimised then apply(). You will find similar benchmarks all over the internet but everyone says that “JS internally optimise it” but being a programmer you should be able to answer the question, How? In this blog I am gonna do the same, answering the quest of the solid proof as why they two happen to be in JS and why one is more optimised then another.

First of all we need to understand how Javascript actually works, now as we have learned from internet and our books we might say, its an Interpreted language, true that i.e. No need to compile, generate a binary and then execute it the way we use to do for compiled languages e.g. C, C++, Java, Go, Rust e.t.c., instead the code gets compiled on the fly while you are executing it. For the purpose to answer our question I will be taking IonMonkey, the JS engine built by Mozilla in context.

JS Invoke Steps

So the steps in which a single Functional Block is been executed by JavaScript follows the following steps.

  1. Load the Code in memory
  2. Generate an intermediate binary code(JS OpCode generation)
  3. Interpret that code
  4. Bailout, and initialise environment variables, context, stack and other necessary things required to execute that block.
  5. Execute the block, save the return states onto the stack, call the callee and jump out of Bailout.

Now, the answer we are looking for our question exists in the 2nd step i.e. Javascript OpCode generation.

Javascript might be the most versatile language but it too has set limits for few things, like setTimeout 0(zero) is never exactly 0ms, its a predefined constant value, some JS engines has set its value to 5ms and other have variant implementation of the same, similarly there is one major limit on calling a function that is, number of arguments!(It’s your homework to figure out what’s the exact count). You can’t pass any number of arguments to invoke a function, you might have got a hint here a little bit about the answer but read on there is little more for your surprise.

A typical JS block/Script/Function is executed until the opcode JSOP_RETRVAL, JSOP_RETURN is encountered. So what are OpCodes?

OpCodes

An OpCode is basically generated by JIT(Just-In-Time) compiler of JS Engine, these are symbols which converts the given code into an engine understandable format, an example of a JIT is CrankShaft that v8 uses. So yeah, the code is compiled first, and then interpreted, but don’t worry a JS compiler never throws any error for your script except Syntax Errors. These compilers perform the primary level of optimisation for your code, it converts your code into tokens which have specific meanings for the Interpreter to understand, an example of optimisation are Atomic values e.g. accessing the length of an Array or an Object i.e. JSOP_LENGTH, it is by default optimised for faster execution. So our dear apply() and call() have their opcodes too i.e. JSOP_FUNAPPLY and JSOP_FUNCALL both are parts of JS_INVOKE opcode. A JS_INVOKE opcode means that this statement need to be invoked separately out of the current running context.

Now what happens with call() is pretty basic, do you remember the C language’s variable length argument declaration? Something that looked like this.

int someCoolFunction()

Yeah, I know you might be laughing now, because we cracked it, almost!

The Catch

Whenever you invoke the call() function of Function.prototype, where Function could be any JS function(anonymous, named) that call to function by default gets converted to (…) a pretty basic optimised implementation. The interpreter now don’t have to worry to expand the arguments when calling that function instead what it does is just point it to the list of arguments that you provided to someFunction.call(), which are easily available on the stack itself.

A Bailout of call when triggered, picks the first argument as the pointer to the *jscontext for the next bailout(actual execution of the function which is been called) and all other arguments following it which are already available on the stack are directly passed to the function for which the call() is invoked.

Now the only drawback with call() is the number of arguments, whenever you invoke call(), you are passing a predefined length of arguments. You can’t pass arbitrary number of arguments using call and as I said its your homework to figure out that limit. So, when Douglas Crockford and team were developing JS they thought of this use case when the programmer can have an arbitrary length of arguments which they might not know about when they want to invoke a function, so they found a workaround for it, and that is the apply() function. An apply function takes the second parameter as an array of arguments. Now when the Bailout is triggered by the invocation of apply() function, JS Engine picks the second parameter, expand it and apply those to whichever function you have triggered apply on. This task has its own cost which is, traversing the array, picking each element, pushing onto the next calling stack of Bailout one by one and then invoking it.

An apply() function too has the same limit on length of arguments, so don’t think that you can pass the whole list of unique ID’s of Penguins that lives on Antarctica(C’mon atleast for next 5–10 years until we make them reach near to extinction)(:angryface:).

Function.prototype.call() and Function.prototype.apply() both will give you a Stack Range Limit Error if you try to pass a very long number of arguments to a function.

Conclusion

Javascript is the most awesome thing that could happen to the programming world, a freedom to code, a speed of execution and lot more things. The frameworks that are built upon JS have their own individual drawbacks and are often correlated with that of JS(:shrug:) which I think is quite baseless. There is lot about JS, internal specs, performance metrics, that I will keep on exploring and sharing with you all, every week, I would rather expand my studies to other languages too provided they have an Open Source Code available on Git(C’mon give me some break!). You can comment and let me know about this post and what you will love to read about in my next upcoming posts.

With ❤ ! Cheers!

--

--

Dron Rathore

A poet, a programmer, Software Engineer @ Uber, a learner