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

函数MyConstructor(数据,传输){this.data=数据;transport.on('数据',函数(){警报(this.data);});}//模拟传输对象var传输={on:函数(事件,回调){setTimeout(回调,1000);}};//称为var obj=新MyConstructor('o',传输);

但是,我无法访问回调中创建的对象的数据属性。看起来这不是指创建的对象,而是指另一个对象。

我还尝试使用对象方法而不是匿名函数:

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

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

但它表现出相同的问题。

如何访问正确的对象?


你应该知道什么

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`

要了解更多信息,请查看MDN文档。


如何参考正确的

使用箭头功能

ECMAScript 6引入了箭头函数,可以认为是lambda函数。他们没有自己的绑定。相反,这是在范围内查找的,就像普通变量一样。这意味着你不必调用.bind。这不是他们唯一的特殊行为,请参阅MDN文档了解更多信息。

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

不要使用此

实际上,您不想特别访问它,而是访问它所引用的对象。这就是为什么一个简单的解决方案是简单地创建一个新的变量,该变量也引用该对象。变量可以有任何名称,但常见的是self和that。

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

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

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

可能看起来您无法控制此值,因为其值是自动设置的,但事实并非如此。

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

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绑定到MyConstructor的this值。

注意:当jQuery的绑定上下文时,请改用jQuery.proxy[docs]。这样做的原因是在解除绑定事件回调时不需要存储对函数的引用。jQuery在内部处理这个问题。

设置回调-第2部分

一些接受回调的函数/方法也接受回调的this应该引用的值。这与自己绑定它基本相同,但函数/方法是为您执行的。数组#map[docs]就是这样一种方法。其签名为:

array.map(callback[, thisArg])

第一个参数是回调,第二个参数是应该引用的值。下面是一个精心设计的示例:

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

注意:该函数/方法的文档中通常会提到是否可以为此传递值。例如,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,则记录的值将未定义,因为在事件处理程序中,它引用的是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);

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

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

或使用箭头功能:

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

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

object.property();

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

var f = object.property;
f();

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

this.saveNextLevelData(this.setAll);

这就是将上下文绑定到函数的地方:

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

如果使用jQuery,则应改用$.proxy方法,因为并非所有浏览器都支持绑定:

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

“上下文”的问题

术语“上下文”有时用于指代此引用的对象。它的使用是不合适的,因为它在语义上或技术上都不符合ECMAScript的this。

“上下文”是指围绕着某事物的环境,它增加了意义,或是一些前后信息赋予了额外的意义。ECMAScript中使用术语“上下文”来指代执行上下文,它是一些执行代码范围内的所有参数、范围和内容。

如ECMA-262第10.4.2节所示:

将ThisBinding设置为与调用执行上下文

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

执行上下文提供为正在执行的代码添加意义的周围信息。它包含了比thisBinding更多的信息。

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


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

您可以使用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);

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

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

2.在另一个变量中存储对context/this的引用

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

首先,您需要清楚地了解这个关键字在范围上下文中的范围和行为。

此范围(&S):


JavaScript中有两种类型的作用域。他们是:

全球范围功能范围

简而言之,全局范围是指窗口对象。全局范围中声明的变量可以从任何地方访问。

另一方面,函数范围位于函数内部。在函数内部声明的变量通常无法从外部世界访问。

全局范围中的this关键字引用窗口对象。函数内部的这个也指窗口对象。因此,这将始终引用窗口,直到我们找到一种方法来处理它,以指示我们自己选择的上下文。

--------------------------------------------------------------------------------
-                                                                              -
-   Global Scope                                                               -
-   (globally "this" refers to window object)                                  -
-                                                                              -
-   function outer_function(callback){                                         -
-                                                                              -
-       // Outer function scope                                                -
-       // Inside the outer function, the "this" keyword                       -
-       //  refers to window object                                            -
-       callback() // "this" inside callback also refers to the  window object -
-   }                                                                          -
-                                                                              -
-   function callback_function(){                                              -
-                                                                              -
-       // Function to be passed as callback                                   -
-                                                                              -
-       // Here "THIS" refers to the window object also                        -
-   }                                                                          -
-                                                                              -
-   outer_function(callback_function)                                          -
-   // Invoke with callback                                                    -
-                                                                              -
--------------------------------------------------------------------------------

在回调函数中操作此函数的不同方法:

这里有一个名为Person的构造函数。它有一个名为name的属性和四个名为sayNameVersion1、sayNameVersion 2、sayName Version3和sayNameversion 4的方法。他们四人都有一项具体任务。接受回调并调用它。回调有一个特定的任务,即记录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构造函数创建一个实例,并使用niceCallback调用不同版本的sayNameVersionX(X引用1,2,3,4)方法,以了解我们可以用多少种方式操纵这个内部回调来引用person实例。

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

绑定:

bind所做的是创建一个新函数,将此关键字设置为所提供的值。

sayNameVersion1和sayNameVersion 2使用bind来操纵回调函数的这一点。

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

电话:

调用方法的第一个参数在函数中用作此参数,该函数通过附加的调用进行调用。

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

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

其名称如下:

p1.sayNameVersion3(niceCallback)

应用:

与调用类似,apply的第一个参数引用将由this关键字指示的对象。

sayNameVersion4使用apply操作此对象以引用person对象

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

它的名称如下。简单地传递回调,

p1.sayNameVersion4(niceCallback)

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

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

另一种方法是EventListener接口中的handleEvent(evt)方法,这是自DOM2以来将其绑定到事件侦听器中的标准方法,它允许您始终删除侦听器(以及其他好处):

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

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

有关使用handleEvent的详细信息可以在以下位置找到:DOM handleEvent:自2000年以来的跨平台标准


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

有了类字段的支持,可以通过以下方式实现:

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

当然,在幕后,绑定上下文的都是旧的好箭头函数,但在这种形式下,它看起来比显式绑定更清楚。

由于这是一个第3阶段的提案,您需要Babel和适当的Babel插件来处理它(08/2018)。


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

根据我的观点,你可以通过三种方式实现“这一点”(自身|箭头函数|绑定方法)

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

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

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

它不能在执行过程中通过赋值来设置,每次调用函数时可能会有所不同。

ES5引入了bind()方法来设置函数的值,

ES2015引入了箭头函数,这些函数不提供自己的绑定(它保留了封闭词汇上下文的值)。

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

参考:此

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

方法2:箭头函数-箭头函数表达式在语法上是正则函数表达式的一种紧凑的替代方法,尽管它本身没有与this、arguments、super或new.target关键字的绑定。

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

参考:箭头函数表达式

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

方法3:Bind-Bind()方法创建一个新函数,当调用该函数时,将其this关键字设置为所提供的值,并在调用新函数时提供的任何参数之前使用给定的参数序列。

引用:Function.pr原型.bind()

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

问题围绕着this关键字在JavaScript中的行为。这表现如下不同,

其值通常由函数执行上下文确定。在全局范围中,这是指全局对象(窗口对象)。如果为任何函数启用了严格模式,则其值将是未定义的,因为在严格模式中,全局对象是指未定义的窗口对象。位于点之前的对象是this关键字将绑定到的对象。我们可以使用call()、bind()和apply()显式设置此值当使用new关键字(构造函数)时,它将绑定到正在创建的新对象。箭头函数不绑定此 — 相反,这是在词汇上绑定的(即,基于原始上下文)

正如大多数答案所建议的那样,我们可以使用arrow函数或bind()方法或Self var。我将引用GoogleJavaScript风格指南中关于lambdas(arrow函数)的一点

更喜欢使用箭头函数而不是f.bind(this),尤其是在goog.绑定(f,this)。避免写入常量self=this。箭头功能对于有时意外传递的回调特别有用其他参数。

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

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

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

参考文献:

https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c箭头函数与绑定


这在JavaScript中:

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

当使用function关键字创建函数时,this的值是调用的函数点左侧的对象如果点后面没有对象,那么函数内部的值通常是全局对象(Node.js中的全局对象和浏览器中的窗口)。我不建议在这里使用this关键字,因为它比使用类似window的关键字更不明确!存在某些构造,如箭头函数和使用Function.prototype.bind()创建的函数,该函数可以修复此值。这些都是规则的例外,但它们确实有助于确定这一点的价值。

Node.js中的示例

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

输出:

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

这是obj2,因为点规则的左侧,我们可以看到test1是如何被称为obj2.test1();的;。obj2位于点的左侧,因此为该值。即使obj2位于点的左侧,test2也通过bind()方法绑定到obj1。此值为obj1。obj2位于调用为:obj2.test3()的函数的点的左侧。因此obj2将是该函数的值。在本例中:obj2.test4()obj2位于点的左侧。但是,箭头函数没有自己的这个绑定。因此,它将绑定到外部作用域的this值,即模块。导出一个开头记录的对象。我们还可以通过使用调用函数指定此值。在这里,我们可以传入所需的this值作为参数,在本例中为obj2。


我遇到了Ngx折线图xAxisTickFormatting函数的问题,该函数是从HTML调用的,如下所示:[xAxisTikFormatting]=“xFormat”。

我无法从声明的函数访问组件的变量。这个解决方案帮助我解决了问题,找到了正确的答案。

不要像这样使用函数:

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
 }

其他一些人已经谈到了如何使用.bind()方法,但这里特别介绍了如果有人在让它们一起工作时遇到困难,如何将其与.then()一起使用:

someFunction()
.then(function(response) {
    //'this' wasn't accessible here before but now it is
}.bind(this))

如注释中所述,另一种方法是使用没有自己的“this”值的箭头函数

someFunction()
.then((response)=>{
    //'this' was always accessible here
})