题 “this”关键字如何运作?


我注意到似乎没有一个明确的解释 this 关键字是以及如何在Stack Overflow站点上的JavaScript中正确(和错误地)使用它。

我亲眼目睹了一些非常奇怪的行为,并且无法理解为什么会发生这种行为。

如何 this 应该什么时候使用?


1073
2018-06-27 13:12


起源


当我用谷歌搜索“这个”时我发现了这个 quirksmode.org/js/this.html - Wai Wong
一些有用的相关问题* jQuery / JavaScript“这个”指针混乱 * 在Javascript中,为什么“this”运算符不一致? 还有一个很好的写作* javascript中的范围/上下文 - Paul Dixon
Peter Michaux主张反对使用 this  peter.michaux.ca/articles/javascript-widgets-without-this - Marcel Korpel
MDN概述不是坏事...... developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/... - dat
一个有趣的解释 this 关键词: rainsoft.io/gentle-explanation-of-this-in-javascript - Dmitri Pavlutin


答案:


我建议阅读 迈克西的文章 JavaScript中的范围 (镜子)首先。这是一个很好的,友好的概念介绍 this 和JavaScript中的范围链。

一旦你开始习惯了 this,规则实际上非常简单。该 ECMAScript 5.1标准 定义 this

§11.1.1 该 this 关键词

this keyword计算为当前执行上下文的ThisBinding的值

ThisBinding是JavaScript解释器在评估JavaScript代码时维护的东西,就像一个特殊的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.4.1.1)。

输入eval代码

  • ......直接打电话给 eval() ThisBinding保持不变;它与调用执行上下文的ThisBinding值相同(§10.4.2 (2)(A))。

  • ......如果不是直接打电话给 eval()
    ThisBinding设置为全局对象 仿佛 在初始全局执行上下文中执行(§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() 呼叫。

输入功能代码

调用函数时会发生这种情况。如果在对象上调用函数,例如in 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,库只是使用一个接受a的内置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);

只是为了好玩,用一些例子测试你的理解

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

  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不同,后者访问方法(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

    这个很棘手。在评估eval代码时, this 是 obj。但是,在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(),接受 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);
      


1151
2017-07-07 17:32



这些标志意味着什么? §10.4.1.1 - Ali
@Ali:它们是对ECMAScript标准版5.1中的部分的引用, ECMA-262。我提供它们,以便您可以根据需要阅读标准技术细节。 - Daniel Trebbien
我认为@supertonsky是关于#2的 - 如果从全局范围调用myFun(),而不是作为对象的方法,“this”将是全局对象,因此问题的措辞很重要。顺便说一句 - 我 真 喜欢使用鼠标悬停来获得这样的答案的想法。 - user655489
但, jsfiddle.net/H4LYm/2 表明了 setTimeout 例子有一个 this 的 window(global)。 - Kevin Meredith
嗨@KevinMeredith。真正。所以要搞清楚什么 this 在超时函数内,它不是适用的“初始全局执行上下文”规则,而是“输入功能代码”规则。因为没有在对象上调用超时功能(例如 someObject.someMethod()) this 是 window 在超时函数中,正如JSFiddle所示。 - Daniel Trebbien


this 与其他语言相比,JavaScript中的关键字行为有所不同。在面向对象的语言中 this keyword是指类的当前实例。在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 运营商。
  • 同样在对象文字方法中,我们可以使用点运算符显式地将成员添加到已定义的对象。这仅添加到特定实例。但是我已经在函数原型中添加了变量,以便它反映在函数的所有实例中。

下面我尝试了用Object和。做的所有事情 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.内部调用(),apply()和bind()函数

  • 所有这些方法都是定义的 Function.prototype
  • 这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定值 this 在执行函数时将使用它。它们还会在调用时将任何参数传递给原始函数。
  • fun.apply(obj1 [, argsArray]) 集 obj1 作为的价值 this 内 fun() 和电话 fun() 传递元素 argsArray 作为其论点。
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])  - 集 obj1 作为的价值 this 内 fun() 和电话 fun() 通过 arg1, arg2, arg3, ... 作为其论点。
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])  - 返回对函数的引用 fun 同 this内心有趣 obj1 和参数 fun 绑定到指定的参数 arg1, arg2, arg3,...
  • 到现在为止的区别 applycall 和 bind 必须变得明显。 apply 允许指定用作类数组对象的参数,即带有数字的对象 length 属性和相应的非负整数属性。而 call 允许直接指定函数的参数。都 apply 和 call 立即调用指定上下文中的函数并使用指定的参数。另一方面, 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>

122
2017-10-26 15:07



