在 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
在这个例子中,foo
它是在全局范围内调用的,因此this
将被绑定到全局对象。
注意:此规则不适用于'use strict'
。
隐式绑定
另一条规则是:调用站点是否有上下文对象。
考虑:
function foo() {
console.log(this.a)
}
const object = {
a: 42,
foo: foo
}
object.foo() // 42
那么隐式绑定规则表明应该将foo
的方法绑定到。object
this
object
只有顶级/最后一级对象对调用站点(调用函数的地方)有影响:
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
隐性丢失
每当我们将函数作为回调函数传递时,我们将失去的绑定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"
在这个例子中,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"
在这个例子中,bar
指向foo
函数,因此当我们调用时,bar()
调用站点将取决于bar
调用的位置,在这个例子中,它是全局对象。
显式绑定
使用call
和apply
考虑:
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
这两者之间的区别是**“C 代表逗号,A 代表数组” ,这意味着您可以执行以下操作:
foo.call(obj, arg1, arg2, arg3)
foo.apply(obj, [arg1, arg2, arg3])
硬绑定
可以通过这样做来解决隐式丢失问题,称为硬绑定
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
这是一种常见的模式,它在 ES5 中提供了内置实用程序:Function.prototype.bind
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
var bar = foo.bind(obj)
bar() // 2
在 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
new
绑定
考虑:
function foo(a) {
this.a = a;
}
var bar = new foo( 2 );
console.log( bar.a ); // 2
通过在其前面调用foo(..)
with ,我们构造了一个新对象并将该新对象设置为foo(..) 调用的对象。new
this
确定this
-
函数调用时是否使用了
new
(新的绑定)?如果是,this
则使用新构造的对象。var bar = new foo()
-
函数是否使用
call
或apply
(显式绑定)调用,甚至隐藏在bind
硬绑定中?如果是,this
则为显式指定的对象。var bar = foo.call( obj2 )
-
函数调用时是否带有上下文(隐式绑定),也就是拥有者或包含者对象?如果是, 那么该
this
上下文对象 是否是 上下文对象。var bar = obj1.foo()
-
否则,默认为
this
(默认绑定)。如果在strict mode
,则选择undefined
,否则选择global
对象。var bar = foo()
例外
忽略 this
如果我们将null
或undefined
传递给call
,apply
或bind
,则这些值将被有效忽略,并且默认绑定规则将在此处应用。
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
注意:为了安全起见,如果你想将 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
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;
};
}
我记得只有当默认值为全局对象时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
词汇 this
考虑:
function foo() {
setTimeout(() => {
// `this` here is lexically adopted from `foo()`
console.log( this.a );
},100);
}
var obj = {
a: 2
};
foo.call( obj ); // 2
当您使用箭头函数时,该函数将绑定到其调用时的任何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