目录
- 为什么要使用this?
- this到底是什么?
- 如何判断this?
- this绑定的四种规则及优先级
- 什么情况绑定会丢失?
- ES5硬绑定bind()函数
- ES6箭头函数不使用this的四种规则
为什么要使用this?
this提供了一种更优雅的方式来隐式“传递”一个对象的引用,使用this,函数可以自动引用合适的上下文对象。
this到底是什么?
this是一种机制,在js运行时进行上下文绑定,this的绑定与函数声明位置没有关系,只取决于函数的调用方式。
更确切的说,this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
如何判断this?
this的四种绑定规则:new绑定、显示绑定、隐式绑定、默认绑定。优先级由高——>低
- 函数使用new调用,this指向新创建的对象
var Obj = new Foo() // this -> Obj
- 函数通过apply、call显示绑定,this指向指定对象
var Obj = foo.call(Obj2) // this -> Obj2
- 函数在上下文对象中调用(隐式绑定),this指向上下文对象
var Obj = obj1.foo() // this -> obj1
- 默认规则:独立函数调用,若foo函数体内(并不是调用foo的位置)声明了处于严格模式下,this指向undefined,否则绑定到window
var Obj = foo()
需要注意的是,隐式绑定会出现丢失绑定对象的情况,根本原因其实是隐式赋值引起的。
看如下例子:
function foo(){
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
var a = 'some'
- 函数被别名
var bar = obj.foo
bar() // 'some'
- 函数被用作参数传递
function baz(fn){
fn() //这里是真正调用foo的位置,this指向全局作用对象
}
baz(obj.foo) // 'some'
- 在js内置函数中
setTimeout(obj.foo , 1000) // 'some'
- 间接引用
//这里还是以上面的栗子为前提,新增下面代码
var obj2 = {
a: 3
}
obj.foo() // 2
(obj2.foo = obj.foo)() // 'some'
obj2.foo = obj.foo
表达式的返回值其实是foo的引用,再执行foo()
前三种情况,其实都隐藏了一个赋值操作: fn = obj.foo ,这里fn引用的是foo函数本身。之后直接调用fn(),很显然这里使用默认规则。
综上:找准函数被调用的真实位置是关键之处,然后应用四条规则及优先级,判定this最终指向。
tips
在apply/call中指定对象为null或者undefined时,这时this使用默认规则,如:
“展开”一个数组
function foo(a,b){
console.log("a:",a,"b:",b)
}
foo.apply(null, [1,2])
这里并不关心this的指向,但是还是需要一个占位值,但是更安全的做法还是将this指向一个特殊的空对象,如:
foo.apply(Object.create(null), [1, 2])
ES5硬绑定bind()函数
ES5的bind()返回一个硬编码的新函数,固定了this的指向。
bind()实现原理类似,创建一个包裹函数,内部手动调用显示绑定apply/call
ES6箭头函数不使用this的四种规则
箭头函数根据外层作用域来决定this指向。
相较于传统的this机制:类似于一种动态作用域,在运行时,被调用的位置处确定绑定对象。
ES6的箭头函数根据当前的词法作用域来决定this,即继承外层函数调用的this绑定。
栗子:
function foo() {
return (a) => {
console.log(this.a)
}
}
var obj = {
a: 1
}
var obj1 = {
a: 2
}
var bar = foo.call(obj)
bar.call(obj1) // 1
this指向外层函数foo调用时的对象.
在ES6之前就有一种方案实现了和箭头函数一样的功能
function foo() {
var self = this
return function () {
console.log(self.a)
}
}
参考图书:《你不知道的JavaScript上卷》.中国工信出版集团.黄宝书