如何访问正确的`this`回调里面?(How to access the correct `this

2019-10-28 15:20发布

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

 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);
};

但它表现出了同样的问题。

我如何才能获得正确的对象?

Answer 1:

你应该知道什么this

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文档 。


如何引用正确的this

不要使用this

你其实并不想访问this特别, 但对象是指 。 这就是为什么一个简单的解决方案是简单地创建一个新的变量,也指该对象。 该变量可以有任何名称,但常见的是selfthat

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

由于self是正常的变量,它服从词法范围的规则,是在回调中访问。 这也有可以访问的优势, this回调本身的价值。

明确设置this回调-第1部分

它看起来像你有过的价值无法控制this ,因为它的值是自动设置的,但实际上并非如此。

每个函数都有所述方法.bind [文档] ,它返回一个新的功能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 [文件]代替。 之所以这样做,这是使取消绑定事件回调时,你并不需要参考存储功能。 jQuery的处理是在内部。

ECMAScript中6:使用箭头功能

的ECMAScript 6引入箭头的功能 ,其可以被认为是lambda函数。 他们没有自己的this具有约束力。 相反, this是抬头范围就像一个正常的变量。 这意味着你不必调用.bind 。 这不是他们唯一的特殊行为,请参阅MDN文档的详细信息。

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

设置this回调-第2部分

其中接受回调的某些功能/方法也接受一个值的回调的到this应该是指。 这基本上是一样的自己约束力,但该函数/方法会为你。 Array#map [文档]是这样一种方法。 它的签名是:

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[文件]描述了一个叫选项context

这个对象将作出所有Ajax相关回调的背景下。


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

当一个对象的方法被用作回调/事件处理此问题的另一个常见的表现是。 函数是JavaScript中的一等公民和术语“方法”仅仅是一个函数,对象属性的值的口语期限。 但是,这个功能并没有一个特定的链接到它的“含”对象。

请看下面的例子:

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

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

该功能this.method被指定为click事件处理,但如果document.body点击,记录值将undefined ,因为在事件处理中, this指的是document.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();


Answer 2:

这里有几种方式可访问子上下文中父上下文 -

  1. 您可以使用bind ()函数。
  2. 存储参考上下文/这个另一变量中(见下例)。
  3. 使用ES6 箭头功能。
  4. 改变代码/功能设计/建筑-这个你应该有指挥权的设计模式在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 Arrow功能

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


Answer 3:

这一切都在调用一个方法的“神奇”的语法:

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));


Answer 4:

有“背景”的麻烦

术语“上下文”有时用来指通过引用的对象。 它的使用是不合适的,因为它不适合任何语义或技术上与ECMAScript中的这个

“背景”是指周围的东西更有意义,或者一些之前和之后的信息提供额外含义的情形。 术语“上下文” ECMAScript中用于指代执行上下文 ,这是所有的参数,范围和这个的一些执行的代码的范围内。

这示出在ECMA-262部分10.4.2 :

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

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

一个执行上下文提供周边信息,增加了含义正在执行的代码。 它包括的不仅仅是更多的信息thisBinding

所以这个值是不是“背景”,它只是一个执行上下文的一部分。 它本质上是可以被调用的任何对象,并在严格模式下都可以设置,为任意值的局部变量。



Answer 5:

首先,你需要有一个清醒的认识scope和行为, this关键字的上下文scope

thisscope


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

   1) Global Scope

   2) Function Scope

总之,全球范围内是指在全球范围内声明的窗口object.Variables从anywhere.On访问另一方面函数范围驻留在函数内声明的function.variable内不能从外界的正常访问。 this关键字在全球范围内指的是window对象。 this里面函数也指窗口object.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里面回调函数:

在这里,我有一个名为的人一个构造函数。 它有一个属性称为name和四个方法称为sayNameVersion1sayNameVersion2sayNameVersion3sayNameVersion4 。 他们四个有一个特定task.Accept的回调和调用后援回调有一个特定的任务是记录人的构造函数的一个实例的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)

}

现在,让我们创建一个从人的构造函数的实例并调用的不同版本sayNameVersionX (X指的是1,2,3,4)法niceCallback ,看看我们有多少种方法可以操纵this里面回调指person实例。

var p1 = new Person('zami') // create an instance of Person constructor

绑定:

什么绑定要做的就是创建一个新的功能与this关键字设置为所提供的价值。

sayNameVersion1sayNameVersion2使用绑定来操纵this回调函数。

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

第一个结合this与方法itself.And内部回调第二个回调被传递与结合于它的对象。

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

p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback

拨打:

first argument的的call方法被用作this被调用与函数内call连接到它。

sayNameVersion3使用call来操作this指的不是window对象,我们创建的Person对象。

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

它被称为像下面这样:

p1.sayNameVersion3(niceCallback)

适用于:

类似call的第一个参数apply是指将被指示的对象this关键字。

sayNameVersion4使用apply来操纵this指Person对象

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

它被称为像following.Simply回调传递,

p1.sayNameVersion4(niceCallback)


Answer 6:

你应该知道“this”关键字。

按我的观点,你可以实现“本”在三个方面 (自我/ Arrow功能/绑定方法)

相对于其他语言在JavaScript函数的此关键字的行为方式有所不同。

它也有严格的之间的模式和非严格模式的一些差别。

在大多数情况下,这个值是通过一个函数是如何被调用来确定。

它不能被分配在执行期间设置,并且每一个函数被调用时它可以是不同的。

ES5引入了bind()方法来设置功能的价值这个不管它怎么叫,

和ES2015引入箭头功能不提供它们自己的这种结合(它保留了包围词汇上下文的此值)。

方法一:自我-自我是被用来保持甚至上下文原来的这个基准发生变化。 它通常在事件处理程序使用(尤其是在闭包)的技术。

参考 : 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:箭头功能-的箭头函数表达式是语法上紧凑的替代正规的函数表达式,

虽然没有自己的绑定到这一点,参数,超,或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()方法创建一个新的功能,

调用时,有该关键字设置为所提供的价值,

与前述的当新功能被调用任何设置参数给定的序列。

参考: 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);


Answer 7:

我们不能绑定这setTimeout()如果你想访问,因为它总是与全局对象(窗口)执行, this然后通过使用回调函数的上下文bind()回调函数我们可以实现如下:

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


Answer 8:

现在的问题是围绕如何this关键字在JavaScript行为。 this下面的行为不同,

  1. 的值this通常是通过一个功能的执行上下文来确定。
  2. 在全球范围内, this指的是全局对象(该window对象)。
  3. 如果严格模式是所有功能则值启用thisundefined为在严格模式下,全局对象指的是undefined替代的window对象。
  4. 即点之前站在目的是什么,这个关键字将被绑定到。
  5. 我们可以明确地设置这个值call()bind()apply()
  6. new使用的关键字(一个构造函数),这势必要创建的新对象。
  7. 箭功能不与this -相反, this是(基于原始上下文IE)词法约束

由于大部分的答案表明,我们可以使用Arrow功能或bind()方法或自我变种。 我想举一个有关的lambda点(Arrow功能)从谷歌的JavaScript风格指南

喜欢使用超过f.bind(本)箭头功能,并且尤其是在goog.bind(F,这一点)。 避免写常量自我=这一点。 箭头功能是回调,这有时会意外传递额外的参数是特别有用的。

谷歌显然推荐使用lambda表达式,而不是绑定或const self = this

所以最好的解决办法是使用lambda表达式如下,

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

参考文献:

  1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
  2. 箭头功能-VS绑定


Answer 9:

目前还有另一种方法可能的,如果类是在代码中使用。

随着支持的类字段它可能使下一个方法:

class someView {
    onSomeInputKeyUp = (event) => {
        console.log(this); // this refers to correct value
    // ....
    someInitMethod() {
        //...
        someInput.addEventListener('input', this.onSomeInputKeyUp)

对于引擎盖下确保它是结合上下文的所有老好箭头的功能,但以这种形式看起来更清楚,明确的约束力。

由于这是第3阶段的提案则需要巴贝尔和适当的巴别塔的插件来处理它为现在(08/2018)。



Answer 10:

另一种方法,这是因为DOM2标准的方式绑定this事件侦听器内, 使您可以随时删除监听器 (还有其他好处),是handleEvent(evt)从法EventListener接口:

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

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

有关使用的详细信息handleEvent可以在这里找到: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38



文章来源: How to access the correct `this` inside a callback?