在 JavaScript 中确定“this”的超级简单方法

2025-05-27

在 JavaScript 中确定“this”的超级简单方法

this是 JavaScript 中的基本概念之一,但它也是最令人困惑的概念之一。在这篇博客中,我想与大家分享我用来判断什么 this ……的方法。

在深入研究所有可用于确定的具体规则之前,你可以记住一个简单的规则,该规则在大多数(并非所有情况下)情况this下都适用。我是这么记住的:

  • this当函数是对象的方法时绑定到对象。
  • this当函数不是方法时,绑定到全局对象或未定义。

当你浏览所有示例时,你可以尝试思考这两条规则。

绑定规则this

默认绑定

在这个规则中,我们将考虑调用函数时最常见的情况:独立函数调用

考虑以下代码:

function foo() {
    console.log(this.a)
}

var a = '2' // If we declare var in global scope => there will be a property with the same name in the global object. 

foo() // 2 => Foo is called within the global scope 
Enter fullscreen mode Exit fullscreen mode

在这个例子中,foo它是在全局范围内调用的,因此this将被绑定到全局对象。

注意:此规则不适用于'use strict'

隐式绑定

另一条规则是:调用站点是否有上下文对象。

考虑:

function foo() {
    console.log(this.a)
}

const object = {
    a: 42,
    foo: foo
}

object.foo() // 42
Enter fullscreen mode Exit fullscreen mode

那么隐式绑定规则表明应该将foo的方法绑定到objectthisobject

只有顶级/最后一级对象对调用站点(调用函数的地方)有影响:

function foo() {
    console.log( this.a );
}

