The pseudo-array of arguments

Lecture




  1. Access to "superfluous" arguments
    1. Example of use: copy(dst, src1,...)
  2. arguments.callee and arguments.callee.caller
    1. arguments.callee
    2. arguments.callee.caller
    3. Why is callee and caller out of date?

In JavaScript, any function can be called with an arbitrary number of arguments.

For example:

1 function go(a,b) {
2    alert( "a=" +a+ ", b=" +b);
3 }
4
5 go(1); // a=1, b=undefined
6 go(1,2); // a=1, b=2
7 go(1,2,3); // a=1, b=2, третий аргумент не вызовет ошибку

There is no "overload" of functions in JavaScript

In some languages, the programmer can create two functions with the same name, but a different set of arguments, and when called, the interpreter chooses the necessary one:

01 function log(a) {
02    ...
03 }
04
05 function log(a,b,c) {
06    ...
07 }
08
09 log(a); // вызовется первая функция
10 log(a,b,c); // вызовется вторая функция

This is called "function polymorphism" or "function overloading." There is nothing like this in JavaScript.

There can be only one function named log , which is called with any arguments. And already inside she can look, with what is caused and to work in different ways.

In the example above, the second log declaration will simply override the first.

Access to "superfluous" arguments

How to get argument values ​​that are not in the parameter list?

They are accessed through the “pseudo-array” of arguments.

It contains a list of arguments by number: arguments[0] , arguments[1] ..., as well as the length property.

For example, we list all the arguments:

1 function sayHi() {
2    for ( var i=0; i<arguments.length; i++) {
3      alert( "Привет, " + arguments[i]);
4    }
5 }
6  
7 sayHi( "Винни" , "Пятачок" ); // 'Привет, Винни', 'Привет, Пятачок'

All parameters are in arguments , even if they are listed. The code above would also work if the function is declared sayHi(a,b,c) .

The relationship between arguments and parameters

In the old JavaScript standard, the pseudo-array of arguments and parameter variables refer to the same values.

As a result, changing the arguments affects the parameters and vice versa.

For example:

1 function f(x) {
2    arguments[0] = 5; // меняет переменную x
3    alert(x); // 5
4 }
5
6 f(1);

On the contrary:

1 function f(x) {
2    x = 5;
3    alert(arguments[0]); // 5, обновленный x
4 }
5
6 f(1);

In the modern edition of the standard, this behavior is changed. Arguments are separated from local variables:

1 function f(x) {
2    "use strict" ; // для браузеров с поддержкой строгого режима
3
4    arguments[0] = 5;
5    alert(x); // не 5, а 1! Переменная "отвязана" от arguments
6 }
7
8 f(1);

If you do not use strict mode, then so that the variables do not change “unexpectedly”, it is recommended that you never change the arguments .

A common mistake for newbies is trying to apply Array methods to arguments . It's impossible:

1 function sayHi() {
2    var a = arguments.shift(); // ошибка! нет такого метода!
3 }
4
5 sayHi(1);

The fact is that arguments is not an Array .

In reality, this is an ordinary object, just the keys are numeric and there is length . At this similarity ends. He does not have any special methods, and he also does not support array methods.

However, no one bothers to make an ordinary array of arguments :

1 var args = [];
2 for ( var i=0; i<arguments.length; i++) {
3    args[i] = arguments[i];
4 }

Example of use: copy(dst, src1,...)

Sometimes there is a task - to copy properties from one or several others into an existing object.

Let's write for this function copy . It will work with any number of arguments, thanks to the use of arguments .

Syntax:

copy (dst, src1, src2 ...)
Copies properties from src1, src2,... objects into a dst object. Returns the resulting object.

Using:

  • To add properties to the user object:

    1 var user = {
    2    name: "Вася" ,
    3 };
    4
    5 // добавить свойства
    6 copy(user, {
    7    age: 25,
    8    surname: "Петров"
    9 });
    Using copy allows you to reduce the code that would be required to manually copy the properties:
    user.age = ...
    user.surname = ...
    ...
    The user object is written only once, and the overall meaning of the code is clearer.

  • To create a copy of the user object:

    // скопирует все свойства в пустой объект
    var userClone = copy({}, user);
    Such a “clone” of an object can be useful where we want to change its properties, while not touching the original user object. In our implementation, we will copy only the properties of the first level, that is, the nested objects are not processed. However, it can be expanded.

We now turn to the implementation.

The first argument to copy always there, so we specify it in the definition. And we will get the rest from arguments , like this:

1 function copy(dst) {
2    for ( var i=1; i<arguments.length; i++) {
3      var obj = arguments[i];
4      for ( var key in obj) {
5        dst[key] = obj[key];
6      }
7    }
8    return dst;
9 }

