Какое свойство функции возвращает массив аргументов

arguments — очень специфическая штука, о которой новички и даже любители знают только то, что это «вроде массив, но какой-то неправильный». На самом деле, у него есть ряд интересных особенностей. Предлагаю в топике пофантазировать на тему TypeHinting, аргументов по-умолчанию и всякого другого.

(function (foo, bar) {
console.log(typeof arguments); // ?

arguments[0] = 42;
console.log(foo); // ?
})(10, 20);

А также покажу интересную идею-библиотеку

function test (foo, bar) {
Args(arguments).defaults(100, 100);

return [foo, bar];
};

test( ); // 100, 100
test(15 ); // 15, 100
test(21, 42); // 21, 42

В первую очередь хотел бы заметить, что множество идей высказанных в топике являются достаточно спорными. Я сам не уверен, что буду ими пользоваться и не советую пользоваться новичкам.

Что такое arguments

Это хэш. Обычный хэш, как var object = {}

(function () { console.log(
typeof arguments, // object
Object.getPrototypeOf(arguments) == Object.prototype // true
) })();

Сделать из него массив просто:

var array = Array.prototype.slice.call(arguments, 0);
// или покороче, но менее производительно:
var array = [].slice.call(arguments, 0);

Мы вызываем метод slice прототипа Array от лица arguments.

Что есть в arguments

arguments.length — количество аргументов, переданных в функцию.

var count = function () {
console.log(arguments.length);
};

count(); // 0
count(first, second); // 2

Не забывайте, что у каждой функции тоже есть свойство length, которое указывает на то, сколько элементов объявлено в её заголовке:

function one (foo) {};
function three (foo, bar, qux) {};

console.log( one.length); // 1
console.log(three.length); // 3

arguments.callee — ссылка на саму функцию.

function foo () {
console.log(arguments.callee === foo); // true
}

Таким образом можно проверить, передано ли правильное количество элементов, или нет:

function test (foo, bar, qux) {
return arguments.callee.length === arguments.length;
}

test(1); // false
test(1,2,3); // true

Аргументы в arguments

В arguments содержится также список переданных аргументов.

function test (foo, bar) {
console.log(foo, bar); // ‘a’, ‘b’
console.log(arguments[0], arguments[1]); // ‘a’, ‘b’
}
test(‘a’, ‘b’);

Теперь к интересному. Многие не знают, что объект arguments — содержит на самом деле ссылки, а не значения, и тесно связан с аргументами:

(function (foo) {
arguments[0] = 42;
console.log(foo); // 42!

foo = 20;
console.log(arguments[0]); // 20
})(5);

При этом связь достаточно крепкая:

function foo (qux) {
change(arguments);
return qux;
};

function change(a) {
a[0] = 42;
}

foo(10); // 42

Что из этого можно получить?

Во многих языках программирования есть «переменные по-умолчанию». К примеру, php:

function ($foo = 30, $bar = ‘test’) {
var_dump($foo);
var_dump($bar);
}

В javascript оно будет выглядеть как-то так:

function (foo, bar) {
if (typeof foo === ‘undefined’) foo = 30;
if (typeof bar === ‘undefined’) bar = ‘test’;

console.log(foo, bar);
}

Зная особенности arguments можно создать красивый интерфейс:

function test(foo, bar) {
Args(arguments).defaults(30, ‘test’);

console.log(foo, bar)
}

test(); // 30, ‘test’

С помощью такого кода:

function Args (args) {
if (this instanceof Args) {
this.args = args;
} else {
// Если создано не через new, а просто вызвана функция, создаем и возвращаем новый объект
return new Args(args);
}
};
Args.prototype = {
defaults: function () {
var defaults = arguments;
for (var i = defaults.length; i–;) {
if (typeof args[i] === ‘undefined’) args[i] = defaults[i];
}
return this;
}
};

Аналогично можно сделать автоматическое приведение типов:

function test(foo) {
Args(arguments)
.defaults(10)
.cast(Number);

console.log(foo)
}

test(‘0100’); // 100

Или Type Hinting:

function test(foo, bar) {
Args(arguments).types(Foo, Bar);

// code
}

test(new Foo(), new Bar());
test(1, 2); // Error

Из интересных идей — сообщение, что все аргументы обязательны:

function test (foo, bar, qux) {
Args(arguments).allRequired();
}

