this到底指向谁的心?

浅谈一下this

this这个东西相信对于不少新人来讲是个糊涂帐,甚至老手来说,在一些稀奇古怪的状况下都不必定完彻底全的讲清楚.特别是面对脑经急转弯同样的面试题,让人苦笑不得.java

一句话总结

this既不指向函数自身,也不指向函数的词法做用域.this其实是在函数被调用时发生的绑定,它的指向只取决于函数被谁调用了或者函数在哪里被调用.es6

5大状况

由于let声明的变量在浏览器中不是顶层全局的,因此个人全部声明都用var

  • 1 : this 在全局环境下指向window (默认绑定)

在全局环境下,咱们写的全部的变量和函数都是window对象里面的属性和方法.面试

在浏览器中打印以上代码,获得这些结果.数组

  • 2 : this函数被谁调用,this在哪里被调用.(隐式绑定)

这句好像不太好理解,我贴一下代码,而后逐句解释一下.浏览器

var a  ={  //声明一个a对象,里面有个isThis方法
	isThis(){
		console.log(this)
	}
}
a.isThis()//调用一下这个方法
复制代码

结果以下:app

如今理解一下这句话, this被谁调用, a对象里面有一个函数isThis, a调用isThis, isThis被a调用, 故this指向了a对象.

咱们再来看第一条:window对象中有个d方法,因此d方法被window调用的时候,this天然指向了window对象.函数

this函数在哪里被调用 this便指向谁ui

var a = 'a'//全局声明一个变量a
    function foo(){
	    return this.a
    }
    var b = {//全局声明一个变量b
    	a:'this is b.a',
    	log(){
    		console.log(this.foo())
    	}
    }
    b.foo = foo 
    将foo方法赋给b
    b.log()//调用log方法
    foo()//全局调用log方法
复制代码

打印结果:this

首先由于foo赋值给了b,故在b调用本身对象中的方法时,this指向b自身,因此foo函数中的this指向b,在b.log()的时候,log函数是b自己的方法且被b调用,故this指向b,且执行了this.foo,foo被b调用,或者说foo在b中调用,故this指向b,logspa

而在全局中调用foo 由于是window调用的b(b在window中被调用)因此this指向window

再看一个例子:

function bar(){
	console.log(this.a)
}

var obj1 ={
	a:1,
	bar
}

var obj2 = {
	a:2,
	obj1
}

obj2.obj1.bar()//

打印结果:1
复制代码

在以上的代码中,bar函数被obj1调用,与obj2无关,因此this指向obj1,因此打印出来了.

继续理解!!

如下方代码为例:

var a  ={ // 声明一个a对象,包含一个isThis方法
	isThis(){
		console.log(this)
	}
}
a.isThis() //a调用此方法,打印出this是a本身
window.isThis = a.isThis // 将a中的isThis方法赋给window对象中
isThis()//在全局中调用该方法
实质上等于 window.isThis()
复制代码

看一下结果:

在以上操做中,咱们将a中的isThis方法赋给window对象中,再用window调用,this便指向了window对象.

再看如下代码:

首先我声明了一个b对象,再将a中的isThis方法赋给b,再用b去调用该方法,即打印出来了b对象本身.

咱们不管是把isThis方法赋给b仍是window对象,对于b对象和window对象而言,他们之中都有了一个isThis方法,再结合这一句 this函数在哪里被调用 this便指向谁.是否是感受很清晰.

  • 3 call apply 中的this(显式绑定)

call和apply都接受两个参数,第一个参数是当该函数运行的时候的this环境(thisObj),第二个参数是函数接收的参数,call接受单个/多个参数,apply只能接受数组参数.

忽略第二个参数,咱们单说第一个参数.

代码以下:

var a ={ //声明一个a变量
	a:'this is a',
	isThis(){
		console.log(this)
	}
}

var b = {//声明一个b变量
	b:'this is b'
}

var c = {//声明一个c变量
	c:'this is c'
}
function isThis(){
    console.log(this)
}

a.isThis() //调用一下
isThis()
isThis.call(b)
isThis.apply(c)
a.isThis.call(b)//call一下b
a.isThis.apply(c)//apply 一下c
复制代码

看一下打印结果:

在以上代码中,不管是call仍是apply,不管是在哪里调用,或者被谁调用, 接受的第一个参数(thisObj),就是函数中this指向的对象,或者说第一个参数就是这个函数运行时候的this

因此,当一个函数以call或者apply方式调用的时候,call或者apply接收的第一个参数,就是函数运行时候的this

这即是显式绑定

  • 4 bind 中的this(显式绑定)

如今咱们认识一下bind函数.

bind 接收两个参数,与call apply很类似。第一个参数是当该函数运行的时候的this环境(thisObj),第二个参数是函数接收的参数,可是不一样的是的,当一个函数调用了 call apply 方法返回的该函数被call apply以后的结果。即:

function bar(){
        return this.a
    }
    var  baz= {
        a:3349
    }
    bar.call(baz)
    返回结果是 3349
