5.7.1 数组的索引与 length
数组中「索引」与「length」是联动的:改索引可能改
length,改length可能删索引,因此触发响应时必须互相带上。
一、通过索引设置元素
- 设置
arr[1] = 'bar'同样走内部方法 [[Set]],而[[Set]]依赖 [[DefineOwnProperty]] —— 数组与常规对象的差异就在这里; - 按规范 10.4.2.1 节:如果设置的索引值大于数组当前的长度,则要更新数组的
length属性; - 所以此时不仅要触发与索引
'1'关联的副作用函数,还要触发与length关联的副作用函数。
数组下的操作类型判断
数组不能再用 hasOwnProperty 判断 ADD/SET,而要比较索引与长度:
const type = Array.isArray(target)
// 索引值小于数组长度 → SET(不改变长度);否则 → ADD(会隐式改变 length)
? (Number(key) < target.length ? 'SET' : 'ADD')
: (Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD')
trigger 中补上 length 依赖
// 当操作类型为 ADD 且目标对象是数组时,取出并执行与 length 相关联的副作用函数
if (type === 'ADD' && Array.isArray(target)) {
const lengthEffects = depsMap.get('length')
lengthEffects && lengthEffects.forEach(effectFn => {
if (effectFn !== activeEffect) effectsToRun.add(effectFn)
})
}
const arr = reactive(['foo']) // 原长度 1
effect(() => {
console.log(arr.length) // 1
})
arr[1] = 'bar' // 索引 1 ≥ 长度 1 → ADD,数组长度变为 2,触发 length 的副作用函数
二、修改 length 属性
arr.length = 0 会隐式删除索引大于等于新 length 的所有元素,因此也应该触发这些元素对应的副作用函数。
要点:只有索引值 ≥ 新 length 的元素才需要触发响应。
function trigger(target, key, type, newVal) { // 新增第四个参数 newVal
// ...收集与 key、ITERATE_KEY 相关的副作用函数
// 目标对象是数组,并且修改的是 length 属性
if (Array.isArray(target) && key === 'length') {
// 对于索引 ≥ 新的 length 值的元素,把相关副作用函数取出待执行
depsMap.forEach((effects, key) => {
if (key >= newVal) {
effects.forEach(effectFn => {
if (effectFn !== activeEffect) effectsToRun.add(effectFn)
})
}
})
}
// ...统一执行 effectsToRun
}
调用处要把新值传进去:
trigger(target, key, type, newVal)
const arr = reactive(['foo'])
effect(() => {
console.log(arr[0]) // foo
})
arr.length = 0 // 第 0 个元素被隐式删除,触发响应
易错点:
depsMap中可能存有Symbol类型的 key(如ITERATE_KEY),key >= newVal会尝试把它转成数字并抛出TypeError: Cannot convert a Symbol value to a number。数组的遍历依赖改用'length'作为 key,正是为了避开这个问题(见 5.7.2)。
三、小结
| 操作 | 影响 | 需要额外触发的 key |
|---|---|---|
arr[1] = 'bar'(索引 ≥ 长度) | 隐式修改 length | 'length' |
arr[1] = 'bar'(索引 < 长度) | 只改元素 | 无 |
arr.length = 0 | 隐式删除索引 ≥ 新长度的元素 | 所有索引 ≥ 新 length 的 key |
相关
- 5.7 代理数组:数组有哪些读写操作要处理
- 5.7.2 遍历数组:
for...in遍历数组时为什么要用length作为追踪的 key