test(1,2,3); // success
test(1,2); // Error: 3 args required, 2 given

Заключение

Все эти идеи и возможности (и даже больше) я оформил в библиотеку — Args.js.
Согласен, что кое-какие вещи (как TypeHinting) не совсем подходят к идеологии языка. В то же время например defaults — очень удобная штука, имхо.
Пока что это прототип и, перед тем как вы будете его использовать — будьте уверены, что оно вам действительно нужно, а не что вы просто стараетесь из прекрасного языка сделать что-то похожее на C#.
Предлагаю обсудить, покритиковать код, найти пару багов и закоммитить несколько строк кода)

Args.js

К сожалению, из-за бага в трёх популярных браузерах(IE, Fx, Opera) я не смог добиться желаемого эффекта, полноценно самое вкусное заработало только в Chrome (ну по крайней мере в node.js работать будет)). Надеюсь, решим эту проблему вместе.

UPD: В комментах выяснили, что таки это бага Хрома, но, зато, какая приятная! Спасибо jamayka

Источник

Объект arguments — это подобный массиву объект, который содержит аргументы, переданные в функцию.

Примечание: Если вы пишите ES6-совместимый код, то лучше использовать rest параметры.

Примечание: “Подобный массиву” означает, что arguments имеет свойство length, а элементы индексируются начиная с нуля. Но при это он не может обращаться к встроенным методам Array, таким как forEach() или map(). Подробнее об этом в §Описании.

The source for this interactive example is stored in a GitHub repository. If you’d like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.

Синтаксис

Описание

Объект arguments — это локальная переменная, доступная внутри любой (нестрелочной) функции. Объект arguments позволяет ссылаться на аргументы функции внутри неё. Он состоит из переданных в функцию аргументов, индексация начинается с 0. Например, если в функцию было передано 3 аргумента, обратиться к ним можно следующим образом:

arguments[0]
arguments[1]
arguments[2]

Аргументам может быть присвоено значение:

arguments[1] = ‘new value’;

Объект arguments не является Array. Он похож на массив, но не обладает ни одним из его свойств, кроме length. Например, у него нет метода pop. Однако он может быть преобразован в обычный массив:

var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

const args = Array.from(arguments);
const args = […arguments];

Использование slice на объекте arguments не позволяет сделать оптимизации в некоторых JavaScript движках (например, V8 — подробнее). Если они важны, можно попробовать вместо этого создать новый массив с аналогичной длиной и заполнить его элементами объекта arguments. Альтернативный вариант — использовать конструктор Array как функцию:

var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));

Объект arguments можно использовать при вызове функции с большим количеством аргументов, чем было предусмотрено в её объявлении. Такой способ удобен для функций, в которые допустимо передавать переменное количество аргументов. Можно воспользоваться arguments.length, чтобы определить количество переданных в функцию аргументов, а затем обработать каждый из них с помощью объекта arguments. Чтобы определить количество параметров функции, описанных в её сигнатуре, можно использовать свойство Function.length.

Использование typeof с объектом arguments

Применение оператора typeof к arguments вернёт ‘object’.

console.log(typeof arguments); // ‘object’

Определение типов аргументов может быть выполнено применением оператора typeof и индексацией.

// выведет тип первого аргумента
console.log(typeof arguments[0]);

Использование оператора расширения для объекта arguments

Как и с обычными массиво-подобными объектами, для преобразования объекта arguments в обычный массив можно использовать метод Array.from() или оператор расширения:

var args = Array.from(arguments);
var args = […arguments];

Свойства

Примеры

Создание функции, соединяющей несколько строк

Данный пример описывает функцию, которая соединяет несколько строк. Для этой функции объявлен только один аргумент, определяющий символ-разделитель соединяемых элементов. Функция определена следующим образом:

function myConcat(separator) {
var args = Array.prototype.slice.call(arguments, 1);
return args.join(separator);
}

Вы можете передать любое количество аргументов в эту функцию. Она создает строку, используя каждый аргумент:

myConcat(“, “, “red”, “orange”, “blue”);

myConcat(“; “, “elephant”, “giraffe”, “lion”, “cheetah”);

myConcat(“. “, “sage”, “basil”, “oregano”, “pepper”, “parsley”);

 Функция, создающая HTML списки

