我希望找到一个“this”关键字的明确解释,以及如何正确使用它。

它的行为似乎很奇怪,我不完全明白为什么。

这是如何工作的,何时使用?


当前回答

要正确理解“这”,就必须理解上下文和范围以及它们之间的区别。

作用域:在javascript中,作用域与变量的可见性有关,作用域通过使用函数实现。(阅读有关范围的更多信息)

上下文:上下文与对象相关。它指函数所属的对象。当您使用JavaScript“this”关键字时,它指的是函数所属的对象。例如,在函数内部,当你说:“this.accoutNumber”时,你指的是属性“accoutNumber”,该属性属于该函数所属的对象。

如果对象“myObj”有一个名为“getMyName”的方法,当JavaScript关键字“this”在“getMyName”中使用时,它将引用“myObj”。如果函数“getMyName”是在全局范围内执行的,那么“this”是指窗口对象(严格模式除外)。

现在让我们看看一些示例:

    <script>
        console.log('What is this: '+this);
        console.log(this);
    </script>

在浏览器输出中运行abobve代码将:

根据窗口对象上下文中的输出,也可以看到窗口原型引用了对象。

现在让我们尝试函数内部:

    <script>
        function myFunc(){
            console.log('What is this: '+this);
            console.log(this);
        }
        myFunc();
    </script>

输出:

输出是相同的,因为我们将“this”变量记录在全局范围中,并将其记录在函数范围中,我们没有更改上下文。在这两种情况下,上下文都是相同的,与寡妇对象相关。

现在让我们创建自己的对象。在javascript中,可以通过多种方式创建对象。

 <script>
        var firstName = "Nora";
        var lastName = "Zaman";
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printNameGetContext:function(){
                console.log(firstName + " "+lastName);
                console.log(this.firstName +" "+this.lastName);
                return this;
            }
        }

      var context = myObj.printNameGetContext();
      console.log(context);
    </script>

输出:

因此,从上面的示例中,我们发现“this”关键字引用的是与myObj相关的新上下文,而myObject也具有到Object的原型链。

让我们再举一个例子:

<body>
    <button class="btn">Click Me</button>
    <script>
        function printMe(){
            //Terminal2: this function declared inside window context so this function belongs to the window object.
            console.log(this);
        }
        document.querySelector('.btn').addEventListener('click', function(){
            //Terminal1: button context, this callback function belongs to DOM element 
            console.log(this);
            printMe();
        })
    </script>
</body>

输出:有道理吧?(阅读评论)

如果您无法理解上面的示例,让我们尝试使用自己的回调;

<script>
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printName:function(callback1, callback2){
                //Attaching callback1 with this myObj context
                this.callback1 = callback1;
                this.callback1(this.firstName +" "+this.lastName)
                //We did not attached callback2 with myObj so, it's reamin with window context by default
                callback2();
                /*
                 //test bellow codes
                 this.callback2 = callback2;
                 this.callback2();
                */
            }
        }
        var callback2 = function (){
            console.log(this);
        }
        myObj.printName(function(data){
            console.log(data);
            console.log(this);
        }, callback2);
    </script>

输出:

现在,让我们了解范围、自我、IIFE和THIS的行为

       var color = 'red'; // property of window
       var obj = {
           color:'blue', // property of window
           printColor: function(){ // property of obj, attached with obj
               var self = this;
               console.log('In printColor -- this.color: '+this.color);
               console.log('In printColor -- self.color: '+self.color);
               (function(){ // decleard inside of printColor but not property of object, it will executed on window context.
                    console.log(this)
                    console.log('In IIFE -- this.color: '+this.color);
                    console.log('In IIFE -- self.color: '+self.color); 
               })();

               function nestedFunc(){// decleard inside of printColor but not property of object, it will executed on window context.
                    console.log('nested fun -- this.color: '+this.color);
                    console.log('nested fun -- self.color: '+self.color);
               }

               nestedFunc(); // executed on window context
               return nestedFunc;
           }
       };

       obj.printColor()(); // returned function executed on window context
   </script> 

输出非常棒,对吧?

其他回答

