深入浅出vuejs读书笔记----------2、Array的变化侦测

1、如何追踪变化

在这里插入图片描述
如上图,使用拦截器将array.prototype覆盖。当使用array原型中的方法时,其实使用的是拦截器中的方法。通过拦截器,就可以侦听Array的变化。

2、拦截器

拦截器:和array.prototype一样的object,里面的属性一样,只不过可改变数组自身的方法是处理过的。
array原型中可改变自身的方法有7个:push, pop, shift, unshift, splice, sort 和 reverse 。

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)  // 创建新的对象
['push','pop','shift','unshift','splice','sort','reverse'].foreach(function(method){
  const original = arrayProto[method]  // 缓存原始方法
  Object.defineProperty(arrayMethods, method, {
    value: function mutator (...args) {
			return original.apply(this, args)
    },
    enumerable:false,
    writable:true,
    configurable:true
  })
})

Object.create
Object.create(proto,[propertiesObject])
proto: 新创建对象的原型对象
[propertiesObject]:可选,添加到新对象的属性,不属于其原型链的属性
详情可参考:https://juejin.im/post/5acd8ced6fb9a028d444ee4e

Object.defineProperty
直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
obj: 要在其上定义属性的对象。
prop: 要定义或修改的属性的名称。
descriptor: 将被定义或修改的属性描述符。
详情可参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

通过Observer将数据转换成响应式的,在Observer中覆盖会被转换成响应式Array的原型。

export class Observer {
	constructor (value) {
  	this.value = value
    
    if (Array.isArray(value)) {
    	value.__prop__ = arrayMethods  // 新增
    }else {
    	this.walk(value)
    }
  }
}

value.prop = arrayMethods :就是将拦截器赋值给value.prop,通过__prop__覆盖value的原型。如下图所示。
在这里插入图片描述
以上是浏览器支持__prop__的情况。当浏览器不支持__prop__该怎么办呢?
vue的做法:如不可以使用__prop__,直接将arrayMethods的方法设置到被侦测的数组上。

const hasProto = '__ prop__' in {}
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)

export class Observer {
  constructor(value){
  	this.value = value
    
    if(Array.isArray(value)) {
    	const augment = hasProto ? protoAugment : copyAugment
      augment(value,arrayMethods,arrayKeys)
    }else {
    	this.walk(value)
    }
  }
  ...
}
  function protoAugment(target,src,keys){
  	target.__prop__ = src
  }
	function copyAugment(target,src,keys){
  	for(let i = 0;i < keys.length;i++){
    		const key = keys[i]
        def(target,key,src[key])
    	}
  }

​Object.getOwnPropertyNames()
返回一个由指定对象的所有自身属性的属性名组成的数组。

var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

// 使用Array.forEach输出属性名和属性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
  console.log(val + " -> " + obj[val]);
});
// 输出
// 0 -> a
// 1 -> b
// 2 -> c

Array和object一样,都是在getter中收集依赖,并将依赖存到def中。

Array在getter中收集依赖,在拦截器中触发依赖

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值