“当你在全局上下文中定义的函数中使用它时,它仍然绑定到全局对象,因为函数实际上是一个全局上下文的方法。”是不正确的。 这个 由函数的调用方式或方式设置 捆绑,而不是它的定义。默认情况下,调用没有基本引用(“上下文”)的任何函数 这个 到全局对象或在严格模式下保持未定义。 - RobG
@RobG嗯可能是,但我找到了 这是在MDN上: 在这种情况下,值 this 没有通过电话设置。由于代码不是严格模式,因此值 this 必须始终是一个对象,因此它默认为全局对象。 事实上,这就是为什么我认为我们可以直接拨打电话 window.f1(),这意味着 f1() 已经附上了 window 对象,我的意思是在调用之前。我弄错了吗? - Mahesha999
我正在评论(或许不清楚)你的链接设置 这个 用“函数实际上是一个全局上下文的方法”,好像它被称为 window.fn但事实并非如此。 这个 默认为全局对象,因为在调用中没有使用基本引用,而不是因为定义了函数的位置(所以 这个 仍然根据函数的调用方式设置)。如果你明确地使用它 window.fn,那你就是在设定 这个 至 窗口。同样的结果,不同的方式。 :-) - RobG
“上面我已经说出了这个词......”不,你没有。你可以修改一下这个错误吗?这似乎是答案的语义,因此我不能继续阅读,直到错误被修复,因为害怕学习不正确的东西。 - TylerH
@TylerH在浏览器的这个页面上按Ctrl + F查找字符串“immediate”(包括双引号)我认为它在那里我是否理解你错了 - Mahesha999


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

在对象上调用函数

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

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. 用the调用函数 new 关键字会立即初始化 Object 类型 Person
  2. 这个的构造函数 Object 将其构造函数设置为 Person。另外,请注意 typeof awal 会回来的 Object 只要。
  3. 这个新的 Object 将被分配原型 Person.prototype。这意味着任何方法或属性 Person 原型将可用于所有实例 Person, 包含 awal
  4. 功能 Person 本身现在被调用; this 作为对新构造的对象的引用 awal

挺直的,嗯?

请注意,官方ECMAScript规范没有声明这类函数是实际的 constructor 功能。它们只是正常的功能,而且 new 可用于任何功能。只是我们这样使用它们,所以我们只将它们称为它们。

调用函数函数: call 和 apply

所以是的,从那以后 functions也是 Objects (以及Javascript中实际上的第一类变量),甚至函数都有方法......好吧,它们的功能是自己的。

所有函数都继承自全局 Function,它的两个方法是 call 和 apply,两者都可以用来操纵价值 this 在它们被调用的函数中。

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

这是一个典型的使用示例 call。它基本上采用第一个参数并设置 this 在功能 foo 作为参考 thisArg。传递给所有其他参数 call 传递给函数 foo 作为论点。
所以上面的代码会记录下来 {myObj: "is cool"}, [1, 2, 3] 在控制台中。非常好的方式来改变价值 this 在任何功能。

apply 几乎是一样的 call 接受它只需要两个参数: thisArg 和一个包含要传递给函数的参数的数组。所以上面 call 电话可以翻译成 apply 喜欢这个:

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

注意 call 和 apply 可以覆盖的值 this 我们在第二个子弹中讨论过dot方法调用。 很简单:)

呈现.... bind

bind 是兄弟的 call 和 apply。它也是全局所有函数继承的方法 Function Javascript中的构造函数。和...之间的不同 bind 和 call/apply 就是这两个 call 和 apply 实际上会调用该函数。 bind另一方面,返回一个新的函数 thisArg 和 arguments 预先设定。让我们举个例子来更好地理解这个:

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

看到三者之间的区别?它很微妙,但它们的使用方式不同。喜欢 call 和 applybind 也将超越价值 this 由点方法调用设置。

另请注意,这三个函数都不会对原始函数进行任何更改。 call 和 apply 将从新构造的函数返回值 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 从原来的范围。所以我们'复制' this 至 that 并使用副本而不是 this。聪明,嗯?

指数:

  1. 什么是举行 this 默认?
  2. 如果我们将该函数称为具有Object-dot表示法的方法,该怎么办?
  3. 如果我们使用该怎么办? new 关键词?
  4. 我们如何操纵 this 同 call 和 apply
  5. 运用 bind
  6. 仿形 this 解决嵌套范围问题。

46
2018-06-27 14:10



为什么会有一节关于 new 关键词?这个问题也被打败了,但我们都有“那段时间我们试图骑上4年前回答的问题的外套尾巴” - Lady Bird


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

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

(function(value){
    this.value = value;
    $(”。一些元素)。每个(函数(ELT){
        elt.innerHTML = this.value; //呃哦!可能未定义
    });
})(2);

所以在这里,在每个()中,“this”不包含你期望它的“值”(来自

this.value = value;
 它上面)。因此,为了克服这个问题(没有双关语)问题,开发人员可以:

(function(value){
    var self = this; // 小变化
    self.value = value;
    $(”。一些元素)。每个(函数(ELT){
        elt.innerHTML = self.value; // p !! == 2
    });
})(2);

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


43
2017-10-06 19:46



“JS中的所有东西都是一个对象”并非如此,JavaScript也有原始价值,请参阅 bclary.com/2004/11/07/#a-4.3.2 - Marcel Korpel
原始值似乎有自己的一些方法,如String #substring(),Number#toString()等。所以,也许没有与该文章相同的命名法,它们的行为就像它们是对象一样(它们是所有原型,即String#substring()实际上是:String.prototype.substring = function(){...})。如果我错了,请纠正我。 - arunjitsingh
该 this 关键字与范围无关。此外,它还具有不属于对象属性的函数的含义。 - Bergi
@ arunjitsingh-有两种思想流派。我喜欢那个说“一切都是一个对象,但为方便起见,有些可以用原语表示“。;-) - RobG
this 并非所有关于范围。它是关于执行上下文的所有内容,它与范围不同。 JavaScript是词法范围的(意味着范围由代码的位置决定),但是 this 取决于如何调用包含它的函数 - 而不是函数的位置。 - Scott Marcus


this 在Javascript中始终引用该函数的“所有者” 被执行

如果未定义显式所有者,则引用最顶层的所有者(窗口对象)。

所以,如果我这样做

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

element.onclick = someKindOfFunction;

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

<element onclick="someKindOfFunction()">

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


12
2017-10-30 03:58





由于这个帖子已经出现了问题,我为新读者编写了几点 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 strictthis 在全局和匿名函数中,未绑定到任何对象的函数的值为 undefined

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

table

什么是未来

定义于 ECMA脚本6,arrow-functions采用 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. 这个和对象原型,凯尔辛普森。 2014 Getify Solutions。
  2. javascriptissexy.com - http://goo.gl/pvl0GX 
  3. 安格斯·克罗尔 - http://goo.gl/Z2RacU 

12
2018-06-27 13:15





一切 功能  执行上下文 在javascript中有一个 范围  上下文  这个 参数设置为:

  1. 如何调用函数(包括作为对象方法,使用 呼叫 和 应用, 用于
  2. 用于 捆绑
  3. 用于箭头功能(他们采用的是 这个 他们的外部执行上下文)

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

您可以 改变这一点 设置值 这个  范围  上下文 运用 func.callfunc.apply 要么 func.bind

默认情况下,大多数初学者会遇到什么困惑 回电话 在DOM元素上引发事件后调用listener 范围上下文  这个 函数的值是DOM元素。

jQuery使用jQuery.proxy改变这一点。


9
2017-11-29 06:01



说出每个功能都更正确一点 呼叫 有范围。换句话说,令人困惑的是什么 this 在Javascript中就是这样 不 函数本身的内在属性,而是函数调用方式的工件。 - Pointy
@pointy谢谢。导致js中最混乱的原因是,在前面使用的所有语言(c#,c ++)中, - 这不能被操纵n总是指向对象实例,而在js中它取决于并且可以在调用时更改功能使用 func.call, func.bind 等 - Sushil - Sushil
this 不 不 引用函数的范围。 this 将引用一个特定的对象(或可能 undefined),正如你所说,可以改变使用 .call() 要么 .apply()。一个功能 范围 是(基本上,简化时)它可以访问哪些变量,这完全取决于声明函数的位置,不能更改。 - nnnnnn
@Pointy: “说每个函数调用都有一个范围,这是更正确的。” 更确切地说,功能(现在是块)具有 范围,功能 电话 有 上下文。范围定义了该范围内的代码可以使用的标识符。 Context定义了这些标识符绑定的内容。 - T.J. Crowder
“无论这个范围是什么,都被”这个“引用。” 没有, this 并且范围在ES5之前和之前没有任何关系(例如,当写这个答案时)。在ES2015(又名ES6)中, this 和范围是相关的 一 箭头功能相当小的方式( this 在箭头函数继承其封闭的范围),但 this 从不提到范围。 - T.J. Crowder


这里 是一个很好的来源 this 在 JavaScript

以下是摘要:

  • 全球这个

    在浏览器中,在全球范围内, this 是个 window目的

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

    node 使用repl, this 是顶级命名空间。你可以称之为 global

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

    node 从脚本执行, this 在全局范围内作为空对象开始。它不一样 global

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
    
  • 这个功能

除了DOM事件处理程序或者a 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这个

在HTML属性中,您可以放置​​JavaScript, 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>

7
2018-04-23 12:57





丹尼尔,很棒的解释!关于这个和好名单的几个词 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)">

6
2018-01-31 08:29