# 数据初始化

数据初始化是initState里实现,涉及数据响应相关的内容

if (opts.data) {
  initData(vm)
} else {
  observe(vm._data = {}, true /* asRootData */)
}

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function' //判断data是否为function,此时data 变量已经不是函数了,而是最终的数据对象
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {//纯对象
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  //防止props 和methods 名字和data的key相同
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  //props优先级 > data优先级 > methods优先级
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {//key 是否是保留键 $_开头
    //实例对象 vm 上定义与 data 数据字段同名的访问器属性,并且这些属性代理的值是 vm._data 上对应属性的值
      proxy(vm, `_data`, key)
    }
  }
  // observe data 响应系统的开始
  observe(data, true /* asRootData */)
}

//通过调用 data 选项从而获取数据对象
function getData (data: Function, vm: Component): any {
  // #7573 disable dep collection when invoking data getters
  pushTarget()  //防止使用 props 数据初始化 data 数据时收集冗余的依赖
  try {
    return data.call(vm, vm)
  } catch (e) {
    handleError(e, vm, `data()`)
    return {}
  } finally {
    popTarget()
  }
}

# observe 工厂函数

function observe (value: any, asRootData: ?boolean): Observer | void { //asRootData根级数据
  if (!isObject(value) || value instanceof VNode) {//不是对象或者是VNode实例 什么不做
    return
  }
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {//避免重复观测数据
    ob = value.__ob__
  } else if (
    shouldObserve &&//是否该观测
    !isServerRendering() &&//不是服务端渲染
    (Array.isArray(value) || isPlainObject(value)) &&//数组或纯对象
    Object.isExtensible(value) &&//必须是可扩展的
    //Object.preventExtensions()、Object.freeze() 以及 Object.seal()可以使对象不可扩展
    !value._isVue//避免Vue 实例对象被观测
  ) {
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}

# Observer 构造函数

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()//收集依赖,dependence
    this.vmCount = 0
    //为数据对象定义了一个 __ob__ 属性
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      //无论是 protoAugment 函数还是 copyAugment 函数,他们的目的只有一个:把数组实例与代理原型或与代理原型中定义的函数联系起来,从而拦截数组变异方法
        const augment = hasProto//是否有__proto__ ie11有
        ? protoAugment
        : copyAugment
        function protoAugment (target, src: Object, keys: any) {//数组实例的原型指向代理原型
          target.__proto__ = src
        }
        function copyAugment (target: Object, src: Object, keys: Array<string>) {
          for (let i = 0, l = keys.length; i < l; i++) {
            const key = keys[i]
            def(target, key, src[key])//使用 def 函数在数组实例上定义与数组变异方法同名的且不可枚举的函数,这样就实现了拦截操作
          }
        }
        //value 数组实例本身
        //arrayMethods 代理原型
        //const arrayKeys = Object.getOwnPropertyNames(arrayMethods) arrayMethods 对象上的 key,所有我们要拦截的数组变异方法的名字
        //arrayKeys = ['push','pop','shift','unshift','splice','sort', 'reverse']
        augment(value, arrayMethods, arrayKeys)
        this.observeArray(value)
    } else {
        this.walk(value)
    }
  }

  walk (obj: Object) {
     const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
        defineReactive(obj, keys[i])
    }
  }
  
  observeArray (items: Array<any>) {
    // 省略...
  }
}

# defineReactive 函数

