vue的this指向详解及改变this的方法
首先要注意的是,JS中的this的指向只有在函数被调用后才能被确定,在函数的定义阶段是确定不了的
在JS中this的指向一般有四种规则
- 一个函数作为对象的方法被调用时,这个函数中的this指向调用它的对象
- 以构造函数的形式进行调用时,this指向new出来的实例对象
- 以函数的形式直接进行调用时,这个函数中的this指向window
- 通过call()和apply()等方法调用时,this是指定的那个对象
在JS的框架下,vue中this的指向有以下规则
- data中的this指向window
- 所有的生命周期函数中的this都指向vue实例对象
- vue的v-on指令中可以接收JS语句,其中的this是window(vue组件中的v-on指令除外)
- computed中的this指向vue实例对象
- methods中的this有以下几种情况
- 普通函数的this指向vue实例对象
- 箭头函数没有自己的this,因此this指向其宿主对象的this(注意宿主对象是函数对象(它被调用后this的指向要进行具体分析),简单对象没有this)
- 普通函数形式的回调函数的this是window,箭头函数形式的回调函数的this遵循箭头函数的原则(大多数情况下是vue实例对象)
vue文档里的原话:
All lifecycle hooks are called with their ‘this’ context pointing to the Vue instance invoking it.
意思是:在Vue所有的生命周期钩子方法(如created,mounted, updated以及destroyed)里使用this,this指向调用它的Vue实例 ,即 new Vue() ---- 每一个点vue后缀的文件都是利用vue的构造函数模板创建的实例 / 新对象
**注:
1.构造函数其实是一种特殊的函数,主要用来初始化对象,也就是为对象成员变量赋初始值,它总与new关键字一起使用
2.我们可以把对象里面一些公有的属性和方法抽象出来封装到这个函数里面。
3.这样我们就可以通过一个构造函数创建多个对象,这些对象拥有相同的构造,都可以使用这个构造函数的方法和属性。
4.new 就是执行构造函数,返回一个新对象,不写new就是普通函数的调用,没有创造对象的能力
5.构造函数的属性和方法前必须加this关键字,this指代要生成的对象
**
对于普通函数(包括匿名函数),this指的是直接的调用者,在非严格模式下,如果没有直接调用者,this指的是window, 严格模式下, this是undefind , showMessage1()里setTimeout使用了匿名函数,this指向window。
箭头函数是没有自己的this,在它内部使用的this是由它定义的宿主对象决定。showMessage2()里定义的箭头函数宿主对象为vue实例,所以它里面使用的this指向vue实例
<script> export default { data(){ return{ message: "你好!" } }, created: function() { console.log(this) //在Vue所有的生命周期钩子方法里使用this,this指向使用它的Vue实例 this.showMessage1(); this.showMessage2(); this.showMessage3(); //vue实例(构造函数)调用了showMessage3方法(new绑定) }, methods:{ showMessage3(){ console.log(this) //vue实例调用了showMessage3方法,this自然指向vue实例 }, showMessage1(){ setTimeout(function() { console.log(this) //指向window 因为没人调用这个普通函数 ) }, 10) }, showMessage2() { setTimeout(() => { console.log(this) //this指向vue实例 }, 10) } } } </script>
绑定vue实例到this的方法
为了避免this指向出现歧义,有两种方法绑定this。
1.使用bind
showMessage1()可以改为:
showMessage1(){ setTimeout(function() { console.log(this) //指向vue实例 }.bind(this), 10) },
对setTimeout()里的匿名函数使用bind()绑定到vue实例的this。这样在匿名函数内的this也指向vue实例。
2.把vue实例的this赋值给另一个变量再使用
showMessage1()也可以改为
showMessage1(){ var self=this; setTimeout(function() { console.log(self) //指向vue实例 }, 10) },
这里表示vue实例的this赋值给变量self,在使用到this的地方改用self
改变this的三种方法
this随处可见,一般谁调用,this就指向谁。this在不同环境下,不同作用下,表现的也不同。
以下几种情况,this都是指向window(附:vue中的this则参考上面)
1、全局作用下,this指向的是window
console.log(window); console.log(this); console.log(window == this); // true
2、函数独立调用时,函数内部的this也指向window
function fun() { console.log('我是函数体'); console.log(this); // Window } fun();
3、被嵌套的函数独立调用时,this默认指向了window
function fun1() { function fun2() { console.log('我是嵌套函数'); console.log(this); // Window } fun2(); } fun1();
4、自调执行函数(立即执行)中内部的this也是指向window
(function() { console.log('立即执行'); console.log(this); // Window })()
需要额外注意的是:
- 构造函数中的this,用于给类定义成员(属性和方法)
-
通过构造函数中设置this
function CreateObj() { this.name = "极客时间"; } var myObj = new CreateObj();
当执行
new CreateObj()
的时候,JavaScript 引擎做了如下四件事:-
首先创建了一个空对象
tempObj
-
接着调用
CreateObj.call
方法,并将tempObj
作为call
方法的参数,这样当CreateObj
的执行上下文创建时,它的this
就指向了tempObj
对象 -
然后执行
CreateObj
函数,此时的CreateObj
函数执行上下文中的this
指向了tempObj
对象 -
最后返回
tempObj
对象
var tempObj = {}; CreateObj.call(tempObj); return tempObj;
这样,就通过
new
关键字构建好了一个新对象,并且构造函数中的this
其实就是新对象本身。 -
-
- 箭头函数中没有this指向,如果在箭头函数中有,则会向上一层函数中查找this,直到window
二、改变this指向
1、call() 方法
call() 方法的第一个参数必须是指定的对象,然后方法的原参数,挨个放在后面。 (1)第一个参数:传入该函数this执行的对象,传入什么强制指向什么; (2)第二个参数开始:将原函数的参数往后顺延一位
用法: 函数名.call()
function fun() { console.log(this); // 原来的函数this指向的是 Window } fun(); function fun(a, b) { console.log(this); // this指向了输入的 字符串call console.log(a + b); } //使用call() 方法改变this指向,此时第一个参数是 字符串call,那么就会指向字符串call fun.call('call', 2, 3) // 后面的参数就是原来函数自带的实参
2、apply() 方法
apply() 方法的第一个参数是指定的对象,方法的原参数,统一放在第二个数组参数中。 (1)第一个参数:传入该函数this执行的对象,传入什么强制指向什么; (2)第二个参数开始:将原函数的参数放在一个数组中
用法: 函数名.apply()
function fun() { console.log(this); // 原来的函数this指向的是 Window } fun(); function fun(a, b) { console.log(this); // this指向了输入的 字符串apply console.log(a + b); } //使用apply() 方法改变this指向,此时第一个参数是 字符串apply,那么就会指向字符串apply fun.apply('apply', [2, 3]) // 原函数的参数要以数组的形式呈现
3、bind() 方法
bind() 方法的用法和call()一样,直接运行方法,需要注意的是:bind返回新的方法,需要重新
调用
是需要自己手动调用的
用法: 函数名.bind()
function fun() { console.log(this); // 原来的函数this指向的是 Window } fun(); function fun(a, b) { console.log(this); // this指向了输入的 字符串bind console.log(a + b); } //使用bind() 方法改变this指向,此时第一个参数是 字符串bind,那么就会指向字符串bind let c = fun.bind('bind', 2, 3); c(); // 返回新的方法,需要重新调用 // 也可以使用下面两种方法进行调用 // fun.bind('bind', 2, 3)(); // fun.bind('bind')(2, 3);