这能帮什么忙?(javascript中“this”的大部分混淆是因为它通常不链接到您的对象,而是链接到当前的执行范围——这可能不是它的确切工作方式,但对我来说总是这样——请参阅文章以获得完整的解释)

Javascript就是这个

简单函数调用

考虑以下功能:

function foo() {
    console.log("bar");
    console.log(this);
}
foo(); // calling the function

请注意,我们在正常模式下运行,即不使用严格模式。

在浏览器中运行时,此值将作为窗口记录。这是因为window是web浏览器范围内的全局变量。

如果在node.js这样的环境中运行相同的代码,这将引用应用程序中的全局变量。

现在,如果我们通过添加语句“usestrict”在严格模式下运行此操作;在函数声明的开头,这将不再引用这两个环境中的全局变量。这样做是为了避免严格模式中的混淆。在这种情况下,这将只是log undefined,因为这就是它,它没有被定义。

在下面的例子中,我们将看到如何操纵这个值。

对对象调用函数

有不同的方法可以做到这一点。如果您在Javascript中调用了本机方法,如forEach和slice,那么您应该已经知道,在这种情况下,this变量指的是调用该函数的Object(注意,在Javascript中,几乎所有的东西都是Object,包括数组和函数)。以以下代码为例。

var myObj = {key: "Obj"};
myObj.logThis = function () {
    // I am a method
    console.log(this);
}
myObj.logThis(); // myObj is logged

如果对象包含包含函数的属性,则该属性称为方法。调用此方法时,将始终将此变量设置为与其关联的对象。对于严格模式和非严格模式都是如此。

请注意,如果一个方法存储(或复制)在另一个变量中,则对该方法的引用将不再保留在新变量中。例如:

// continuing with the previous code snippet

var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation

考虑更常见的实际情况:

var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself

新关键字

考虑Javascript中的构造函数:

function Person (name) {
    this.name = name;
    this.sayHello = function () {
        console.log ("Hello", this);
    }
}

var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`

这是如何工作的?好吧,让我们看看当我们使用新关键字时会发生什么。

使用new关键字调用函数将立即初始化Person类型的Object。此对象的构造函数将其构造函数设置为Person。此外,请注意,typeof awal将仅返回Object。这个新对象将被分配Person.prototype的原型。这意味着Person原型中的任何方法或属性都可用于Person的所有实例,包括awal。现在调用函数Person本身;这是对新构建的对象awal的引用。

很简单,嗯?

请注意,官方的ECMAScript规范中没有说明此类函数是实际的构造函数。它们只是普通函数,新函数可以用于任何函数。只不过我们是这样使用它们的,所以我们只是这样称呼它们。

在函数上调用函数:调用和应用

所以,是的,因为函数也是Object(实际上是Javascript中的第一类变量),所以即使函数也有方法。。。嗯,功能本身。

所有函数都继承自全局函数,它的许多方法中有两个是调用和应用,这两个方法都可以用于在调用它们的函数中操纵此值。

function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);

这是使用call的典型示例。它基本上接受第一个参数,并在函数foo中将其设置为对thisArg的引用。传递给调用的所有其他参数都作为参数传递给函数foo。因此,上面的代码将在控制台中记录{myObj:“is cool”},[1,2,3]。在任何函数中更改此值的非常好的方法。

apply与callaccept几乎相同,它只接受两个参数:thisArg和一个包含要传递给函数的参数的数组。因此,可以将上述调用转换为如下应用:

foo.apply(thisArg, [1,2,3])

请注意,调用和应用可以覆盖我们在第二个项目中讨论的点方法调用集的值。足够简单:)

呈现。。。。绑定

bind是应召而来的兄弟。它也是Javascript中所有函数从全局函数构造函数继承的方法。绑定和调用/应用之间的区别在于,调用和应用实际上都会调用函数。另一方面,bind会返回一个预设了thisArg和参数的新函数。让我们举个例子来更好地理解这一点:

function foo (a, b) {
    console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */

bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`

看到三者之间的区别了吗?这很微妙,但它们的用法不同。与调用和应用一样,bind也将通过点方法调用覆盖此集合的值。

还要注意,这三个函数都没有对原始函数进行任何更改。call和apply将返回新构造的函数的值,而bind将返回新构建的函数本身,以便调用。