If desired, this copying can be implemented recursively.

arguments.callee and arguments.callee.caller

The arguments object not only stores the list of arguments, but also provides access to a number of interesting properties. They are absent in the modern JavaScript standard, but they are often found in the old code.

arguments.callee

The arguments.callee property contains a reference to the function that is currently being executed.

This property is deprecated. The current specification recommends using named functional expressions (NFE).

Browsers can more efficiently optimize code if arguments.callee not used.

However, the arguments.callee property is often more convenient, since the function with it can be renamed as you like and you don’t have to change anything inside. In addition, NFE does not work correctly in IE <9.

For example:

1 function f() {
2    alert( arguments.callee === f); // true
3 }
4
5 f();

Why do you need to do some callee property if you can just use f ? To understand this, consider a slightly different example.

JavaScript has a built-in function setTimeout(func, ms) , which calls func in ms milliseconds, for example:

1 // выведет 1 через 1000 ms (1 секунда)
2 setTimeout( function () { alert(1) }, 1000);

The function that setTimeout calls is declared as Function Expression , without a name.

And what if you want to call yourself again from the function itself? By the name of something you can not apply. For such cases, the arguments.callee invented, which guarantees that the function is addressed to itself from the inside.

That is, the recursive call will look like this:

1 setTimeout(
2    function () {
3      alert(1);
4      arguments.callee(); // вызвать себя
5    },
6    1000
7 )

Arguments can be passed to arguments.callee() in the same way as a normal function.

An example with factorial:

1 // factorial(n) = n*factorial(n-1)
2 var factorial = function (n) {
3    return n==1 ? 1 : n* arguments.callee(n-1) ; n==1 ? 1 : n* arguments.callee(n-1) ;
4 }
The factorial function does not use its name inside, so recursive calls will go correctly, even if the function has "moved" to another variable.

1 // factorial(n) = n*factorial(n-1)
2 var factorial = function (n) {
3    return n==1 ? 1 : n* arguments.callee(n-1) ; n==1 ? 1 : n* arguments.callee(n-1) ;
4 }
5
6 var g = factorial;
7 factorial = 0; // функция переместилась в переменную g
8
9 alert( g(5) ); // 120, работает!

The recommended alternative to arguments.callee are named functional expressions.

arguments.callee.caller

The arguments.callee.caller property stores a reference to the function that called this .

This property is deprecated, similar to arguments.callee .

There is also a similar arguments.caller property (without callee ). It is not cross browser, do not use it. The arguments.callee.caller property is supported everywhere.

For example:

01 f1();
02
03 function f1() {
04    alert(arguments.callee.caller); // null
05    f2();
06 }
07
08 function f2() {
09    alert(arguments.callee.caller); // f1, функция вызвавшая меня
10 }

In practice, this property is used very rarely, for example, to obtain information about the stack (the current chain of nested calls) for the purpose of error logging. But in modern browsers there are other ways to do this.

Why is callee and caller out of date?

In the modern standard, these properties are declared obsolete and are not recommended to be used. Although de facto, they are used at least because IE to version 9 does not support Named Function Expression as it should.

The reason for abandoning these properties is simple - the interpreter can optimize JavaScript more efficiently. Moreover, for arguments.callee there is a replacement - NFE.

Importance: 5

How does the function distinguish the missing argument from undefined ?

1 function f(x) {
2    // ..ваш код..
3    // выведите 1, если первый аргумент есть, и 0 - если нет
4 }
5
6 f( undefined ); // 1
7 f(); // 0

Decision

You can find the number of actually passed arguments by the value of arguments.length :

1 function f(x) {
2    alert(arguments.length ? 1 : 0);
3 }
4
5 f( undefined );
6 f();

[Open task in new window]

Importance: 5

Write the function sum(...) , which returns the sum of all its arguments:

1 sum() = 0
2 sum(1) = 1
3 sum(1, 2) = 3
4 sum(1, 2 ,3) = 6
5 sum(1, 2, 3, 4) = 10

Decision

01 function sum() {
02    var result = 0;
03
04    for ( var i=0; i<arguments.length; i++) {
05      result += arguments[i];
06    }
07
08    return result;
09 }
10
11 alert( sum() ); // 0
12 alert( sum(1) ); // 1
13 alert( sum(1, 2) ); // 3
14 alert( sum(1, 2, 3) ); // 6
15 alert( sum(1, 2, 3, 4) ); // 10

created: 2014-10-07
updated: 2021-03-13
132773



Rating 9 of 10. count vote: 2
Are you satisfied?:



Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Scripting client side JavaScript, jqvery, BackBone

Terms: Scripting client side JavaScript, jqvery, BackBone