“this” 关键字如何工作?

我注意到,对于 Stack Overflow 网站上的 JavaScript, this关键字是什么以及如何正确(以及错误地)使用this关键字似乎没有明确的解释。

我目睹了它的一些非常奇怪的行为,并且未能理解为什么会发生它。

怎么this时候它应该被用来工作?

答案

我建议先阅读Mike West的文章Scope in JavaScript镜像 )。这是一个很好的,友好的介绍的概念, this在 JavaScript 和范围链。

一旦你开始习惯this的规则实际上是相当简单的。 ECMAScript 5.1 标准 this定义:

§11.1.1this关键字

this关键字的值为当前执行上下文的 ThisBinding 的值

JavaScript 解释器在评估 JavaScript 代码时会维护 ThisBinding,例如特殊的 CPU 寄存器,其中包含对对象的引用。每当仅在以下三种情况之一中建立执行上下文时,解释器就会更新 ThisBinding:

1. 初始全局执行上下文

在顶级代码中评估的 JavaScript 代码就是这种情况,例如,直接在<script>内部时:

<script>
  alert("I'm evaluated in the initial global execution context!");

  setTimeout(function () {
      alert("I'm NOT evaluated in the initial global execution context.");
  }, 1);
</script>

在初始的全局执行上下文中评估代码时,ThisBinding 设置为全局对象window (第10.0.4.1.1 节 )。

输入评估码

  • … 直接调用eval() Binding 保持不变;它与调用执行上下文的ThisBinding (第10.4.2 (2)(a)节)的值相同。

  • … 如果不是通过直接调用eval()
    将这个 Binding 设置为全局对象,就像在初始全局执行上下文中执行一样(第10.4.2 (1)节)。

§15.1.2.1.1 定义了对eval()的直接调用。基本上, eval(...)是直接调用,而类似(0, eval)(...)var indirectEval = eval; indirectEval(...);是对eval()的间接调用。看到chuckj 对 JavaScript 中的 (1,eval)('this')vs eval('this') 的回答 吗?Dmitry Soshnikov 的 ECMA-262-5 详细信息。第 2 章严格模式。当您可能使用间接eval()调用时。

输入功能码

调用函数时会发生这种情况。如果在对象上调用了函数,例如obj.myMethod()或等效的obj["myMethod"]() ,则 ThisBinding 设置为对象(示例中为obj ;第13.2.1 节 )。在大多数其他情况下,ThisBinding 设置为全局对象(第10.4.3 节 )。

之所以写 “在大多数情况下”,是因为有八个 ECMAScript 5 内置函数可以在参数列表中指定 ThisBinding。这些特殊功能采用了一个所谓的thisArg ,当调用该函数时,它会成为 ThisBinding(第10.4.3 节 )。

