关于this的指向
前言
在写js
代码的时候,尤其是写带有面向对象编程式的代码的时候,我总是会想我这里使用的this
到底指的是什么,这个问题真是困扰了我很久。虽然代码写久了会有一些基于经验的直觉感受,不过从本质出发是最直接的理解途径。
this的指向
默认情况
当在某个函数(function
)中使用了this
时,这个this
指向的是该函数调用时所在的作用域,比如:
1 | function foo(){ |
可以看出上面的foo
函数在执行的时候,实际上位于全局作用域(即window
的作用域)中,所以this
实际上指向了全局对象window
,而全局作用域内声明的变量实际上都变成了window
对象的属性,所以foo
函数内部的this.a
实际上就是window.a
;但是需要注意的是,如果使用的为严格模式("use strict"
),函数在执行时是不能指向全局对象的!
1 |
|
这时候foo
函数的内部的this
实际上就是undefined
;
函数引用
当函数被某个对象的属性所引用时,该函数属性(即指向该函数的对象属性)在执行的时候,this
指向的为该属性所属的对象(需要注意的是对象属性可以为对象,也就是对象可以嵌套,因此该对象指的是直接包含该属性的那层对象);比如:
1 | function foo(){ |
注:当函数已经被某个对象属性所引用,然后又有其他变量或对象属性指向该对象属性,其实际上还是指向原函数;如同a → foo
,b → a
,实际上就是b → foo
;不管中间经历多少个指向,最终指向的一定是原函数。比如:
1 | function foo(){ |
从上面可以看出,中间经历多重指向后,实际上还是指向原函数,因此this
具体指向要看执行时该函数是作为函数属性还是普通函数执行的。
直接绑定
Function.prototype
具有3个特殊的方法:call()
、apply()
和bind()
。这个三个函数之所以特殊,是因为它们都提供了一个参数用来绑定函数内部的this
对象,因此可以利用这3个函数直接将我们想要this
指向的对象与this
进行绑定。
1 | function foo(){ |
上面的obj
对象利用call
方法绑定到foo
函数的this
上,因此执行的时候this.a
实际上就是obj.a
。
new绑定
在进行面向对象式的编程时,经常将函数作为构造器使用new
来『构造』一个新对象,之前总以为new
返回了函数的内部this
对象,且this
指向的应该就是新『构造』的实例。然而实际上这种想法是错误的,new
关键词所做的无非是一个稍微特殊的函数调用:当该函数没有返回值或返回值不是对象(引用类型?)时时,new
会构造一个对象,将该对象的__proto__
属性连接到函数的prototype
属性,并将该对象指向函数内部的this
对象,然后返回该对象;若该函数有对象返回值,则直接返回该对象返回值(即此时相当于普通函数的调用而已)。
1 | // 1. 没有返回值的情况 |
箭头函数
ES6
新增的箭头函数中使用的this
跟普通定义的函数有所不同,这里的this
指向的实际上是箭头函数定义时离得最近的一层外层函数作用域内的this
对象;
1 | function foo(){ |
上面通过将obj
对象作为foo
函数的this
对象,然后返回一个箭头函数,该箭头函数在定义的时候内部的this
实际上指向的就是此时foo
函数内部的this
对象,即obj
对象。
注:当有多个箭头函数嵌套时,this
的指向也是在定义处一层一层往上直至最近的外层非箭头函数的内部this
对象。
参考文档
- 《你不知道的JavaScript(上卷)》——第二章:this全面解析