//将数据对象的数据属性转换为访问器属性,数据对象的属性设置一对 getter/setter
function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()
  const property = Object.getOwnPropertyDescriptor(obj, key)//属性描述对象
  if (property && property.configurable === false) { //以被改变或者属性可被删除
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
//即 每一个数据字段都通过闭包引用着属于自己的 dep 常量
  let childOb = !shallow && observe(val)//shallow 深度观测
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
      //正确地返回属性值以及收集依赖
    get: function reactiveGetter () {
      //getter 常量中保存的是属性原型的 get 函数,如果 getter 存在那么直接调用该函数,
      //并以该函数的返回值作为属性的值,保证属性的原有读取操作正常运作
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        // 这里闭包引用了上面的 dep 常量
        dep.depend()//收集依赖
         if (childOb) {
          childOb.dep.depend()//
          if (Array.isArray(value)) {
            dependArray(value)//数组每个元素的依赖收集

            function dependArray (value: Array<any>) {
            for (let e, i = 0, l = value.length; i < l; i++) {
              e = value[i]
              //该元素的值拥有 __ob__ 对象和 __ob__.dep 对象,那说明该元素也是一个对象或数组,此时只需要手动执行 __ob__.dep.depend() 即可达到收集依赖的目的
              e && e.__ob__ && e.__ob__.dep.depend()
              //数组的元素仍然是一个数组,那么需要递归调用 dependArray 继续收集依赖
              if (Array.isArray(e)) {
                dependArray(e)
              }
            }
}
          }
        }
      }
      return value
    },
    //set 正确地为属性设置新值,第二是能够触发相应的依赖
    set: function reactiveSetter (newVal) {
      //需要拿到原有的值与新的值作比较,并且只有在原有值与新设置的值不相等的情况下才需要触发依赖和重新设置属性值
      const value = getter ? getter.call(obj) : val
      //NaN===NaN  false
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()//用来打印辅助信息
      }
      if (setter) {//原有的set存在
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      //在 !shallow 为真,使用新的观测对象重写 childOb 的值
      childOb = !shallow && observe(newVal)
        // 这里闭包引用了上面的 dep 常量
      dep.notify()//触发依赖
    }
  })
}

# 拦截数组变异方法

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)//arrayMethods 对象的原型是真正的数组构造函数的原型

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  //使用 def 函数在 arrayMethods 对象上定义与数组变异方法同名的函数,从而做到拦截的目的
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args) //原本变异方法的返回值
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2) //第三个参数开始到最后一个参数都是数组的新增元素
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change 该数组的所有依赖(观察者)全部拿出来执行
    ob.dep.notify()
    return result //保证了拦截函数的功能与数组原本变异方法的功能是一致
  })
})

function observeArray (items: Array<any>) {
  for (let i = 0, l = items.length; i < l; i++) {
    observe(items[i])
  }
}

# Vue.$set和Vue.$delete

//对象 key,value
function set (target: Array<any> | Object, key: any, val: any): any {
  //isUndef 函数用来判断一个值是否是 undefined 或 null,如果是则返回 true
  //isPrimitive 函数用来判断一个值是否是原始类型值,如果是则返回 true
  //理论上只能为对象(或数组)添加属性(或元素)
  if (process.env.NODE_ENV !== 'production' &&
  (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${target}`)
  }
  //isValidArrayIndex 检验是否是有效的数组索引
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    //将数组的长度修改为 target.length 和 key 中的较大者
    target.length = Math.max(target.length, key)
    //指定位置元素的值替换为新值,splice 方法本身是能够触发响应的
    target.splice(key, 1, val)
    return val
  }
  //key 在 target 对象上,或在 target 的原型链上,同时必须不能在 Object.prototype 
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  //数据对象 __ob__ 属性的引用
  const ob = target.__ob__
  //vue实例  当使用 Vue.set/$set 函数为根数据对象添加属性时,是不被允许的,这样做是永远触发不了依赖的。原因就是根数据对象的 Observer 实例收集不到依赖(观察者),
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn('...')
    return val
  }
  if (!ob) {//不存在ob,赋值
    target[key] = val
    return val
  }
  //设置属性值
  defineReactive(ob.value, key, val)
  //触发响应
  ob.dep.notify()
  return val
  }
//target key
  export function del (target: Array<any> | Object, key: any) {
    if (process.env.NODE_ENV !== 'production' &&
    //target 是否是 undefined 或 null 或者是原始类型值
      (isUndef(target) || isPrimitive(target))
    ) {
      warn(`Cannot delete reactive property on undefined, null, or primitive value: ${target}`)
    }
    //去删除一个数组的索引
    if (Array.isArray(target) && isValidArrayIndex(key)) {
      target.splice(key, 1)
      return
    }
    //同样不能使用 Vue.delete/$delete 删除 Vue 实例对象或根数据的属性
    const ob = target.__ob__
    if (target._isVue || (ob && ob.vmCount)) {
      process.env.NODE_ENV !== 'production' && warn(
        'Avoid deleting properties on a Vue instance or its root $data ' +
        '- just set it to null.'
      )
      return
    }
    //检测 key 是否是 target 对象自身拥有的属性
    if (!hasOwn(target, key)) {
      return
    }
    delete target[key]
    if (!ob) {
      return
    }
    //触发响应
    ob.dep.notify()
  }