This commit is contained in:
Ilya Kantor 2016-03-04 19:06:22 +03:00
parent e78e527866
commit 05a93ced80
212 changed files with 3213 additions and 3968 deletions

View file

@ -1,12 +1,12 @@
# Псевдомассив аргументов "arguments"
В JavaScript любая функция может быть вызвана с произвольным количеством аргументов.
В JavaScript любая функция может быть вызвана с произвольным количеством аргументов.
[cut]
Например:
```js
//+ run no-beautify
```js run no-beautify
function go(a,b) {
alert("a="+a+", b="+b);
}
@ -16,8 +16,7 @@ go(1,2); // a=1, b=2
go(1,2,3); // a=1, b=2, третий аргумент не вызовет ошибку
```
[smart header="В JavaScript нет \"перегрузки\" функций"]
````smart header="В JavaScript нет \"перегрузки\" функций"
В некоторых языках программист может создать две функции с одинаковым именем, но разным набором аргументов, а при вызове интерпретатор сам выберет нужную:
```js
@ -37,25 +36,24 @@ log(a, b, c); // вызовется вторая функция
Это называется "полиморфизмом функций" или "перегрузкой функций". В JavaScript ничего подобного нет.
**Может быть только одна функция с именем `log`, которая вызывается с любыми аргументами.**
**Может быть только одна функция с именем `log`, которая вызывается с любыми аргументами.**
А уже внутри она может посмотреть, с чем вызвана и по-разному отработать.
В примере выше второе объявление `log` просто переопределит первое.
[/smart]
````
## Доступ к "лишним" аргументам
## Доступ к "лишним" аргументам
Как получить значения аргументов, которых нет в списке параметров?
Доступ к ним осуществляется через "псевдо-массив" <a href="https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments">arguments</a>.
Доступ к ним осуществляется через "псевдо-массив" <a href="https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments">arguments</a>.
Он содержит список аргументов по номерам: `arguments[0]`, `arguments[1]`..., а также свойство `length`.
Например, выведем список всех аргументов:
```js
//+ run
```js run
function sayHi() {
for (var i = 0; i < arguments.length; i++) {
alert( "Привет, " + arguments[i] );
@ -65,19 +63,16 @@ function sayHi() {
sayHi("Винни", "Пятачок"); // 'Привет, Винни', 'Привет, Пятачок'
```
Все параметры находятся в `arguments`, даже если они есть в списке. Код выше сработал бы также, будь функция объявлена `sayHi(a,b,c)`.
[warn header="Связь между `arguments` и параметрами"]
Все параметры находятся в `arguments`, даже если они есть в списке. Код выше сработал бы также, будь функция объявлена `sayHi(a,b,c)`.
````warn header="Связь между `arguments` и параметрами"
**В старом стандарте JavaScript псевдо-массив `arguments` и переменные-параметры ссылаются на одни и те же значения.**
В результате изменения `arguments` влияют на параметры и наоборот.
В результате изменения `arguments` влияют на параметры и наоборот.
Например:
```js
//+ run
```js run
function f(x) {
arguments[0] = 5; // меняет переменную x
alert( x ); // 5
@ -88,11 +83,10 @@ f(1);
Наоборот:
```js
//+ run
```js run
function f(x) {
x = 5;
alert( arguments[0] ); // 5, обновленный x
alert( arguments[0] ); // 5, обновленный x
}
f(1);
@ -100,8 +94,7 @@ f(1);
В современной редакции стандарта это поведение изменено. Аргументы отделены от локальных переменных:
```js
//+ run
```js run
function f(x) {
"use strict"; // для браузеров с поддержкой строгого режима
@ -112,15 +105,14 @@ function f(x) {
f(1);
```
**Если вы не используете строгий режим, то чтобы переменные не менялись "неожиданно", рекомендуется никогда не изменять `arguments`.**
[/warn]
**Если вы не используете строгий режим, то чтобы переменные не менялись "неожиданно", рекомендуется никогда не изменять `arguments`.**
````
### arguments -- это не массив
Частая ошибка новичков -- попытка применить методы `Array` к `arguments`. Это невозможно:
```js
//+ run
```js run
function sayHi() {
var a = arguments.shift(); // ошибка! нет такого метода!
}
@ -128,14 +120,13 @@ function sayHi() {
sayHi(1);
```
Дело в том, что `arguments` -- это не массив `Array`.
Дело в том, что `arguments` -- это не массив `Array`.
В действительности, это обычный объект, просто ключи числовые и есть `length`. На этом сходство заканчивается. Никаких особых методов у него нет, и методы массивов он тоже не поддерживает.
В действительности, это обычный объект, просто ключи числовые и есть `length`. На этом сходство заканчивается. Никаких особых методов у него нет, и методы массивов он тоже не поддерживает.
Впрочем, никто не мешает сделать обычный массив из `arguments`, например так:
```js
//+ run
```js run
var args = [];
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
@ -146,59 +137,53 @@ for (var i = 0; i < arguments.length; i++) {
## Пример: копирование свойств copy(dst, src1, src2...) [#copy]
Иногда встаёт задача -- скопировать в существующий объект свойства из одного или нескольких других.
Иногда встаёт задача -- скопировать в существующий объект свойства из одного или нескольких других.
Напишем для этого функцию `copy`. Она будет работать с любым числом аргументов, благодаря использованию `arguments`.
Синтаксис:
<dl>
<dt>copy(dst, src1, src2...)</dt>
<dd>Копирует свойства из объектов `src1, src2,...` в объект `dst`. Возвращает получившийся объект.</dd>
</dl>
copy(dst, src1, src2...)
: Копирует свойства из объектов `src1, src2,...` в объект `dst`. Возвращает получившийся объект.
Использование:
<ul>
<li>Для объединения нескольких объектов в один:
- Для объединения нескольких объектов в один:
```js
//+ run
var vasya = {
age: 21,
name: 'Вася',
surname: 'Петров'
};
```js run
var vasya = {
age: 21,
name: 'Вася',
surname: 'Петров'
};
var user = {
isAdmin: false,
isEmailConfirmed: true
};
var user = {
isAdmin: false,
isEmailConfirmed: true
};
var student = {
university: 'My university'
};
var student = {
university: 'My university'
};
// добавить к vasya свойства из user и student
*!*
copy(vasya, user, student);
*/!*
// добавить к vasya свойства из user и student
*!*
copy(vasya, user, student);
*/!*
alert( vasya.isAdmin ); // false
alert( vasya.university ); // My university
```
alert( vasya.isAdmin ); // false
alert( vasya.university ); // My university
```
- Для создания копии объекта `user`:
</li>
<li>Для создания копии объекта `user`:
```js
// скопирует все свойства в пустой объект
var userClone = copy({}, user);
```
```js
// скопирует все свойства в пустой объект
var userClone = copy({}, user);
```
Такой "клон" объекта может пригодиться там, где мы хотим изменять его свойства, при этом не трогая исходный объект `user`.
Такой "клон" объекта может пригодиться там, где мы хотим изменять его свойства, при этом не трогая исходный объект `user`.
В нашей реализации мы будем копировать только свойства первого уровня, то есть вложенные объекты как-то особым образом не обрабатываются. Впрочем, её можно расширить.</li>
</ul>
В нашей реализации мы будем копировать только свойства первого уровня, то есть вложенные объекты как-то особым образом не обрабатываются. Впрочем, её можно расширить.
А вот и реализация:
@ -269,21 +254,19 @@ function showWarning(width, height, title, contents) {
## Устаревшее свойство arguments.callee [#arguments-callee]
[warn header="Используйте NFE вместо `arguments.callee`"]
```warn header="Используйте NFE вместо `arguments.callee`"
Это свойство устарело, при `use strict` оно не работает.
Единственная причина, по которой оно тут -- это то, что его можно встретить в старом коде, поэтому о нём желательно знать.
Современная спецификация рекомендует использовать ["именованные функциональные выражения (NFE)"](#functions-nfe).
Современная спецификация рекомендует использовать ["именованные функциональные выражения (NFE)"](#functions-nfe).
```
[/warn]
В старом стандарте JavaScript объект `arguments` не только хранил список аргументов, но и содержал в свойстве `arguments.callee` ссылку на функцию, которая выполняется в данный момент.
В старом стандарте JavaScript объект `arguments` не только хранил список аргументов, но и содержал в свойстве `arguments.callee` ссылку на функцию, которая выполняется в данный момент.
Например:
```js
//+ run
```js run
function f() {
alert( arguments.callee === f ); // true
}
@ -295,32 +278,31 @@ f();
```js
// подвызов через NFE
var factorial = function f(n) {
var factorial = function f(n) {
return n==1 ? 1 : n**!*f(n-1)*/!*;
};
// подвызов через arguments.callee
var factorial = function(n) {
var factorial = function(n) {
return n==1 ? 1 : n**!*arguments.callee(n-1)*/!*;
};
```
В учебнике мы его использовать не будем, оно приведено для общего ознакомления.
### arguments.callee.caller
### arguments.callee.caller
Устаревшее свойство `arguments.callee.caller` хранит ссылку на *функцию, которая вызвала данную*.
[warn header="Это свойство тоже устарело"]
Это свойство было в старом стандарте, при `use strict` оно не работает, как и `arguments.callee`.
```warn header="Это свойство тоже устарело"
Это свойство было в старом стандарте, при `use strict` оно не работает, как и `arguments.callee`.
Также ранее существовало более короткое свойство `arguments.caller`. Но это уже раритет, оно даже не кросс-браузерное. А вот свойство `arguments.callee.caller` поддерживается везде, если не использован `use strict`, поэтому в старом коде оно встречается.
[/warn]
```
Пример работы:
```js
//+ run
```js run
f1();
function f1() {
@ -340,15 +322,14 @@ function f3() {
В учебнике мы это свойство также не будем использовать.
## "Именованные аргументы"
*Именованные аргументы* -- альтернативная техника работы с аргументами, которая вообще не использует `arguments`.
Некоторые языки программирования позволяют передать параметры как-то так: `f(width=100, height=200)`, то есть по именам, а что не передано, тех аргументов нет. Это очень удобно в тех случаях, когда аргументов много, сложно запомнить их порядок и большинство вообще не надо передавать, по умолчанию подойдёт.
Некоторые языки программирования позволяют передать параметры как-то так: `f(width=100, height=200)`, то есть по именам, а что не передано, тех аргументов нет. Это очень удобно в тех случаях, когда аргументов много, сложно запомнить их порядок и большинство вообще не надо передавать, по умолчанию подойдёт.
Такая ситуация часто встречается в компонентах интерфейса. Например, у "меню" может быть масса настроек отображения, которые можно "подкрутить" но обычно нужно передать всего один-два главных параметра, а остальные возьмутся по умолчанию.
В JavaScript для этих целей используется передача аргументов в виде объекта, а в его свойствах мы передаём параметры.
Получается так:
@ -401,17 +382,12 @@ showWarning(opts); // вызвать с новым текстом, без коп
Именованные аргументы применяются во многих JavaScript-фреймворках.
## Итого
## Итого
<ul>
<li>Полный список аргументов, с которыми вызвана функция, доступен через `arguments`.</li>
<li>Это псевдомассив, то есть объект, который похож на массив, в нём есть нумерованные свойства и `length`, но методов массива у него нет.</li>
<li>В старом стандарте было свойство `arguments.callee` со ссылкой на текущую функцию, а также свойство `arguments.callee.caller`, содержащее ссылку на функцию, которая вызвала данную. Эти свойства устарели, при `use strict` обращение к ним приведёт к ошибке.</li>
<li>Для указания аргументов по умолчанию, в тех случаях, когда они заведомо не `false`, удобен оператор `||`.</li>
</ul>
- Полный список аргументов, с которыми вызвана функция, доступен через `arguments`.
- Это псевдомассив, то есть объект, который похож на массив, в нём есть нумерованные свойства и `length`, но методов массива у него нет.
- В старом стандарте было свойство `arguments.callee` со ссылкой на текущую функцию, а также свойство `arguments.callee.caller`, содержащее ссылку на функцию, которая вызвала данную. Эти свойства устарели, при `use strict` обращение к ним приведёт к ошибке.
- Для указания аргументов по умолчанию, в тех случаях, когда они заведомо не `false`, удобен оператор `||`.
В тех случаях, когда возможных аргументов много и, в особенности, когда большинство их имеют значения по умолчанию, вместо работы с `arguments` организуют передачу данных через объект, который как правило называют `options`.
@ -423,19 +399,3 @@ function showMessage(text, options) {
}
```
[head]
<script>
function copy() {
var dst = arguments[0];
for (var i = 1; i < arguments.length; i++) {
var arg = arguments[i];
for (var key in arg) {
dst[key] = arg[key];
}
}
return dst;
}
</script>
[/head]