aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/javascript/reference/operators/spread_syntax/index.html
blob: 1019bdd757c088bbc96722a36bc37b4a5834705b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
---
title: Spread syntax
slug: Web/JavaScript/Reference/Operators/Spread_syntax
translation_of: Web/JavaScript/Reference/Operators/Spread_syntax
---
<div>{{jsSidebar("Operators")}}</div>

<div><strong>Spread syntax</strong> позволяет расширить доступные для итерации элементы (например, массивы или строки) в местах</div>

<ul>
 <li>для функций: где ожидаемое количество аргументов для вызовов функций равно нулю или больше нуля</li>
 <li>для элементов (литералов массива)</li>
 <li>для выражений объектов: в местах, где количество пар "ключ-значение" должно быть равно нулю или больше (для объектных литералов)</li>
</ul>

<div>{{EmbedInteractiveExample("pages/js/expressions-spreadsyntax.html")}}</div>

<p class="hidden">Источник для этого интерактивного примера находится в репозитории на GitHub. Если вы желаете внести свой вклад в этот проект, то, пожалуйста, склонируйте  себе <a href="https://github.com/mdn/interactive-examples">https://github.com/mdn/interactive-examples</a> и пришлите нам pull request.</p>

<h2 id="Синтаксис">Синтаксис</h2>

<p>Для вызовов функций:</p>

<pre class="syntaxbox notranslate">myFunction(...iterableObj);
</pre>

<p>Для литералов массива или строк:</p>

<pre class="syntaxbox notranslate">[...iterableObj, '4', 'five', 6];</pre>

<p>Для литералов объекта (новое в ECMAScript 2018):</p>

<pre class="syntaxbox notranslate">let objClone = { ...obj };</pre>

<h2 id="Примеры">Примеры</h2>

<h3 id="Spread_в_вызовах_функций">Spread в вызовах функций</h3>

<h4 id="Замена_apply">Замена apply</h4>

<p>Обычно используют {{jsxref( "Function.prototype.apply")}} в случаях, когда хотят использовать элементы массива в качестве аргументов функции.</p>

<pre class="brush: js notranslate">function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);</pre>

<p>С <strong>spread syntax</strong> вышеприведённое можно записать как:</p>

<pre class="brush: js notranslate">function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);</pre>

<p>Любой аргумент в списке аргументов может использовать <strong>spread syntax</strong>, и его можно использовать несколько раз.</p>

<pre class="brush: js notranslate">function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);</pre>

<h4 id="Apply_для_new">Apply для new</h4>

<p>Вызывая конструктор через ключевое слово <code>new</code>, невозможно использовать массив и <code>apply</code> <strong>напрямую </strong>(<code>apply</code> выполняет <code>[[Call]]</code>, а не <code>[[Construct]]</code>).Однако благодаря spread syntax, массив может быть с лёгкостью использован со словом <code>new:</code></p>

<pre class="brush: js notranslate">var dateFields = [1970, 0, 1];  // 1 Jan 1970
var d = new Date(...dateFields);
</pre>

<p>Чтобы использовать <code>new</code> с массивом параметров без spread syntax, вам потребуется использование частичного применения:</p>

<pre class="brush: js notranslate">function applyAndNew(constructor, args) {
   function partial () {
      return constructor.apply(this, args);
   };
   if (typeof constructor.prototype === "object") {
      partial.prototype = Object.create(constructor.prototype);
   }
   return partial;
}


function myConstructor () {
   console.log("arguments.length: " + arguments.length);
   console.log(arguments);
   this.prop1="val1";
   this.prop2="val2";
};

var myArguments = ["hi", "how", "are", "you", "mr", null];
var myConstructorWithArguments = applyAndNew(myConstructor, myArguments);

console.log(new myConstructorWithArguments);
// (internal log of myConstructor):           arguments.length: 6
// (internal log of myConstructor):           ["hi", "how", "are", "you", "mr", null]
// (log of "new myConstructorWithArguments"): {prop1: "val1", prop2: "val2"}</pre>

<h3 id="Spread_в_литералах_массива">Spread в литералах массива</h3>

<h4 id="Более_мощный_литерал_массива">Более мощный литерал массива</h4>

<p>Без spread syntax, применение синтаксиса литерала массива для создания нового массива на основе существующего недостаточно и требуется императивный код вместо комбинации методов <code>push</code>, <code>splice</code>, <code>concat</code> и т.д. С spread syntax реализация становится гораздо более лаконичной:</p>

<pre class="brush: js notranslate">var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
</pre>

<p>Аналогично развёртыванию в массиве аргументов, <code>...</code> может быть использован повсеместно и многократно в литерале массива.</p>

<h4 id="Копирование_массива">Копирование массива</h4>

<pre class="brush: js notranslate">var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()
arr2.push(4);

// arr2 becomes [1, 2, 3, 4]
// arr remains unaffected
</pre>

<p><strong>Примечание:</strong> Spread syntax на самом деле переходит лишь на один уровень глубже при копировании массива. Таким образом, он может не подходить для копирования многоразмерных массивов, как показывает следующий пример: (также как и c {{jsxref("Object.assign()")}}) и синтаксис spred </p>