复制代码

而bind 不同,当一个函数调用了bind方法,并传入了参数返回的一个新的函数,而且该函数的this被绑定了,谁都改变不了。一样咱们不讨论第二个参数的意义,咱们只研究this的问题

function bar(){
        return this.a
    };
    var a = 0;
    var b = {
        a:1
    };
    var c = {
        a:2
    };
    var d = {
        a:3
    };
    bar()
    bar.call(b)
    bar.apply(c)
复制代码

结果显而易见。

那么咱们如今要作这样的操做,刚才已经说过了,bind会返回一个新的函数。

因此,在下列代码中,baz赋值为bar经过bind(d)以后返回的函数

再调用一下bar,咱们来看看结果

function bar(){
        return this.a
    };
    var a = 0;
    var b = {
        a:1
    };
    var c = {
        a:2
    };
    var d = {
        a:3
    };
    var baz = bar.bind(d);
    baz()
复制代码

结果为3 ,显然,在baz这个函数被调用的时候,函数里面的this指向了d,下面一组代码将展现什么叫作硬绑定。

function bar(){
        return this.a
    };
    var a = 0;
    var b = {
        a:1
    };
    var c = {
        a:2
    };
    var d = {
        a:3
    };
    var baz = bar.bind(d);
    
    baz.call(a);
    baz.call(b);
    baz.call(c);
    baz()
    
    var e = {
        baz
    };
    e.baz();
复制代码

这即是硬绑定,baz函数是bar函数经过bind返回的新函数,而bar函数进行了绑定对象d的操做,因此返回的baz函数始终指向d。

换言之,一个bar函数经过调用bind绑定了一个d对象,bar.bind(d)这个调用将会返回一个新函数baz,而新函数baz的this始终都指向d这个对象,不管使用call仍是apply,仍是被其余对象调用或全局调用,this都不会发生改变。

  • 5 new 优先级最高的this

关于js是否有类这个概念,各有说辞,有的说有,有的说没有,我比较认同一个观点是js中确实没有类,可是有类的概念,包括es6的class也不是真的类,只是语法糖,一样说道构造函数,我比较认同《你不知道的javaScript》上卷中的一段话。

“在js中,构造函数只是一些使用new操做符时被调用的函数,他们并不会属于某个类,也不会实例化一个类。实际上,它们甚至都不能说是一种特殊的函数类型,它们只是被new操做符调用的普通函数而已”

言归正传,咱们今天讨论的是this的指向。

一下面代码为例:

function bar(a){
        this.a = a
    }
    var baz = new bar(2)
    
    console.log(baz,baz.a)
复制代码

以上操做:

1,创造一个全新的对象(也能够认为是实例)

2,这个新对象会被执行到原型链上,即:baz.__proto__ === bar.prototype

3,这个新对象(实例)的this会指向他本身,而再也不指向函数的this。

最后一句话是否是很难理解。

咱们看看一下代码。

function bar(){
    	this.a = 2
    }
    
    var foo ={
	    bar
    }
    
    var baz = new foo.bar()
    
    foo.bar()   
复制代码

第一个操做中咱们是foo调用了bar,可是结果并非咱们预期所想的,foo这个对象会由于调用了bar方法而向本身赋值了a=2这个属性,显然,在被new调用的时候,bar方法里面的this指向了新建立的对象baz,并给baz复制了a=2这个属性,在第二个操做中,bar函数的this才指向了foo,而且给foo赋值a=2这个属性。

其实在咱们第一段的代码中,全局调用了var baz = new bar(2) bar函数中的this也没有指向window 而是指向了心建立的对象baz

因此可得new的优先级是高于 上文提出的情况1和情况2的

由于new和call/apply没法一块儿使用,咱们直接比较new和bind之间的优先级,只要比bind还高,那一样就比call/apply高

function bar(a){
        this.a = a
     }
   var a = {
       bar
   }
   var b = {
       
   }
   var c =  a.bar.bind(b,'a')
   
   c()
复制代码

以上结果很符合bind的直觉对不对,那么下来咱们继续操做

function bar(a){
        this.a = a
     }
   var a = {
       bar
   }
   var b = {
       
   }
    var c =  a.bar.bind(b,'a')
    
    var d = new c()
复制代码

在上面的操做中,咱们经过new操做符,调用了a对象的bar函数经过绑定(bind) b对象后返回的c方法,但是c方法并无像咱们以前直接调用c方法那样,this指向了b对象,而且给b对象赋值a='a'这个属性,而是直接将this指向了新建立的d对象(实例),而且给d对象赋值了a='a'这个属性,因此new操做符在this的操做中是优先级最高的。

this 一直以来就是一个比较麻烦的东西,可是只要你们记清楚这几个this指向的规则,碰见问题逐字分析,必定能够获得想要的答案。(如文章中有错误之处,但愿你们指正,不喜勿喷)

相关文章
相关标签/搜索