这些特殊的内置函数是:

  • Function.prototype.apply( thisArg, argArray )
  • Function.prototype.call( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Function.prototype.bind( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Array.prototype.every( callbackfn [ , thisArg ] )
  • Array.prototype.some( callbackfn [ , thisArg ] )
  • Array.prototype.forEach( callbackfn [ , thisArg ] )
  • Array.prototype.map( callbackfn [ , thisArg ] )
  • Array.prototype.filter( callbackfn [ , thisArg ] )

对于Function.prototype函数,它们是在函数对象上调用的,而不是将 ThisBinding 设置为函数对象,而是将 ThisBinding 设置为thisArg

对于Array.prototype函数,将在执行上下文中调用给定的callbackfn ,其中,如果提供,则将 ThisBinding 设置为thisArg 。否则,转到全局对象。

这些是纯 JavaScript 的规则。当您开始使用 JavaScript 库(例如 jQuery)时,您可能会发现某些库函数会操纵this的值。这些 JavaScript 库的开发人员之所以这样做是因为它倾向于支持最常见的用例,并且该库的用户通常会发现此行为更加方便。将引用this回调函数传递给库函数时,应确保参考该文档,以确保有关在调用函数时this的值的任何保证。

如果您想知道 JavaScript 库如何处理this的值,则该库仅使用接受thisArg的内置 JavaScript 函数thisArg 。您也可以使用回调函数和thisArg编写自己的函数:

function doWork(callbackfn, thisArg) {
    //...
    if (callbackfn != null) callbackfn.call(thisArg);
}

我还没有提到一个特殊情况。通过new运算符构造新对象时,JavaScript 解释器会创建一个新的空对象,设置一些内部属性,然后在新对象上调用构造函数。因此,当一个函数被调用在构造方面,的值this是解释创建的新对象:

function MyType() {
    this.someData = "a string";
}

var instance = new MyType();
// Kind of like the following, but there are more steps involved:
// var instance = {};
// MyType.call(instance);

箭头功能

箭头功能 (在 ECMA6 引入)改变的范围this 。参见现有规范问题, 箭头函数与函数声明 / 表达式:它们是否等效 / 可互换?了解更多信息。简而言之:

箭自己的职能没有this .... 结合。相反,这些标识符像任何其他变量一样在词法范围内解析。这意味着,一个箭头函数内部, this ... 参照(一个或多个),以的值this在箭头函数是在定义的环境。

只是为了好玩,请通过一些例子测试您的理解

要显示答案,请将鼠标悬停在浅黄色框上。

  1. 什么是值this在标线?为什么?

    window —在初始全局执行上下文中评估标记的行。

    if (true) {
        // What is `this` here?
    }
  2. 什么是价值this当标线obj.staticFunction()执行?为什么?

    obj —在对象上调用函数时,ThisBinding 设置为该对象。

    var obj = {
        someData: "a string"
    };
    
    function myFun() {
        return this // What is `this` here?
    }
    
    obj.staticFunction = myFun;
    
    console.log("this is window:", obj.staticFunction() == window);
    console.log("this is obj:", obj.staticFunction() == obj);

  3. 什么是值this在标线?为什么?

    window

    在此示例中,JavaScript 解释器输入函数代码,但是由于未在对象上调用myFun / obj.myMethod ,因此 ThisBinding 设置为window

    这与 Python 不同,在 Python 中,访问方法( obj.myMethod )创建绑定的方法对象

    var obj = {
        myMethod: function () {
            return this; // What is `this` here?
        }
    };
    var myFun = obj.myMethod;
    console.log("this is window:", myFun() == window);
    console.log("this is obj:", myFun() == obj);

  4. 什么是值this在标线?为什么?

    window

    这个很棘手。在评估评估代码时, thisobj 。然而,在 EVAL 代码, myFun不是在对象上调用,所以 ThisBinding 设置为window的呼叫。

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
  5. 什么是值this在标线?为什么?

    obj

    myFun.call(obj);正在调用特殊的内置函数Function.prototype.call() ,该Function.prototype.call()接受thisArg作为第一个参数。

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        someData: "a string"
    };
    console.log("this is window:", myFun.call(obj) == window);
    console.log("this is obj:", myFun.call(obj) == obj);

与其他语言相比, this关键字在 JavaScript 中的行为有所不同。在面向对象的语言中, this关键字引用该类的当前实例。在 JavaScript 中, this值取决于函数的调用上下文( context.function() )及其调用位置。

1. 在全球范围内使用

在全局上下文中使用this时,它将绑定到全局对象(浏览器中的window

document.write(this);  //[object Window]

当您在全局上下文中定义的函数中使用this函数时,由于该函数实际上已成为全局上下文的方法, this它仍然绑定到全局对象。

function f1()
{
   return this;
}
document.write(f1());  //[object Window]

f1之上是全局对象的方法。因此,我们还可以在window对象上调用它,如下所示:

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2. 当在内部对象方法中使用时

当您使用this关键字的对象方法中, this势必会 “立即” 包围对象。

var obj = {
    name: "obj",
    f: function () {
        return this + ":" + this.name;
    }
};
document.write(obj.f());  //[object Object]:obj

在上面,我已经将单词立即用双引号引起来。要指出的是,如果将对象嵌套在另一个对象中,则this对象将绑定到直接父对象。

var obj = {
    name: "obj1",
    nestedobj: {
        name:"nestedobj",
        f: function () {
            return this + ":" + this.name;
        }
    }            
}

document.write(obj.nestedobj.f()); //[object Object]:nestedobj

即使你明确的对象添加功能的方法,但它仍然遵循上述规则,那就是this仍然指向直接父对象。

var obj1 = {
    name: "obj1",
}

function returnName() {
    return this + ":" + this.name;
}

obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1

3. 调用无上下文功能时

当您使用this是没有任何上下文中调用(即没有任何物体上)里面的功能,它被绑定到全局对象( window浏览器)(即使该函数的对象中定义)。

var context = "global";

var obj = {  
    context: "object",
    method: function () {                  
        function f() {
            var context = "function";
            return this + ":" +this.context; 
        };
        return f(); //invoked without context
    }
};

document.write(obj.method()); //[object Window]:global

尝试所有功能

我们也可以使用功能尝试上述几点。但是有一些区别。

  • 上面我们使用对象文字表示法将成员添加到对象中。我们可以使用this将成员添加到函数中。指定它们。
  • 对象文字表示法创建了一个对象实例,我们可以立即使用它。使用函数,我们可能需要首先使用new运算符创建其实例。
  • 同样在对象文字方法中,我们可以使用点运算符将成员显式添加到已定义的对象中。这仅添加到特定实例。但是,我已将变量添加到函数原型中,以使其在函数的所有实例中得到体现。

下面我尝试了所有的事情,我们做了与对象与this之上,但首先创建功能,而不是直接书写的对象。

/********************************************************************* 
  1. When you add variable to the function using this keyword, it 
     gets added to the function prototype, thus allowing all function 
     instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
    this.name = "ObjDefinition";
    this.getName = function(){                
        return this+":"+this.name;
    }
}        

obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition   

/********************************************************************* 
   2. Members explicitly added to the function protorype also behave 
      as above: all function instances have their own copy of the 
      variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
    return "v"+this.version; //see how this.version refers to the
                             //version variable added through 
                             //prototype
}
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   3. Illustrating that the function variables added by both above 
      ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
    this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1

obj2.incrementVersion();      //incrementing version in obj2
                              //does not affect obj1 version

document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj', 
                                    getName1 : function(){
                                        return this+":"+this.name;
                                    }                            
                                  };

document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj

/********************************************************************* 
   5. If the method is on an object's prototype chain, `this` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

4. 在构造函数内部使用时

当将该函数用作构造函数时(即使用new关键字调用它时), this内部函数主体指向正在构造的新对象。

var myname = "global context";
function SimpleFun()
{
    this.myname = "simple function";
}

var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
//   object being constructed thus adding any member
//   created inside SimipleFun() using this.membername to the
//   object being constructed
//2. And by default `new` makes function to return newly 
//   constructed object if no explicit return value is specified

document.write(obj1.myname); //simple function

5. 在原型链上定义的内部函数中使用时

如果该方法在对象的原型链上,则this方法内部的对象将引用该方法在其上调用的对象,就好像该方法是在对象上定义的一样。

var ProtoObj = {
    fun: function () {
        return this.a;
    }
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun() 
//to be the method on its prototype chain

var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999

//Notice that fun() is defined on obj3's prototype but 
//`this.a` inside fun() retrieves obj3.a

6. 在 call(),apply()和 bind()函数内部

  • 所有这些方法都在Function.prototype上定义。
  • 这些方法允许一次编写一个函数,然后在不同的上下文中调用它。换句话说,它们允许指定的值this同时被执行的功能是将被使用。它们还可以在调用原始函数时采用任何要传递给原始函数的参数。
  • fun.apply(obj1 [, argsArray]) obj1设置为fun()内部的this值,并调用传递给argsArray元素的fun()作为其参数。
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - 将obj1设置this fun()的值,并通过arg1, arg2, arg3, ...调用fun() arg1, arg2, arg3, ...作为它的参数。
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - 返回对该函数fun的引用,其中this内部 fun 绑定到obj1 ,将fun参数绑定到指定的参数arg1, arg2, arg3,...
  • 到目前为止, applycallbind之间的区别必须已经很明显。 apply允许指定参数以充当类似数组的对象,即具有数字length属性和相应的非负整数属性的对象。而call允许直接指定函数的参数。 applycall立即在指定的上下文中使用指定的参数调用该函数。另一方面, bind仅返回绑定到指定的this值和参数的函数。我们可以通过将其分配给变量来捕获对该返回函数的引用,以后我们可以随时调用它。
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
      //above add.call(o,5,6) sets `this` inside
      //add() to `o` and calls add() resulting:
      // this.a + inc1 + inc2 = 
      // `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
      // `o.a` i.e. 4 + 5 + 6 = 15

var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />");    //15

var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
      // 4 + 5 + 6 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. this内部事件处理程序

  • 当直接将函数分配给元素的事件处理程序时,在事件处理函数内部直接使用this函数将引用相应的元素。可以使用addeventListener方法或通过传统的事件注册方法(如onclick来完成此类直接功能分配。
  • 类似地,当使用this直接事件属性内(如<button onclick="...this..." >的元素,它是指该元素。
  • 但是,通过在事件处理函数或事件属性内部调用的另一个函数间接使用this函数将解析为全局对象window
  • 当我们使用 Microsoft 的事件注册模型方法attachEvent将函数附加到事件处理程序时,可以实现上述相同的行为。它没有将功能分配给事件处理程序(因而没有将其分配为元素的功能方法),而是在事件上调用了功能(在全局上下文中有效地调用了该功能)。

我建议最好在JSFiddle 中尝试一下

<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>

8. this在 ES6 箭头功能

在箭头函数中, this行为类似于普通变量:它将从其词法范围继承。函数的this ,其中定义了 arrow 函数,将是 arrow 函数的this

因此,这与以下行为相同:

(function(){}).bind(this)

请参见以下代码:

const globalArrowFunction = () => {
  return this;
};

console.log(globalArrowFunction()); //window

const contextObject = {
  method1: () => {return this},
  method2: function(){
    return () => {return this};
  }
};

console.log(contextObject.method1()); //window

const contextLessFunction = contextObject.method1;

console.log(contextLessFunction()); //window

console.log(contextObject.method2()()) //contextObject

const innerArrowFunction = contextObject.method2();

console.log(innerArrowFunction()); //contextObject

Javascript 的this

简单函数调用

考虑以下功能:

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

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

当在浏览器中运行,值this将被记录为window 。这是因为window是 Web 浏览器范围内的全局变量。

如果你像 node.js 中的环境中运行该同一段代码, this将涉及到全局变量在你的应用程序。

现在,如果我们在严格模式下通过添加语句"use strict";到函数声明的开头, this将不再引用这两种环境中的全局变量。这样做是为了避免在严格模式下造成混淆。 this会,在这种情况下,只要登录undefined ,因为那是它是什么,没有定义它。

在以下情况下,我们将看到如何操纵this的值。

在对象上调用函数

有不同的方法可以做到这一点。如果您已经使用诸如forEachslice类的 Javascript 调用了本机方法,那么您应该已经知道,在this情况下, this变量引用了您在其上调用了该函数的Object (请注意,在 javascript 中,几乎所有东西都是Object ,包括ArrayFunction )。以下面的代码为例。

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

如果Object包含一个包含Function的属性,则该属性称为方法。调用此方法时,将始终this变量设置为与之关联的Object 。对于严格和非严格模式都是如此。

注意,如果一个方法被存储(或更确切地说,复制)在另一变量中,参照this不再保留在新的变量。例如:

// continuing with the previous code snippet

var myVar = myObj.thisMethod;
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

new关键字

考虑一下 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关键字时会发生什么。

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

很简单,是吗?

请注意,官方 ECMAScript 规范无处声明此类函数是实际的constructor函数。它们只是正常功能,而new可以在任何功能上使用。只是我们原样使用它们,因此我们仅如此称呼它们。

在函数上调用函数: callapply

是的,因为function也是Objects (实际上是 Javascript 中的第一类变量),所以即使函数也具有本身就是函数的方法。

所有的功能从全球继承Function ,及其两个多方法callapply ,都可以用来操纵的值this在它们所调用的函数。

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

这是使用call的典型示例。它基本上采用第一个参数,并在函数foo this参数设置为对thisArg的引用。传递给call所有其他参数作为参数传递给foo函数。
因此,上面的代码将在控制台中记录{myObj: "is cool"}, [1, 2, 3] 。相当不错的方法来改变的值this在任何功能。

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

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

注意, callapply可以通过我们在第二个项目符号中讨论的点方法调用来覆盖this设置的值。很简单:)

呈现.... bind

bindcallapply的兄弟。它也是所有函数从 Javascript 的全局Function构造函数继承的方法。 bindcall / apply之间的区别是callapply都将实际调用该函数。另一方面, bind返回一个带有thisArgarguments预设的新函数。让我们举个例子来更好地理解这一点:

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]`

看到两者之间的区别了吗?它很微妙,但是用法不同。像callapply一样, bind也将通过点方法调用来覆盖this设置的值。

另请注意,这三个功能均未对原始功能进行任何更改。 callapply将从新构造的函数返回值,而bind将返回新构造的函数本身,随时可以调用。

多余的东西,复制此

有时候,你不喜欢的事实, this改变与范围,尤其是嵌套的范围。看下面的例子。

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

在上面的代码中,我们看到的价值this与嵌套的范围内改变,但我们想要的值, this从原来的范围。因此,我们 “复制” thisthat和所使用的副本,而不是this 。聪明吧?

指数:

  1. 默认情况下, this是什么?
  2. 如果我们将该函数作为带有对象点表示法的方法来调用该怎么办?
  3. 如果我们使用new关键字怎么办?
  4. 我们如何通过callapply操作this
  5. 使用bind
  6. 复制this可解决嵌套范围问题。

“这” 是关于范围的。每个函数都有自己的作用域,并且由于 JS 中的所有对象都是对象,所以即使一个函数也可以使用 “此” 将一些值存储到自身中。 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” 不包含您期望它(从

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始终是指所执行函数的 “所有者”。

如果未定义任何显式所有者,则引用最高的所有者,即窗口对象。

所以如果我做到了

function someKindOfFunction() {
   this.style = 'foo';
}

element.onclick = someKindOfFunction;

this将引用元素对象。但是要小心,很多人都会犯这个错误。

<element onclick="someKindOfFunction()">

在后一种情况下,您仅引用函数,而不将其移交给元素。因此, this将引用窗口对象。

由于该主题的发展,我为刚接触this主题的读者提供了几点建议。

价值如何this决定的?

我们使用这种方式类似于在自然语言(如英语)中使用代词的方式:“约翰之所以跑得快,是因为正试图赶上火车。” 相反,我们可以写成 “…… 约翰正试图赶上火车”。

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

this直到对象调用定义它的功能不被分配的值 。在全局范围内,所有全局变量和函数都在window对象上定义。因此,全局函数中的this引用全局window对象(并具有其值)。

use strict ,在未绑定到任何对象的全局函数和匿名函数中, this函数的值均为undefined

this关键字是最容易被误解时:1)我们借用一个使用方法this ,2)我们分配方法,该方法使用this到一个变量,3)的函数,它使用this作为一个回调函数传递,和 4) this是在闭包内部使用 - 内部函数。 (2)

表

什么把握未来

ECMA 脚本 6 中定义,箭头功能从封闭的(功能或全局)范围采用this绑定。

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)


参考文献:

  1. this&Object Prototypes ,作者:凯尔 · 辛普森(Kyle Simpson)。 ©2014 Getify 解决方案。
  2. javascriptissexy.com- http://goo.gl/pvl0GX
  3. 安格斯卡罗尔 - http: //goo.gl/Z2RacU

javascript 中的每个函数 执行上下文都有一个范围 上下文, 范围以下参数设置:

  1. 函数的调用方式(包括作为对象方法,使用callapply ,使用new
  2. 绑定的使用
  3. 用词法表示箭头函数(它们采用外部执行上下文的this

无论范围上下文是什么,均由 “this” 引用。

您可以使用func.callfunc.applyfunc.bind 更改设置 范围 上下文值的值。

默认情况下,什么迷惑大多数初学者,当事件被一个 DOM 元素上引发后回调监听器被调用, 范围方面功能的这个值是的 DOM 元素。

jQuery 使用 jQuery.proxy 可以轻松地进行更改。

这里是一个很好的来源thisJavaScript

这是摘要:

  • 全球这个

    在浏览器中,在全局范围内, thiswindow对象

    <script type="text/javascript">
      console.log(this === window); // true
      var foo = "bar";
      console.log(this.foo); // "bar"
      console.log(window.foo); // "bar"

    在使用 repl 的nodethis是顶级名称空间。您可以将其称为global

    >this
      { ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
        Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
        ...
    >global === this
     true

    在通过脚本执行的nodethis在全局范围内从一个空对象开始。它与global

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
  • 发挥这个作用

除了在 DOM 事件处理程序中或在提供thisArg时(参见下文),在节点和浏览器中都使用this函数,而该函数未使用new引用调用全局范围…

<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; ,在这种情况下this将是undefined

<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将是一个新的上下文,它将不会引用全局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>
  • 原型

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

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

Thing.prototype.foo = "bar";

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

prototype上分配数组对象通常是一个错误。如果您希望实例各自具有自己的数组,请在函数(而不是原型)中创建它们。

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 []
  • 反对这个

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

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

obj.logFoo(); //logs "bar"
  • DOM 事件

在 HTML DOM 事件处理程序中, this始终是对事件附加到的 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();

除非您bind上下文

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 属性内, this是对该元素的引用。

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

您可以使用eval来访问this

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

var thing = new Thing();
thing.logFoo();
  • 有了这个

您可以使用with添加this到目前的范围,读取和写入值在this没有提到this明确。

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 的这个

jQuery 在许多地方都this引用 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>

丹尼尔,很棒的解释!在事件处理程序的情况下,在this上下文上下文指针以及this列表中列出了几个单词。

在两个词, this在 JavaScript 中指出,与人(或将从其执行上下文)当前函数运行对象和它的始终是只读的,你不能设置也无妨(这种尝试将结束 “左无效手在作业中的讯息。

对于事件处理程序:内联事件处理程序(例如<element onclick="foo"> )会覆盖之前和之前附加的所有其他处理程序,因此请当心,最好不要使用内联事件委托。还要感谢 Zara Alaverdyan,他通过一次持异议的辩论启发了我列举了以下示例:)

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick = function() {doSomething();} // In the doSomething - Window
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onclick, function () { // this }') // window, all the compliance to IE :)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <button onclick="foo"> // In the foo - window, but you can <button onclick="foo(this)">

可能有关this内容的最详细,最全面的文章如下:

在 JavaScript 中对 “this” 关键字的温和解释

背后的想法this是了解函数调用类型对设置显著重视this值。


在遇到问题时识别this问自己:

this从哪里来的

不要问自己:

该函数如何调用

对于箭头功能(上下文透明的特殊情况),请问自己:

定义箭头功能的情况下, this值有什么值?

在处理this问题时, this心态是正确的,可以使您免于头痛。