<pre class="brush: js notranslate">const a = [[1], [2], [3]];
const b = [...a];
b.shift().shift(); // 1
// О нет. Теперь на массив "а" относятся также: а
//[[], [2], [3]]
</pre>

<h4 id="Лучший_способ_конкатенации_массивов">Лучший способ конкатенации массивов</h4>

<p>Для конкатенации массива часто используется {{jsxref("Array.concat")}}:</p>

<pre class="brush: js notranslate">var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
arr1 = arr1.concat(arr2);</pre>

<p>С использованием spread syntax:</p>

<pre class="brush: js notranslate">var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
</pre>

<p>{{jsxref("Array.unshift")}} часто используется для вставки массива значений в начало существующего массива. Без spread syntax:</p>

<pre class="brush: js notranslate">var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Prepend all items from arr2 onto arr1
Array.prototype.unshift.apply(arr1, arr2) // arr1 is now [3, 4, 5, 0, 1, 2]</pre>

<p>С использованием spread syntax [Следует отметить, что такой способ создаёт новый массив <code>arr1</code>. В отличие от {{jsxref("Array.unshift")}}, исходный массив не мутируется]:</p>

<pre class="brush: js notranslate">var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1]; // arr1 is now [3, 4, 5, 0, 1, 2]
</pre>

<h3 id="Spread_в_литералах_объекта">Spread в литералах объекта</h3>

<p>Предложение <a href="https://github.com/tc39/proposal-object-rest-spread">Rest/Spread Properties for ECMAScript</a> (стадия 4) добавляет свойства spread в <a href="/ru/docs/Web/JavaScript/Reference/Operators/Object_initializer">литералы объекта</a>. Оно копирует собственные перечисляемые свойства данного объекта в новый объект.</p>

<p>Поверхностное копирование (без прототипа) или объединение объектов теперь возможно с использованием более короткого, чем {{jsxref("Object.assign()")}}, синтаксиса.</p>

<pre class="brush: js notranslate">var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }</pre>

<p>Обратите внимание, что {{jsxref("Object.assign()")}} запускает <a href="/en-US/docs/Web/JavaScript/Reference/Functions/set">setters</a>, а <strong>spread syntax</strong> нет.</p>

<p>Обратите внимание, что вы не можете заменить или имитировать функцию {{jsxref("Object.assign()")}}:</p>

<pre class="brush: js notranslate">var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
const merge = ( ...objects ) =&gt; ( { ...objects } );

var mergedObj = merge ( obj1, obj2);
// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }

var mergedObj = merge ( {}, obj1, obj2);
// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }</pre>

<p>В приведённом выше примере оператор распространения не работает так, как можно было бы ожидать: он распространяет <em>массив</em> аргументов в литерал <em>объекта</em> благодаря параметру rest.</p>

<h3 id="Только_для_итерируемых_объектов">Только для итерируемых объектов</h3>

<p>Spread syntax ( кроме случаев spread properties) может быть применён только к итерируемым объектам (<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator">iterable</a> objects) :</p>

<pre class="brush: js notranslate">var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
</pre>

<h3 id="Spread_с_большим_количеством_значений">Spread с большим количеством значений</h3>

<p>При использовании spread оператора в вызовах функций необходимо быть внимательным к возможному переполнению в ядре JavaScript. Существует ограничение по максимально возможному количеству аргументов функции. См. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" title="The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object)."><code>apply()</code></a> для уточнения.</p>

<h2 id="Rest_синтаксис_параметры">Rest синтаксис (параметры)</h2>

<p>Синтаксис для rest оператора выглядит таким же как и для spread оператора, однако он используется для деструктуризации массивов и объектов. Фактически, rest оператор противоположен spread оператору: последний раскладывает массив на элементы, тогда как первый собирает много элементов в один. См. <a href="/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/rest_parameters">rest parameters.</a></p>

<h2 id="Specifications">Specifications</h2>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">Specification</th>
   <th scope="col">Status</th>
   <th scope="col">Comment</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>{{SpecName('ES2015', '#sec-array-initializer')}}</td>
   <td>{{Spec2('ES2015')}}</td>
   <td>Defined in several sections of the specification: <a href="http://www.ecma-international.org/ecma-262/6.0/#sec-array-initializer">Array Initializer</a>, <a href="http://www.ecma-international.org/ecma-262/6.0/#sec-argument-lists">Argument Lists</a></td>
  </tr>
  <tr>
   <td>{{SpecName('ES2018', '#sec-object-initializer')}}</td>
   <td>{{Spec2('ES2018')}}</td>
   <td>Defined in <a href="http://www.ecma-international.org/ecma-262/9.0/#sec-object-initializer">Object Initializer</a></td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-array-initializer')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td>Без изменений.</td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-object-initializer')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td>Без изменений.</td>
  </tr>
 </tbody>
</table>

<h2 id="Browser_compatibility">Browser compatibility</h2>



<p>{{Compat("javascript.operators.spread")}}</p>

<h2 id="See_also">See also</h2>

<ul>
 <li><a href="/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/rest_parameters">Rest parameters</a> (also ‘<code>...</code>’)</li>
 <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">fn.apply</a> (also ‘<code>...</code>’)</li>
</ul>