关于javascript中的this默认绑定以及如何修改

this默认绑定

先总结:

  1. 有明确调用方时:

    多数情况下,方法中的this总是绑定方法的调用方;

  2. 没有明确调用方时:

    ​ 在全局环境调用时是全局对象(浏览器中是window对象);

    ​ 严格模式下没有调用方,方法里访问this就是undefined

  3. 箭头函数:

    ​ 箭头函数内部的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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值