var obj2 = {
    a: 42,
    foo: foo // for stays in obj2 => obj2 will be the call-site for foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};

obj1.obj2.foo(); // 42
Enter fullscreen mode Exit fullscreen mode

隐性丢失

每当我们将函数作为回调函数传递时,我们将失去的绑定this,这通常意味着它会回退到默认绑定(全局对象未定义)。

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

var a = "oops, global"; // `a` also property on global object

setTimeout( obj.foo, 100 ); // "oops, global"
Enter fullscreen mode Exit fullscreen mode

在这个例子中,foo 作为回调传递,因此this将绑定到被调用的调用站点setTimeout

或者:

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

var bar = obj.foo; // function reference/alias!

var a = "oops, global"; // `a` also property on global object

bar(); // "oops, global"
Enter fullscreen mode Exit fullscreen mode

在这个例子中,bar指向foo函数,因此当我们调用时,bar()调用站点将取决于bar调用的位置,在这个例子中,它是全局对象。

显式绑定

使用callapply

考虑:

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

foo.call( obj ); // 2
Enter fullscreen mode Exit fullscreen mode

这两者之间的区别是**“C 代表逗号,A 代表数组” 这意味着您可以执行以下操作:

foo.call(obj, arg1, arg2, arg3)

foo.apply(obj, [arg1, arg2, arg3])
Enter fullscreen mode Exit fullscreen mode

硬绑定

可以通过这样做来解决隐式丢失问题,称为硬绑定

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

var bar = function() {
    foo.call( obj );
};

bar(); // 2
setTimeout( bar, 100 ); // 2

// `bar` hard binds `foo`'s `this` to `obj`
// so that it cannot be overriden
bar.call( window ); // 2
Enter fullscreen mode Exit fullscreen mode

这是一种常见的模式,它在 ES5 中提供了内置实用程序:Function.prototype.bind

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

var bar = foo.bind(obj)
bar() // 2
Enter fullscreen mode Exit fullscreen mode

在 ES6 中,函数提供了一个名为“context”的可选参数,这是人们不使用的一种解决方法bind()

function foo(el) {
    console.log( el, this.id );
}

var obj = {
    id: "awesome"
};

// use `obj` as `this` for `foo(..)` calls
[1, 2, 3].forEach( foo, obj ); // 1 awesome  2 awesome  3 awesome
Enter fullscreen mode Exit fullscreen mode

new绑定

考虑:

function foo(a) {
    this.a = a;
}

var bar = new foo( 2 );
console.log( bar.a ); // 2
Enter fullscreen mode Exit fullscreen mode

通过在其前面调用foo(..)with ,我们构造了一个新对象并将该新对象设置为foo(..) 调用的对象。newthis

确定this

  1. 函数调用时是否使用了 new (新的绑定)?如果是, this 则使用新构造的对象。
    var bar = new foo()

  2. 函数是否使用 call 或 apply (显式绑定)调用,甚至隐藏在 bind 硬绑定中?如果是, this 则为显式指定的对象。
    var bar = foo.call( obj2 )

  3. 函数调用时是否带有上下文(隐式绑定),也就是拥有者或包含者对象?如果是, 那么该this上下文对象 是否是  上下文对象。
    var bar = obj1.foo()

  4. 否则,默认为 this (默认绑定)。如果在 strict mode,则选择 undefined,否则选择 global 对象。
    var bar = foo()

例外

忽略 this

如果我们将nullundefined传递给callapplybind,则这些值将被有效忽略,并且默认绑定规则将在此处应用。

function foo() {
    console.log( this.a );
}

var a = 2;

foo.call( null ); // 2
Enter fullscreen mode Exit fullscreen mode

注意:为了安全起见,如果你想将 this 绑定到来自库或框架的函数调用上,而该函数确实会创建this引用,那么你可能会意外地指向this全局对象。

更安全 this

null我们可以通过以下方式传递一个空对象,而不是传递一个Object.create(null)

{}您可能想知道和之间有什么区别Object.create(null)

{}:有Object.prototype

Object.create(null)实际上是一个空对象,它什么也没有,所以它被认为更干净。

软化结合

因此,如果你还记得硬绑定,它并不是非常灵活,因为它只指向指定的对象

const foo = bar.bind(obj) // this always bounds to obj
Enter fullscreen mode Exit fullscreen mode

bind()我们可以构建一个与called类似的替代实用程序softBind()

if (!Function.prototype.softBind) {
    Function.prototype.softBind = function(obj) {
        var fn = this,
            curried = [].slice.call( arguments, 1 ),
            bound = function bound() {
                return fn.apply(
                    (!this ||
                        (typeof window !== "undefined" &&
                            this === window) ||
                        (typeof global !== "undefined" &&
                            this === global)
                    ) ? obj : this,
                    curried.concat.apply( curried, arguments )
                );
            };
        bound.prototype = Object.create( fn.prototype );
        return bound;
    };
}
Enter fullscreen mode Exit fullscreen mode

我记得只有当默认值为全局对象softBind(obj)才会回退obj this

让我们看看softBind()

function foo() {
   console.log("name: " + this.name);
}

var obj = { name: "obj" },
    obj2 = { name: "obj2" },
    obj3 = { name: "obj3" };

var fooOBJ = foo.softBind( obj );

fooOBJ(); // name: obj

obj2.foo = foo.softBind(obj);
obj2.foo(); // name: obj2   <---- look!!!

fooOBJ.call( obj3 ); // name: obj3   <---- look!

setTimeout( obj2.foo, 10 ); // name: obj   <---- falls back to soft-binding
Enter fullscreen mode Exit fullscreen mode

词汇 this

考虑:

function foo() {
    setTimeout(() => {
        // `this` here is lexically adopted from `foo()`
        console.log( this.a );
    },100);
}

var obj = {
    a: 2
};

foo.call( obj ); // 2 
Enter fullscreen mode Exit fullscreen mode

当您使用箭头函数时,该函数将绑定到其调用时的任何foo内容。this

概括

有 4 条规则可以确定这一点:

  • 使用new? 使用新构造的对象
  • 使用call, apply, bind? 使用指定的对象
  • 对象的方法?使用该对象
  • 默认值:全局对象,严格模式下未定义。

大多数情况下,你只要记住:

  • this当函数是方法时绑定到对象
  • this当函数不是方法时,绑定到全局对象或未定义。

P/s:如果您想阅读更多关于 JavaScript 或 React 的文章,请访问我的网站:https://kelvinnguyen97.com/blog

文章来源:https://dev.to/kelvin0712/a-super-easy-way-to-define-this-in-javascript-ob5
PREV
别再用那么多 div 了!语义化 HTML 入门 div 的玩法 HTML5:如此标准 主结构 内容指示器 结论
NEXT
使用动画 GIF 在 GitHub README 中演示你的应用程序