В данном примере приведена функция, которая создает строку с HTML-разметкой для списка. Единственный ее аргумент – строка, определяющая вид списка: если его значение равно “u”, формируется неупорядоченный (маркированный) список, а если “o”, то упорядоченный (нумерованный):

function list(type) {
var result = “<” + type + “l><li>”;
var args = Array.prototype.slice.call(arguments, 1);
result += args.join(“</li><li>”);
result += “</li></” + type + “l>”;

return result;
}

Вы можете использовать любое количество аргументов, а функция добавит каждый элемент в список заданного первым аргументом типа. Например:

var listHTML = list(“u”, “One”, “Two”, “Three”);

Оставшиеся, деструктурированные и параметры по умолчанию

Объект arguments может использоваться совместно с оставшимися параметрами, параметрами по умолчанию или деструктурированными параметрами.

function foo(…args) {
return arguments;
}
foo(1, 2, 3);

Тем не менее, в нестрогих функциях соответствие между их аргументами и объектом arguments существует только в том случае, если функция не содержит никаких оставшихся параметров, параметров по умолчанию или деструктурированных параметров. Например, в функции, приведенной ниже, используется параметр по умолчанию, и в данном случае возвращаемый результат будет равен 10, а не 100:

function bar(a=1) {
arguments[0] = 100;
return a;
}
bar(10);

В следующем примере возвращается 100, поскольку здесь нет оставшихся параметров, параметров по умолчанию или деструктурированных параметров:

function zoo(a) {
arguments[0] = 100;
return a;
}
zoo(10);

На самом деле, если оставшиеся параметры, параметры по умолчанию или деструктурированные параметры не используются, формальные аргументы будут ссылаться на последние значения объекта arguments, при считывании значений формальных аргументов будут считаны последние данные из arguments, а при изменении значений формальных аргументов будет обновлен и объект arguments. Пример приведен в коде ниже:

function func(a, b) {
arguments[0] = 90;
arguments[1] = 99;
console.log(a + ” ” + b);
}

func(1, 2);

или

function func(a, b) {
a = 9;
b = 99;
console.log(arguments[0] + ” ” + arguments[1]);
}

func(3, 4);

Но в случае, когда применяются оставшиеся параметры, параметры по умолчанию или деструктурированные параметры, будет обработано нормальное поведение, как в случае параметров по умолчанию:

function func(a, b, c=9) {
arguments[0] = 99;
arguments[1] = 98;
console.log(a + ” ” + b);
}

func(3, 4);

Спецификации

Поддержка браузерами

BCD tables only load in the browser

Смотрите также

Источник

  • Значения параметров по умолчанию
  • Дополнительный параметр

Параметры функции играют роль локальных переменных в теле функции. При указании параметров функции ключевое слово var или let использовать не нужно, JavaScript объявляет их в качестве локальных переменные автоматически. Параметров может быть указано любое количество:

function foo(a, b, c) { … } // a, b, c – параметры функции

При вызове функции, ей могут передаваться значения, которыми будут инициализированы параметры. Значения, которые передаются при вызове функции называются аргументами. Аргументы, указываются через запятую:

function foo(a, b) {
var sum = a + b;
alert(sum);
}

foo(5, 7); // 12

Когда при вызове функции ей передаётся список аргументов, эти аргументы присваиваются параметрам функции в том порядке, в каком они указаны: первый аргумент присваивается первому параметру, второй аргумент – второму параметру и т. д.