额外的东西,复制这个

有时,您不喜欢这样一个事实,即这会随范围而改变,尤其是嵌套范围。看看下面的例子。

var myObj = {
    hello: function () {
        return "world"
        },
    myMethod: function () {
        // copy this, variable names are case-sensitive
        var that = this;
        // callbacks ftw \o/
        foo.bar("args", function () {
            // I want to call `hello` here
            this.hello(); // error
            // but `this` references to `foo` damn!
            // oh wait we have a backup \o/
            that.hello(); // "world"
        });
    }
  };

在上面的代码中,我们看到这个值随嵌套范围而改变,但我们希望从原始范围中获得这个值。所以我们将这个“复制”到那个,并使用副本代替这个。聪明,嗯?

索引:

默认情况下,此中包含什么?如果我们将函数作为一个带有Object点符号的方法调用呢?如果我们使用新关键字呢?我们如何通过调用和应用来处理这一点?使用bind。复制此以解决嵌套范围问题。

“这”是关于范围的。每个函数都有自己的作用域,因为JS中的所有内容都是一个对象,所以即使是一个函数也可以使用“this”将一些值存储到自身中。OOP 101教导“这”仅适用于对象的实例。因此,每次函数执行时,该函数的新“实例”都具有“this”的新含义。

大多数人在尝试在匿名闭包函数中使用“this”时会感到困惑,例如:

(function(value) {
    this.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = this.value;        // uh oh!! possibly undefined
    });
})(2);

所以这里,在each()内部,“this”不包含您期望的“value”(从this.value=value;在其上方)。因此,为了克服这个(并非双关语)问题,开发人员可以:

(function(value) {
    var self = this;            // small change
    self.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = self.value;        // phew!! == 2 
    });
})(2);

试试看;你会开始喜欢这种编程模式

要正确理解“这”,就必须理解上下文和范围以及它们之间的区别。

作用域:在javascript中,作用域与变量的可见性有关,作用域通过使用函数实现。(阅读有关范围的更多信息)

上下文:上下文与对象相关。它指函数所属的对象。当您使用JavaScript“this”关键字时,它指的是函数所属的对象。例如,在函数内部,当你说:“this.accoutNumber”时,你指的是属性“accoutNumber”,该属性属于该函数所属的对象。

如果对象“myObj”有一个名为“getMyName”的方法,当JavaScript关键字“this”在“getMyName”中使用时,它将引用“myObj”。如果函数“getMyName”是在全局范围内执行的,那么“this”是指窗口对象(严格模式除外)。

现在让我们看看一些示例:

    <script>
        console.log('What is this: '+this);
        console.log(this);
    </script>

在浏览器输出中运行abobve代码将:

根据窗口对象上下文中的输出,也可以看到窗口原型引用了对象。

现在让我们尝试函数内部:

    <script>
        function myFunc(){
            console.log('What is this: '+this);
            console.log(this);
        }
        myFunc();
    </script>

输出:

输出是相同的,因为我们将“this”变量记录在全局范围中,并将其记录在函数范围中,我们没有更改上下文。在这两种情况下,上下文都是相同的,与寡妇对象相关。

现在让我们创建自己的对象。在javascript中,可以通过多种方式创建对象。

 <script>
        var firstName = "Nora";
        var lastName = "Zaman";
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printNameGetContext:function(){
                console.log(firstName + " "+lastName);
                console.log(this.firstName +" "+this.lastName);
                return this;
            }
        }

      var context = myObj.printNameGetContext();
      console.log(context);
    </script>

输出:

因此,从上面的示例中,我们发现“this”关键字引用的是与myObj相关的新上下文,而myObject也具有到Object的原型链。

让我们再举一个例子:

<body>
    <button class="btn">Click Me</button>
    <script>
        function printMe(){
            //Terminal2: this function declared inside window context so this function belongs to the window object.
            console.log(this);
        }
        document.querySelector('.btn').addEventListener('click', function(){
            //Terminal1: button context, this callback function belongs to DOM element 
            console.log(this);
            printMe();
        })
    </script>
</body>

输出:有道理吧?(阅读评论)

如果您无法理解上面的示例,让我们尝试使用自己的回调;

