目录
三、使用apply()、call()、bind()方法修改this指向:
前言
浏览器在调用函数时,每次都会向函数内部传递一些隐含的参数,this就是其中之一,this的指向是函数执行的上下文对象,而当函数的调用方式不同时,this所指向的这个上下文对象也是不一样的,常见的具体情况如下。
一、JS中五种情形下的this指向:
1.函数
当以函数的形式调用时,this指向 全局对象window :
function test() {
console.log(this)
}
//输出内容(全局对象window):Window {window: Window, self: Window, document: document, name: '', location: Location, …}
test()
2.方法
当以方法的形式调用时,this指向 该方法的调用者:
var obj = {
//当对象的一个属性为函数时,称这个函数为该对象的方法
test: function() {
console.log(this)
}
}
//输出内容(对象obj):{test: ƒ}
obj.test()
3.构造函数
当以构造函数的形式调用时,this指向 新创建的那个对象:
function Test() {
//使用二元运算符instanceof来验证此时的这个this是Test()类的实例,即该构造函数创建出的新对象
console.log(this , this instanceof Test)
}
//输出内容(分别为新对象demo和布尔值true):Test {} true
var demo = new Test()
4.响应函数
在事件的响应函数中,this指向 响应函数的绑定者:
<button id = 'btn'>
点我输出this
</button>
var btn = document.getElementById('btn')
//给按钮btn绑定一个点击事件
btn.onclick = function() {
console.log(this)
}
//点击按钮后的输出内容(按钮btn):<button id = 'btn'> 点我输出this </button>
5.修改指向时
使用apply()、call()、bind()方法改变this指向时,this的指向就是 方法中所指定的那个对象(详细示例见第三部分)。
二、Vue 项目中三种情形下的this指向:
1.被Vue所管理的普通函数
被Vue所管理的函数为普通函数时(如 methods 、computed 、watch 等配置项中的函数是普通函数的形式时),其this指向 Vue的实例对象vm或组件实例对象vc(以后者为例):
//此为组件HelloWorld.vue内容:
<template>
<div class="hello">
<button @click="test()">点我输出this</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
//举methods中的普通函数为例
methods: {
test() {
console.log(this)
}
},
};
</script>
//点击按钮后输出内容为(组件实例对象vc):VueComponent {_uid: 5, _isVue: true, __v_skip: true, _scope: EffectScope, $options: {…}, …}
2.不被Vue所管理的函数
不被Vue所管理的函数若为箭头函数时(当methods、computed、watch等配置项中的定时器回调函数、ajax的回调函数都写为箭头函数的形式时),由于箭头函数的this总是指向词法作用域,即外层调用者,所以此时其this同样指向 Vue的实例对象vm或组件实例对象vc(以后者为例):
//以下为组件HelloWorld.vue的内容:
<template>
<div class="hello">
<button @click="test()">点我输出this</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
methods: {
test() {
//开启定时器
setTimeout(()=> {
console.log(this);
}, 200);
},
},
};
</script>
<style scoped>
</style>
//点击按钮200ms后输出内容为(组件实例对象vc):VueComponent {_uid: 2, _isVue: true, __v_skip: true, _scope: EffectScope, $options: {…}, …}
3.其他情况
在 组件的自定义事件(下面的代码以此为例)、全局事件总线、消息订阅与发布 中,若其回调函数配置在methods中(见注释写法一),或是其回调函数为箭头函数(见注释写法二),则该回调函数的this指向也是 Vue的实例对象vm或组件实例对象vc(以后者为例):
//以组件的自定义事件为例,以下是App.vue的内容:
<template>
<div id="app">
<HelloWorld ref="demo"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld,
},
//写法一:将事件回调放在methods中
methods:{
test() {
//输出内容(组件实例对象vc):VueComponent {_uid: 1, _isVue: true, __v_skip: true, _scope: EffectScope, $options: {…}, …}
console.log(this)
}
},
mounted() {
this.$refs.demo.$on('showThis',this.test)
}
//写法二:将事件回调写在mounted中,使用箭头函数的写法,不写methods中的test()方法
mounted() {
//输出内容(组件实例对象vc):VueComponent {_uid: 1, _isVue: true, __v_skip: true, _scope: EffectScope, $options: {…}, …}
this.$refs.demo.$on('showThis', () => console.log(this))
}
}
</script>
//以下是HelloWorld.vue的内容:
<template>
<div class="hello">
<button @click="emitShowThis()">点我触发showThis事件</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
methods: {
emitShowThis() {
//触发showThis事件
this.$emit('showThis')
//解绑showThis事件(若在此时解绑,则点击按钮总共只进行一次输出)
this.$off('showThis')
}
},
};
</script>
注意:
这种情况不多,因为 Vuex 可以替代组件的自定义事件、全局事件总线、消息订阅与发布,如会使用Vuex,则此种情况可以忽略。
另外,将上述三种情况分别反过来,则this的指向将不再为Vue的实例对象vm或组件实例对象vc,此时this指向 需要具体分析。
三、使用apply()、call()、bind()方法修改this指向:
注意:
值得注意的是,虽然这三种方法都可以改变this的指向,但在使用时也稍有区别:
(1)在调用apply()或call()时,都会直接调用函数执行,但在调用bind()时,就不会调用函数执行,而是会返回一个this指向被修改好的新函数供我们调用。
(2)在传参时,第一个参数就是需要让this去指向的对象,若还有其他参数,在使用apply()时,需要将第二个及以后的参数封装到同一个数组中进行传递;在使用call()时,只需要将其他参数依次在第一个参数之后传递即可;在使用bind()时,第二个及以后的参数需要在新函数中传,传递方式同call()一样,无需封装。
修改示例 具体如下(第一部分中第2种情况下的this指向应为对象obj,现将其修改为window):
1. apply()
使用apply()方法修改this指向:
var obj = {
test: function (val) {
console.log(val, this)
}
}
//输出内容(100和window):100 Window {window: Window, self: Window, document: document, name: '', location: Location, …}
obj.test.apply(window, [100])
2. call()
使用call()方法修改this指向:
var obj = {
test: function (val) {
console.log(val, this)
}
}
//输出内容(200和window):200 Window {window: Window, self: Window, document: document, name: '', location: Location, …}
obj.test.call(window, 200)
3. bind()
使用bind()方法修改this指向:
var obj = {
test: function (val) {
console.log(val, this)
}
}
//输出内容(300和window):300 Window {window: Window, self: Window, document: document, name: '', location: Location, …}
var demo = obj.test.bind(window)
demo(300)