一篇文章让你搞懂this
前言:this到底是什么
当一个函数被调用时,会产生一个活动记录(也称之为执行上下文),这个记录包含调用栈(函数在哪里被调用),函数调用方式,调用传入参数等信息,this就是活动记录的一个属性。this实际上是函数调用时发生的绑定,它的指向取决于函数在哪里被调用。
一.this调用位置
查找方法:在工具中给foo()函数的第一行代码设置断点,或者直接在第一行代码之前插入一条debugger;语句,运行代码时。调试器会在那个位置暂停,同时显示当前位置的函数调用列表,这就是你的调用栈,因此如果分析this绑定,先查找调用栈,找到栈中第二个元素就是真正的调用位置。
二.绑定规则
默认绑定
1.使用非严格模式 ,默认绑定才能绑定到全局对象
2.如果使用严格模式,则不能将全局对象用于默认绑定,this会绑定到undefined
隐式绑定
在一个对象内部包含一个指向函数的属性,通过这个属性间接调用这个函数,从而使this间接(隐式)绑定到这个对象上
- 当函数引用有上下文对象时,隐式绑定会将this绑定到这个上下文对象上
- 对象属性引用链中只有上一层或者说最后一层在调用位置起作用
- 隐式丢失
被隐式绑定的函数会丢失绑定对象,也就是它会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式
显式绑定
- 使用call()或apply()方法
- 上述两种方法,第一个参数是对象,给this准备的,接着在调用函数时将其绑定到this
1.使用硬绑定可以解决丢失绑定的问题,硬绑定无法修改this值
补充: 使用bind()返回一个新编码的新函数,它会把你指定参数设置为this的上下文并调用原函数。
2.API调用“上下文”,其作用和bind()一样,确保回调函数使用确定this
new绑定
构造函数是使用new操作符时调用的函数
一.使用new来调用函数过程
- 创建(或构造)一个全新的对象
- 这个对象会执行[[Prototype]]连接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回对象,那么new表达式中的函数调用会自动返回这个对象
使用new调用foo(...)时,我们会创建一个新对象并把它绑定到foo(...)调用中的this
三.绑定优先级
总结:new绑定>显式绑定>隐式绑定>默认绑定
ps:bind()功能之一就是可以把除了第一个参数(第一个参数用于this绑定)之外其他参数传给下层函数(该技术称为this)
四.判断this
按照以下规则判断
- 函数是否在new中调用=>this绑定的就是新创建对象
var bar=new foo() //this绑定bar
- 函数是否通过call,apply(显式绑定)或硬绑定调用=>this绑定的就是指定的对象
var bar =foo.call(obj1) //this绑定obj1
- 函数是否在某个上下文中调用(隐式绑定)=>this绑定上下文对象
var bar=obj1.foo() //this绑定obj1,谁调用绑定谁
- 如果都不是=>使用默认绑定,在严格模式下就绑定到undefined,否则就绑定到全局对象
var bar=foo()
五.绑定例外
被忽略的this
1.如果把null或undefined作为this的绑定对象传入call、apply或者bind,这些值会在调用时被忽略,实际应用默认绑定规则
由于当某些函数确实用了this,但是默认绑定规则把this绑定到全局对象,会导致不可预见结果,必须采用一种更安全的this
更安全的this
2.传入一个特殊对象,把this绑定到这个特殊对象上不会对程序产生任何副作用,创建一个DMZ对象,它是一个空的非委托对象
使用ø不仅会让函数变得更安全,还能提高代码可读性,因为ø表示"我希望this是空"
间接引用
可能有意或无意创建一个函数的"间接引用",在这个情况下函数调用会应用默认绑定
软绑定
六.this词法
箭头函数不使用this的四条绑定规则,而是根据外层(函数或全局)作用域来决定this
箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)