@feng3d/reactivity 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,448 +1,448 @@
1
- /* eslint-disable prefer-rest-params */
2
-
3
- import { batchRun } from './batch';
4
- import { noTrack } from './Reactivity';
5
- import { PropertyReactivity } from './property';
6
- import { isProxy, toReactive } from './reactive';
7
- import { ARRAY_ITERATE_KEY, TrackOpTypes } from './shared/constants';
8
- import { isArray, toRaw } from './shared/general';
9
-
10
- export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
11
- __proto__: null,
12
-
13
- /**
14
- * 返回一个迭代器,用于遍历数组的响应式值
15
- */
16
- [Symbol.iterator]()
17
- {
18
- return iterator(this, Symbol.iterator, toReactive);
19
- },
20
-
21
- /**
22
- * 连接数组并返回新数组,处理响应式数组
23
- */
24
- concat(...args: unknown[])
25
- {
26
- return reactiveReadArray(this).concat(
27
- ...args.map((x) => (isArray(x) ? reactiveReadArray(x) : x)),
28
- );
29
- },
30
-
31
- /**
32
- * 返回一个迭代器,用于遍历数组的键值对,并将值转换为响应式
33
- */
34
- entries()
35
- {
36
- return iterator(this, 'entries', (value: [number, unknown]) =>
37
- {
38
- value[1] = toReactive(value[1]);
39
-
40
- return value;
41
- });
42
- },
43
-
44
- /**
45
- * 测试数组中的所有元素是否都通过了指定函数的测试
46
- */
47
- every(fn: (item: unknown, index: number, array: unknown[]) => unknown,
48
- thisArg?: unknown,
49
- )
50
- {
51
- return apply(this, 'every', fn, thisArg, undefined, arguments);
52
- },
53
-
54
- /**
55
- * 创建一个新数组,包含通过指定函数测试的所有元素
56
- */
57
- filter(fn: (item: unknown, index: number, array: unknown[]) => unknown,
58
- thisArg?: unknown,
59
- )
60
- {
61
- return apply(this, 'filter', fn, thisArg, (v) => v.map(toReactive), arguments);
62
- },
63
-
64
- /**
65
- * 返回数组中满足指定测试函数的第一个元素
66
- */
67
- find(fn: (item: unknown, index: number, array: unknown[]) => boolean,
68
- thisArg?: unknown,
69
- )
70
- {
71
- return apply(this, 'find', fn, thisArg, toReactive, arguments);
72
- },
73
-
74
- /**
75
- * 返回数组中满足指定测试函数的第一个元素的索引
76
- */
77
- findIndex(fn: (item: unknown, index: number, array: unknown[]) => boolean,
78
- thisArg?: unknown,
79
- )
80
- {
81
- return apply(this, 'findIndex', fn, thisArg, undefined, arguments);
82
- },
83
-
84
- /**
85
- * 返回数组中满足指定测试函数的最后一个元素
86
- */
87
- findLast(fn: (item: unknown, index: number, array: unknown[]) => boolean,
88
- thisArg?: unknown,
89
- )
90
- {
91
- return apply(this, 'findLast', fn, thisArg, toReactive, arguments);
92
- },
93
-
94
- /**
95
- * 返回数组中满足指定测试函数的最后一个元素的索引
96
- */
97
- findLastIndex(fn: (item: unknown, index: number, array: unknown[]) => boolean,
98
- thisArg?: unknown,
99
- )
100
- {
101
- return apply(this, 'findLastIndex', fn, thisArg, undefined, arguments);
102
- },
103
-
104
- // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement
105
-
106
- /**
107
- * 对数组中的每个元素执行指定函数
108
- */
109
- forEach(fn: (item: unknown, index: number, array: unknown[]) => unknown,
110
- thisArg?: unknown,
111
- )
112
- {
113
- return apply(this, 'forEach', fn, thisArg, undefined, arguments);
114
- },
115
-
116
- /**
117
- * 判断数组是否包含指定元素,处理响应式值
118
- */
119
- includes(...args: unknown[])
120
- {
121
- return searchProxy(this, 'includes', args);
122
- },
123
-
124
- /**
125
- * 返回数组中指定元素第一次出现的索引,处理响应式值
126
- */
127
- indexOf(...args: unknown[])
128
- {
129
- return searchProxy(this, 'indexOf', args);
130
- },
131
-
132
- /**
133
- * 将数组的所有元素连接成一个字符串
134
- */
135
- join(separator?: string)
136
- {
137
- return reactiveReadArray(this).join(separator);
138
- },
139
-
140
- // keys() iterator only reads `length`, no optimisation required
141
-
142
- /**
143
- * 返回数组中指定元素最后一次出现的索引,处理响应式值
144
- */
145
- lastIndexOf(...args: unknown[])
146
- {
147
- return searchProxy(this, 'lastIndexOf', args);
148
- },
149
-
150
- /**
151
- * 创建一个新数组,包含对原数组每个元素调用指定函数的结果
152
- */
153
- map(fn: (item: unknown, index: number, array: unknown[]) => unknown,
154
- thisArg?: unknown,
155
- )
156
- {
157
- return apply(this, 'map', fn, thisArg, undefined, arguments);
158
- },
159
-
160
- /**
161
- * 移除数组的最后一个元素并返回该元素,避免跟踪长度变化
162
- */
163
- pop()
164
- {
165
- return noTracking(this, 'pop');
166
- },
167
-
168
- /**
169
- * 向数组末尾添加一个或多个元素,并返回新的长度,避免跟踪长度变化
170
- */
171
- push(...args: unknown[])
172
- {
173
- return noTracking(this, 'push', args);
174
- },
175
-
176
- /**
177
- * 对数组中的每个元素执行累加器函数,并返回最终结果
178
- */
179
- reduce(fn: (
180
- acc: unknown,
181
- item: unknown,
182
- index: number,
183
- array: unknown[],
184
- ) => unknown,
185
- ...args: unknown[]
186
- )
187
- {
188
- return reduce(this, 'reduce', fn, args);
189
- },
190
-
191
- /**
192
- * 从右到左对数组中的每个元素执行累加器函数,并返回最终结果
193
- */
194
- reduceRight(
195
- fn: (
196
- acc: unknown,
197
- item: unknown,
198
- index: number,
199
- array: unknown[],
200
- ) => unknown,
201
- ...args: unknown[]
202
- )
203
- {
204
- return reduce(this, 'reduceRight', fn, args);
205
- },
206
-
207
- /**
208
- * 移除数组的第一个元素并返回该元素,避免跟踪长度变化
209
- */
210
- shift()
211
- {
212
- return noTracking(this, 'shift');
213
- },
214
-
215
- // slice could use ARRAY_ITERATE but also seems to beg for range tracking
216
-
217
- /**
218
- * 测试数组中的某些元素是否通过了指定函数的测试
219
- */
220
- some(
221
- fn: (item: unknown, index: number, array: unknown[]) => unknown,
222
- thisArg?: unknown,
223
- )
224
- {
225
- return apply(this, 'some', fn, thisArg, undefined, arguments);
226
- },
227
-
228
- /**
229
- * 通过删除或替换现有元素或添加新元素来修改数组,避免跟踪长度变化
230
- */
231
- splice(...args: unknown[])
232
- {
233
- return noTracking(this, 'splice', args);
234
- },
235
-
236
- /**
237
- * 返回一个新数组,包含原数组的反转副本
238
- */
239
- toReversed()
240
- {
241
- // @ts-expect-error user code may run in es2016+
242
- return reactiveReadArray(this).toReversed();
243
- },
244
-
245
- /**
246
- * 返回一个新数组,包含原数组的排序副本
247
- */
248
- toSorted(comparer?: (a: unknown, b: unknown) => number)
249
- {
250
- // @ts-expect-error user code may run in es2016+
251
- return reactiveReadArray(this).toSorted(comparer);
252
- },
253
-
254
- /**
255
- * 返回一个新数组,包含原数组的切片副本
256
- */
257
- toSpliced(...args: unknown[])
258
- {
259
- // @ts-expect-error user code may run in es2016+
260
- return (reactiveReadArray(this).toSpliced as any)(...args);
261
- },
262
-
263
- /**
264
- * 向数组开头添加一个或多个元素,并返回新的长度,避免跟踪长度变化
265
- */
266
- unshift(...args: unknown[])
267
- {
268
- return noTracking(this, 'unshift', args);
269
- },
270
-
271
- /**
272
- * 返回一个迭代器,用于遍历数组的响应式值
273
- */
274
- values()
275
- {
276
- return iterator(this, 'values', toReactive);
277
- },
278
- };
279
-
280
- // instrument iterators to take ARRAY_ITERATE dependency
281
- function iterator(
282
- self: unknown[],
283
- method: keyof Array<unknown>,
284
- wrapValue: (value: any) => unknown,
285
- )
286
- {
287
- // note that taking ARRAY_ITERATE dependency here is not strictly equivalent
288
- // to calling iterate on the proxified array.
289
- // creating the iterator does not access any array property:
290
- // it is only when .next() is called that length and indexes are accessed.
291
- // pushed to the extreme, an iterator could be created in one effect scope,
292
- // partially iterated in another, then iterated more in yet another.
293
- // given that JS iterator can only be read once, this doesn't seem like
294
- // a plausible use-case, so this tracking simplification seems ok.
295
- const arr = shallowReadArray(self);
296
- const iter = (arr[method] as any)() as IterableIterator<unknown> & {
297
- _next: IterableIterator<unknown>['next']
298
- };
299
- if (arr !== self)
300
- {
301
- iter._next = iter.next;
302
- iter.next = () =>
303
- {
304
- const result = iter._next();
305
- if (result.value)
306
- {
307
- result.value = wrapValue(result.value);
308
- }
309
-
310
- return result;
311
- };
312
- }
313
-
314
- return iter;
315
- }
316
-
317
- /**
318
- * 跟踪数组的迭代操作并返回原始数组
319
- */
320
- function shallowReadArray<T>(arr: T[]): T[]
321
- {
322
- PropertyReactivity.track((arr = toRaw(arr)), TrackOpTypes.ITERATE, ARRAY_ITERATE_KEY);
323
-
324
- return arr;
325
- }
326
-
327
- function reactiveReadArray<T>(array: T[]): T[]
328
- {
329
- const raw = toRaw(array);
330
- if (raw === array) return raw;
331
- PropertyReactivity.track(raw, TrackOpTypes.ITERATE, ARRAY_ITERATE_KEY);
332
-
333
- return raw.map(toReactive);
334
- }
335
-
336
- // in the codebase we enforce es2016, but user code may run in environments
337
- // higher than that
338
- type ArrayMethods = keyof Array<any> | 'findLast' | 'findLastIndex';
339
-
340
- // instrument functions that read (potentially) all items
341
- // to take ARRAY_ITERATE dependency
342
- function apply(
343
- self: unknown[],
344
- method: ArrayMethods,
345
- fn: (item: unknown, index: number, array: unknown[]) => unknown,
346
- thisArg?: unknown,
347
- wrappedRetFn?: (result: any) => unknown,
348
- args?: IArguments,
349
- )
350
- {
351
- const arr = shallowReadArray(self);
352
- const needsWrap = arr !== self;
353
- const methodFn = arr[method];
354
-
355
- // #11759
356
- // If the method being called is from a user-extended Array, the arguments will be unknown
357
- // (unknown order and unknown parameter types). In this case, we skip the shallowReadArray
358
- // handling and directly call apply with self.
359
- if (methodFn !== Array.prototype[method as any])
360
- {
361
- const result = methodFn.apply(self, args);
362
-
363
- return needsWrap ? toReactive(result) : result;
364
- }
365
-
366
- let wrappedFn = fn;
367
- if (arr !== self)
368
- {
369
- if (needsWrap)
370
- {
371
- wrappedFn = function (this: unknown, item, index)
372
- {
373
- return fn.call(this, toReactive(item), index, self);
374
- };
375
- }
376
- else if (fn.length > 2)
377
- {
378
- wrappedFn = function (this: unknown, item, index)
379
- {
380
- return fn.call(this, item, index, self);
381
- };
382
- }
383
- }
384
- const result = methodFn.call(arr, wrappedFn, thisArg);
385
-
386
- return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result;
387
- }
388
-
389
- // instrument reduce and reduceRight to take ARRAY_ITERATE dependency
390
- function reduce(
391
- self: unknown[],
392
- method: keyof Array<any>,
393
- fn: (acc: unknown, item: unknown, index: number, array: unknown[]) => unknown,
394
- args: unknown[],
395
- )
396
- {
397
- const arr = shallowReadArray(self);
398
- let wrappedFn = fn;
399
- if (arr !== self)
400
- {
401
- wrappedFn = function (this: unknown, acc, item, index)
402
- {
403
- return fn.call(this, acc, toReactive(item), index, self);
404
- };
405
- }
406
-
407
- return (arr[method] as any)(wrappedFn, ...args);
408
- }
409
-
410
- // instrument identity-sensitive methods to account for reactive proxies
411
- function searchProxy(
412
- self: unknown[],
413
- method: keyof Array<any>,
414
- args: unknown[],
415
- )
416
- {
417
- const arr = toRaw(self) as any;
418
- PropertyReactivity.track(arr, TrackOpTypes.ITERATE, ARRAY_ITERATE_KEY);
419
- // we run the method using the original args first (which may be reactive)
420
- const res = arr[method](...args);
421
-
422
- // if that didn't work, run it again using raw values.
423
- if ((res === -1 || res === false) && isProxy(args[0]))
424
- {
425
- args[0] = toRaw(args[0]);
426
-
427
- return arr[method](...args);
428
- }
429
-
430
- return res;
431
- }
432
-
433
- // instrument length-altering mutation methods to avoid length being tracked
434
- // which leads to infinite loops in some cases (#2137)
435
- function noTracking(
436
- self: unknown[],
437
- method: keyof Array<any>,
438
- args: unknown[] = [],
439
- )
440
- {
441
- const res = batchRun(() =>
442
- noTrack(() =>
443
- (toRaw(self) as any)[method].apply(self, args)
444
- )
445
- );
446
-
447
- return res;
448
- }
1
+ /* eslint-disable prefer-rest-params */
2
+
3
+ import { batchRun } from './batch';
4
+ import { noTrack } from './Reactivity';
5
+ import { PropertyReactivity } from './property';
6
+ import { isProxy, toReactive } from './reactive';
7
+ import { ARRAY_ITERATE_KEY, TrackOpTypes } from './shared/constants';
8
+ import { isArray, toRaw } from './shared/general';
9
+
10
+ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
11
+ __proto__: null,
12
+
13
+ /**
14
+ * 返回一个迭代器,用于遍历数组的响应式值
15
+ */
16
+ [Symbol.iterator]()
17
+ {
18
+ return iterator(this, Symbol.iterator, toReactive);
19
+ },
20
+
21
+ /**
22
+ * 连接数组并返回新数组,处理响应式数组
23
+ */
24
+ concat(...args: unknown[])
25
+ {
26
+ return reactiveReadArray(this).concat(
27
+ ...args.map((x) => (isArray(x) ? reactiveReadArray(x) : x)),
28
+ );
29
+ },
30
+
31
+ /**
32
+ * 返回一个迭代器,用于遍历数组的键值对,并将值转换为响应式
33
+ */
34
+ entries()
35
+ {
36
+ return iterator(this, 'entries', (value: [number, unknown]) =>
37
+ {
38
+ value[1] = toReactive(value[1]);
39
+
40
+ return value;
41
+ });
42
+ },
43
+
44
+ /**
45
+ * 测试数组中的所有元素是否都通过了指定函数的测试
46
+ */
47
+ every(fn: (item: unknown, index: number, array: unknown[]) => unknown,
48
+ thisArg?: unknown,
49
+ )
50
+ {
51
+ return apply(this, 'every', fn, thisArg, undefined, arguments);
52
+ },
53
+
54
+ /**
55
+ * 创建一个新数组,包含通过指定函数测试的所有元素
56
+ */
57
+ filter(fn: (item: unknown, index: number, array: unknown[]) => unknown,
58
+ thisArg?: unknown,
59
+ )
60
+ {
61
+ return apply(this, 'filter', fn, thisArg, (v) => v.map(toReactive), arguments);
62
+ },
63
+
64
+ /**
65
+ * 返回数组中满足指定测试函数的第一个元素
66
+ */
67
+ find(fn: (item: unknown, index: number, array: unknown[]) => boolean,
68
+ thisArg?: unknown,
69
+ )
70
+ {
71
+ return apply(this, 'find', fn, thisArg, toReactive, arguments);
72
+ },
73
+
74
+ /**
75
+ * 返回数组中满足指定测试函数的第一个元素的索引
76
+ */
77
+ findIndex(fn: (item: unknown, index: number, array: unknown[]) => boolean,
78
+ thisArg?: unknown,
79
+ )
80
+ {
81
+ return apply(this, 'findIndex', fn, thisArg, undefined, arguments);
82
+ },
83
+
84
+ /**
85
+ * 返回数组中满足指定测试函数的最后一个元素
86
+ */
87
+ findLast(fn: (item: unknown, index: number, array: unknown[]) => boolean,
88
+ thisArg?: unknown,
89
+ )
90
+ {
91
+ return apply(this, 'findLast', fn, thisArg, toReactive, arguments);
92
+ },
93
+
94
+ /**
95
+ * 返回数组中满足指定测试函数的最后一个元素的索引
96
+ */
97
+ findLastIndex(fn: (item: unknown, index: number, array: unknown[]) => boolean,
98
+ thisArg?: unknown,
99
+ )
100
+ {
101
+ return apply(this, 'findLastIndex', fn, thisArg, undefined, arguments);
102
+ },
103
+
104
+ // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement
105
+
106
+ /**
107
+ * 对数组中的每个元素执行指定函数
108
+ */
109
+ forEach(fn: (item: unknown, index: number, array: unknown[]) => unknown,
110
+ thisArg?: unknown,
111
+ )
112
+ {
113
+ return apply(this, 'forEach', fn, thisArg, undefined, arguments);
114
+ },
115
+
116
+ /**
117
+ * 判断数组是否包含指定元素,处理响应式值
118
+ */
119
+ includes(...args: unknown[])
120
+ {
121
+ return searchProxy(this, 'includes', args);
122
+ },
123
+
124
+ /**
125
+ * 返回数组中指定元素第一次出现的索引,处理响应式值
126
+ */
127
+ indexOf(...args: unknown[])
128
+ {
129
+ return searchProxy(this, 'indexOf', args);
130
+ },
131
+
132
+ /**
133
+ * 将数组的所有元素连接成一个字符串
134
+ */
135
+ join(separator?: string)
136
+ {
137
+ return reactiveReadArray(this).join(separator);
138
+ },
139
+
140
+ // keys() iterator only reads `length`, no optimisation required
141
+
142
+ /**
143
+ * 返回数组中指定元素最后一次出现的索引,处理响应式值
144
+ */
145
+ lastIndexOf(...args: unknown[])
146
+ {
147
+ return searchProxy(this, 'lastIndexOf', args);
148
+ },
149
+
150
+ /**
151
+ * 创建一个新数组,包含对原数组每个元素调用指定函数的结果
152
+ */
153
+ map(fn: (item: unknown, index: number, array: unknown[]) => unknown,
154
+ thisArg?: unknown,
155
+ )
156
+ {
157
+ return apply(this, 'map', fn, thisArg, undefined, arguments);
158
+ },
159
+
160
+ /**
161
+ * 移除数组的最后一个元素并返回该元素,避免跟踪长度变化
162
+ */
163
+ pop()
164
+ {
165
+ return noTracking(this, 'pop');
166
+ },
167
+
168
+ /**
169
+ * 向数组末尾添加一个或多个元素,并返回新的长度,避免跟踪长度变化
170
+ */
171
+ push(...args: unknown[])
172
+ {
173
+ return noTracking(this, 'push', args);
174
+ },
175
+
176
+ /**
177
+ * 对数组中的每个元素执行累加器函数,并返回最终结果
178
+ */
179
+ reduce(fn: (
180
+ acc: unknown,
181
+ item: unknown,
182
+ index: number,
183
+ array: unknown[],
184
+ ) => unknown,
185
+ ...args: unknown[]
186
+ )
187
+ {
188
+ return reduce(this, 'reduce', fn, args);
189
+ },
190
+
191
+ /**
192
+ * 从右到左对数组中的每个元素执行累加器函数,并返回最终结果
193
+ */
194
+ reduceRight(
195
+ fn: (
196
+ acc: unknown,
197
+ item: unknown,
198
+ index: number,
199
+ array: unknown[],
200
+ ) => unknown,
201
+ ...args: unknown[]
202
+ )
203
+ {
204
+ return reduce(this, 'reduceRight', fn, args);
205
+ },
206
+
207
+ /**
208
+ * 移除数组的第一个元素并返回该元素,避免跟踪长度变化
209
+ */
210
+ shift()
211
+ {
212
+ return noTracking(this, 'shift');
213
+ },
214
+
215
+ // slice could use ARRAY_ITERATE but also seems to beg for range tracking
216
+
217
+ /**
218
+ * 测试数组中的某些元素是否通过了指定函数的测试
219
+ */
220
+ some(
221
+ fn: (item: unknown, index: number, array: unknown[]) => unknown,
222
+ thisArg?: unknown,
223
+ )
224
+ {
225
+ return apply(this, 'some', fn, thisArg, undefined, arguments);
226
+ },
227
+
228
+ /**
229
+ * 通过删除或替换现有元素或添加新元素来修改数组,避免跟踪长度变化
230
+ */
231
+ splice(...args: unknown[])
232
+ {
233
+ return noTracking(this, 'splice', args);
234
+ },
235
+
236
+ /**
237
+ * 返回一个新数组,包含原数组的反转副本
238
+ */
239
+ toReversed()
240
+ {
241
+ // @ts-expect-error user code may run in es2016+
242
+ return reactiveReadArray(this).toReversed();
243
+ },
244
+
245
+ /**
246
+ * 返回一个新数组,包含原数组的排序副本
247
+ */
248
+ toSorted(comparer?: (a: unknown, b: unknown) => number)
249
+ {
250
+ // @ts-expect-error user code may run in es2016+
251
+ return reactiveReadArray(this).toSorted(comparer);
252
+ },
253
+
254
+ /**
255
+ * 返回一个新数组,包含原数组的切片副本
256
+ */
257
+ toSpliced(...args: unknown[])
258
+ {
259
+ // @ts-expect-error user code may run in es2016+
260
+ return (reactiveReadArray(this).toSpliced as any)(...args);
261
+ },
262
+
263
+ /**
264
+ * 向数组开头添加一个或多个元素,并返回新的长度,避免跟踪长度变化
265
+ */
266
+ unshift(...args: unknown[])
267
+ {
268
+ return noTracking(this, 'unshift', args);
269
+ },
270
+
271
+ /**
272
+ * 返回一个迭代器,用于遍历数组的响应式值
273
+ */
274
+ values()
275
+ {
276
+ return iterator(this, 'values', toReactive);
277
+ },
278
+ };
279
+
280
+ // instrument iterators to take ARRAY_ITERATE dependency
281
+ function iterator(
282
+ self: unknown[],
283
+ method: keyof Array<unknown>,
284
+ wrapValue: (value: any) => unknown,
285
+ )
286
+ {
287
+ // note that taking ARRAY_ITERATE dependency here is not strictly equivalent
288
+ // to calling iterate on the proxified array.
289
+ // creating the iterator does not access any array property:
290
+ // it is only when .next() is called that length and indexes are accessed.
291
+ // pushed to the extreme, an iterator could be created in one effect scope,
292
+ // partially iterated in another, then iterated more in yet another.
293
+ // given that JS iterator can only be read once, this doesn't seem like
294
+ // a plausible use-case, so this tracking simplification seems ok.
295
+ const arr = shallowReadArray(self);
296
+ const iter = (arr[method] as any)() as IterableIterator<unknown> & {
297
+ _next: IterableIterator<unknown>['next']
298
+ };
299
+ if (arr !== self)
300
+ {
301
+ iter._next = iter.next;
302
+ iter.next = () =>
303
+ {
304
+ const result = iter._next();
305
+ if (result.value)
306
+ {
307
+ result.value = wrapValue(result.value);
308
+ }
309
+
310
+ return result;
311
+ };
312
+ }
313
+
314
+ return iter;
315
+ }
316
+
317
+ /**
318
+ * 跟踪数组的迭代操作并返回原始数组
319
+ */
320
+ function shallowReadArray<T>(arr: T[]): T[]
321
+ {
322
+ PropertyReactivity.track((arr = toRaw(arr)), TrackOpTypes.ITERATE, ARRAY_ITERATE_KEY);
323
+
324
+ return arr;
325
+ }
326
+
327
+ function reactiveReadArray<T>(array: T[]): T[]
328
+ {
329
+ const raw = toRaw(array);
330
+ if (raw === array) return raw;
331
+ PropertyReactivity.track(raw, TrackOpTypes.ITERATE, ARRAY_ITERATE_KEY);
332
+
333
+ return raw.map(toReactive);
334
+ }
335
+
336
+ // in the codebase we enforce es2016, but user code may run in environments
337
+ // higher than that
338
+ type ArrayMethods = keyof Array<any> | 'findLast' | 'findLastIndex';
339
+
340
+ // instrument functions that read (potentially) all items
341
+ // to take ARRAY_ITERATE dependency
342
+ function apply(
343
+ self: unknown[],
344
+ method: ArrayMethods,
345
+ fn: (item: unknown, index: number, array: unknown[]) => unknown,
346
+ thisArg?: unknown,
347
+ wrappedRetFn?: (result: any) => unknown,
348
+ args?: IArguments,
349
+ )
350
+ {
351
+ const arr = shallowReadArray(self);
352
+ const needsWrap = arr !== self;
353
+ const methodFn = arr[method];
354
+
355
+ // #11759
356
+ // If the method being called is from a user-extended Array, the arguments will be unknown
357
+ // (unknown order and unknown parameter types). In this case, we skip the shallowReadArray
358
+ // handling and directly call apply with self.
359
+ if (methodFn !== Array.prototype[method as any])
360
+ {
361
+ const result = methodFn.apply(self, args);
362
+
363
+ return needsWrap ? toReactive(result) : result;
364
+ }
365
+
366
+ let wrappedFn = fn;
367
+ if (arr !== self)
368
+ {
369
+ if (needsWrap)
370
+ {
371
+ wrappedFn = function (this: unknown, item, index)
372
+ {
373
+ return fn.call(this, toReactive(item), index, self);
374
+ };
375
+ }
376
+ else if (fn.length > 2)
377
+ {
378
+ wrappedFn = function (this: unknown, item, index)
379
+ {
380
+ return fn.call(this, item, index, self);
381
+ };
382
+ }
383
+ }
384
+ const result = methodFn.call(arr, wrappedFn, thisArg);
385
+
386
+ return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result;
387
+ }
388
+
389
+ // instrument reduce and reduceRight to take ARRAY_ITERATE dependency
390
+ function reduce(
391
+ self: unknown[],
392
+ method: keyof Array<any>,
393
+ fn: (acc: unknown, item: unknown, index: number, array: unknown[]) => unknown,
394
+ args: unknown[],
395
+ )
396
+ {
397
+ const arr = shallowReadArray(self);
398
+ let wrappedFn = fn;
399
+ if (arr !== self)
400
+ {
401
+ wrappedFn = function (this: unknown, acc, item, index)
402
+ {
403
+ return fn.call(this, acc, toReactive(item), index, self);
404
+ };
405
+ }
406
+
407
+ return (arr[method] as any)(wrappedFn, ...args);
408
+ }
409
+
410
+ // instrument identity-sensitive methods to account for reactive proxies
411
+ function searchProxy(
412
+ self: unknown[],
413
+ method: keyof Array<any>,
414
+ args: unknown[],
415
+ )
416
+ {
417
+ const arr = toRaw(self) as any;
418
+ PropertyReactivity.track(arr, TrackOpTypes.ITERATE, ARRAY_ITERATE_KEY);
419
+ // we run the method using the original args first (which may be reactive)
420
+ const res = arr[method](...args);
421
+
422
+ // if that didn't work, run it again using raw values.
423
+ if ((res === -1 || res === false) && isProxy(args[0]))
424
+ {
425
+ args[0] = toRaw(args[0]);
426
+
427
+ return arr[method](...args);
428
+ }
429
+
430
+ return res;
431
+ }
432
+
433
+ // instrument length-altering mutation methods to avoid length being tracked
434
+ // which leads to infinite loops in some cases (#2137)
435
+ function noTracking(
436
+ self: unknown[],
437
+ method: keyof Array<any>,
438
+ args: unknown[] = [],
439
+ )
440
+ {
441
+ const res = batchRun(() =>
442
+ noTrack(() =>
443
+ (toRaw(self) as any)[method].apply(self, args)
444
+ )
445
+ );
446
+
447
+ return res;
448
+ }