5. this的指向
- this既不指向函数自身也不指向函数的词法作用域。this是在函数被调用时发生的绑定取决于函数在哪里被调用。
5.1 this绑定有四条规则。
- 1. 默认绑定
this的默认绑定,指向全局对象。严格模式的this会绑定到undefined
function foo() {
console.log(this.a)
}
var a = 2
foo() //2
- 2. 隐式绑定
把函数调用中的this
绑定到这个对象的上下文对象,this.a
和obj.a
一样
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
obj.foo() //2
- 3. 显示绑定
使用call
和apply
方法,第一个参数是一个对象即this
。
function foo() {
console.log(this.a)
}
var obj = {
a: 2
}
foo.call(obj) //2
- 硬绑定:在
call
和apply
方法外包裹函数,ES5
的内置方法Function.prototype.bind
就是硬绑定
// 硬绑定
function foo(something) {
console.log(this.a, something)
return this.a + something
}
var obj = {
a: 2
}
var bar = foo.bind(obj)
var b = bar(3) // 2 3
console.log(b) //5
- 4. new绑定
JS中的构造函数是new
操作符调用普通函数,新对象会绑定到函数调用的this
function foo(a) {
this.a = a
}
var bar = new foo(2)
console.log(bar.a) //2
5.2 判断this的方法
规则优先级从高到低
• 1. 函数是否在new
中调用(new绑定)? 是,this
绑定的是新创建的对象bar
var bar = new foo()
• 2. 函数是否通过call、apply
(显示绑定)或者硬绑定bind
? 是,this
绑定的是指定的对象obj2
var bar = foo.call(obj2)
• 3. 函数是否在某个上下文对象中调用(隐式调用)? 是,this
绑定的是obj1
var bar = obj1.foo()
• 4. 都不是的情况下,使用默认绑定。 严格模式下,this
绑定到undefined
,否则绑定到全局对象
var bar = foo()
综上:
- 对于直接调用
foo
来说,不管foo
函数被放在了什么地方。在非严格模式下,this
一定是window
- 对于
obj.foo()
来说,我们只需要记住,谁调用了函数,谁就是this
,所以在这个场景下foo
函数中的this
就是obj
对象 - 对于
new
的方式来说,this
被永远绑定在了新对象上面,不会被任何方式改变this
- 当出现多种规则时,根据规则间的优先级来决定
this
的最终指向
5.3 绑定例外
- 被忽略的this:把
null
或undefined
作为this
的绑定,传入call、apply、bind
,则在这些方法调用时,会被忽略,实际应用的是默认绑定规则,绑定到全局对象(window,global
)
方法传入null的情况:
• 当使用apply
来“展开”一个数组,并当做参数传入一个函数。
• 或bind
对参数进行柯里化(预先设置一些参数)
function foo(a,b) {
console.log("a:" + a + ", b:" + b)
}
//把数组“展开”成参数
foo.apply(null, [2,3]) //a:2, b:3
//使用bind进行柯里化
var bar = foo.bind(null, 2)
bar(3) //a:2, b:3
//使用更安全的this,
//使用Object.create(null)替代null
var ∅ = Object.create(null)
foo.bind(∅, 2)
- 间接引用:容易发生在赋值的时候,会应用默认绑定
function foo() {
console.log(this.a)
}
var a = 2
var o = {a: 3, foo: foo}
var p = {a: 4}
o.foo() //3
(p.foo() = o.foo())() //2
赋值表达式
p.foo() = o.foo()
的返回值是对目标函数的引用,调用位置是foo()
,会应用默认绑定。
- 软绑定:解决硬绑定不能使用隐式绑定或显式绑定修改
this
的问题
5.4 箭头函数
- 不使用
this
的四种标准规则,根据声明时函数的外层(函数或全局)作用域来决定this
- 箭头函数的
this
绑定无法被修改