Если число аргументов отличается от числа параметров, то никакой ошибки не происходит. В JavaScript подобная ситуация разрешается следующим образом:

  • Если при вызове функции ей передаётся больше аргументов, чем задано параметров, “лишние” аргументы просто не присваиваются параметрам. Допустим, имеется следующее объявление функции:

    function foo(a, b, c) { // … }

    Если при её вызове указать foo(1, 2, 3, 4), то аргументы 1, 2 и 3 будут присвоены параметрам a, b и c соответственно. В то же время аргумент 4 не будут присвоен ни одному из параметров данной функции.

  • Если функция имеет больше параметров, чем передано ей аргументов, то параметрам без соответствующих аргументов присваивается значение undefined.

Все функции имеют два неявных параметра: arguments и this. Под словом неявный подразумевается, что эти параметры не перечисляются явно в объявлении функции. Эти параметры также неявно инициализируются при вызове функции. К ним можно обращаться в самой функции так же, как и к любым другим явно установленным параметрам.

Значения параметров по умолчанию

Параметры функции можно инициализировать значениями по умолчанию, которые будут использоваться в том случае, если параметры не были инициализированы аргументами при вызове функции:

function foo(greeting = “Привет”, name = “Гость”) {
console.log(greeting + “, ” + name + “!”);
}

foo(); // “Привет, Гость!”

Передача значения undefined в качестве аргумента воспринимается как отсутствие аргумента:

function foo(greeting = “Привет”, name = “Гость”) {
console.log(greeting + “, ” + name + “!”);
}

foo(undefined, “Гомер”); // “Привет, Гомер!”

Функция, в которой используются параметры со значениями по умолчанию, всегда работает как в строгом режиме, даже если он не был включен:

function foo(a, b = 2) {
console.log(a === arguments[0]);
console.log(b === arguments[1]);
}
foo(1); // true
// false

Так как в функцию передаётся только один аргумент, arguments[1] имеет значение undefined, поэтому сравнение console.log(b === arguments[1]); в результате даёт false.

Если в функции используются параметры со значениями по умолчанию, то использование объявления строгого режима внутри функции приведёт к синтаксической ошибке:

function foo(a = 1) {
“use strict”; // Ошибка
}

Значением по умолчанию может быть как простое, так и сложное выражение:

function foo(a = 1, b = 2 + 2) {
console.log(a, b);
}

foo(1); // 1 4

Значение предыдущего параметра можно использовать в качестве значения по умолчанию для любого из последующих параметров:

function foo(a, b = a) {
console.log(a, b);
}

foo(1); // 1 1

Попытка использовать значение последующего параметра в качестве значения по умолчанию для предшествующего параметра вызовет ошибку:

function foo(a = b, b) {
console.log(a, b);
}

foo(undefined, 1); // Ошибка

Параметры создаются в момент вызова функции в том порядке, в котором они следуют в определении функции. Каждый параметр создаётся по аналогии объявления переменной с помощью ключевого слова let. Это означает, что использование параметра до того как он был создан вызовет ошибку.

Попытка использовать значение переменной, объявленной в теле функции, в качестве значения по умолчанию для параметров функции, также вызовет ошибку:

function foo(a = b) {
var b = 5;
console.log(a);
}

foo(); // Ошибка

Дополнительный параметр

Параметр с префиксом … называется дополнительным (или необязательным). Дополнительный параметр указывается в качестве последнего параметра. Дополнительный параметр – это массив, в который сохраняются лишние аргументы, когда количество переданных аргументов превышает количество параметров:

function foo(a, b, …args) {
console.log(args);
}

foo(1, 2, 3, 4, 5); // 3, 4, 5

Источник

Именованные функции (FunctionDeclaration)

/*

функция sum

нормально отработает

*/

var a = sum(2,2)

function sum(x,y) {

   return x+y

}

Анонимные функции (FunctionExpression)

/*

будет ошибка,

т.к sum еще не существует

*/

var a = sum(2,2)

var sum = function(x,y) {

   return x+y

}

Анонимная самовызывающаяся функция

/*

позворяет изолировать код от глобальной видимости

*/

(function(){

    var i=1;

    console.log(“Анонимная самовызывающаяся функция”);

})();

console.log(i); // i is not defined

Функции — объекты

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/*

возможность передавать значение свойства в функции как в объект

*/

function func() {

   return …

}

func.test = 13

alert(func.test) // 13

function func() {

    var funcObj = arguments.callee; // получаем объект функции со всеми методами и свойствами

    funcObj.test++; // обращаемся к локальной области

    alert(funcObj.test);

}

func.test = 1

func() // 2

func() // 3

Области видимости

/*

локальная область видимости функции определяется фигурными скобками и объявлением var, let, const

*/

var x = 10;

function a() {

    // присвоить значение локальной переменной можно до ее объявления

    z=5;

    console.log(x+z);

    // объявить локальную переменную можно в любой части области (она поднимется вверх)

    var z;

}

delete z // очистим на всякий случай глобальную z

a() // 15

console.log(window.z) // => undefined, т.к z была задана локально

Параметры функции

/*

передаем функции параметры, обрабатываем и возвращаем результат

*/

var run = function(distance, speed) {

    var time;

    var speed = speed||10;

    var time=distance/speed;

    return time;

}

console.log(run(10)); // 1

console.log(run(10,5)); // 2

console.log(run(10,5,46,11)); // 2

console.log(run()); // NaN

Неопределенным числом параметров

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/*

объект arguments содержит все аргументы с которыми вызвалась функция,

при этом он поход на массив но не содержит методов присущих массиву (.push, .pop),

имеет длинну в свойстве length

и ссылку на саму функцию в свойстве callee

*/

function func() {

    for(vari=0;i<arguments.length;i++) {

       alert(“arguments[“+i+”] = “+arguments[i])

   }

}

func(‘a’,’b’,true)

console.log(arguments[0]); // a

console.log(arguments[1]); // b

console.log(arguments[2]); // true

/*

внутри функции можно создать массив из переданных аргументов

*/

var args = Array.prototype.slice.call(arguments); // теперь args – настоящий массив аргументов

args.shift() // удаляем первый элемент массива

(function(args) {

})(); //используем аргументы во вложенной функции

Передача функции по ссылке

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/*

Передача функции по ссылке

*/

var map = function(func, arr) {

    var result= [ ]

    for(vari=0; i<arr.length; i++) {

        result[i] =func(arr[i])

     }

    return result

}

// передаем функцию внутрь массива по ссылке

function run(a) { return a*3; }

console.log(map( run , [1,2,3])); // [3, 6, 9]

// передаем ананимную функцию как аргумент в вызове

console.log(map( function (a) { return a*3 } , [1,2,3])); // [3, 6, 9]

Сворачивание параметров в объект

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/*

Сворачивание параметров в объект через “keyword arguments”

если в функции много необязательных параметров func(1,2,null,null,true,null)

*/

function resize(setup) {

    var animate = setup.animate || true,

    toHeight=setup.toHeight||10,

    toWidth=setup.toHeight||20;

}

// передаем параметры по ключу в разном порядке

resize({toWidth: 100, animate: true, toHeight: 25})

===

var array = {toWidth: 100, animate: true, toHeight: 25};

resize(array);

Источник

Функции в JavaScript

Функции – ключевая концепция в JavaScript. Важнейшей особенностью языка является первоклассная поддержка функций​ (functions as first-class citizen). Любая функция это объект, и следовательно ею можно манипулировать как объектом, в частности:

  • передавать как аргумент и возвращать в качестве результата при вызове других функций (функций высшего порядка);
  • создавать анонимно и присваивать в качестве значений переменных или свойств объектов.

Это определяет высокую выразительную мощность JavaScript и позволяет относить его к числу языков, реализующих функциональную парадигму программирования (что само по себе есть очень круто по многим соображениям).

Функция в JavaScript специальный тип объектов, позволяющий формализовать средствами языка определённую логику поведения и обработки данных.

Для понимания работы функций необходимо (и достаточно?) иметь представление о следующих моментах:

  • способы объявления
  • способы вызова
  • параметры и аргументы вызова (arguments)
  • область данных (Scope) и замыкания (Closures)
  • объект привязки (this)
  • возвращаемое значение (return)
  • исключения (throw)
  • использование в качестве конструктора объектов
  • сборщик мусора (garbage collector)

Объявление функций

Функции вида “function declaration statement”

Объявление функции (function definition, или function declaration, или function statement) состоит из ключевого слова function и следующих частей:

  • Имя функции.
  • Список параметров (принимаемых функцией) заключенных в круглые скобки () и разделенных запятыми.
  • Инструкции, которые будут выполненны после вызова функции, заключают в фигурные скобки { }.

Например, следующий код объявляет простую функцию с именем square:

function square(number) {
return number * number;
}

Функция square принимает один параметр, названный number. Состоит из одной инструкции, которая означает вернуть параметр этой функции (это number) умноженный на самого себя. Инструкция return указывает на значение, которые будет возвращено функцией.

return number * number;

Примитивные параметры (например, число) передаются функции значением; значение передаётся в функцию, но если функция меняет значение параметра, это изменение не отразится глобально или после вызова функции.

Если Вы передадите объект как параметр (не примитив, например, массив или определяемые пользователем объекты), и функция изменит свойство переданного в неё объекта, это изменение будет видно и вне функции, как показано в следующим примере:

function myFunc(theObject) {
theObject.make = ‘Toyota’;
}

var mycar = {make: ‘Honda’, model: ‘Accord’, year: 1998};
var x, y;

x = mycar.make;

myFunc(mycar);
y = mycar.make;

Функции вида “function definition expression”

Функция вида “function declaration statement” по синтаксису является инструкцией (statement), ещё функция может быть вида “function definition  expression”. Такая функция может быть анонимной (она не имеет имени). Например, функция square может быть вызвана так:

var square = function(number) { return number * number; };
var x = square(4);

Однако, имя может быть и присвоено для вызова самой себя внутри самой функции и для отладчика (debugger) для идентифицирования функции в стек-треках (stack traces; “trace” — “след” / “отпечаток”).

var factorial = function fac(n) { return n < 2 ? 1 : n * fac(n – 1); };

console.log(factorial(3));

Функции вида “function definition expression” удобны, когда функция передается аргументом другой функции. Следующий пример показывает функцию map, которая должна получить функцию первым аргументом и массив вторым.

function map(f, a) {
var result = [],
i;
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}

В следующим коде наша функция принимает функцию, которая является function definition expression, и выполняет его для каждого элемента принятого массива вторым аргументом.

function map(f, a) {
var result = [];
var i;
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}
var f = function(x) {
return x * x * x;
}
var numbers = [0, 1, 2, 5, 10];
var cube = map(f,numbers);
console.log(cube);

Функция возвращает: [0, 1, 8, 125, 1000].

В JavaScript функция может быть объявлена с условием. Например, следующая функция будет присвоена переменной myFunc только, если num равно 0: 

var myFunc;
if (num === 0) {
myFunc = function(theObject) {
theObject.make = ‘Toyota’;
}
}

В дополнение к объявлениям функций, описанных здесь, Вы также можете использовать конструктор Function для создания функций из строки во время выполнения (runtime), подобно eval().

Метод — это функция, которая является свойством объекта. Узнать больше про объекты и методы можно по ссылке: Работа с объектами.

Вызовы функций

Объявление функции не выполняет её. Объявление функции просто называет функцию и указывает, что делать при вызове функции.

Вызов функции фактически выполняет указанные действия с указанными параметрами. Например, если Вы определите функцию square, Вы можете вызвать её следующим образом:

square(5);

Эта инструкция вызывает функцию с аргументом 5. Функция вызывает свои инструкции и возвращает значение 25.

Функции могут быть в области видимости, когда они уже определены, но функции вида “function declaration statment” могут быть подняты (поднятие — hoisting), также как в этом примере:

console.log(square(5));

function square(n) { return n * n; }

Область видимости функции — функция, в котором она определена, или целая программа, если она объявлена по уровню выше.

Примечание: Это работает только тогда, когда объявлении функции использует вышеупомянутый синтаксис (т.е. function funcName(){}). Код ниже не будет работать. Имеется в виду то, что поднятие функции работает только с function declaration и не работает с function expression.

console.log(square);
console.log(square(5));
var square = function(n) {
return n * n;
}

Аргументы функции не ограничиваются строками и числами. Вы можете передавать целые объекты в функцию. Функция show_props() (объявленная в Работа с объектами) является примером функции, принимающей объекты аргументом.

Функция может вызвать саму себя. Например, вот функция рекурсивного вычисления факториала:

function factorial(n) {
if ((n === 0) || (n === 1))
return 1;
else
return (n * factorial(n – 1));
}

Затем вы можете вычислить факториалы от одного до пяти следующим образом:

var a, b, c, d, e;
a = factorial(1);
b = factorial(2);
c = factorial(3);
d = factorial(4);
e = factorial(5);

Есть другие способы вызвать функцию. Существуют частые случаи, когда функции необходимо вызывать динамически, или поменять номера аргументов функции, или необходимо вызвать функцию с привязкой к определенному контексту. Оказывается, что функции сами по себе являются объектами, и эти объекты в свою очередь имеют методы (посмотрите объект Function). Один из них это метод apply(), использование которого может достигнуть этой цели.

Область видимости функций

(function scope)

Переменные объявленные в функции не могут быть доступными где-нибудь вне этой функции, поэтому переменные (которые нужны именно для функции) объявляют только в scope функции. При этом функция имеет доступ ко всем переменным и функциям, объявленным внутри её scope. Другими словами функция объявленная в глобальном scope имеет доступ ко всем переменным в глобальном scope. Функция объявленная внутри другой функции ещё имеет доступ и ко всем переменным её родителькой функции и другим переменным, к которым эта родительская функция имеет доступ.

var num1 = 20,
num2 = 3,
name = ‘Chamahk’;

function multiply() {
return num1 * num2;
}

multiply();

function getScore() {
var num1 = 2,
num2 = 3;

function add() {
return name + ‘ scored ‘ + (num1 + num2);
}

return add();
}

getScore();

Scope и стек функции

(function stack)

Рекурсия

Функция может вызывать саму себя. Три способа такого вызова:

  1. по имени функции
  2. arguments.callee
  3. по переменной, которая ссылается на функцию

Для примера рассмотрим следующие функцию:

var foo = function bar() {

};

Внутри функции (function body) все следующие вызовы эквивалентны:

  1. bar()
  2. arguments.callee()
  3. foo()

Функция, которая вызывает саму себя, называется рекурсивной функцией (recursive function). Получается, что рекурсия аналогична циклу (loop). Оба вызывают некоторый код несколько раз, и оба требуют условия (чтобы избежать бесконечного цикла, вернее бесконечной рекурсии). Например, следующий цикл:

var x = 0;
while (x < 10) {

x++;
}

можно было изменить на рекурсивную функцию и вызовом этой функции:

function loop(x) {
if (x >= 10)
return;

loop(x + 1);
}
loop(0);

Однако некоторые алгоритмы не могут быть простыми повторяющимися циклами. Например, получение всех элементов структуры дерева (например, DOM) проще всего реализуется использованием рекурсии:

function walkTree(node) {
if (node == null)
return;

for (var i = 0; i < node.childNodes.length; i++) {
walkTree(node.childNodes[i]);
}
}

В сравнении с функцией loop, каждый рекурсивный вызов сам вызывает много рекурсивных вызовов.

Также возможно превращение некоторых рекурсивных алгоритмов в нерекурсивные, но часто их логика очень сложна, и для этого потребуется использование стека (stack). По факту рекурсия использует stack: function stack.

Поведение stack’а можно увидеть в следующем примере:

function foo(i) {
if (i < 0)
return;
console.log(‘begin: ‘ + i);
foo(i – 1);
console.log(‘end: ‘ + i);
}
foo(3);

Вложенные функции (nested functions) и замыкания (closures)

Вы можете вложить одну функцию в другую. Вложенная функция (nested function; inner) приватная (private) и она помещена в другую функцию (outer). Так образуется замыкание (closure). Closure — это выражение (обычно функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменые (что “закрывает” (“close”) выражение).

Поскольку вложенная функция это closure, это означает, что вложенная функция может “унаследовать” (inherit) аргументы и переменные функции, в которую та вложена. Другими словами, вложенная функция содержит scope внешней (“outer”) функции.

Подведем итог:

  • Вложенная функция имеет доступ ко всем инструкциям внешней функции.
  • Вложенная функция формирует closure: она может использовать аргументы и переменные внешней функции, в то время как внешняя функция не может использовать аргументы и переменные вложенной функции.

Следующий пример показывает вложенную функцию:

function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2, 3);
b = addSquares(3, 4);
c = addSquares(4, 5);

Поскольку вложенная функция формирует closure, Вы можете вызвать внешную функцию и указать аргументы для обоих функций (для outer и innner).

function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
fn_inside = outside(3);

result = fn_inside(5);

result1 = outside(3)(5);

Сохранение переменных

Обратите внимание, значение x сохранилось, когда возвращалось inside. Closure должно сохранять аргументы и переменные во всем scope. Поскольку каждый вызов предоставляет потенциально разные аргументы, создается новый closure для каждого вызова во вне. Память может быть очищена только тогда, когда inside уже возвратился и больше не доступен.

Это не отличается от хранения ссылок в других объектах, но часто менее очевидно, потому что не устанавливаются ссылки напрямую и нельзя посмотреть там.

Несколько уровней вложенности функций (Multiply-nested functions)

Функции можно вкадывать несколько раз, т.е. функция (A) хранит в себе функцию (B), которая хранит в себе функцию (C). Обе фукнкции B и C формируют closures, так B имеет доступ к переменным и аргументам A, и C имеет такой же доступ к B. В добавок, поскольку C имеет такой доступ к B, который имеет такой же доступ к A, C ещё имеет такой же доспут к A. Таким образом closures может хранить в себе несколько scope; они рекурсивно хранят scope функций, содержащих его. Это называется chaining (chain — цепь; Почему названо “chaining” будет объяснено позже)

Рассмотрим следующий пример:

function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1);

