手抄Vue(三)—— 数组响应的补充

上篇 Vue数据响应思路(二)—— 数组的响应 中,没有考虑兼容性问题,__proto__ 属性在 IE10 以及更低版本 IE 中是不支持的,需实现兼容方案。

思路就是直接在数组实例上面定义新的同名变异方法作为“拦截器”:

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
const mutationMethods = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
const arrayMethods = Object.create(Array.prototype)
const arrayProto = Array.prototype

mutationMethods.forEach(method => {
  arrayMethods[method] = function (...args) {
    const result = arrayProto[method].apply(this, args)
    console.log(`我截获了对数组的${method}操作`)
    return result
  }
})

const arr = ['kobe', 'jordan']

mutationMethods.forEach(method => {
  arr[method] = arrayMethods[method]
})

arr.push('wade') // '我截获了对数组的push操作'
console.log(JSON.stringify(arr)) // '["kobe","jordan","wade"]'

数组变异方法扩展功能以及原功能均正常。

但仍然有一个问题:

1
console.log(JSON.stringify(Object.keys(arr))) // ["0","1","2","push","pop","shift","unshift","splice","sort","reverse"]

直接添加到数组实例上的这些方法都是可枚举的,按理说,数组本身的变异方法不应该被枚举出来。

那么,使用 Object.defineProperty 将其定义为不可枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
mutationMethods.forEach(method => {
  Object.defineProperty(arr, method, {
    enumerable: false,
    writable: true,
    configurable: true,
    value: arrayMethods[method]
  })
})

arr.push('wade') // '我截获了对数组的push操作'
console.log(JSON.stringify(arr)) // '["kobe","jordan","wade"]'

console.log(JSON.stringify(Object.keys(arr))) // '["0","1","2"]'

那么低版本 IE 的兼容代码就成了这样:

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
const mutationMethods = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
const arrayMethods = Object.create(Array.prototype)
const arrayProto = Array.prototype

mutationMethods.forEach(method => {
  arrayMethods[method] = function (...args) {
    const result = arrayProto[method].apply(this, args)
    console.log(`我截获了对数组的${method}操作`)
    return result
  }
})

const arr = ['kobe', 'jordan']

mutationMethods.forEach(method => {
  Object.defineProperty(arr, method, {
    enumerable: false,
    writable: true,
    configurable: true,
    value: arrayMethods[method]
  })
})

结合上一篇,便可实现最终兼容方案:

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
const mutationMethods = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
const arrayMethods = Object.create(Array.prototype)
const arrayProto = Array.prototype

mutationMethods.forEach(method => {
  arrayMethods[method] = function (...args) {
    const result = arrayProto[method].apply(this, args)
    console.log(`我截获了对数组的${method}操作`)
    return result
  }
})

const arr = ['kobe', 'jordan']

if ('__proto__' in {}) {
  arr.__proto__ = arrayMethods
} else {
  mutationMethods.forEach(method => {
    Object.defineProperty(arr, method, {
      enumerable: false,
      writable: true,
      configurable: true,
      value: arrayMethods[method]
    })
  })
}