您好,登錄后才能下訂單哦!
這篇文章主要介紹“Vue2為什么能通過(guò)this訪問(wèn)到data與methods的屬性”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Vue2為什么能通過(guò)this訪問(wèn)到data與methods的屬性”文章能幫助大家解決問(wèn)題。
看這一段代碼我們能知道有個(gè)Vue的構(gòu)造函數(shù) ,然后我們使用new Vue創(chuàng)建了它的實(shí)例,并給它傳了一個(gè)對(duì)象參數(shù),里面有data和methods,那么在這個(gè)Vue構(gòu)造函數(shù)做了什么才能讓我使用this可以直接訪問(wèn)里面的屬性或者方法呢?
//創(chuàng)建vue的實(shí)例 const vm = new Vue({ data: { desc: '為什么this能夠直接訪問(wèn)data中的屬性', }, methods: { sayName() { console.log(this.desc); } }, }); console.log(vm.name); console.log(vm.sayName());
接收一個(gè)options參數(shù)
使用 instanceof 判斷 this對(duì)象上是否出現(xiàn)了Vue的prototype,我們都知道this的指向是取決于誰(shuí)調(diào)用
this._init(options) 證明在這調(diào)用要么我們創(chuàng)建的實(shí)例上有_init方法要么方法在Vue的prototype上,但是我們可以看到實(shí)例上并沒(méi)有_init方法 ,那么肯定在一個(gè)地方給Vue的prototype上加上了_init方法 繼續(xù)往下看
function Vue(options) { if (!(this instanceof Vue) ) { warn('Vue是一個(gè)構(gòu)造函數(shù),應(yīng)使用“new”關(guān)鍵字調(diào)用'); } this._init(options); } //Vue() //錯(cuò)誤的調(diào)用方式 進(jìn)入警告判斷 此時(shí)this指向window 然后window的 window.__proto__的指向的Window構(gòu)造函數(shù)的prototype
在源碼中會(huì)看到很多初始化的函數(shù)在這我們initMixin()
這個(gè)函數(shù)就是在Vue的原型上增加了_init方法,方法接收一個(gè)參數(shù),然后定義了vm變量,在我看的時(shí)候就想看看這個(gè)函數(shù)的this指向誰(shuí),其實(shí)也不難函數(shù)掛在Vue構(gòu)造函數(shù)的原型上,調(diào)用還是在構(gòu)造函數(shù)里面使用this調(diào)用,構(gòu)造函數(shù)的this指向Vue實(shí)例,根據(jù)this的指向規(guī)則 此時(shí)的vm就指向了Vue構(gòu)造函數(shù)的實(shí)例。
使用this的訪問(wèn)規(guī)則如果實(shí)例上沒(méi)有就去原型上找
然后執(zhí)行 initState(vm)
initMixin(Vue) function initMixin(Vue) { //prototype上增加init方法 Vue.prototype._init = function (options) { var vm = this; //Vue實(shí)例 vm.age = 30 //代碼進(jìn)行了刪減 initState(vm); } } //這里只是舉例測(cè)試 const vm = new Vue({}) console.log(vm.age) //30
這里就是對(duì)我們傳入的data 或者methods進(jìn)行不同的處理
//initState方法代碼進(jìn)行了刪減 function initState(vm) { vm._watchers = []; var opts = vm.$options; //這里是我們?cè)趧?chuàng)建實(shí)例的時(shí)候傳的參數(shù) //如果傳了methods 則去調(diào)用 if (opts.methods) { initMethods(vm, opts.methods); } if (opts.data) { initData(vm); } else { observe(vm._data = {}, true /* asRootData */); } }
如果有methods則取調(diào)用initMethods方法
前面主要是判斷 methods中的值是不是函數(shù),key有沒(méi)有跟props沖突等
最后一段代碼就是在vm的實(shí)例上增加方法vm[key]=methods[key],在讀的時(shí)候我有這樣一個(gè)以為為什么還要用bind改變this指向呢不本來(lái)就是寫在vm實(shí)例上的方法嗎 只能使用vm調(diào)用 那么方法的this不就指向vm嗎?
/* vm:構(gòu)造函數(shù)實(shí)例 methods:我們傳的methods對(duì)象 */ function initMethods(vm, methods) { var props = vm.$options.props; //循環(huán)methods對(duì)象 for (var key in methods) { { //判斷是否是函數(shù) 不是的化則作出警告 if (typeof methods[key] !== 'function') { warn( "Method "" + key + "" has type "" + (typeof methods[key]) + "" in the component definition. " + "Did you reference the function correctly?", vm ); } //判斷 methods 中的每一項(xiàng)是不是和 props 沖突了,如果是,警告。 if (props && hasOwn(props, key)) { warn( ("Method "" + key + "" has already been defined as a prop."), vm ); } //判斷 methods 中的每一項(xiàng)是不是已經(jīng)在 new Vue實(shí)例 vm 上存在,而且是方法名是保留的 _ $ (在JS中一般指內(nèi)部變量標(biāo)識(shí))開頭,如果是警告。 if ((key in vm) && isReserved(key)) { warn( "Method "" + key + "" conflicts with an existing Vue instance method. " + "Avoid defining component methods that start with _ or $." ); } } //給實(shí)例增加methods中的方法 這樣其實(shí)我們就已經(jīng)可以用vm訪問(wèn) 到methods中的方法了 vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm); } }
問(wèn)了群里大佬之后原來(lái)這步操作時(shí)為了防止用戶改變this指向,專門做了個(gè)例子
在這我有定義了對(duì)象a里面有個(gè)age屬性和fn,fn我賦值vm實(shí)例上的sayHi,然后a.fn()調(diào)用很明顯this的指向已經(jīng)被改變了,使用bind之后則不會(huì)
const vm = new Vue({ methods: { sayHi() { console.log(this.age, 'hello-this') } } }); let a = { age: 15, fn: vm.sayHi } console.log(a.fn(), 'vm') //打印15
data是如何做到的使用this可以直接訪問(wèn)的,其實(shí)原理都一樣,
首先在vm實(shí)例上增加了_data,里面存的我們傳入的data參數(shù)
function initData(vm) { var data = vm.$options.data; data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}; //如果不是對(duì)象則警告 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]; //判斷key值有沒(méi)有跟methods中的key重名 { if (methods && hasOwn(methods, key)) { warn( ("Method "" + key + "" has already been defined as a data property."), vm ); } } //判斷key值有沒(méi)有跟props中的key重名 if (props && hasOwn(props, key)) { warn( "The data property "" + key + "" is already declared as a prop. " + "Use prop default value instead.", vm ); //是否是內(nèi)部私有保留的字符串$ 和 _ 開頭 } else if (!isReserved(key)) { //代理 proxy(vm, "_data", key); } } // observe data observe(data, true /* asRootData */); }
get 和 set 方法 注意里面的this 指向vm實(shí)例對(duì)象,上面已經(jīng)在vm實(shí)例對(duì)象上增加了_data 所有在獲取或者設(shè)置屬性值的時(shí)候 都是this._data[key] 也就是vm._data[key],
然后通過(guò)Object.defineProperty往實(shí)例對(duì)象上添加屬性,所以當(dāng)我們?cè)L問(wèn)vm[key] 也就是 vm._data[key]
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); }
//創(chuàng)建vue構(gòu)造函數(shù) function Vue(options) { if (!(this instanceof Vue) ) { warn('Vue是一個(gè)構(gòu)造函數(shù),應(yīng)使用“new”關(guān)鍵字調(diào)用'); } this._init(options); } //初始化 initMixin(Vue); function initMixin(Vue) { //prototype上增加init方法 Vue.prototype._init = function (options) { var vm = this; //Vue實(shí)例 let methods = options.methods initState(vm); } } //initState方法代碼進(jìn)行了刪減 function initState(vm) { vm._watchers = []; var opts = vm.$options; //這里是我們?cè)趧?chuàng)建實(shí)例的時(shí)候傳的參數(shù) //如果傳了methods 則去調(diào)用 if (opts.methods) { initMethods(vm, opts.methods); } if (opts.data) { initData(vm); } else { observe(vm._data = {}, true /* asRootData */); } } /* vm:構(gòu)造函數(shù)實(shí)例 methods:我們傳的methods對(duì)象 */ function initMethods(vm, methods) { var props = vm.$options.props; //循環(huán)methods對(duì)象 for (var key in methods) { { //一些條件判斷 } //給實(shí)例增加methods中的方法 這樣其實(shí)我們就已經(jīng)可以用vm訪問(wèn) 到methods中的方法了 vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm); } } const vm = new Vue({ methods: { sayHi() { console.log('hello-this') } } }); vm.sayHi() //hello-this
關(guān)于“Vue2為什么能通過(guò)this訪問(wèn)到data與methods的屬性”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。