В этом примере C имеет доступ к y функции B и к x функции A. Так получается, потому что:

  1. Функция B формирует closure, включающее A, т.е. B имеет доступ к аргументам и переменным функции A.
  2. Функция C формирует closure, включающее B.
  3. Раз closure функции B включает A, то closure С тоже включает A, C имеет доступ к аргументам и переменным обоих функций B и A. Другими словами, С cвязывает цепью (chain) scopes функций B и A в таком порядке.

В обратном порядке, однако, это не верно. A не имеет доступ к переменным и аргументам C, потому что A не имеет такой доступ к B. Таким образом, C остается приватным только для B.

Конфликты имен (Name conflicts)

Когда два аргумента или переменных в scope у closure имеют одинаковые имена, происходит конфликт имени (name conflict). Более вложенный (more inner) scope имеет приоритет, так самый вложенный scope имеет наивысший приоритет, и наоборот. Это цепочка областей видимости (scope chain). Самым первым звеном является самый глубокий scope, и наоборот. Рассмотрим следующие:

function outside() {
var x = 5;
function inside(x) {
return x * 2;
}
return inside;
}

outside()(10);

Конфликт имени произошел в инструкции return x * 2 между параметром x функции inside и переменной x функции outside. Scope chain здесь будет таким: {inside ==> outside ==> глобальный объект (global object)}. Следовательно x функции inside имеет больший приоритет по сравнению с outside, и нам вернулось 20 (= 10 * 2), а не 10 (= 5 * 2).

