this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用(也就是函数的调用方法)。
1. 默认绑定
独立函数调用时,应用 this
的默认绑定, this
指向全局对象
如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this
会绑定到 undefined
观察下面这段代码
function aba () {
console.log(this)
}
function bab () {
"use strict"
console.log(this)
}
var obj = {
aba: aba,
bab: bab
}
aba() //Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
bab() //undefined
obj.aba() //{aba: ƒ, bab: ƒ}
obj.bab() //{aba: ƒ, bab: ƒ}
2. 隐式绑定
在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把 this 间接(隐式)绑定到这个对象上。
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this
绑定到这个上下文对象。
所以上个示例代码中, 调用 obj.aba()
时 this
被绑定到 obj
,因此 this.aba
和 obj.aba
是一样的。
对象属性引用链中只有
最顶层或者说最后一层会影响调用位置。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决于是否是严格模式。
function fun1 () {
console.log(this)
}
var obj = {
fun1: fun1,
fun2: function () {
console.log(this)
}
}
var getfun1 = obj.fun1
var getfun2 = obj.fun2
function doCallBack (cb) { //参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值
cb()
}
function testSetTimeout (fun) {
setTimeout(fun, 0)
}
fun1() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
obj.fun1() // {fun1: ƒ, fun2: ƒ}
obj.fun2() // {fun1: ƒ, fun2: ƒ}
getfun1() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
getfun2() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
doCallBack(obj.fun1) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
doCallBack(obj.fun2) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
testSetTimeout(obj.fun1) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
testSetTimeout(obj.fun2) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
3. 显式绑定
使用 call
、 apply
、 bind
或者箭头函数显示绑定函数调用的 this
如果把 null
或者 undefined
作为 this
的绑定对象传入 call
、apply
或者 bind
,这些值在调用时会被忽略,实际应用的是默认绑定规则
var obj = {
c: 33,
fun: function (a,b) {
console.log( "a:" + a + ", b:" + b + ", c:" + this.c )
}
}
// 把数组“展开”成参数
obj.fun.apply( null, [2, 3] ); a:2, b:3, c:undefined
// 使用 bind(..) 进行柯里化
var bar = obj.fun.bind( null, 2 );
bar( 3 ); // a:2, b:3, c:undefined
上面代码中 apply
、 bind
这两种方法都需要传入一个参数当作 this 的绑定对象。如果函数并不关心 this
的话,仍然需要传入一个占位值,这时 null 可能是一个不错的选择。
但是如果某个函数确实使用了 this
,那默认绑定规则会把 this
绑定到全局对象(在浏览器中这个对象是 window
),这将导致不可预计的后果(比如修改全局对象)。
4. new绑定
在 JavaScript 中,构造函数只是一些被 new 操作符调用的普通函数而已。
实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1 创建(或者说构造)一个全新的对象。
2 这个新对象会被执行 [[ 原型 ]] 连接。
3 这个新对象会绑定到函数调用的 this。
4 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
注意: 对 bind
返回的函数进行构造调用时,实际调用的是原函数,调用 bind
时传入的第一个参数将被忽略,剩余参数作为构造调用的参数传入原函数
function test (a, b) {
this.a = a
this.b = b
this.say = function () {
console.log(`a: ${this.a}, b: ${this.b}, c: ${this.c}`)
}
}
var bindObj = {
c: 12
}
var bindReturn = test.bind(bindObj, 1, 2)
var newBindReturn = new bindReturn()
newBindReturn.say() // a: 1, b: 2, c: undefined
console.log(bindObj) // {c: 12}
bindReturn()
console.log(bindObj) // {c: 12, a: 1, b: 2, say: ƒ}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。