Vue2中的this为什么指向Vue实例(Vm)

不懂程序的小白 小白
前端领域优质创作者
博客专家认证
2022-07-28 14:34:39

initState函数

export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

     在这个函数中主要做了5件事;

    • 初始化props
    • 初始化methods
    • 初始化data
    • 初始化computed
    • 初始化watch

    我们先重点看看初始化methods做了什么

    initMethods 初始化方法

    function initMethods (vm, methods) {
    
        var props = vm.$options.props;
    
        for (var key in methods) {
    
          {
    
            if (typeof methods[key] !== 'function') {
    
              warn(
    
                "Method \"" + key + "\" has type \"" + (typeof methods[key]) + "\" in the component definition. " +
    
                "Did you reference the function correctly?",
    
                vm
    
              );
    
            }
    
            if (props && hasOwn(props, key)) {
    
              warn(
    
                ("Method \"" + key + "\" has already been defined as a prop."),
    
                vm
    
              );
    
            }
    
            if ((key in vm) && isReserved(key)) {
    
              warn(
    
                "Method \"" + key + "\" conflicts with an existing Vue instance method. " +
    
                "Avoid defining component methods that start with _ or $."
    
              );
    
            }
    
          }
    
          vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
    
        }
    }

    initMethods主要是一些判断:

    判断methods中定义的函数是不是函数,不是函数就抛warning;
    
    判断methods中定义的函数名是否与props冲突,冲突抛warning;
    
    判断methods中定义的函数名是否与已经定义在Vue实例上的函数相冲突,冲突的话就建议开发者用_或者$开头命名;

    除去上述说的这些判断,最重要的就是在vue实例上定义了一遍methods里所有的方法,并且使用bind函数将函数的this指向Vue实例上,就是我们new Vue()的实例对象上。

    这就解释了为啥this可以直接访问到methods里的方法。

    initData 初始化数据

    function initData (vm) {
    
        var data = vm.$options.data;
    
        data = vm._data = typeof data === 'function'
    
          ? getData(data, vm)
    
          : data || {};
    
        if (!isPlainObject(data)) {
    
          data = {};
    
          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
    
        var keys = Object.keys(data);
    
        var props = vm.$options.props;
    
        var methods = vm.$options.methods;
    
        var i = keys.length;
    
        while (i--) {
    
          var key = keys[i];
    
          {
    
            if (methods && hasOwn(methods, key)) {
    
              warn(
    
                ("Method \"" + key + "\" has already been defined as a data property."),
    
                vm
    
              );
    
            }
    
          }
    
          if (props && hasOwn(props, key)) {
    
            warn(
    
              "The data property \"" + key + "\" is already declared as a prop. " +
    
              "Use prop default value instead.",
    
              vm
    
            );
    
          } else if (!isReserved(key)) {
    
            proxy(vm, "_data", key);
    
          }
    
        }
    
        // observe data
    
        observe(data, true /* asRootData */);
    
    }

    initdata做了哪些事情呢:

    • 先在实例 _data 上赋值,getData函数处理 data 这个 function,返回的是一个对象
    • 判断最终获取到的 data, 不是对象给出警告。
    • 判断methods里的函数和data里的key是否有冲突
    • 判断props和data里的key是否有冲突
    • 判断是不是内部私有的保留属性,若不是就做一层代理,代理到 _data 上
    • 最后监听data,使之成为响应式数据

    再看下proxy函数做了什么:

    function noop (a, b, c) {}
    
    var sharedPropertyDefinition = {
    
        enumerable: true,
    
        configurable: true,
    
        get: noop,
    
        set: noop
    
    };
    
     
    
    function proxy (target, sourceKey, key) {
    
        sharedPropertyDefinition.get = function proxyGetter () {
    
          return this[sourceKey][key]
    
        };
    
        sharedPropertyDefinition.set = function proxySetter (val) {
    
          this[sourceKey][key] = val;
    
        };
    
        Object.defineProperty(target, key, sharedPropertyDefinition);
    
    }

    其实这里的Object.defineProperty就是用来定义对象的

    proxy的用处就是使this.name指向this._data.name

    剩下的observe函数不在此次探讨范围,感兴趣的朋友可以自己去看看源码。

    总结

    • methods里的方法通过 bind 指定了this为 new Vue的实例(vm),methods里的函数也都定义在vm上了,所以可以直接通过this直接访问到methods里面的函数。

    • data函数返回的数据对象也都存储在了new Vue的实例(vm)上的_data上了,访问 this.name时实际访问的是Object.defineProperty代理后的 this._data.name

    ...全文
    512 1 打赏 收藏 转发到动态 举报
    写回复
    用AI写文章
    1 条回复
    切换为时间正序
    请发表友善的回复…
    发表回复
    不懂程序的小白 小白 2022-11-18
    精选
    • 打赏
    • 举报
    回复
    1.00元
    1

    新功能,新支持哦!

    16

    社区成员

    发帖
    与我相关
    我的任务
    社区描述
    再疯狂的梦想,也抵不过程序员一味地坚持...
    javascriptvue.jses6 个人社区
    社区管理员
    • 木偶☜
    加入社区
    • 近7日
    • 近30日
    • 至今
    社区公告
    暂无公告

    试试用AI创作助手写篇文章吧