this
这个东西相信对于不少新人来讲是个糊涂帐,甚至老手来说,在一些稀奇古怪的状况下都不必定完彻底全的讲清楚.特别是面对脑经急转弯同样的面试题,让人苦笑不得.java
this
既不指向函数自身,也不指向函数的词法做用域.this
其实是在函数被调用时发生的绑定,它的指向只取决于函数被谁调用了或者函数在哪里被调用.es6
this
在全局环境下指向window (默认绑定)在全局环境下,咱们写的全部的变量和函数都是window对象里面的属性和方法.面试
在浏览器中打印以上代码,获得这些结果.数组
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
便指向谁.是否是感受很清晰.
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
这即是显式绑定
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都不会发生改变。
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的操做中是优先级最高的。