自定义指令中无法获取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 编译生成的虚拟节点。oldVnode
:上一个虚拟节点,仅在 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是由动态作用域决定,它总指向于它的直接调用者。具体可以分为以下四项: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.全局环境下,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,当执行p.f()时,会查找p的原型链,找到 f 函数并执行,但这与函数内部this指向对象 //p 没有任何关系,只需记住谁调用指向谁。
?
//以上对于函数作为getter & setter 调用时同样适用。
5.构造函数中this,构造函数中的this与被创建的新对象绑定
注意:当构造器返回的默认值是一个this引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回this。
6.call & apply
当函数通过Function对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的this值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。
7.bind 方法
bind方法在ES5引入, 在Function的原型链上, Function.prototype.bind。通过bind方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。
8.DOM事件处理函数,当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件)
? // 被调用时,将关联的元素变成蓝色
? ? function bluify(e){
? ? ? console.log(this);//在控制台打印出所点击元素
? ? ? e.stopPropagation();//阻止时间冒泡
? ? ? e.preventDefault();//阻止元素的默认事件 ? ?
? ? ? this.style.backgroundColor = '#A5D9F3';
? ? }
? ? var elements = document.getElementsByTagName('*');// 获取文档中的所有元素的列表
? ? // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
? ? for(var i=0 ; i<elements.length ; i++){
? ? ? elements[i].addEventListener('click', bluify, false);
? ? }
9.内联事件,内联事件中的this指向分两种情况:
- 当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
- 当代码被包括在函数内部执行时,其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 相关的规则都将被忽略。(可以忽略是否在严格
? ? //模式下的影响)
?? ?//因为箭头函数可以捕获其所在上下文的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
通常,事件监听函数中的 this 都指向绑定事件的那个元素, 但是在 Vue 中,监听函数中的 this 也指向 Vue 实例
5.在data里定义Object类型的变量时的this
在data里定义Object类型的变量时,会发现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的形式定义属性并获取,这样就解决问题啦。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。