Замыкания

(Closures)

Closures это один из главных особенностей JavaScript. JavaScript разрешает вложенность функций и предоставляет вложенной функции полный доступ ко всем переменным и функциям, объявленным внутри внешней функции (и другим переменным и функцим, к которым имеет доступ эта внешняя функция).

Однако, внешняя функция не имеет доступа к переменным и функциям, объявленным во внутренней функции. Это обеспечивает своего рода инкапсуляцию для переменных внутри вложенной функции.

Также, поскольку вложенная функция имеет доступ к scope внешней функции, переменные и функции, объявленные во внешней функции, будет продолжать существовать и после её выполнения для вложенной функции, если на них и на неё сохранился доступ (имеется ввиду, что переменные, объявленные во внешней функции, сохраняются, только если внутренняя функция обращается к ним).

Closure создается, когда вложенная функция как-то стала доступной в неком scope вне внешней функции.

var pet = function(name) {
var getName = function() {
return name;
}
return getName;

}
myPet = pet(‘Vivie’);

myPet();

Более сложный пример представлен ниже. Объект с методами для манипуляции вложенной функции внешней функцией можно вернуть (return).

var createPet = function(name) {
var sex;

return {
setName: function(newName) {
name = newName;
},

getName: function() {
return name;
},

getSex: function() {
return sex;
},

setSex: function(newSex) {
if(typeof newSex === ‘string’ && (newSex.toLowerCase() === ‘male’ ||
newSex.toLowerCase() === ‘female’)) {
sex = newSex;
}
}
}
}

var pet = createPet(‘Vivie’);
pet.getName();

pet.setName(‘Oliver’);
pet.setSex(‘male’);
pet.getSex();
pet.getName();

В коде выше переменная name внешней функции доступна для вложенной функции, и нет другого способа доступа к вложенным переменным кроме как через вложенную функцию. Вложенные переменные вложенной функции являются безопасными хранилищами для внешних аргументов и переменных. Они содержат “постоянные” и “инкапсулированные” данные для работы с ними вложенными функциями. Функции даже не должны присваиваться переменной или иметь имя.

var getCode = (function() {
var apiCode = ‘0]Eal(eh&2’;

return function() {
return apiCode;
};
}());

getCode();

Однако есть ряд подводных камней, которые следует учитывать при использовании замыканий. Если закрытая функция определяет переменную с тем же именем, что и имя переменной во внешней области, нет способа снова ссылаться на переменную во внешней области.

var createPet = function(name) {
return {
setName: function(name) {
name = name;
}
}
}

Использование объекта arguments

Объект arguments функции является псевдо-массивом. Внутри функции Вы можете ссылаться к аргументам следующим образом:

arguments[i]

где i — это порядковый номер аргумента, отсчитывающийс