Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/464.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在回调中访问正确的'this'?_Javascript_Callback_This - Fatal编程技术网

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

Javascript 如何在回调中访问正确的'this'?,javascript,callback,this,Javascript,Callback,This,我有一个注册事件处理程序的构造函数: 函数MyConstructordata,传输{ 这个数据=数据; 传输。关于“数据”,函数{ 警报此数据; }; } //模拟传输对象 var传输={ 开启:functionevent,回调{ setTimeoutcallback,1000; } }; //称为 var obj=新的MyConstructor'foo',transport;关于这件事你应该知道什么 上下文是每个函数中的一个特殊关键字,其值仅取决于函数的调用方式,而不取决于函数的定义方式/时

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

函数MyConstructordata,传输{ 这个数据=数据; 传输。关于“数据”,函数{ 警报此数据; }; } //模拟传输对象 var传输={ 开启:functionevent,回调{ setTimeoutcallback,1000; } }; //称为 var obj=新的MyConstructor'foo',transport;关于这件事你应该知道什么 上下文是每个函数中的一个特殊关键字,其值仅取决于函数的调用方式,而不取决于函数的定义方式/时间/地点。除了箭头函数之外,它与其他变量一样不受词法作用域的影响,见下文。以下是一些例子:

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`
要了解更多信息,请查看

如何参考本手册中的正确答案 使用 ECMAScript 6引入了箭头函数,可以将其视为lambda函数。他们没有自己的这个绑定。相反,这是在范围内查找的,就像普通变量一样。这意味着你不必打电话给bind。这不是他们唯一的特殊行为,更多信息请参考MDN文档

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}
不要用这个 实际上,您不想特别访问它,但它所引用的对象。这就是为什么一个简单的解决方案是创建一个新的变量,该变量也引用该对象。变量可以有任何名称,但常用的名称是self,即self

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

显式设置回调函数的这一部分-第1部分 看起来您可能无法控制它的值,因为它的值是自动设置的,但事实并非如此

每个函数都有一个方法,该方法返回一个新函数,并将其绑定到一个值。该函数的行为与您调用的.bind on完全相同,只是这是由您设置的。无论如何或何时调用该函数,它都将引用传递的值

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);
}
第一个参数是回调,第二个参数是它应该引用的值。下面是一个人为的例子:

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.method被指定为click事件处理程序,但是如果单击document.body,则记录的值将是未定义的,因为在事件处理程序中,它指的是document.body,而不是Foo的实例。 正如前面提到的,它所指的内容取决于函数的调用方式,而不是函数的定义方式。 如果代码如下所示,则函数没有对对象的隐式引用可能更为明显:

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


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

Foo.prototype.method = method;
解决方案与上面提到的相同:如果可用,请使用.bind将其显式绑定到特定值

document.body.onclick = this.method.bind(this);
或者使用匿名函数作为回调/事件处理程序显式调用函数作为对象的方法,并将此对象分配给另一个变量:

var self = this;
document.body.onclick = function() {
    self.method();
};
或使用箭头功能:

document.body.onclick = () => this.method();
this.saveNextLevelData(this.setAll.bind(this));

这一切都体现在调用方法的神奇语法中:

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

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

this.saveNextLevelData(this.setAll);
这就是将上下文绑定到函数的位置:

document.body.onclick = () => this.method();
this.saveNextLevelData(this.setAll.bind(this));
如果使用jQuery,则应改用$.proxy方法,因为并非所有浏览器都支持绑定:

this.saveNextLevelData($.proxy(this.setAll, this));
语境的问题 术语“上下文”有时用于指本文档引用的对象。它的使用是不恰当的,因为它在语义上或技术上都不适合

意思是指围绕某事物而增加意义的环境,或是一些赋予额外意义的前后信息。术语context在ECMAScript中用来指代,它是一些正在执行的代码范围内的所有参数、范围和内容

如图所示:

将ThisBinding设置为与的ThisBinding相同的值 调用执行上下文

这清楚地表明这是执行上下文的一部分

执行上下文提供周围信息,为正在执行的代码添加意义。它所包含的信息远不止这些

因此,它的价值不是上下文,它只是执行上下文的一部分。它本质上是一个局部变量,可以通过调用任何对象并在严格模式下设置为任何值。

以下是在子上下文中访问父上下文的几种方法- 您可以使用函数。 将对context/this的引用存储在另一个变量中参见下面的示例。 使用ES6功能。 更改代码/功能设计/归档 体系结构-对于这一点,您应该拥有javascript中的命令。 1.使用绑定函数 函数MyConstructordata,传输{ 这个数据=数据; 传输。关于“数据”,函数{ 警报此数据; }.这一点; } //模拟传输对象 var传输={ 开启:functionevent,回调{ setTimeoutcallback,1000; } }; //称为 var obj=新的MyConstructor'foo',transport; 如果您使用的是下划线.js-

2将对上下文/此的引用存储在另一个变量中 函数MyConstructordata,传输{ var self=这个; 这个数据=数据; 传输。关于“数据”,函数{ 警报自我数据; }; } 3箭头功能 函数MyConstructordata,传输{ 这个数据=数据; 传输。在“数据”上,=>{ 警报此数据; }; }
首先,您需要清楚地了解这个关键字在范围上下文中的范围和行为

这是一个范围(&S):

总之,,全局作用域指窗口对象。全局作用域中声明的变量可以从任何地方访问。另一方面,函数作用域位于函数内部。函数内部声明的变量通常不能从外部世界访问。全局作用域中的此关键字指窗口对象。此内部函数也指窗口对象。所以它总是指窗口,直到我们找到一种方法来处理它,以表示我们自己选择的上下文

--------------------------------------------------------------------------------
-                                                                              -
-   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                                              -
--------------------------------------------------------------------------------
在回调函数中操作此函数的不同方法:

这里有一个名为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)

}
this.sayNameVersion1 = function(callback){
    callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
    callback()
}
现在,让我们从person构造函数创建一个实例,并使用niceCallback调用不同版本的sayNameVersionX引用1,2,3,4方法,看看有多少种方法可以操作this内部回调来引用person实例

var p1 = new Person('zami') // create an instance of Person constructor
bind要做的是创建一个新函数,将this关键字设置为提供的值

sayNameVersion1和sayNameVersion2使用bind来操作回调函数的这个部分

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)

}
this.sayNameVersion1 = function(callback){
    callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
    callback()
}
第一个是在方法本身内部绑定回调,第二个是在对象绑定的情况下传递回调

p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method

p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
call方法的第一个参数在函数中使用,函数调用时附加了call

sayNameVersion3使用调用操纵this来引用我们创建的person对象,而不是window对象

this.sayNameVersion3 = function(callback){
    callback.call(this)
}
它的名称如下所示:

p1.sayNameVersion3(niceCallback)
与call类似,apply的第一个参数引用将由该关键字指示的对象

sayNameVersion4使用apply来处理此对象以引用person对象

this.sayNameVersion4 = function(callback){
    callback.apply(this)
}
它的调用如下所示。只需传递回调

p1.sayNameVersion4(niceCallback)
我们不能将其绑定到setTimeout,因为它总是在全局对象窗口中执行,如果您想在回调函数中访问此上下文,那么通过使用绑定到回调函数,我们可以实现如下:

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);

另一种方法是EventListener接口中的HandleEventVTMethod,这是自DOM2以来在事件侦听器中绑定此功能的标准方法,它让您始终可以删除侦听器,还有其他好处:

var obj = {
  handleEvent(e) {
    // always true
    console.log(this === obj);
  }
};

document.body.addEventListener('click', obj);

有关使用handleEvent的详细信息可在此处找到:

如果在代码中使用类,则目前还有另一种方法

有了支持,就有可能实现下一步:

class someView {
    onSomeInputKeyUp = (event) => {
        console.log(this); // this refers to correct value
    // ....
    someInitMethod() {
        //...
        someInput.addEventListener('input', this.onSomeInputKeyUp)
当然,在幕后,绑定上下文的都是旧的GoodArrow函数,但在这种形式下,显式绑定看起来更清晰

由于这是第3阶段的提案,您将需要巴别塔,并在2018年8月开始适当地处理它。

您应该知道这个关键词

根据我的观点,你可以通过三种方式实现这一点 Self/Arrow函数/绑定方法

与其他语言相比,函数的this关键字在JavaScript中的行为稍有不同

严格模式和非严格模式也有一些区别

在大多数情况下,其值取决于函数的调用方式

它不能在执行期间通过赋值进行设置,每次调用函数时可能会有所不同

ES5引入了bind方法来设置函数this的值,不管它是如何调用的

ES2015引入了arrow函数,这些函数不提供自己的绑定,它保留了封闭词法上下文的值

方法1:Self-Self被用来维护对原始文档的引用,即使上下文在变化。这是一种经常在事件处理程序中使用的技术,尤其是在闭包中

参考:

方法2:Arrow函数-Arrow函数表达式是常规函数表达式的一种语法紧凑的替代方法 离子

尽管没有它自己对this、arguments、super或new.target关键字的绑定

箭头函数表达式不适合用作方法,并且不能用作构造函数

参考:

方法3:Bind-Bind方法创建一个新函数

调用时,将其this关键字设置为提供的值

调用新函数时,在任何参数之前提供给定的参数序列

参考:


问题围绕着这个关键字在javascript中的行为。其行为方式如下所示:

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
其值通常由函数执行上下文确定。 在全局范围中,这是指窗口对象的全局对象。 如果为任何函数启用了严格模式,则其值将未定义,因为在严格模式下,全局对象是指未定义的窗口对象。 站在点前面的对象就是这个关键字将绑定到的对象。 我们可以通过call、bind和apply显式地设置这个值 当new关键字用作构造函数时,它将绑定到正在创建的新对象。 箭头函数不绑定此项 — 相反,这是词汇上的约束,即基于原始上下文 正如大多数答案所建议的,我们可以使用Arrow函数、bind方法或Self-var

与f.bindthis相比,更喜欢使用箭头函数,尤其是 goog.bindf,这个。避免编写const self=this。箭头函数 对于有时意外通过的回调特别有用 附加参数

谷歌明确建议使用lambdas,而不是bind或const self=this

因此,最好的解决方案是使用lambdas,如下所示

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
参考资料:

这在JS中是: JS中的值100%取决于函数的调用方式,而不是函数的定义方式。我们可以通过“点的左侧规则”相对容易地找到该值:

使用function关键字创建函数时,该关键字的值是调用函数点左侧的对象 如果点的左边没有对象,那么函数中的值通常是全局对象在节点、浏览器窗口中的全局对象。我不建议在这里使用this关键字,因为它比使用window之类的东西更显式! 存在某些构造,如箭头函数和使用Function.prototype.bind创建的函数。可以修复此函数的值。这些都是规则的例外情况,但确实有助于修复该规则的值。 nodeJS中的示例 输出:

让我逐一介绍输出,忽略从第二个日志开始的第一个日志:

这是obj2,因为点规则的左边,我们可以看到test1如何被称为obj2.test1;。obj2位于点的左侧,因此该值为。 即使obj2位于点的左侧,test2也通过bind方法绑定到obj1。所以这个值的最大值是obj1。 obj2位于名为:obj2.test3的函数的点的左侧。因此,obj2将是该值。 在这种情况下:obj2.test4 obj2位于点的左侧。但是,箭头函数没有自己的此绑定。因此,它将绑定到外部范围(即模块)的this值。导出一个在开始时记录的对象。 我们还可以使用call函数指定该值。在这里,我们可以将所需的this值作为参数传递,在本例中为obj2。 我遇到了Ngx折线图XaxistickFormat函数的问题,该函数是从HTML调用的,如下所示:[XaxistickFormat]=xFormat。我无法从声明的函数访问组件的变量。这个解决方案帮助我解决了这个问题,找到了正确的解决方法。希望这对Ngx折线图的用户有所帮助

不要像这样使用函数:

xFormat (value): string {
  return value.toString() + this.oneComponentVariable; //gives wrong result 
}
使用以下命令:

 xFormat = (value) => {
   // console.log(this);
   // now you have access to your component variables
   return value + this.oneComponentVariable
 }

相关:相关:,主要也适用于JS。@Stratton:自答问题是UI中的一个选项,建议:。我为一次又一次出现的问题做了几次,以提供一个规范的答案。经典问答的问题在于,现有问题的措辞往往不够笼统,不能集中于核心问题。Felix,我以前读过这个答案,但从未回答过。我越来越担心人们用自我和自我来指代这一点。我有这种感觉,因为这是一个在不同上下文中使用的重载变量;而self通常对应于本地实例,它通常引用另一个对象。我知道你没有制定这条规则,正如我在许多其他地方看到的那样,但这也是我开始使用这条规则的原因,但我不确定其他人的感受,除了由此产生的非统一实践之外。@FelixKling是否可以安全地假设,使用此内部原型函数将始终具有预期的行为,而不管它们通常如何调用?在p中使用回调时
rototype函数,是否有绑定、self或其他替代方法?@FelixKling依赖Function.prototype.call和Function.prototype.apply有时会很有用。特别是使用apply,我跑了很多里程。我不太倾向于使用绑定,也许只是出于习惯,虽然我知道,但不能肯定的是,使用BIN比其他选项可能有轻微的开销优势。很好的答案,但是考虑添加一个额外的可选解决方案,只是为了不使用类、新或这一点。这是在范围内查找的,就像普通变量一样。完全为我点击,谢谢!=>点击这个按钮;如果您对答案有任何建设性的批评,我们将不胜感激!全局范围中的this关键字不一定指窗口对象。只有在浏览器中才是这样。@RandallFlagg我从浏览器的角度写下了这个答案。如果有必要,可以随意使用这个答案:这与任何现有答案有何不同?这正是我在Typescript:public methodName=params=>{body}中得到它的方式这个问题是关于使用函数/方法作为回调的。你的回答可能更适合我的工作。我发现你的第四点措辞含糊不清。考虑一下,右边的对象是站在点之前,但是上下文不是那个对象。绑定选项是惊人的,它只是通过这个对象的指针在另一个对象上做这个:谢谢!子上下文中的父上下文是一个误导性短语,因为这不是父/子关系的一部分。它通常指调用方法的对象,但可以是任何对象,也可以是严格模式下的任何值。指一个执行上下文,该上下文是许多无法引用的参数中的一个,因为ECMA-262禁止它。不同意此答案。“执行上下文”一词的存在并不禁止上下文的其他用途,正如它不禁止执行的其他用途一样。也许有一个更好的术语来描述这一点,但这里没有提供,而且可以说现在关闭上下文的大门已经太迟了。@Roamer-1888-谢谢您的编辑。你是对的,但我的论点并不依赖于执行上下文的存在,而排除了出于其他目的的执行上下文。相反,它是基于从技术和语义的角度来看上下文都不合适。我还认为,使用上下文而不是这一点正在消失。我看不出有任何理由为this或thisBinding找到一个替代术语,它只是混淆了,意味着在某个点上,你必须解释上下文实际上是this,而它无论如何都不在上下文中-当你已经承认这是执行上下文的一部分,执行只是形容词时,我认为你不能说这与上下文无关。@Roamer-1888-我不打算在这一点之后继续这个对话。是的,这是执行上下文的一部分。说它是上下文就像说一个团队的一个成员就是团队。
function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);

const obj1 = {
    data: "obj1 data",
    met1: function () {
        console.log(this.data);
    },
    met2: () => {
        console.log(this.data);
    },
};

const obj2 = {
    data: "obj2 data",
    test1: function () {
        console.log(this.data);
    },
    test2: function () {
        console.log(this.data);
    }.bind(obj1),
    test3: obj1.met1,
    test4: obj1.met2,
};

obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
xFormat (value): string {
  return value.toString() + this.oneComponentVariable; //gives wrong result 
}
 xFormat = (value) => {
   // console.log(this);
   // now you have access to your component variables
   return value + this.oneComponentVariable
 }