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

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

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


当前回答

这里有一个很好的JavaScript源代码。

以下是总结:

全局this在浏览器中,在全局范围内,这是windowobject<script type=“text/javascript”>console.log(此==窗口);//真的var foo=“bar”;console.log(this.foo);//“条形图”console.log(window.foo);//“条形图”在使用repl的节点中,这是顶级命名空间。您可以将其称为全局。>这个{ArrayBuffer:[Function:ArrayBuffer],Int8Array:{[Function:Int8Array]BYTES_PER_ELEMENT:1},Uint8Array:{[Function:Uint8Array]BYTES_PER_ELEMENT:1},...>全局==此真的在从脚本执行的节点中,全局范围中的这个对象以空对象开始。它与全球不同\\测试.jsconsole.log(此);\\{}console.log(this==全局);\\时尚函数this

除了在DOM事件处理程序的情况下,或者当提供了thisArg时(请参见下文),在节点和浏览器中都会在不使用新引用调用全局范围的函数中使用此函数…

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

如果使用use strict;,在这种情况下,这将是未定义的

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      "use strict";
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

若使用new调用函数,这将是一个新上下文,它将不会引用全局this。

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    new testThis();
    console.log(this.foo); //logs "bar"

    console.log(new testThis().foo); //logs "foo"
</script>

原型

您创建的函数将成为函数对象。它们会自动获得一个特殊的原型属性,这是您可以为其赋值的属性。当您使用new调用函数来创建实例时,您可以访问分配给原型属性的值。您可以使用此访问这些值。

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

Thing.prototype.foo = "bar";

var thing = new Thing(); //logs "bar"
console.log(thing.foo);  //logs "bar"

在原型上分配数组或对象通常是错误的。如果希望每个实例都有自己的数组,请在函数中创建它们,而不是在原型中创建它们。

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []

反对这个

您可以在对象的任何函数中使用它来引用该对象的其他财产。这与使用new创建的实例不同。

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"

DOM事件this

在HTML DOM事件处理程序中,这始终是对事件附加到的DOM元素的引用

function Listener() {
    document.getElementById("foo").addEventListener("click",
       this.handleClick);
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs "<div id="foo"></div>"
}

var listener = new Listener();
document.getElementById("foo").click();

除非您绑定上下文

function Listener() {
    document.getElementById("foo").addEventListener("click", 
        this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs Listener {handleClick: function}
}

var listener = new Listener();
document.getElementById("foo").click();

HTML此

在可以放入JavaScript的HTML属性中,这是对元素的引用。

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>

评估此

您可以使用eval访问此。

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();

用这个

您可以使用with将其添加到当前范围中,以读取和写入其值,而无需显式引用它。

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"

jQuery this

jQuery在很多地方都会引用DOM元素。

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>

其他回答

总结此Javascript:

它的值取决于函数的调用方式,而不是创建位置!通常,该值由点左侧的对象确定。(全球空间中的窗口)在事件侦听器中,this的值引用调用事件的DOM元素。当使用new关键字调用函数时,this的值引用新创建的对象您可以使用以下函数操纵此值:call、apply、bind

例子:

let对象={prop1:function(){console.log(this);}}object.prop1();//对象位于点的左侧,因此这是对象constmyFunction=object.prop1//我们将函数存储在变量myFunction中myFunction();//我们在全球空间//myFunction是全局对象的属性//因此,它记录窗口对象

事件侦听器示例:

document.querySelector('.foo').addEventListener('click',函数(){console.log(this);//这是指调用eventListener的DOM元素})document.querySelector('.foo').addEventListener('click',()=>{console.log(this);//提示,es6箭头函数没有自己的绑定到此v})//因此,这将记录全局对象.foo:悬停{颜色:红色;光标:指针;}<div class=“foo”>单击我</div>

示例构造函数:

职能人员(姓名){this.name=名称;}const me=新人('Willem');//当使用new关键字时,构造函数中的this将引用新创建的对象console.log(me.name);//因此,name属性被放置在使用new关键字创建的对象上。

由于这篇文章的篇幅有所增加,我为读者整理了一些新的观点。

这个值是如何确定的?

我们使用这个词的方式与我们在英语等自然语言中使用代词的方式类似:“约翰跑得很快,因为他正在赶火车。”相反,我们可以写“……约翰正在赶火车”。

var person = {    
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {

    // We use "this" just as in the sentence above:
       console.log(this.firstName + " " + this.lastName);

    // We could have also written:
       console.log(person.firstName + " " + person.lastName);
    }
}

在对象调用定义它的函数之前,不会为其赋值。在全局范围中,所有全局变量和函数都在窗口对象上定义。因此,这在全局函数中引用(并且具有的值)全局窗口对象。

当使用strict时,在未绑定到任何对象的全局函数和匿名函数中,它的值为undefined。

this关键字在以下情况下最容易被误解:1)我们借用了一个使用this的方法,2)我们将一个使用它的方法分配给一个变量,3)使用this的函数作为回调函数传递,4)这在闭包内部使用-一个内部函数。(2)

未来是什么

在ECMA脚本6中定义,箭头函数采用来自封闭(函数或全局)范围。

function foo() {
     // return an arrow function
     return (a) => {
     // `this` here is lexically inherited from `foo()`
     console.log(this.a);
  };
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };

var bar = foo.call(obj1);
bar.call( obj2 ); // 2, not 3!

虽然箭头函数提供了使用bind()的替代方法,但需要注意的是,它们实际上是在禁用传统的this机制,以支持更广泛理解的词汇范围。(1)


参考文献:

凯尔·辛普森(Kyle Simpson)的《this&Object Prototypes》。©2014 Getify解决方案。javascriptissxy.com-http://goo.gl/pvl0GX 安格斯·克罗尔http://goo.gl/Z2RacU

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

作用域:在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。复制此以解决嵌套范围问题。