我希望找到一个“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。复制此以解决嵌套范围问题。

其他回答

丹尼尔,太棒了!在事件处理程序的情况下,这里有几个单词,以及这个执行上下文指针的良好列表。

换句话说,JavaScript中的这一点指向运行当前函数的对象(或其执行上下文),并且它始终是只读的,无论如何都不能设置它(这样的尝试将以“赋值中的左侧无效”消息结束)。

对于事件处理程序:内联事件处理程序,例如<elementonclick=“foo”>,会覆盖之前附加的任何其他处理程序,因此要小心,最好不要进行内联事件委派。感谢Zara Alaverdyan,他通过一场不同意见的辩论激发了我的灵感:)

el.onclick=foo;//在foo-obj中el.onclick=函数(){this.style.color=“#fff”;}//objel.onclick=function(){doSomething();}//在doSometing中-窗el.addEventListener('click',foo,false)//在foo-obj中el.attachEvent('包含,函数(){//this}')//窗口,所有符合IE:)<button onclick=“this.style.color='#fff';”>//obj<button onclick=“foo”>//在foo窗口中,但您可以<buttononclick=“foo(this)”>

关于此关键字的一些信息

让我们将此关键字记录到全局范围内的控制台中,无需任何代码

console.log(this)

在客户端/浏览器中,此关键字是一个全局对象,它是窗口

console.log(this === window) // true

and

在Server/Node/Javascript运行时中,此关键字也是一个全局对象,即module.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

请记住,导出只是对module.exports的引用

“this”的值取决于执行函数的“上下文”。上下文可以是任何对象或全局对象,即窗口。

因此,“this”的语义不同于传统的OOP语言。这会导致问题:1.当一个函数传递给另一个变量时(很可能是回调);和2。当从类的成员方法调用闭包时。

在这两种情况下,这都设置为window。

“这”是关于范围的。每个函数都有自己的作用域,因为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中“this”的大部分混淆是因为它通常不链接到您的对象,而是链接到当前的执行范围——这可能不是它的确切工作方式,但对我来说总是这样——请参阅文章以获得完整的解释)