背景
- 在vue中做本地计时器的场景 需要用到setTimeInteval
- 在页面做debouce的场景, debouce的本质上是一个setTimeout
问题的具体场景
在绑定setTimeout 的时候需要绑定执行函数 有以下两种绑定方式
// 方式 1
mounted () {
const timer = setInteval(() => {
this.doSth();
}, 1000
)
}
// 执行结果 this 是 window
// this.doSth 是undefined
// 方式 2
mounted () {
const timer = setInteval(this.doSth(), 1000
)
// 执行结果 this 是 vue 实例
// this.doSth 是可执行的
}
由于setInterval 内的执行函数已经脱离了当前vue实例的执行环境,这个时候this是window ,window是没有doSth 方法的
如果一定要在执行函数内调起 doSth 怎么办?在方法1外部 const self = this 再在执行函数中 self.doSth()调用即可
延伸问题
抛开Vue 只说timeout 中的this
以下代码引自于 settimeout this试验 并加入了timer5
let obj = {
fn2() {
console.log('我是timer5的this指向:', this)
},
fn() {
console.log('我是fn内的this指向:', this) // obj
// 示例1
let timer1 = setTimeout(function() {
console.log('我是timer1的this指向:', this) // Window
}, 1000)
// 示例2 (让定时器函数中的this是obj:使用变量保存的方式)
let _this = this
let timer2 = setTimeout(function() {
console.log('我是timer2的this指向:', _this) // obj
}, 1000)
// 示例3 (让定时器函数中的this是obj:使用bind方法改变this指针)
let timer3 = setTimeout(
function() {
console.log('我是timer3的this指向:', this) // obj
}.bind(this),
1000
)
// 示例4 (让定时器函数中的this是obj:使用箭头函数,箭头函数中的this继承宿主环境(上级作用域中的this))
let timer4 = setTimeout(() => {
console.log('我是timer4的this指向:', this) // obj
}, 1000)
let timer5 = setTimeout(() => {
this.fn2()
}, 1000)
}
}
obj.fn()
执行结果如下
为什么方式2 的doSth执行中的this指向为vue实例,而不是window?
这个问题困扰了我很久,通过阅读源码才恍然大悟
原来Vue的methods里的this是通过bind方法绑定了当前vue实例为this,可看vue源码 state.js
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
故而即时传入了this.doSomething,在doSomthing中this永远指向了vm 实例
这块我们可以做2个试验 试验1 在vue中主动更改method的指向并执行查看效果
methods: {
doSomthing () {
// const func1 = this.doSomthing1;
this.doSomthing1.apply(null)
// setTimeout(func1, 1000);
},
doSomthing1 () {
console.log('this------');
console.log('this------', this);
}
}
试验效果
我们可以看到是没有效果的,因为doSomething1的this已经被指定为了vm
试验2
在Vue中重新绑定this 使用bind
也是没有效果的
试验3 在vanilla js中重复绑定bind 是有效果的
function func1 () {
console.log(this)
}
const obj1 = {
a: 1,
b: 2
}
const obj2 = {
a: 1,
b: 2
}
let funcBind = func1.bind(obj1)
funcBind();
funcBind = func1.bind(obj2)
funcBind();
在Vue-class-compoent中的问题
当在vue-class-component中写
debouceFunction1: Function = () => {
console.log('debouceFunction1 this---------------',this);
// console.log('debouceFunction1 this.$route---------------',this.$route);
// console.log('debouceFunction1 router', this.$router);
// this.debounceMethod();
};
debouceFunction2: Function = function hey () {
console.log('debouceFunction2 this---------------',this);
console.log('debouceFunction2 this.$route---------------',this.$route);
console.log('debouceFunction2 router', this.$router);
// this.debounceMethod();
};
执行结果如下
debouceFunction1 this--------------- EventBanner {activeKey: "", routerPre: "", levelConst: {…}, tabList: Array(3), eventId: 0, …}
eventBanner.vue?080d:201 debouceFunction2 this--------------- VueComponent {_uid: 17, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
eventBanner.vue?080d:202 debouceFunction2 this.$route--------------- {name: undefined, meta: {…}, path: "/em/detail/517/root", hash: "", query: {…}, …}
eventBanner.vue?080d:203 debouceFunction2 router VueRouter {app: Vue, apps: Array(1), options: {…}, beforeHooks: Array(0), resolveHooks: Array(0), …}
Function1作为匿名函数的this在定义时就被指定为了当前的class,而Function2 则是具名函数,是动态加载的时候指定 this 这里需要去理解一下vue-class-component 的原理,理解下它的装饰器是如果起作用的 解读 vue-class-component 源码实现原理