在JavaScript中,
this是一个特殊的关键字,它通常用于指代当前执行上下文中的对象。但this的指向是动态的,它取决于代码执行的方式和上下文。这可能导致在实际使用中分不清this实际指向的内容,从而非常困扰我们,不过不用担心,接下来我们将详细剖析这个关键词的指向规则。
this的绑定规则
- 默认绑定 --- 函数在哪里调用,
this就指向哪里(不全对)->函数在哪个词法作用域里生效,this就指向哪个词法作用域(实际上都会指向全局作用域) - 隐式绑定 ---当函数被一个对象所拥有,再调用时,这种情况下,在调用时,
this会指向该对象
隐式丢失 --- 当函数被多个对象链式调用时,this指向最近引用函数的对象 - 显式绑定 ---
call,apply,bind - new 绑定
- 箭头函数问题
默认绑定
默认绑定的情况下
this关键字指向的是全局作用域
可能别的解释会说"函数在哪里调用,this就指向哪里"实际上具体应该是"函数在哪个词法作用域里生效,this就指向哪个词法作用域",但是最后在默认调用的情况下,this所指向的都是全局作用域。这个结论建议直接记,仔细分析也可以。请分析以下代码的执行结果:
本文中涉及默认绑定的代码需要在浏览器环境下运行,node环境当中没有window对象
//默认绑定
var a=1
function foo(){
var a=2
function bar(){
baz()
}
function baz(){
console.log(this.a)
}
bar()
}
foo()
分析
- 调用函数
foo,执行内部逻辑 - 执行
bar内部语句baz(),执行baz内部语句console.log(this.a) this指向该函数的词法作用域,那么就指向了foo,读取到a的值为2- 最终输出2
其实以上是错的
正确分析:
- 调用函数foo,执行内部逻辑
- 执行
bar内部语句baz(),执行baz内部语句console.log(this.a) this指向该函数的词法作用域,那么就指向了foo的作用域,但是foo的作用域并不是其词法作用域,故最后指向foo的词法作用域-全局作用域- 最终输出全局变量a,值为1
默认绑定总结: 在默认绑定(除去以下3种情况)时,实际上我们不用去管实际是如何指向全局作用域,只要是默认绑定情况,this指向的就是全局作用域
隐式绑定
能直接看得见的我们称之为显式,不能直接看见的我们称为隐式
function foo() {
console.log(this.a)
}
var obj={
a:2,
foo:foo
}
obj.foo()//输出2
默认调用是通过函数名直接调用,那么通过对象调用函数,该函数中的this会指向该对象。那么要是是多个对象呢?
function foo() {
console.log(this.a)
}
var obj={
a:1,
foo:foo
}
var obj2={
a:2 ,
obj:obj
}
obj2.obj.foo()//输出1
隐式丢失
当函数被多个对象链式调用时,this指向最近引用函数的对象
显式绑定
call
foo.call(obj,n1,n2...),调用call函数,第一个参数为需要指向的对象,后面放置该多个函数的传入实参
function foo(n,m){
console.log(this.a,n,m)
}
var obj={
a:2
}
//强行把foo函数的this指向改为后面括号中的第一个参数
//后面放置函数需要传入的参数
foo.call(obj,100,1000)//输出2 100 1000
apply
apply函数与call函数类似不同点是call函数传入实参是一个一个的,而apply传入实参,是放在一个数组当中
function foo(n,m){
console.log(this.a,n,m)
}
var obj={
a:2
}
//强行把foo函数的this指向改为后面括号中的第一个参数对象
//后面放置函数需要传入的参数的数组
foo.apply(obj,[200,2000])//输出 2 200 2000
bind
function foo(n,m){
console.log(this.a,n,m,p,q)
}
var obj={
a:2
}
//强行把强行把foo函数的this指向obj
//并可以传入函数所需的参数,也可以在返回函数里面传入,不够再到返回函数的传入参数里面找
var bar=foo.bind(obj,200)
bar(100,200)//输出2 200 100 200
易错
var a='外部'
function foo() {
var a='内部'
function bar (){
console.log(this)
console.log(this.a)
}
var baz= bar.bind(foo)
baz()
}
foo() //输出undefined
this被强制指向了foo,函数是函数,函数虽然是一种特殊对象,但函数当中声明的变量并不是函数的属性,访问对象不存在的属性,会在对象中创新创建变量赋值为undefined
var a='外部'
function foo() {
var a='内部'
function bar (){
console.log(this)
console.log(this.a)
}
var baz= bar.bind(foo)
baz()
}
foo.a=2
foo() //输出2
new绑定
当使用new方法创建新对象时,this所指的就是新生成的对象
new关键词的作用:
- 创建
this对象 - 执行函数里面的逻辑为该对象增加属性
- 将隐式对象值赋予为构造函数的显式对象
- 返回这个
this对象
//new关键词生成对象的作用过程
this={
height:1400
towner:user1
color:red
[[prototype]]:Car.prototype
}
return this
箭头函数
箭头函数没有this这个概念,写在箭头函数中的this也是它外层的函数的this 构造函数一定要用this关键字,所以箭头函数不能作为构造函数使用
请分析以下代码执行结果:
var obj={
name:"TOM",
show:function show (){
var bar=()=>{
console.log(this.name);
}
bar()
}
}
obj.show()
分析
- 当调用
show方法时,执行内部的bar函数,执行bar内部console.log(this.name)语句 - 但是
bar是一个箭头函数,this属于外部函数的this - 于是
this实际上是show函数的this回到了情况2隐式绑定,this指向对象obj - 打印TOM
总结
一共5种情况
- 默认绑定
this指向全局作用域 - 隐式绑定规则 ---当函数被一个对象所拥有,再调用时,这种情况下,在调用时,
this会指向该对象
隐式丢失 --- 当函数被多个对象链式调用时,this指向最近引用函数的对象 - 显式绑定 --- 3个函数:
call,apply,bind5.new绑定,this指向新生成的对象 - 写在箭头函数中的
this是它外层的函数的this