理解 JavaScript 的 this

本文详细解析了JavaScript中的this关键字,包括其绑定规则、默认绑定、隐式绑定、显式绑定及new绑定等内容,并对比了箭头函数中的this行为。

初识 this 关键字

this 是个特殊的关键字,被自动定义在所有函数的作用域中。假设我们要写一个 sayName 函数,应该是这样写的:

function sayName(who) {
    console.log(who.name);
}
var me = {
    name: 'Lwenn';
}
var you = {
    name: 'Nana';
}
sayName(me); // Lwenn
sayName(you); // Nana

上面的 sayName 需要显式传入一个对象引用,现在有 this 了,我们可以用 this 改写上面的代码:

function sayName() {
    console.log(this.name);
}
var me = {
    name: 'Lwenn';
}
var you = {
    name: 'Nana';
}
sayName.call(me); // Lwenn
sayName.call(you); // Nana

可以看出,this 提供了一种隐式传递一个对象引用的方式,注意到这里用了 call,为什么这样写,之后会讲到。

this 绑定规则

每个函数的 this 是在函数调用时被绑定的,所以 this 的绑定和函数被调用的位置有关。this 有四种绑定规则。

默认绑定

function foo() {
    console.log(this.a);
}
var a = 2;
foo(); // 2 <-- foo的调用位置

当调用 foo() 时,调用位置是全局作用域,由于函数调用时应用了 this 的默认绑定,所以 this 指向全局对象。

注:如果使用严格模式,全局对象将无法使用默认绑定,因此 this 会绑定到 undefined

隐式绑定

隐式绑定是在一个对象内部包含一个指向函数的属性,通过这个属性引用函数从而把 this 绑定到这个对象上。

var obj = {
    a: 2,
    foo: function() {
        console.log(this.a);
    }
}
obj.foo(); // 2

在这里 foo() 的调用位置会使用 obj 的上下文来引用函数。当函数引用有上下文对象时,会应用隐式绑定把函数中的 this 绑定到这个上下文对象。所以 foo() 中的 this 指向的是 obj

多层属性引用

对象属性引用链中只有最后一层会影响调用位置。

var obj = {
    a: 2,
    foo: function() {
        console.log(this.a);
    }
}
var obj2 = {
    a: 3,
    obj: obj
}
obj2.obj.foo(); // 2

foo() 中的 this 还是指向 obj,即引用链最后一层。

隐式丢失

隐式丢失是指被隐式绑定的函数丢失绑定对象,它会应用默认绑定。

var obj = {
    a: 2,
    foo: function() {
        console.log(this.a);
    }
}
var bar = obj.foo;
bar(); // undefined
(0, obj.foo)(); // undefined

barobj.foo 的一个引用,而 this 是在函数调用时绑定的,当调用 bar() 时应用了默认绑定,进而把 this 绑定到全局对象,而全局对象没有 a 属性。

同理,(0, obj.foo) 是运用逗号表达式返回了一个 obj.foo 的引用。

显式绑定

如果想在对象上强制调用不属于这个对象属性的函数,可以使用显式绑定。JavaScript 提供了这些 API。

  • call
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2
};
foo.call(obj); // 2
  • apply
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2
};
foo.apply(obj); // 2

applycall 的区别在于参数形式。

  • bind
function foo() {
    console.log(this.a);
}
var obj = {
    a: 2
};
var bar = foo.bind(obj);
bar(); // 2

这里的 bar 虽然是 foo 的一个引用,但是它的 this 已经绑定到 obj 上了。

new 绑定

在 JavaScript 中,称作构造函数的函数可以被 new 操作符调用,不过它们实际上和普通函数没有区别。当函数被 new 调用时,会创建一个新对象,并且把函数调用的 this 绑定到这个新对象。

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2

this 与箭头函数

ES6 中新增了特殊的函数类型:箭头函数。它不使用 this 的四种绑定规则,而是根据词法作用域来决定 this

箭头函数最常用于回调函数中,例如事件处理器、定时器。

var obj = {
    a: 2,
    foo: function () {
        setTimeout(() => {
            console.log(this.a);
        }, 100);
    }
}
obj.foo(); // 2

如果不使用箭头函数,就需要用变量把 this 存起来,或者使用绑定规则改变回调函数的 this。所以箭头函数的 this 是特别的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值