1

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.abaobj.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. 显式绑定

使用 callapplybind 或者箭头函数显示绑定函数调用的 this
如果把 null 或者 undefined 作为 this 的绑定对象传入 callapply 或者 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

上面代码中 applybind 这两种方法都需要传入一个参数当作 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: ƒ}

王恩智
171 声望3 粉丝

打杂的大龄90后