聊聊vue2.x中this的指向问题,它为什么指向vue实例?
本篇文章带大家聊聊vue2.x中this的指向问题,介绍一下this为什么指向vue实例,希望对大家有所帮助!
组内代码走查偶然提起为什么this可以直接调用到data、methods和props、computed里的值,然后大家都有一些猜想,但没有一个明确的答案,为搞清这个问题,查阅了vue的源码,有一些了解,写个文章记录一下。
抛出问题
正常开发vue代码,大差不差都会这么写
export default { data() { return { name: '彭鱼宴' } }, methods: { greet() { console.log(`hello, 我是${this.name}`) } } }
为什么这里的this.name可以直接访问到data里定义的name呢,或者this.someFn可以直接访问到methods里定义的函数呢,带着这个问题开始看vue2.x的源码找答案。
源码分析
这里先贴个vue的源码地址vue源码。我们先看看vue实例的构造函数,构造函数在源码的目录/vue/src/core/instance/index.js下,代码量不多,全部贴出来看看
function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) export default Vue
构造函数很简单,if (!(this instanceof Vue)){}
判断是不是用了 new
关键词调用构造函数,没有则抛出warning,这里的this
指的是Vue的一个实例。如果正常使用了new
关键词,就走_init
函数,是不是很简单。
_init函数分析
let uid = 0 export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
_init函数有点长,做了很多事情,这里就不一一解读,和我们此次探索相关的内容应该在initState(vm)这个函数中,我们继续到initState这个函数里看看。
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) } }
可以看出initState做了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
。
至于data的这种设计模式的优缺点,大家也可以继续探索,毕竟不在此次问题讨论之中。
【相关推荐:vue.js视频教程】
以上是聊聊vue2.x中this的指向问题,它为什么指向vue实例?的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

同事因为this指向的问题卡住的bug,vue2的this指向问题,使用了箭头函数,导致拿不到对应的props。当我给他介绍的时候他竟然不知道,随后也刻意的看了一下前端交流群,至今最起码还有70%以上的前端程序员搞不明白,今天给大家分享一下this指向,如果啥都没学会,请给我一个大嘴巴子。

本篇文章带大家解读vue源码,来介绍一下Vue2中为什么可以使用 this 访问各种选项中的属性,希望对大家有所帮助!

jQuery中this关键字的灵活运用在jQuery中,this关键字是一个非常重要且灵活的概念,它用来引用当前正在操作的DOM元素。通过合理的运用this关键字,我们可以方便地操作页面中的元素,实现各种交互效果和功能。本文将结合具体的代码示例,介绍this关键字在jQuery中的灵活运用。简单的this示例首先,我们来看一个简单的this示例。假设我们有一

一、this关键字1.this的类型:哪个对象调用就是哪个对象的引用类型二、用法总结1.this.data;//访问属性2.this.func();//访问方法3.this();//调用本类中其他构造方法三、解释用法1.this.data这种是在成员方法中使用让我们来看看不加this会出现什么样的状况classMyDate{publicintyear;publicintmonth;publicintday;publicvoidsetDate(intyear,intmonth,intday){ye

什么是this?下面本篇文章给大家介绍一下JavaScript中的this,并聊聊this在函数不同调用方式下的区别,希望对大家有所帮助!

JavaScript中箭头函数是一种比较新的语法,没有自己的this关键字,相反箭头函数的this指向包含它的作用域对象,影响方面有:1、箭头函数中的this是静态的;2、箭头函数不能作为构造函数使用;3、箭头函数不能用作方法。

1.this引用1.1为什么要有this引用先来写一个日期类的例子:publicclassclassCode{publicintyear;publicintmonth;publicintday;publicvoidsetDay(inty,intm,intd){year=y;month=m;day=d;}publicvoidprintDate(){System.out.println(year+"-"+month+"-"+day);}publicstatic

JavaScript如何改变this指向?下面本篇文章给大家介绍一下JS改变this指向的三种方法,希望对大家有所帮助!
