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: Object.assign()
slug: Web/JavaScript/Reference/Global_Objects/Object/assign
translation_of: Web/JavaScript/Reference/Global_Objects/Object/assign
---
<div>{{JSRef}}</div>
<div><strong><code>Object.assign()</code></strong>被用來複製一個或多個物件自身所有可數的屬性到另一個目標物件。回傳的值為該目標物件。</div>
<h2 id="語法">語法</h2>
<pre class="syntaxbox">Object.assign(<var>target</var>, ...<var>sources</var>)</pre>
<h3 id="參數">參數</h3>
<dl>
<dt><code>target</code></dt>
<dd>目標物件</dd>
<dt><code>sources</code></dt>
<dd>來源物件</dd>
</dl>
<h3 id="回傳值">回傳值</h3>
<p> 合併目標物件及(多個)來源物件所得到的最終物件。</p>
<h2 id="說明">說明</h2>
<p>如果在目標物件裡的屬性名稱(key)和來源物件的屬性名稱相同,將會被覆寫。若來源物件之間又有相同的屬性名稱,則後者會將前者覆寫。</p>
<p><code>Object.assign()</code>只會從來源物件將自身可列舉的屬性複製到目標物件。此函式方法(method) 使用來源物件的<code>[[Get]]</code>事件和目標物件的<code>[[Set]]</code>事件,使它將會執行getters和setters。因此,這邊的指派(<em>assigns</em>)屬性不只是複製或定義新屬性。若在合併包含getters的來源物件時,這個事件可能就不適合用來合併屬性。至於複製屬性的定義(包含其可列舉性)到各屬性,反倒是會用到 {{jsxref("Object.getOwnPropertyDescriptor()")}} 和 {{jsxref("Object.defineProperty()")}} 。</p>
<p>{{jsxref("String")}} 和 {{jsxref("Symbol")}} 類型的屬性都會被複製。</p>
<p>若發生錯誤,例如: 當一個屬性不可被寫入時,將會引發 {{jsxref("TypeError")}} 的錯誤,且目標物件剩餘的屬性將不會改變。</p>
<p>注意: <code>Object.assign()</code> 不會在來源物件屬性的值為{{jsxref("null")}} 或 {{jsxref("undefined")}} 的時候拋出錯誤。</p>
<h2 id="範例">範例</h2>
<h3 id="複製物件">複製物件</h3>
<pre class="brush: js">var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
</pre>
<h3 id="Deep_Clone" name="Deep_Clone">警告:非深層複製</h3>
<p>深層複製(deep clone)需要使用其他的替代方案,因為 <code>Object.assign()</code> 僅複製屬性值。若來源物件的值參照到一個子物件,它只會複製該子物件的參照。</p>
<pre class="brush: js">function test() {
let a = { b: {c:4} , d: { e: {f:1}} }
let g = Object.assign({},a) // 淺層
let h = JSON.parse(JSON.stringify(a)); // 深層
console.log(g.d) // { e: { f: 1 } }
g.d.e = 32
console.log('g.d.e set to 32.') // g.d.e set to 32.
console.log(g) // { b: { c: 4 }, d: { e: 32 } }
console.log(a) // { b: { c: 4 }, d: { e: 32 } }
console.log(h) // { b: { c: 4 }, d: { e: { f: 1 } } }
h.d.e = 54
console.log('h.d.e set to 54.') // h.d.e set to 54.
console.log(g) // { b: { c: 4 }, d: { e: 32 } }
console.log(a) // { b: { c: 4 }, d: { e: 32 } }
console.log(h) // { b: { c: 4 }, d: { e: 54 } }
}
test();
</pre>
<h3 id="合併物件">合併物件</h3>
<pre class="brush: js">var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 目標物件本身也被改變。</pre>
<h3 id="有相同屬性時合併物件">有相同屬性時合併物件</h3>
<pre class="brush: js">var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };
var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 },屬性c為o3.c的值,最後一個出現的屬性c。</pre>
<p>所有的屬性會被後方相同屬性名稱的值覆寫。</p>
<h3 id="複製_Symbol_型別的屬性">複製 Symbol 型別的屬性</h3>
<pre class="brush: js">var o1 = { a: 1 };
var o2 = { [Symbol('foo')]: 2 };
var obj = Object.assign({}, o1, o2);
console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]非不在
</pre>
<h3 id="在屬性鏈中的不可列舉屬性不會被複製">在屬性鏈中的不可列舉屬性不會被複製</h3>
<pre class="brush: js">var obj = Object.create({ foo: 1 }, { // foo 是 obj 的屬性鏈。
bar: {
value: 2 // bar 是不可列舉的屬性,因為enumerable預設為false。
},
baz: {
value: 3,
enumerable: true // baz 是自身可列舉的屬性。
}
});
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
</pre>
<h3 id="原始型別會被包成物件">原始型別會被包成物件</h3>
<pre class="brush: js">var v1 = 'abc';
var v2 = true;
var v3 = 10;
var v4 = Symbol('foo');
var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始型別會被打包,null和undefined則會被忽略。
// 注意: 只有打包成物件的字串是可列舉的,即可被複製的。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
</pre>
<h3 id="任何異常將會中斷正進行的複製程序">任何異常將會中斷正進行的複製程序</h3>
<pre class="brush: js">var target = Object.defineProperty({}, 'foo', {
value: 1,
writable: false
}); // target.foo 是 read-only (唯讀)屬性
Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" 是 read-only
// 在指派值給 target.foo 時,異常(Exception)會被拋出。
console.log(target.bar); // 2, 第一個來源物件複製成功。
console.log(target.foo2); // 3, 第二個來源物件的第一個屬性複製成功。
console.log(target.foo); // 1, 異常在這裡拋出。
console.log(target.foo3); // undefined, 複製程式已中斷,複製失敗。
console.log(target.baz); // undefined, 第三個來源物件也不會被複製。
</pre>
<h3 id="複製的存取程序">複製的存取程序</h3>
<pre class="brush: js">var obj = {
foo: 1,
get bar() {
return 2;
}
};
var copy = Object.assign({}, obj);
console.log(copy);
// { foo: 1, bar: 2 }, copy.bar的值,是obj.bar的getter回傳的值。
// 這個函式用來複製完整的描述內容。
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// Object.assign 預設會複製可列舉的Symbols。
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
var copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }
</pre>
<h2 id="Polyfill">Polyfill</h2>
<p>{{Glossary("Polyfill","polyfill")}} 不支援Symbol屬性,因為ES5沒有Symbol型別。</p>
<pre class="brush: js">if (typeof Object.assign != 'function') {
Object.assign = function (target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
</pre>
<h2 id="規格">規格</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td>{{SpecName('ES6', '#sec-object.assign', 'Object.assign')}}</td>
<td>{{Spec2('ES6')}}</td>
<td>Initial definition.</td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-object.assign', 'Object.assign')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="瀏覽器相容性">瀏覽器相容性</h2>
<div>{{Compat("javascript.builtins.Object.assign")}}</div>
<div></div>
<h2 id="參閱">參閱</h2>
<ul>
<li>{{jsxref("Object.defineProperties()")}}</li>
<li><a href="/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties">Enumerability and ownership of properties</a></li>
</ul>
|