Qafoo GmbH - passion for software quality ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :Author: Jakob Westhoff :Date: Mon, 01 Jul 2013 08:10:13 +0200 :Revision: 7 :Copyright: All rights reserved A tricky Thing: The ``arguments``-object ======================================== :Description: Explanation of the JavaScript arguments object and related mysteries. :Keywords: JavaScript, arguments, strict mode, caller, callee, array, object, functions :Abstract: Incited by a `tweet `_ regarding JavaScripts ``arguments``-object, I decided to detail this object in this blog post. A lot of people are somewhat confused about the inner workings of JavaScript engines in general. Every now and then most JavaScript developers can't explain why certain things happen and others don't. One of those mysteries fueling the confusion is the ``arguments``-object. This post will help to solve this mystery once and for all. Incited by a `tweet `_ regarding JavaScripts ``arguments``-object, I decided to detail this object in this blog post. A lot of people are somewhat confused about the inner workings of JavaScript engines in general. Every now and then most JavaScript developers can't explain why certain things happen and others don't. One of those mysteries fueling the confusion is the ``arguments``-object. This post will help to solve this mystery once and for all. The ``arguments``-object ------------------------ .. note:: Get `expert knowledge`__ for the **full stack of your web application** architecture and software design. __ /services/consulting.html Some people know it, others might never have gotten in contact with the mysterious object called ``arguments``. It provides dynamic ways of accessing parameters provided to a function during calltime, besides other things. If any function needs to support an arbitrary amount of parameters, the ``arguments`` object comes into play. A mostly well known example of this is the implementation of ``sprintf``. This function takes a formatting string as well as a variable amount of arguments, one for each part of the formatting string. Utilizing all of those informations it creates a formatted string output according to the given directions: :: var sprintf = function(format /*, args... */) { // Format string and output value } var formattedString = sprintf( "Some embedded %s and a number %d", "string", 42 ); >>> "Some embedded string and a number 42" The ``arguments``-object is automatically available inside each invoked function. It can be accessed simply calling it by its name: :: var sprintf = function(format /*, args... */) { // Access the second argument, if it is there alert( arguments[1] ); } The ``sprintf`` function is known from languages like C, where it is part of the standard library. Other languages like PHP do provide counterparts as well. JavaScript unfortunately does not have something like this built in. Therefore it needs to be implemented in the user land. Implementing the ``sprintf`` function exceeds the scope of this blog post. However, if you are interested in how something like this could be accomplished, you are welcome to take a look at an `sprintf implementation `_ I wrote ages ago. It may be old, but it still does the trick. ``arguments`` Array-like Behavior --------------------------------- As it is shown in the initial examples, ``arguments`` can be accessed like an array. It is automatically filled with numeric indices, representing each of the parameters given to the called function. Given a function call like this: :: var formattedString = sprintf( "Some embedded %s and a number %d", "string", 42 ); The ``arguments``-object available inside the ``sprintf`` function would contain the following numerical indices: :: // Pseudo representation of arguments inside the sprintf function call: arguments = { 0: "Some embedded %s and a number %d", 1: "string", 2: 42 } Each of the provided parameters is referenced with a zero-based index from left to right. All given parameters are included, those who have a defined name as well as those who don't. Regarding the example this implies the first string ``"Some embedded %s and a number %d"`` may be accessed using each of the following ways: :: alert(format); alert(arguments[0]); The rest of the provided parameters may only be accessed using the ``arguments`` object, as they haven't been given a proper name: :: alert(arguments[1]); alert(arguments[2]); **Note:** In ECMAScript 6 it will be possible to name and directly access this kind of arbitrary length arguments using `Rest Parameters `_. You may even utilize this feature today using Google's `Traceur Transpiler `_ Where the confusing parts begin ------------------------------- Okay, now we know about the ``arguments`` object. At a first glance this thing seems to be simple enough. It's an array-like structure containing all the given parameters using a zero-based index. Within this simple sentence the first catch is already hidden: ``arguments`` may seem to be an array, but in fact it is something else. ``arguments`` is NO array ~~~~~~~~~~~~~~~~~~~~~~~~~ The ``arguments`` variable may look like an array, but essentially it isn't. The confusion is understandable, though: A data-structure with zero-based numerical indices, which is accessed using the square-bracket notation (``[n]``), surely does feel like an array. In fact, ``arguments`` is a basic JavaScript object. As objects are mostly key-value pairs, there is no reason why an object should not have numerical keys. Unfortunately, this behavior can cause some headaches, as in JavaScript arrays provide a certain functionality like ``concat``, ``slice``, ``splice`` and others. Those methods would be quite useful for the ``arguments`` object as well, but unfortunately are not available on that object, due to the fact that it is no real array. **Note:** In JavaScript every array is essentially an object as well. However, it is an object inheriting from the ``Array.prototype``, which implies that all of the known functionality is available on every *Array*. With ``arguments`` this inheritance connection does not exist. Therefore it is valid to say ``arguments`` is no Array. Calling one of those functions on the object causes a ``TypeError``: :: function sprintf(format /*, args... */) { alert( arguments.slice(1) ); } sprintf("Foo %s Bar", "some string"); >>> TypeError: Object # has no method 'slice' Utilizing array functionality on ``arguments`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As mentioned before, in certain situations the usage of those array functions on the ``arguments`` object could be a time saver. Luckily for us, JavaScript's prototypical inheritance system combined with the fact that JavaScript engines don't do strict type checking, embrace us with everything needed to call the ``slice`` method (or any other array method) on ``arguments``: :: function sprintf(format /*, args... */) { alert( Array.prototype.slice.call(arguments, 1) ); } sprintf("Foo %s Bar", "some string"); >>> ["some string"] What exactly did happen here? First, the ``prototype`` property of the ``Array`` object is accessed. The contents of this property houses all the functionality that is inherited down to each array, which is created using either the square-bracket notation or the ``Arrray`` constructor: :: var squareBrackets = [1,2,3,4]; var arrayCtor = new Array(4); alert( Object.getPrototypeOf(squareBrackets) ); >>> Array.prototype alert( Object.getPrototypeOf(arrayCtor) ); >>> Array.prototype If every method an array inherits is defined on this ``prototype`` property, the ``slice`` function obviously is stored there as well. We may simply access this function by using its name. One possibility would be to simply call the function utilizing the usual calling parenthesis: :: Array.prototype.slice(1); But on which data structure would this method be called on if we did it this way? Remember, under normal circumstances the method is called directly on the array which should be modified: :: var arr = [1,2,3]; alert( arr.slice(1) ); >>> [2,3] Due to this way of calling, the method has access to the array it should modify using the ``this`` keyword. If it is called directly on the ``prototype`` property, the method does not really know what to do. Fortunately, JavaScript allows us to specify the value of the special variable ``this``, while a function called, using the ``call`` or ``apply`` method: :: function f() { alert(this.foo); } var obj = { foo: "Some string" } f.call(obj) >>> "Some string" Using all of those techniques in combination, it is now possible to do what we essentially wanted in the first place: 1. Locate the ``slice`` function, which is usable on arrays 2. Execute this function 3. Let ``this`` be the array-like ``arguments`` objects, we are currently working with :: Array.prototype.slice.call(arguments, 1); Why does this work if ``arguments`` is no real array? It works, because JavaScript does not make further assumptions about the structure or type while processing data as an array. It works using `DuckTyping `_. Everything that looks like an array can be used in conjunction with the array function. Something is supposed to be an array if it provides numeric keys as well as a ``length`` property. As both of these assumptions are true for the ``arguments`` object, everything is in order. References instead of copies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Another mostly confusing fact about the ``arguments`` object is, that parameters are **referenced**, not copied. Therefore changing either the named argument itself, or altering the numeric index on the ``arguments`` object will affect both incarnations the same way. Especially with this tricky constellation it is true that an example says more than a thousand words: :: function f(a) { alert(a); >>> 23 alert(arguments[0]); >>> 23 a = 1000; alert(arguments[0]); >>> 1000 arguments[0] = 42; alert(a) >>> 42 } f(23); This behavior is irritating for most JavaScript developers, as they are not really comfortable with the concept of real references, due to the fact that the language does not support them natively. Nevertheless, after knowing about that weird behavior, using ``arguments`` is quite easily and productively possible. Declared, but not provided arguments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unfortunately, JavaScript's referencing behavior leads to another common pitfall: Declared, but not provided function parameters are not referenced. JavaScript engines are establishing references only to provided arguments, therefore declared but not used arguments cause a problem: :: function f(a, b) { alert(arguments[1]); >>> undefined alert(b); >>> undefined b = 100; alert(arguments[1]); >>> undefined arguments[1] = 23; alert(b); >>> 100 } // The second parameter is intentionally not given f(42); This example demonstrates that index based references on the ``arguments`` object are only created for parameters which are provided while the function is called. Other arguments are available within the function's scope using their declared name (``b``) as an identifier. However, they are not connected to the ``arguments`` object. This behavior can cause major headaches as well as nights of debugging. A best practice which solves half of this problem is to access the ``arguments`` object in a read-only fashion. Therefore one should never change or write to new values to the ``arguments`` object. Nevertheless, this does only solve one part of the problem. In order to fully solve every parameter, named ones as well as anonymous ones need to be accessed read-only. Even though this might seem possible, there are certain situations, where writing to function parameters is a perfectly fine usecase, for instance optional arguments with default values. Essentially, the solution boils down to minimizing the write access to named arguments, while never writing to the ``arguments`` object itself. Strict Mode and the ``arguments`` object ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unfortunately, the attempt to solve the problems with the ``argument`` object may cause even more confusion. Within `ECMAScript 5 Strict Mode `_ the before described referencing behavior is completely removed. Essentially being replaced by using shallow value copies instead of references within the ``arguments`` object. The `Strict Mode `_ has been introduced with the latest finalized specification of the ECMAScript/JavaScript language. In conjunction with certain security related functionality, it does mainly provide automatic checks of common syntactical pitfalls, which were valid JavaScript Syntax before, but should never have made it into the language in the first place: - Different function arguments with the same identifier function f(a, a, b) {/*…*/} - Object literals with multiple properties using the same name var obj = {a: 1, a: 2, b: 3}; - Redefining language constructs NaN = "something else"; - … **Note:** Strict Mode should be enabled in every modern JavaScript project to improve code quality and reduce common syntactical mistakes. Once Strict Mode is enabled, the ``arguments`` object looses all of its referencing magic. Access to numeric indexed parameters is provided through simple shallow copies of the given arguments: :: function f(a) { "use strict"; alert(a); >>> "foobar" alert(arguments[0]); >>> "foobar" alert(a === arguments[0]); >>> false } f("foobar"); Callstack traversal using ``arguments`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Besides numerical indexed access to all provided parameters as well as a ``length`` property for array like access, the ``arguments`` object provides further functionality. A property ``callee`` is defined on every ``arguments`` object, which provides a reference to the called function itself: :: function f() { alert(arguments.callee === f); >>> true } f(); Even though usually this property is not needed by most applications, there are certain use cases for it, like automatic calling of parent methods inside a prototypical *"class based"* structure as well as referencing anonymous functions recursively. Utilizing this feature has however certain major drawbacks as well. First of all, it is possible to use ``callee`` in conjunction with the ``caller`` property on functions to traverse the callstack. This allows JavaScript functions to *escape* from their environment. Especially in situations where user-generated JavaScript code is allowed, this can lead to security issues. The second even more important issue with the ``callee`` property is related to optimization. It is impossible for any optimizer to properly inline any function using ``callee`` for performance reasons, as the engine has to be able to provide a clean reference to the original function context. In worst case scenarios, this may slow down execution speed quite dramatically in relevant situations. In ES5 Strict Mode the usage of ``callee`` as well as ``caller`` is therefore prohibited. Summing everything up --------------------- As you have seen, the ``arguments`` object can be a very useful companion in JavaScript. It is indeed the only way of handling functions with an arbitrary parameter count. Unfortunately, this object is not what you would think it is (an array). Furthermore, it has some surprises in store for you, which you might not encounter anywhere else inside the language (referencing behavior). Nevertheless, once knowing all those nifty little secrets about the ``arguments`` object, it can be used safely as well as productively. Don't be mad at JavaScript for all of the confusion. The language was created in a haste. The changes within Strict Mode clearly show that there are efforts being made to fix up the language. The future of JavaScript might not be as dark as you think. Trackbacks ========== Comments ========