<script>
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printName:function(callback1, callback2){
                //Attaching callback1 with this myObj context
                this.callback1 = callback1;
                this.callback1(this.firstName +" "+this.lastName)
                //We did not attached callback2 with myObj so, it's reamin with window context by default
                callback2();
                /*
                 //test bellow codes
                 this.callback2 = callback2;
                 this.callback2();
                */
            }
        }
        var callback2 = function (){
            console.log(this);
        }
        myObj.printName(function(data){
            console.log(data);
            console.log(this);
        }, callback2);
    </script>

输出:

现在,让我们了解范围、自我、IIFE和THIS的行为

       var color = 'red'; // property of window
       var obj = {
           color:'blue', // property of window
           printColor: function(){ // property of obj, attached with obj
               var self = this;
               console.log('In printColor -- this.color: '+this.color);
               console.log('In printColor -- self.color: '+self.color);
               (function(){ // decleard inside of printColor but not property of object, it will executed on window context.
                    console.log(this)
                    console.log('In IIFE -- this.color: '+this.color);
                    console.log('In IIFE -- self.color: '+self.color); 
               })();

               function nestedFunc(){// decleard inside of printColor but not property of object, it will executed on window context.
                    console.log('nested fun -- this.color: '+this.color);
                    console.log('nested fun -- self.color: '+self.color);
               }

               nestedFunc(); // executed on window context
               return nestedFunc;
           }
       };

       obj.printColor()(); // returned function executed on window context
   </script> 

输出非常棒,对吧?

关于如何在JavaScript中解释“this”关键字,存在很多困惑。希望这篇文章能让所有这些人一劳永逸。还有更多。请仔细阅读整篇文章。预先警告,这篇文章很长。

无论使用的上下文如何,“this”总是引用Javascript中的“当前对象”。然而,“当前对象”是什么根据上下文而不同。上下文可能正好是以下6项中的1项:

全局(即在所有功能之外)内部直接“非绑定函数”调用(即尚未通过调用functionName.bind绑定的函数)内部间接“非绑定函数”通过functionName.Call和functionName.apply调用在“绑定函数”内部调用(即通过调用functionName.bind绑定的函数)通过“新建”创建对象内联DOM事件处理程序内部

以下逐一介绍了每种情况:

全局上下文(即所有功能外部):在所有功能之外(即在全局上下文中)对象”(因此“this”的值)始终是浏览器的“窗口”对象。内部直接“非绑定函数”调用:在直接“非绑定函数”调用中调用的函数调用变为“当前对象”(因此“this”的值)。如果在没有显式当前对象的情况下调用函数,则当前对象要么是“窗口”对象(对于非严格模式),要么是未定义的(对于严格模式)。中定义的任何函数(或变量)全局上下文自动成为“窗口”对象的属性。例如,假设函数在全局上下文中定义为函数UserDefinedFunction(){警报(this)}它成为窗口对象的属性,就像您定义了它作为window.UserDefinedFunction=函数(){警报(this)} 在“非严格模式”下,直接通过“UserDefinedFunction()”调用/调用此函数将自动调用/调用它作为“window.UserDefinedFunction()”,将“window”作为“UserDefinedFunction”中的“当前对象”(以及“this”的值)。在“非严格模式”中调用此函数将导致以下结果UserDefinedFunction()//显示[object Window],因为它自动被调用为窗口。UserDefined Function()在“严格模式”下,通过“UserDefinedFunction()”将“NOT”自动将其调用为“window.UserDefinedFunctions()”。因此,“当前对象”(以及“this”的值)“UserDefinedFunction”应未定义。在“严格模式”下调用此函数将导致以下结果UserDefinedFunction()//显示未定义然而,使用窗口对象显式调用它将导致以下内容window.UserDefinedFunction()//“无论模式如何,始终显示[object window]。”让我们看看另一个例子。请查看以下代码函数UserDefinedFunction(){警报(this.a+“,”+this.b+“,“+this.c+”,“+this.d”)}变量o1={a: 1中,b: 2,f: 用户定义函数}无功氧气={c: 3中,d: 4中,f: 用户定义函数}o1.f()//应显示1,2,未定义,未定义o2.f()//应显示未定义、未定义、3,4在上面的示例中,我们看到当“UserDefinedFunction”通过o1调用,“this”取值为o1显示其财产“a”和“b”的值。价值“c”和“d”的定义与o1相同没有定义这些财产类似地,当通过o2调用“UserDefinedFunction”时,“this”取o2的值,并显示其财产“c”和“d”的值。“a”和“b”的值显示为未定义,因为o2未定义这些财产。间接“非绑定函数”内部通过functionName.Call和functionName.apply调用:当通过调用“非绑定函数”时functionName.call或functionName.apply,“当前对象”(因此“this”的值)设置为传递给调用/应用的“this”参数(第一个参数)。下面的代码也演示了这一点。函数UserDefinedFunction(){警报(this.a+“,”+this.b+“,“+this.c+”,“+this.d”)}变量o1={a: 1中,b: 2,f: 用户定义函数}无功氧气={c: 3中,d: 4中,f: 用户定义函数}UserDefinedFunction.call(o1)//应显示1,2,未定义,未定义UserDefinedFunction.apply(o1)//应显示1,2,未定义,未定义UserDefinedFunction.call(o2)//应显示未定义、未定义、3、4UserDefinedFunction.apply(o2)//应显示未定义、未定义、3、4o1.f.call(o2)//应显示未定义、未定义、3、4o1.f.apply(o2)//应显示未定义、未定义、3、4o2.f.call(o1)//应显示1,2,未定义,未定义o2.f.apply(o1)//应显示1,2,未定义,未定义上述代码清楚地表明,任何“NON”的“this”值绑定函数”可以通过调用/应用来更改。此外,如果“this”参数未显式传递给调用/应用,“current object”(因此“this”的值)在非严格模式下设置为“window”,在严格模式下为“undefined”。在“绑定函数”调用(即通过调用functionName.bind绑定的函数)内部:绑定函数是其“this”值为固定的下面的代码演示了“this”在有界函数的函数UserDefinedFunction(){警报(this.a+“,”+this.b+“,“+this.c+”,“+this.d”)}变量o1={a: 1中,b: 2,f: 用户定义函数,bf:空}无功氧气={c: 3中,d: 4中,f: 用户定义函数,bf:空}var bound1=用户定义函数.bind(o1);//将函数“bound1”的“this”值永久固定到对象o1bound1()//应显示1,2,未定义,未定义var bound2=UserDefinedFunction.bind(o2);//将函数“bound2”的“this”值永久固定到对象o2bound2()//应显示undefined,undefineed,3,4var边界3=o1.f.bind(o2);//将函数“bound3”的“this”值永久固定到对象o2bound3()//应显示undefined,undefineed,3,4var绑定4=o2.f.bind(o1);//永久地将函数“bound4”的“this”值固定到对象o1bound4()//应显示1,2,undefined,undefineo1.bf=UserDefinedFunction.bind(o2)//将函数“o1.bf”的“this”值永久固定到对象o2o1.bf()//应显示未定义、未定义、3,4o2.bf=UserDefinedFunction.bind(o1)//将函数“o2.bf”的“this”值永久固定到对象o1o2.bf()//应显示1,2,未定义,未定义bound1.call(o2)//仍应显示1,2,undefined,undefine。“call”不能更改绑定函数的“this”值bound1.apply(o2)//仍应显示1,2,undefined,undefine。“apply”不能更改绑定函数的“this”值o2.bf。

以下总结了整篇文章

在全局上下文中,“this”总是指“window”对象每当调用函数时,都会在对象(“当前对象”)。如果未明确提供当前对象,当前对象是NON Strict中的“窗口对象”默认情况下,模式和严格模式中的“未定义”。非绑定函数中“this”的值是对调用该函数的上下文中的对象(“当前对象”)的引用非绑定函数中“this”的值可以由调用和应用函数的方法。“this”的值对于Bound函数是固定的,不能被函数的调用和应用方法覆盖。绑定和已绑定函数不会更改“this”的值。它保持设置为第一个绑定函数设置的值。构造函数中“this”的值是已创建并初始化内联DOM事件处理程序中“this”的值是引用指定事件处理程序的元素。