如何在回调中访问正确的'this'?

vfhzx4xs  于 2021-09-23  发布在  Java
关注(0)|答案(6)|浏览(310)

我有一个注册事件处理程序的构造函数:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);

但是,我无法访问 data 在回调中创建的对象的属性。看起来像 this 不是指已创建的对象,而是指另一个对象。
我还尝试使用对象方法而不是匿名函数:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

但它也表现出同样的问题。
如何访问正确的对象?

toiithl6

toiithl61#

关于这件事你应该知道什么 this (也称为“上下文”)是每个函数中的一个特殊关键字,其值仅取决于函数的调用方式,而不是函数的定义方式/时间/地点。与其他变量一样,它不受词法范围的影响(箭头函数除外,见下文)。以下是一些例子:

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

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

了解更多关于 this ,请查看mdn文档。

如何参考本手册中的正确答案

使用箭头函数

ecmascript 6引入了箭头函数,可以将其视为lambda函数。他们没有自己的 this 结合相反 this 在范围内查找,就像普通变量一样。这意味着你不必打电话 .bind . 这不是他们唯一的特殊行为,更多信息请参考mdn文档。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

不要用这个

实际上,您不想访问 this 尤其是它所指的对象。这就是为什么一个简单的解决方案是创建一个新的变量,该变量也引用该对象。变量可以有任何名称,但常用名称是 selfthat .

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

自从 self 是一个普通变量,它遵守词法范围规则,可以在回调中访问。这还有一个优点,即您可以访问 this 回调本身的值。

显式设置回调函数的这一部分-第1部分

看起来您可能无法控制 this 因为它的值是自动设置的,但事实并非如此。
每个函数都有方法 .bind [docs],返回一个新函数,其中包含 this 绑定到一个值。该函数的行为与您调用的函数完全相同 .bind 对,只有那个 this 是你设定的。无论该函数如何调用或何时调用, this 将始终引用传递的值。

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

在本例中,我们将绑定回调函数的 this 价值 MyConstructorthis .
注意:当为jquery创建绑定上下文时,请使用 jQuery.proxy [docs]取而代之。这样做的原因是,在解除绑定事件回调时,不需要存储对函数的引用。jquery在内部处理这个问题。

设置这个回调函数-第2部分

一些接受回调的函数/方法也接受一个回调函数 this 应参考。这与您自己绑定基本相同,但函数/方法会为您执行此操作。 Array#map [docs]就是这样一种方法。其签名为:

array.map(callback[, thisArg])

第一个参数是回调,第二个参数是值 this 应参考。下面是一个人为的例子:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

注意:是否可以为传递值 this 通常在该函数/方法的文档中提到。例如,jquery的 $.ajax 方法[docs]描述了一个名为 context :
此对象将成为所有ajax相关回调的上下文。

常见问题:将对象方法用作回调/事件处理程序

此问题的另一个常见表现形式是将对象方法用作回调/事件处理程序。函数在javascript中是一等公民,术语“方法”只是一个口语术语,用于表示作为对象属性值的函数。但该函数没有到其“包含”对象的特定链接。
考虑下面的例子:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

功能 this.method 指定为单击事件处理程序,但如果 document.body 单击时,将记录值 undefined ,因为在事件处理程序中, thisdocument.body ,而不是 Foo .
正如开始时已经提到的,什么 this 引用取决于函数的调用方式,而不是函数的定义方式。
如果代码如下所示,则函数没有对对象的隐式引用可能更为明显:

function method() {
    console.log(this.data);
}

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

解决方案与上述相同:如果可用,请使用 .bind 显式绑定 this 到一个特定的值

document.body.onclick = this.method.bind(this);

或者使用匿名函数作为回调/事件处理程序显式调用函数作为对象的“方法”,并分配对象( this )到另一个变量:

var self = this;
document.body.onclick = function() {
    self.method();
};

或使用箭头功能:

document.body.onclick = () => this.method();
uqxowvwt

uqxowvwt2#

以下是在子上下文中访问父上下文的几种方法-

你可以用 bind() 功能。
将对context/this的引用存储在另一个变量中(参见下面的示例)。
使用es6箭头功能。
改变代码/功能设计/体系结构-为此,您应该拥有javascript设计模式的控制权。

1.使用bind()函数

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', ( function () {
        alert(this.data);
    }).bind(this) );
}
// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};
// called as
var obj = new MyConstructor('foo', transport);

如果您正在使用 underscore.js - http://underscorejs.org/#bind

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));

2将对上下文/此的引用存储在另一个变量中

function MyConstructor(data, transport) {
  var self = this;
  this.data = data;
  transport.on('data', function() {
    alert(self.data);
  });
}

3箭头功能

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
gwo2fgha

gwo2fgha3#

这都是调用方法的“神奇”语法:

object.property();

当您从对象获取属性并一次性调用它时,该对象将成为该方法的上下文。如果调用相同的方法,但在不同的步骤中,则上下文是全局范围(窗口):

var f = object.property;
f();

当您获得方法的引用时,它不再附加到对象,它只是对普通函数的引用。当获取要用作回调的引用时,也会发生同样的情况:

this.saveNextLevelData(this.setAll);

这就是将上下文绑定到函数的位置:

this.saveNextLevelData(this.setAll.bind(this));

如果您使用的是jquery,那么应该使用 $.proxy 方法,而不是 bind 并非所有浏览器都支持:

this.saveNextLevelData($.proxy(this.setAll, this));
yacmzcpb

yacmzcpb4#

“语境”的麻烦

术语“上下文”有时指的是本文档引用的对象。它的使用是不合适的,因为它在语义上或技术上都不符合ecmascript的this。
“上下文”是指围绕某事物的环境,它增加了意义,或者是一些前后信息,赋予了额外的意义。ecmascript中使用的术语“上下文”指的是执行上下文,它是一些执行代码范围内的所有参数、范围和范围。
如ecma-262第10.4.2节所示:
将thisbinding设置为与调用执行上下文的thisbinding相同的值
这清楚地表明这是执行上下文的一部分。
执行上下文提供周围信息,为正在执行的代码添加意义。它包含的信息远不止thisbinding。
所以它的价值不是“上下文”,它只是执行上下文的一部分。它本质上是一个局部变量,可以通过调用任何对象,在严格模式下设置为任何值。

jdgnovmf

jdgnovmf5#

你应该知道“this”关键字。
根据我的观点,您可以通过三种方式实现“this”(self/arrow函数/bind方法)
与其他语言相比,函数的this关键字在javascript中的行为稍有不同。
严格模式和非严格模式也有一些区别。
在大多数情况下,其值取决于函数的调用方式。
它不能在执行期间通过赋值进行设置,每次调用函数时可能会有所不同。
es5引入了bind()方法来设置函数this的值,而不管它是如何调用的,
es2015引入了arrow函数,这些函数不提供自己的绑定(它保留了封闭词法上下文的这个值)。
方法1:self-self被用来维护对原始文档的引用,即使上下文在变化。这是一种常用于事件处理程序(尤其是闭包)的技术。
参考:https://developer.mozilla.org/en-us/docs/web/javascript/reference/operators/this

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function () {
        alert(self.data);
    });
}

方法2:arrow函数-arrow函数表达式是正则函数表达式的语法紧凑的替代方法,
尽管没有它自己对this、arguments、super或new.target关键字的绑定。
箭头函数表达式不适合用作方法,并且不能用作构造函数。
参考:https://developer.mozilla.org/en-us/docs/web/javascript/reference/functions/arrow_functions

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',()=> {
        alert(this.data);
    });
}

方法3:bind-bind()方法创建一个新函数,
调用时,将其this关键字设置为提供的值,
调用新函数时,在任何参数之前提供给定的参数序列。
参考:https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/function/bind

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',(function() {
        alert(this.data);
    }).bind(this);
sirbozc5

sirbozc56#

首先,你需要清楚地了解 scope 以及 this 上下文中的关键字 scope . this & scope :

there are two types of scope in javascript. They are :

   1) Global Scope

   2) Function Scope

简而言之,全局作用域是指窗口对象。全局作用域中声明的变量可以从任何地方访问。另一方面,函数作用域位于函数内部。函数内部声明的变量通常不能从外部世界访问。 this 全局范围中的关键字引用窗口对象。 this inside函数还引用window对象。so this 将始终引用窗口,直到找到操纵的方法 this 表明我们自己选择的背景。

--------------------------------------------------------------------------------
-                                                                              -
-   Global Scope                                                               -
-   ( globally "this" refers to window object)                                 -     
-                                                                              -
-         function outer_function(callback){                                   -
-                                                                              -
-               // outer function scope                                        -
-               // inside outer function"this" keyword refers to window object -                                                                              -
-              callback() // "this" inside callback also refers window object  -

-         }                                                                    -
-                                                                              -
-         function callback_function(){                                        -
-                                                                              -
-                //  function to be passed as callback                         -
-                                                                              -
-                // here "THIS" refers to window object also                   -
-                                                                              -
-         }                                                                    -
-                                                                              -
-         outer_function(callback_function)                                    -
-         // invoke with callback                                              -
--------------------------------------------------------------------------------

不同的操纵方式 this 内部回调函数:
这里有一个名为person的构造函数。它有一个叫做 name 然后调用四个方法 sayNameVersion1 , sayNameVersion2 , sayNameVersion3 , sayNameVersion4 . 这四个函数都有一个特定的任务。接受回调并调用它。回调有一个特定的任务,即记录person构造函数示例的name属性。

function Person(name){

    this.name = name

    this.sayNameVersion1 = function(callback){
        callback.bind(this)()
    }
    this.sayNameVersion2 = function(callback){
        callback()
    }

    this.sayNameVersion3 = function(callback){
        callback.call(this)
    }

    this.sayNameVersion4 = function(callback){
        callback.apply(this)
    }

}

function niceCallback(){

    // function to be used as callback

    var parentObject = this

    console.log(parentObject)

}

现在让我们从person构造函数创建一个示例并调用diff

相关问题