迫于
迫于在写 React 事件处理中的 this 时,写到 this 突然卡壳了,一顿冷静分析之后发现自己对于 this 的理解又忘了一些,于是痛定思痛,重新读了 《You-Dont-Know-JS》中关于 this 的文章。
函数的执行环境
首先上一段代码
function fn(){
console.log(this.a)
}
let obj = {
a: 1,
fn: fn
}
var a = 'window'
fn() //window
obj.fn() //1
首先我们要明确一点 ,this 不是编写代码时就绑定的,而是运行时绑定的。
比如 fn() 在调用时,所处的环境是 global,所以他的 this 就是 window
而 obj.fn() 在调用时,所处的环境是在对象 obj 内,所以他的 this 指向 obj 对象
当一个函数被调用时,会建立一个称为执行环境的活动记录。这个记录包含函数是从何处(调用栈 —— call-stack)被调用的,函数是 如何 被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的 this 引用。
所以,this是根据调用点(函数如何被调用)来为每次函数调用建立的绑定
调用点的规则
-
默认绑定: 首先是独立函数调用。在这种倾向下,
this规则是在没有其他规则适用时的默认规则。function fn(){ console.log(this) } fn() //windowfn()被独立调用,此时的this是默认绑定,所以this指向了window -
隐含绑定 这种规则是:调用点是否拥有一个对象环境。
function fn(){ console.log(this.a) } let obj = { a: 1, fn: fn } obj.fn() //1首先,
fn被声明然后作为引用属性添加到obj上,我们知道,对象、函数、数组等都是引用类型,存在于堆内存中,以传值(地址)的方式被变量引 用,所以obj并不包含或者拥有函数fnobj.fn()的调用点以obj环境来引用函数,所以,函数在被调用的时间节点上拥有对象环境obj对象属性引用链的最后一层是影响调用点的
function fn() { console.log( this.a ); } let obj2 = { a: 42, fn: fn }; let obj1 = { a: 2, obj2: obj2 }; obj1.obj2.fn(); // 42 -
隐含丢失 隐含丢失:隐含绑定丢失了他的绑定,如何丢的?看代码
function fn(){ console.log(this.a) } let obj = { a: 1, fn: fn } let foo = obj.fn var a = 'window' foo() //window这段代码,将
foo引用了obj.fn,但是在foo(),他依然属于函数独立调用,所以this指向全局作用域的afunction fn(){ console.log(this.a) } function bar(f){ f() } let obj = { a: 1, fn: fn } var a = 'window' bar(obj.fn) // window这段代码中,将
obj.fn作为回调函数传入bar中,但是bar调用回调函数的方式依然属于 函数独立调用,所以this指向 全局作用域的afunction fn(){ console.log(this.a) } let obj = { a: 1, fn: fn } var a = 'window' setTimeout(obj.fn, 1000) // window这段代码跟上一段一样。
-
箭头函数 箭头函数的
this不是在运行时生效的,而是在定义这个箭头函数时生效的let obj = { fn(){ setTimeout(function(){ console.log(this) }, 1000) }, arrow(){ let a = () => {console.log(this)} setTimeout( a, 1000) } } obj.fn() //window obj.arrow() //对象objobj.fn中setTimeout中的回调函数相当于函数独立调用,this指向window,obj.arrow中的setTimeout中的回调函数是一个 箭头函数,而箭头函数的this时函数定义生效时所在的对象,也就是obj,arrow中的a引用了箭头函数,而这个箭头函数是在arrow执行时定义的,所以所指向的对象环境为obj。