this默认绑定
先总结:
-
有明确调用方时:
多数情况下,方法中的this总是绑定方法的调用方;
-
没有明确调用方时:
在全局环境调用时是全局对象(浏览器中是window对象);
严格模式下没有调用方,方法里访问this就是undefined
-
箭头函数:
箭头函数内部的this是方法定义时就确定的,也就是说这个this跟箭头函数外部的this上下文一致
再分别举例:
全局上下文中的this指向顶层对象,浏览器中是window对象↓;
var a = 1;
console.log(this.a); // 1
console.log(window.a); // 1
function test() {
console.log(this.a);
}
test() // 1
严格模式下方法中this绑定调用方,如果没有设置调用方则返回undefined↓;
var a = 1;
console.log(this.a); // 1
console.log(window.a); // 1
function test() {
'use strict';
console.log(this.a);
}
window.test(); // 1
test() // 此时报错,因为this是undefined
箭头函数中的this绑定于它被定义的环境上下文↓;
var obj = {
a: 1,
f: ()=>console.log(this.a)
}
obj.f(); // 1
var test = {
a: 2,
ff: obj.f
}
test.ff(); // 1
// 可以通过改变外层this调用的方式改变箭头函数中this
var obj1 = {
a: 3,
f: function() {
return ()=>console.log(this.a)
}
}
obj1.f()() // 3
var test1 = {
a: 4,
ff: obj1.f
}
test1.ff()() // 4
对象的普通方法中的this绑定这个方法的实际调用方↓;
var obj = {
a: 1,
f: function() {
console.log(this.a)
}
}
obj.f(); // 1
var test = {
a: 2,
ff: obj.f
}
test.ff(); // 2
原型链上定义的方法中的this绑定于方法的实际调用方↓;
var obj = {
a: 1,
f: function() {
console.log(this.a)
}
}
var obj2 = Object.create(obj);
obj2.a = 2;
obj2.f() // 2
getter与setter中的this绑定于它的调用方↓;
var a = 2
function test() {
return this.a
}
var obj = {
a: 1,
get a1() {
return this.a
}
}
Object.defineProperty(obj, 'a2', {
get: test,
enumerable: true,
configurable: true
})
console.log(obj.a1) // 1
console.log(obj.a2) // 1
js里定义的事件处理函数外层this绑定事件关联的DOM元素,内层this为undefined;
// 被调用时,将关联的元素变成蓝色
function bluify(e) {
console.log(this); // 打印出来事件关联的DOM元素
(() => console.log(this))(); // 箭头函数打印出来事件关联的DOM元素
(function() { console.log(this) })() // 普通内部函数打印出来undefined
this.style.backgroundColor = '#A5D9F3'
}
// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*')
// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', bluify, false)
}
HTML内联的事件处理函数外层this绑定事件关联的DOM元素,内层this绑定顶层window↓;
<button onclick="console.log(this.innerHTML);(()=>console.log(this.innerHTML))();(function(){console.log(this)})()">button的innerHTML</button>
<!-- 打印结果:
button的innerHTML
button的innerHTML
window对象
-->
类的构造函数中的this:1.如果构造函数没有return对象,那么this绑定new的实例;2.如果构造函数有return对象,那么this绑定的默认对象就被丢弃了↓;
class Base{
constructor() {
this.a = 1
}
}
class Base2 {
constructor() {
this.a = 1
return {
a: 2
}
}
}
console.log(new Base()) // { a: 1 }
console.log(new Base2()) // { a: 2 }
派生类中的this在调用super()之前是undefined,调用super()等价于this = new 基类(),如果构造函数没有return对象,返回的就是this绑定的对象;
派生类如果没有构造函数或者构造函数有return对象都是可以实例化的,但如果有构造函数又没有return对象,那就必须调用super()方法绑定一个父实例给this,不然派生类实例化时就会报错;
class Base {
constructor() {
this.a = 1
}
}
class Extend1 extends Base{}
class Extend2 extends Base{
constructor() {
return {
a: 2
}
}
}
class Extend3 extends Base{
constructor() {}
}
class Extend4 extends Base{
constructor() {
super()
this.a = 4
}
}
console.log(new Extend1()) // {a: 1}
console.log(new Extend2()) // {a: 2}
console.log(new Extend4()) // {a: 4}
console.log(new Extend3()) // 报错
类的方法中的this,默认绑定于调用方,但可以在构造函数中通过bind()绑定方法的this为类实例;
class Base {
constructor() {
this.a = 1
// 绑定了func1的this为类实例,但没绑定func2
this.func1 = this.func1.bind(this)
}
func1() {
console.log(this.a)
}
func2() {
console.log(this.a)
}
}
var b = new Base()
var obj = {
a: 2,
func1: b.func1,
func2: b.func2
}
// 所以func1中的this绑定b这个Base实例,跟调用方obj没关系
obj.func1() // 1
b.a = 3
obj.func1() // 3
// func2中的this绑定调用方obj
obj.func2() // 2
obj.a = 4
obj.func2() // 4
vue中的this,也一样,组件上绑定的方法调用里的this一般是vue组件实例,如果不是组件上绑定的方法调用,有明确调用方的this就是这个调用方,没有调用方的方法内部访问this就是undefined;
test.vue
<template>
<button @click="test">test</button>
</template>
<script>
export default {
methods: {
test() {
this // button点击时this是这个vue组件实例
},
test1() {
this.$options.methods.test() // 这时候test方法内部的this是这个methods对象
},
test2() {
var a = function () {
this // 这时候this是undefined
}
a()
}
}
}
</script>
改变this绑定
call(),apply(),bind()使用以及区别
可以通过方法.apply()、方法.call()、方法.bind()来改变this的绑定,三者的区别:
方法.apply(this要绑定的对象,[要传递的参数1,…,要传递的参数n])
方法.call(this要绑定的对象,要传递的参数1,…,要传递的参数n)
方法.bind(this要绑定的对象,要传递的参数1,…,要传递的参数n)()
apply()的参数部分是个数组,call()和bind()的参数部分是多个参数;
bind()返回的是一个与调用方作用域和函数体皆相同的新函数(所以也可以用来做函数的拷贝),需要调用才能执行返回结果,这个新函数的this永久绑定bind()的第一个参数,如果对于这个返回的新函数再次进行bind()或者apply()或者call()是不生效的,函数体中的this还是依赖于第一次bind()的对象。
apply()和call()返回的是方法执行后的结果,可以多次调用生效。
function test(c, d) {
return this.a + this.b + c + d
}
// 定义两个对象对比用
var obj = {
a: 1,
b: 2
}
var obj2 = {
a: 3,
b: 4
}
// apply()返回的是方法调用结果,可以多次调用互不影响↓
console.log(test.apply(obj, [3, 4])) // 10
console.log(test.apply(obj2, [3, 4])) // 14
// call()返回的是方法的调用结果,可以多次调用互不影响↓
console.log(test.call(obj, 1, 2)) // 6
console.log(test.call(obj2, 1, 2)) // 10
// bind()返回的是新函数,需要调用才会得到结果,test可以多次调用互不影响↓
console.log(test.bind(obj, 1, 2)()) // 6(绑定时传参)
console.log(test.bind(obj)(1, 2)) // 6(调用时传参)
console.log(test.bind(obj2, 1, 2)()) // 10
// 但如果对调用bind()返回的新函数再次bind()或call()等操作就不会生效,方法体this还是绑定第一次绑定的对象↓
var newTest = test.bind(obj, 1, 2)
console.log(newTest()) // 6
console.log(newTest.bind(obj2, 1, 2)()) // 6
console.log(newTest.apply(obj2, [1, 2])) // 6
console.log(newTest.call(obj2, 1, 2)) // 6
end