Vue自定義指令中無法獲取this的問題及解決
自定義指令中無法獲取this
問題
最近在使用自定義指令時遇到一個問題,我想在指令里通過this直接去訪問vue實例數據,但是顯示未定義,經大佬提醒,里面的this很可能不是指向vue實例
解決方法
在函數里增加第三個參數vnode,vnode.context就是指向當前的vue實例
總結
指令里的this不是指向vue實例,可以使用vnode.context獲取this
自定義指令可傳入以下參數
el
:指令所綁定的元素,可以用來直接操作 DOM。binding
:一個對象,包含以下 property:name
:指令名,不包括 v- 前綴。value
:指令的綁定值,例如:v-my-directive=“1 + 1” 中,綁定值為 2。oldValue
:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。expression
:字符串形式的指令表達式。例如 v-my-directive=“1 + 1” 中,表達式為 “1 + 1”。arg
:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數為 “foo”。modifiers
:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象為 { foo: true, bar: true }。vnode
:Vue 編譯生成的虛擬節(jié)點。oldVnode
:上一個虛擬節(jié)點,僅在 update 和 componentUpdated 鉤子中可用。
Vue使用this的這幾個坑你都知道嗎
最近寫vue項目遇到很多this指向的問題,今天來寫一下我總結的this指向
看了很多文章、博客,對于正常函數,誰調用的它,this就指向誰,而箭頭函數沒有this,它的this指向一般就是上下文中,與誰調用它沒關系。
但是在Vue實例中,methods、生命周期函數中如果用的是正常函數,那么它的this就指向Vue實例,也就是vm(本文中的vm是指const vm = new Vue({···})中的vm);如果是箭頭函數,在非嚴格模式下this就指向window對象,嚴格模式下是undefind。
這里我分別來說一下普通函數中的this和Vue中的this
一、普通函數
普通函數的this是由動態(tài)作用域決定,它總指向于它的直接調用者。具體可以分為以下四項:this總是指向它的直接調用者, 例如 obj.func() ,那么func()里的this指的是obj。在默認情況(非嚴格模式,未使用 'use strict'),如果函數沒有直接調用者,this為window;在嚴格模式下,如果函數沒有直接調者,this為undefined使用call,apply,bind綁定的,this指的是綁定的對象
簡單總結:
(1)全局函數中的this指向window
(2)對象中的方法(函數)中的this,指向對象,理解:obj.m=function(){},m和fn等價,因此調用m也相當于調用fn,原理同3)
(3)構造函數中的this指向調用該構造函數的實例對象
(4)特殊this指向: 箭頭函數沒有綁定this,this繼承自外圍作用域,理解:查看上一層級的函數的this的指向,繼承它??!
(5)綁定this指向:apply,call,bind綁定的對象
接下來用實例來介紹下各種this問題
1.全局環(huán)境下,this 始終指向全局對象(window), 無論是否嚴格模式
?? ?console.log(this.document === document); // true ?? ?// 在瀏覽器中,全局對象為 window 對象: ?? ?console.log(this === window); // true ?? ?this.a = 3; ?? ?console.log(window.a); // 3
2.函數直接調用,普通函數內部的this分兩種情況,嚴格模式和非嚴格模式
?? ?//嚴格模式下, this為undefined ?? ?function f2(){ ?? ? ?"use strict"; // 這里是嚴格模式 ?? ? ?return this; ?? ?} ?? ?f2() === undefined; // true ? ? //而非嚴格模式下,this 默認指向全局對象window ?? ?function f1(){ ?? ? ?return this; ?? ?} ?? ?f1() === window; // true
3.對象中的this,對象內部方法的this指向調用這些方法的對象
?? ?//函數的定義位置不影響其this指向,this指向只和調用函數的對象有關。 ?? ?//多層嵌套的對象,內部方法的this指向離被調用函數最近的對象(window也是對象,其內部對象調 ? ? //用方法的this指向內部對象, 而非window)。 ?? ?//例一: ?? ?var obj = { ?? ? ?prop: 37, ?? ? ?f: function() { ?? ? ? ?return this.prop; ?? ? ?} ?? ?}; ?? ?console.log(obj.f()); ?//37 ?? ?var a = obj.f; ?? ?console.log(a()); ?//undefined ?? ? ?? ?var obj = {prop: 37}; ?? ? ?? ?function independent() { ?? ? ?return this.prop; ?? ?} ?? ? ?? ?obj.f = independent; ?? ? ?? ?console.log(obj.f()); //37 ?? ? ?? ?//例二: ?? ?obj.b = { ?? ? ?num: independent, ?? ? ?prop: 42 ?? ?}; ?? ?console.log(obj.b.num()); //42
4.原型鏈中this,原型鏈中的方法的this仍然指向調用它的對象
?? ?var obj = { ?? ? ?f : function(){? ?? ? ? ?return this.a + this.b;? ?? ? ?} ?? ?}; ?? ?var p = Object.create(obj); ?? ?p.a = 1; ?? ?p.b = 4; ?? ? ?? ?console.log(p.f()); // 5 //在p中沒有屬性f,當執(zhí)行p.f()時,會查找p的原型鏈,找到 f 函數并執(zhí)行,但這與函數內部this指向對象 //p 沒有任何關系,只需記住誰調用指向誰。 ? //以上對于函數作為getter & setter 調用時同樣適用。
5.構造函數中this,構造函數中的this與被創(chuàng)建的新對象綁定
注意:當構造器返回的默認值是一個this引用的對象時,可以手動設置返回其他的對象,如果返回值不是一個對象,返回this。
6.call & apply
當函數通過Function對象的原型中繼承的方法 call() 和 apply() 方法調用時, 其函數內部的this值可綁定到 call() & apply() 方法指定的第一個對象上, 如果第一個參數不是對象,JavaScript內部會嘗試將其轉換成對象然后指向它。
7.bind 方法
bind方法在ES5引入, 在Function的原型鏈上, Function.prototype.bind。通過bind方法綁定后, 函數將被永遠綁定在其第一個參數對象上, 而無論其在什么情況下被調用。
8.DOM事件處理函數,當函數被當做監(jiān)聽事件處理函數時, 其 this 指向觸發(fā)該事件的元素 (針對于addEventListener事件)
? // 被調用時,將關聯(lián)的元素變成藍色 ? ? function bluify(e){ ? ? ? console.log(this);//在控制臺打印出所點擊元素 ? ? ? e.stopPropagation();//阻止時間冒泡 ? ? ? e.preventDefault();//阻止元素的默認事件 ? ? ? ? ? this.style.backgroundColor = '#A5D9F3'; ? ? } ? ? var elements = document.getElementsByTagName('*');// 獲取文檔中的所有元素的列表 ? ? // 將bluify作為元素的點擊監(jiān)聽函數,當元素被點擊時,就會變成藍色 ? ? for(var i=0 ; i<elements.length ; i++){ ? ? ? elements[i].addEventListener('click', bluify, false); ? ? }
9.內聯(lián)事件,內聯(lián)事件中的this指向分兩種情況:
- 當代碼被內聯(lián)處理函數調用時,它的this指向監(jiān)聽器所在的DOM元素
- 當代碼被包括在函數內部執(zhí)行時,其this指向等同于函數直接調用的情況,即在非嚴格模式指向全局對象window, 在嚴格模式指向undefined。
10.setTimeout & setInterval,對于延時函數內部的回調函數的this指向全局對象window(當然我們可以通過bind方法改變其內部函數的this指向)
?? ?//默認情況下 ?? ?function Person() { ? ?? ? ? ?this.age = 0; ? ?? ? ? ?setTimeout(function() { ?? ? ? ? ? ?console.log(this); ?? ? ? ?}, 3000); ?? ?} ?? ?//通過bind綁定 ?? ?function Person() { ? ?? ? ? ?this.age = 0; ? ?? ? ? ?setTimeout((function() { ?? ? ? ? ? ?console.log(this); ?? ? ? ?}).bind(this), 3000); ?? ?} ?? ?var p = new Person();//3秒后返回構造函數新生成的對象 Person{...}
11.箭頭函數中的 this,由于箭頭函數不綁定this, 它會捕獲其所在(即定義的位置)上下文的this值, 作為自己的this值
?? ?// call() / apply() / bind() 方法對于箭頭函數來說只是傳入參數,對它的 this 毫無影響。 ?? ?//考慮到 this 是詞法層面上的,嚴格模式中與 this 相關的規(guī)則都將被忽略。(可以忽略是否在嚴格 ? ? //模式下的影響) ?? ?//因為箭頭函數可以捕獲其所在上下文的this值 所以: ?? ?function Person() { ? ?? ? ? ?this.age = 0; ? ?? ? ? ?setInterval(() => { ?? ? ? ? ? ?this.age++;// 回調里面的 `this` 變量就指向了期望的那個對象了 ?? ? ? ?}, 3000); ?? ?} ?? ?var p = new Person(); ?? ?//以上代碼可以得到我們所以希望的值,下圖可以看到,在setTimeout中的this指向了構造函數新生成 ? ? //的對象,而普通函數指向了全局window對象
二、Vue中的this
1.Vue methods
來看看官方文檔給出的解釋:
methods 將被混入到 Vue 實例中??梢灾苯油ㄟ^ 實例vm 訪問這些方法,或者在指令表達式中使用。方法中的 this自動綁定為 Vue 實例(vm)。
注意,不應該使用箭頭函數來定義 method 函數 (例如 plus: () => this.a++)。理由是箭頭函數綁定了父級作用域的上下文,所以 this 將不會按照期望指向 Vue 實例,this.a 將是 undefined。
長話短說,官方的意思是:在Vue實例中,methods中如果用的是正常函數,那么它的this就指向Vue實例;如果是箭頭函數,this就指向window對象;
總結:
Vue methods 中不應該箭頭函數定義methods函數,因為箭頭函數綁定了父級作用域上下文,所以 this 打印出的結果是Window 對象;不使用箭頭函數的情況下,this 實際上是指向了一個 Proxy 對象。
原因是vue 內部實際上對methods屬性中的方法進行了遍歷,將對應的方法通過bind綁定了this,使得this指向實例vm
2.Vue中生命周期鉤子和自定義方法中的this指向當前的 Vue 實例
所有的生命周期鉤子自動綁定 this 上下文到實例中,因此你可以訪問數據,對 property 和方法進行運算。這意味著你不能使用箭頭函數來定義一個生命周期方法 (例如 created: () => this.checkTodos())。這是因為箭頭函數綁定了父上下文,因此 this 與你期待的 Vue 實例不同,this.checkTodos 的行為未定義。
3. Vue 中回調函數中的 this:
- 若回調函數為匿名函數,非嚴格模式下指向 window,嚴格模式下為 undefined。
- 若回調函數為自定義方法,則 this 指向 Vue 實例。
- 若回調函數為 箭頭函數,則 this 指向 Vue 實例。
4. Vue 中 addEventListener 中的 this
通常,事件監(jiān)聽函數中的 this 都指向綁定事件的那個元素, 但是在 Vue 中,監(jiān)聽函數中的 this 也指向 Vue 實例
5.在data里定義Object類型的變量時的this
在data里定義Object類型的變量時,會發(fā)現Object中訪問不到vue的this屬性,例如:
export default { ? data(){ ? ? return { ? ? ? a: "123", ? ? ? b: { ? ? ? ? c: this.a ? ? ? } ? ? }; ? }, ? created() { ? ? console.log("b: ", this.b.c); // undefined ? } }
想在b中訪問this.a的數據,直接訪問會返回undefined,因為這時c中的this指向的是b。這種情況可以用到Object的get屬性進行屬性定義,例如:
export default { ? data(){ ? ? return { ? ? ? a: "123", ? ? ? b: { ? ? ? ? _target: () => this, ? ? ? ? get target() { ? ? ? ? ? return this._target(); ? ? ? ? }, ? ? ? ? ? get c() { ? ? ? ? ? return this.target.a; ? ? ? ? }, ? ? ? }, ? ? }; ? }, ? created() { ? ? console.log("b: ", this.b.c); // 123 ? } }
此處將this映射到了Object變量內部,然后通過get的形式定義屬性并獲取,這樣就解決問題啦。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
使用vue-cli創(chuàng)建項目并webpack打包的操作方法
本文給大家分享使用vue-cli創(chuàng)建項目基于webpack模板打包的配置方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-07-07