javascript中的this的四种绑定方式

在面向对象的编程中,最常用到的就是this关键词。

this是在执行函数时绑定的,而不是在声明的时候绑定的,他的上下文取决于函数调用时的各种条件,和声明的位置没有关系,只和调用的位置有关。

this有四种绑定方式:

第一种:默认绑定

var a = 9;
function abc() {
    console.log(this.a)
}
abc() // 9

在这段代码中,this.a被解析成了全局的a,等同于window.a。

其实可以这样理解,调用abc()函数的时候,完整的调用方法应该是window.abc(),因为函数abc()是全局的,默认挂载到了window下面,那么根据this指向只取决于函数执行,也就是调用的位置,且此时离abc()函数最近的上下文是window,所以在这里函数运用了this的默认绑定,this也就指向了window。而变量a的作用域是全局的,所以也是挂载到window下的,故this.a = 9。那么打印结果为9也就顺理成章了。

需要指出的是,如果在严格模式下,this是无法指向全局的

var a = 9;
function abc() { 
	"use strict"
	console.log(this) // undefined
    console.log(this.a) // Cannot read property 'a' of undefined
}
abc()

因为this是undefined,所以this.a也同样报错

第二种:隐式绑定

这取决于调用位置是否有上下文对象

function abc() {
    console.log(this.a)
}
var obj = {
    a: 9,
    abc: abc
}
obj.abc() // 9

虽然obj能成功调用abc函数,但实际上函数并不属于obj对象,obj拥有的只是函数的引用。但是调用位置会使用obj上下文来引用函数,所以可以这么认为:函数被调用时,obj对象是“拥有”函数引用的。

在函数abc执行的时候,他前面加上了对obj的引用,此时隐式绑定会把调用函数中的this指向这个上下文对象,即obj对象。所以this.a就等同于obj.a

并且,对象属性引用链中,this指向离调用函数最近的上下文对象。

function abc() {
    console.log(this.a)
}
var obj1 = {
    a: 56,
    abc: abc
}
var obj2 = {
    a: 33,
    obj1 : obj1 
}
obj2.obj1.abc() // 56

这里this指向离执行函数最近的obj1,而不是obj2。

隐式丢失

被隐式绑定的this有时候会丢失绑定对象,从而默认绑定到全局对象上去:

var a = 9 // 全局变量
function abc() {
    console.log(this.a)
}
var obj = {
    a: 99,
    abc: abc
}
var baz = obj.abc
baz() // 9

这里虽然baz是obj.abc的一个引用,但因为obj.abc是abc的一个引用,也就是obj.abc实际上是abc()函数,所以baz实际上也就等于abc函数本身,和obj对象没啥关系了,因此,baz()是一个不带任何修饰的函数调用,等同于abc()调用,this也就自然绑定到了全局window上了。

还有一种比较隐蔽的情况会导致this的绑定丢失

var a = 9 // 全局变量
function abc() {
    console.log(this.a)
}
function doAbc(fn) {
    fn() // 调用位置,引用的其实是abc()
}
var obj = {
    a: 3,
    abc: abc
}
doAbc(obj.abc) // 9

和之前的原因一样,传入的其实是abc的引用,最终和对象obj没有一点关系,相当于直接调用abc函数,所以this引用指向全局。

第三种:显式绑定

要达到隐式绑定的效果,必须在一个对象内部包含一个指向函数的属性,通过调用这个属性间接引用函数,从而把this间接绑定到这个对象上。如果我们不想在对象内包含函数引用,而想在某个对象上强制调用函数,达到把this绑定到该对象上,那就要用到显式绑定

显式绑定依赖于javascript给所有函数提供的两个方法:call()和apply()

第一个参数是一个对象,也就是想要在调用时让this指向的对象

function abc() {
    console.log(this.a)
}
var obj = {
    a: 5
}
abc.call(obj) // 5

通过.call()方法,在调用函数时,强制把this绑定到obj上。

如果传入的是一个原始值,比如字符串、数字或者布尔类型,那么调用的时候回自动转换成其对应的对象形式:new String()、new Number()、new Boolean()。

为了解决绑定丢失的问题,需要用到硬绑定

function abc() {
    console.log(this.a)
}
var obj = {
    a: 5
}
var bar = function() {
    abc.call(obj)
}
bar() // 2
setTimeout(bar(), 100) // 2
bar.call(window) // 2

每次调用bar()函数的时候,内部都会显示的将this指向obj,bar.call()是无法修改this指向的,这种显式的强制绑定称为硬绑定.

es5提供了内置方法bind来实现硬绑定:

function foo(num) {
    console.log(this.a, num)
    return this.a + num
}
var obj = {
    a: 4
}
var bar = foo.bind(obj)
var b = bar(3) // 4 3
console.log(b) // 7

.bind()会返回一个硬编码的函数,把你指定的参数设置为this的上下文并调用原始函数。

第三种:new绑定

在定义好函数后,都会new一下,生成一个新的对象,new的过程中会执行下面的操作:

1、创建一个全新的对象

2、给这个对象挂载prototype属性

3、新对象会绑定到函数调用的this(调用这个对象下的函数方法时,this会指向该对象)

4、如果函数没有返回其他对象,那new表达式中的函数调用会返回这个新对象

es6箭头函数

es6中用箭头定义函数=>,不遵循this的四条规则,而是根据外层作用域来决定this:

function foo() {
    return (a) => {
        console.log(this.a)
    }
}
var obj = {
        a: 7
    }
var obj1 = {
        a: 9
    }
var bar = foo.call(obj)
bar.call(obj1) // 7

结果是7,而不是9,这是因为foo()内部创建的箭头函数会捕获调用时foo()的this,由于调用时this绑定了obj,bar(引用箭头函数)的this也会绑定到obj,箭头函数的绑定无法被修改。

发布了59 篇原创文章 · 获赞 29 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/dongguan_123/article/details/85108936