说实话会被关小黑屋 發表於 2019-6-6 15:43:00

彻底理解javascript中的this指针

<p>http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/</p>
<p>https://www.bennadel.com/blog/2265-changing-the-execution-context-of-javascript-functions-using-call-and-apply.htm</p>
<p>this关键字对于javascript初学者,即便是老手可能都是一个比较容易搞晕的东西。本文试图理顺这个问题。</p>
<h2>this和自然语言的类比</h2>
<p>实际上js中的this和我们自然语言中的代词有类似性。比如英语中我们写"John is running fast because <strong><span style="color: rgba(0, 128, 0, 1)">he</span> </strong>is trying to catch the train"</p>
<p>注意上面的代词"he",我们当然可以这样写:"John is running fast because&nbsp;<strong>John&nbsp;</strong>is trying to catch the train" ,这种情况下我们没有使用this去重用代替John。</p>
<p>在js中this关键字就是一个引用的shortcut,他指代一个object,是执行的代码body的context环境。看下面的代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> person =<span style="color: rgba(0, 0, 0, 1)"> {
    firstName: </span>"Penelope"<span style="color: rgba(0, 0, 0, 1)">,
    lastName: </span>"Barrymore"<span style="color: rgba(0, 0, 0, 1)">,
    fullName: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
      ​</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Notice we use "this" just as we used "he" in the example sentence earlier?:</span>
      console.log(<span style="color: rgba(0, 0, 255, 1)">this</span>.firstName + " " + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.lastName);
    ​</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We could have also written this:​</span>
      console.log(person.firstName + " " +<span style="color: rgba(0, 0, 0, 1)"> person.lastName);
    }
}</span></pre>
</div>
<p>如果我们使用person.firstName,person.lastName的话,我们的代码可能会产生歧义。比如,如果有一个全局变量,名字就是person,那么person.firstName可能试图从全局的person变量来访问其属性,这可能导致错误,并且难以调试。因此,我们使用this关键字,不仅为了代码更美(作为以个referent),而且为了更加精确而不会产生歧义。类似于刚刚举例的自然语言中,因为代词"he"使得句意更清晰,因为更加清晰地表明我们正在引用一个特定的John这个人。</p>
<p>正如代词"he"用于引用存于context中无歧义的名词一样,this关键字用于引用一个对象,而这个对象就是function(在该函数中,使用了this指针)所绑定的对象.(the this keyword is similarly used to refer to an object that the function(where this is used) is bound to.) t</p>
<h2>this基础</h2>
<h3>什么是执行上下文(execution context)?</h3>
<p>在js中,所有的函数体(function body)都能访问this关键字. <strong><span style="color: rgba(0, 128, 0, 1)">this keyword就是函数执行的context.默认情况下,this引用着调用该函数的那个对象(也就是thisObj.thisFunction中的thisObj)</span></strong>(this is a reference to the object on which a particular function is called),在js中,所有的函数都会被绑定到一个object.然而,我们可以使用call(), apply()来在运行时变更这种binding关系。</p>
<p>首先,我们需要澄清的是:js中所有的函数实际上都是"methods".也就是说<strong><span style="color: rgba(0, 128, 0, 1)">所有的function都是某个对象的属性</span></strong>。虽然我们可以定义看起来像是独立自由的函数,但是实际上这时候,这些含糊被隐式地被创建为window object的属性properties(在node环境中是其他的object,可能是process)。这也就意味着,即使我们创建一个自由独立的function而没有显然指定其context,我们也可以以window属性的方式来调用该函数。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Define the free-floating function.</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> someMethod(){ ... }

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Access it as a property of Window.</span>
window.someMethod();</pre>
</div>
<p>既然我们知道了js的所有函数度作为一个对象的属性而存在,我们可以具体探讨下执行上下文的默认绑定这个概念了。默认情况下,一个<strong><span style="color: rgba(0, 128, 0, 1)">js函数在该函数所属的对象的上下文中执行</span></strong>(js function is executed in the context of the object for which it is a property).也就是说,在函数body中,this关键词就是对父亲对象的引用,看看下面的代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">"this" keyword within the sayHello() method is a reference to the sarah object</span>
<span style="color: rgba(0, 0, 0, 1)">sarah.sayHello();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> "this" keyword within the getScreenResolution() function is a reference to the window object (since unbound functions are implicitly bound to the global scope)</span>
getScreenResolution();</pre>
</div>
<p>以上是默认的情况,除此之外,js提供了一种变更任何一个method的执行上下文的机制: call()或者apply().这两个函数都能用于绑定"this"关键字到一个明确的context(object)上去。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">method.call( newThisContext, Param1, ..., Param N )
method.apply( newThisContext, [ Param1, ..., Param N ] );</span></pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/737565/201906/737565-20190610115109755-344975320.png"></p>
<p>再看一个复杂一点的代码案例:</p>
<div class="cnblogs_code">
<pre>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Changing Execution Context In JavaScript&lt;/title&gt;

    &lt;script type="text/javascript"&gt;
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Create a global variable for context (this lives in the</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> global scope - window).</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> context = "Global (ie. window)"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Create an object.</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> objectA =<span style="color: rgba(0, 0, 0, 1)"> {
            context: </span>"Object A"<span style="color: rgba(0, 0, 0, 1)">
      };
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Create another object.</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> objectB =<span style="color: rgba(0, 0, 0, 1)"> {
            context: </span>"Object B"<span style="color: rgba(0, 0, 0, 1)">
      };
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Define a function that uses an argument AND a reference</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> to this THIS scope. We will be invoking this function</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> using a variety of approaches.</span>
      <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> testContext( approach ){
            console.log( approach, </span>"==&gt; THIS ==&gt;", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.context );
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Invoke the unbound method with standard invocation.</span>
      testContext( "testContext()"<span style="color: rgba(0, 0, 0, 1)"> );
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Invoke it in the context of Object A using call().</span>
<span style="color: rgba(0, 0, 0, 1)">      testContext.call(
            objectA,
            </span>".call( objectA )"<span style="color: rgba(0, 0, 0, 1)">
      );
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Invoke it in the context of Object B using apply().</span>
<span style="color: rgba(0, 0, 0, 1)">      testContext.apply(
            objectB,
            [ </span>".apply( objectB )"<span style="color: rgba(0, 0, 0, 1)"> ]
      );
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Now, let's set the test method as an actual property</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> of the object A.</span>
      objectA.testContext =<span style="color: rgba(0, 0, 0, 1)"> testContext;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -------------------------------------------------- //</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Invoke it as a property of object A.</span>
      objectA.testContext( "objectA.testContext()"<span style="color: rgba(0, 0, 0, 1)"> );
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Invoke it in the context of Object B using call.</span>
<span style="color: rgba(0, 0, 0, 1)">      objectA.testContext.call(
            objectB,
            </span>"objectA.testContext.call( objectB )"<span style="color: rgba(0, 0, 0, 1)">
      );
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Invoke it in the context of Window using apply.</span>
<span style="color: rgba(0, 0, 0, 1)">      objectA.testContext.apply(
            window,
            [ </span>"objectA.testContext.apply( window )"<span style="color: rgba(0, 0, 0, 1)"> ]
      );
    </span>&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- Left intentionally blank. --&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
</div>
<p>&nbsp;以上代码的输出如下:</p>
<div class="cnblogs_code">
<pre>testContext() ==&gt; THIS ==&gt;<span style="color: rgba(0, 0, 0, 1)"> Global (ie. window)
.call( objectA ) </span>==&gt; THIS ==&gt;<span style="color: rgba(0, 0, 0, 1)"> Object A
.apply( objectB ) </span>==&gt; THIS ==&gt;<span style="color: rgba(0, 0, 0, 1)"> Object B
objectA.testContext() </span>==&gt; THIS ==&gt;<span style="color: rgba(0, 0, 0, 1)"> Object A
objectA.testContext.call( objectB ) </span>==&gt; THIS ==&gt;<span style="color: rgba(0, 0, 0, 1)"> Object B
objectA.testContext.apply( window ) </span>==&gt; THIS ==&gt; Global (ie. window)</pre>
</div>
<p>&nbsp;</p>
<h3>为什么this是undefined?</h3>
<div class="cnblogs_code">
<pre>(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
    </span>"use strict"<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.foo = "bar"; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> *this* is undefined, why?</span>
<span style="color: rgba(0, 0, 0, 1)">}());
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> myConstructor() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.a = 'foo'<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.b = 'bar'<span style="color: rgba(0, 0, 0, 1)">;
}

myInstance   </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> myConstructor(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> all cool, all fine. a and b were created in a new local object</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 如果是strict mode, 则显示 "TypeError: this is undefined"</span>
myBadInstance= myConstructor(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> oh my gosh, we just created a, and b on the window object</span></pre>
</div>
<p>在js中有一种所谓沙盒模型"boxing" mechanism. 这个盒子在进入被调用函数执行上下文之前将包裹或者变更this object.在匿名函数中,由于在strict mode下并未以obj.method方式来调用匿名函数,因此this就为undefined(原因是匿名函数就是一个闭包,其作用就是隔离了global scope,因此不会默认到window.method上去).而在非strict mode下则this指向window.</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Request(destination, stay_open) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state = "ready"<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.xhr = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.destination =<span style="color: rgba(0, 0, 0, 1)"> destination;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.stay_open =<span style="color: rgba(0, 0, 0, 1)"> stay_open;

    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.open = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(data) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.xhr =<span style="color: rgba(0, 0, 0, 1)"> $.ajax({
            url: destination,
            success: </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handle_response,
            error: </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handle_failure,
            timeout: </span>100000000<span style="color: rgba(0, 0, 0, 1)">,
            data: data,
            dataType: </span>'json'<span style="color: rgba(0, 0, 0, 1)">,
      });
    };

    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> snip... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">

}

Request.prototype.start </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>( <span style="color: rgba(0, 0, 255, 1)">this</span>.stay_open == <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> ) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.open({msg: 'listen'<span style="color: rgba(0, 0, 0, 1)">});
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {

    }
};
</span><span style="color: rgba(0, 0, 255, 1)">var</span> o = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Request(destination, stay_open);
o.start()</span></pre>
</div>
<p><code>this</code>&nbsp;object is not set based on declaration, but by&nbsp;<em>invocation</em>. What it means is that if you assign the function property to a variable like&nbsp;<code>x = o.start</code>&nbsp;and call&nbsp;<code>x()</code>,&nbsp;<code>this</code>&nbsp;inside start no longer refers to&nbsp;<code>o</code></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> o = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Request(...);
setTimeout(</span><span style="color: rgba(0, 0, 255, 1)">function</span>() { o.start(); }, 1000);</pre>
</div>
<p>&nbsp;</p>
<h2>另一个角度来看this-functionObj.this</h2>
<p>首先,需要知道的是js中的所有函数都有peroperties,就像objects都有properties一样,因为function本身也是一个object对象。<strong><span style="color: rgba(0, 128, 0, 1)">this可以看作是this-functionObj的属性</span></strong>,并且只有<strong><span style="color: rgba(0, 128, 0, 1)">当该函数执行时,该函数对象的this属性将会被赋值</span></strong>,"it gets the this property ---- a variable with the value of the object that invokes the function where this is used"</p>
<p>this总是指向或者说引用(并包含了对应的value)一个对象,并且<strong>this往往在一个function或者说method中来使用</strong>。注意:虽然在global scope中我们可以不在function body中使用this,而是直接在global scope中使用this(实际上指向了window),但是如果我们在<strong>strict mode</strong>的话,在global function中,或者说<strong><span style="color: rgba(0, 128, 0, 1)">没有绑定任何object的匿名函数中,如果使用this, 那么这个this将是undefined值</span></strong>.</p>
<p>假设this在一个function A中被使用,那么this就将引用着调用 function A的那个对象。我们需要这个this来访问调用function A对象的method和property.特别地,有些情况下我们不知道调用者对象的名称,甚至有时候调用者对象根本没有名字,这时就必须用this关键字了!</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">var</span> person =<span style="color: rgba(0, 0, 0, 1)"> {
    firstName   :</span>"Penelope"<span style="color: rgba(0, 0, 0, 1)">,
    lastName    :</span>"Barrymore"<span style="color: rgba(0, 0, 0, 1)">,
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Since the "this" keyword is used inside the showFullName method below, and the showFullName method is defined on the person object,</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> "this" will have the value of the person object because the person object will invoke showFullName ()</span>
    showFullName:<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
    console.log (</span><span style="color: rgba(0, 0, 255, 1)">this</span>.firstName + " " + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.lastName);
    }

    }
    person.showFullName (); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Penelope Barrymore</span></pre>
</div>
<p>再看一个jquery事件处理函数中使用this关键字的常见例子:</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> A very common piece of jQuery code</span>
<span style="color: rgba(0, 0, 0, 1)">
    $ (</span>"button").click (<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (event) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> $(this) will have the value of the button ($("button")) object</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> because the button object invokes the click () method, this指向button</span>
    console.log ($ (<span style="color: rgba(0, 0, 255, 1)">this</span>).prop ("name"<span style="color: rgba(0, 0, 0, 1)">));
    });</span></pre>
</div>
<p>&nbsp;The use of&nbsp;<em>$(this)</em>, which is jQuery’s syntax for the&nbsp;<em>this</em>&nbsp;keyword in JavaScript, is used inside an anonymous function, and the anonymous function is executed in the button’s click () method. The reason $(this) is bound to the button object is because the jQuery library&nbsp;binds$(this) to the object that invokes the click method. Therefore, $(this) will have the value of the jQuery button ($(“button”)) object, even though $(this) is defined inside an anonymous function that cannot itself access the “this” variable on the outer function.</p>
<h3>深入一步理解this</h3>
<p>我们先抛出一个心法: this不会有value,直到一个object invoke了这个函数(this在这个函数中使用).为了行文方便,我们将使用this关键字的函数称为thisFunction.</p>
<p>虽然默认情况下,this都会引用定义了this(也就是有this引用)的对象,但是只到一个对象调用了thisFunction,这个this指针才会被赋值。而这个this value只决定于调用了thisFunction的对象。尽管默认情况下this的值就是invoking ojbect(xxObj.thisFunction),但是我们也可以通过xxObj.thisFunction.call(yyObj,parameters), apply()等方式来修改this的默认值!~</p>
<h3>在global scope中使用this</h3>
<p>在global scope中,当代码在浏览器中执行时,所有的全局variable和function都被定义在window object上,因此,在一个全局函数中当使用this时,this是引用了全局的window对象的(注意必须是非stric mode哦),而window对象则是整个js应用或者说web page的容器</p>
<h3>最让人头疼和误解的this使用场景</h3>
<p>有以下几个场景,this会变得非常易于令人误解:</p>
<p>1.当我们借用一个使用了this的方法method;</p>
<p>2.当我们将使用了this的method给到一个变量时;</p>
<p>3.当一个使用了this的函数被作为回调函数参数时;</p>
<p>4.当在一个闭包closure里面的函数中使用this时</p>
<p>下面我们将一一探讨这些情况下this的正确取值是什么</p>
<h3>继续下文前,再聊一聊"context"</h3>
<p>javascript中context的概念和自然语言中的主语有类似性。“John is the winner who returned the money”.本句的主语是John, 我们可以说本剧的context上下文就是John,因为本句此时的焦点就在他身上,甚至who这个代词指代的也是前面的这个主语John.正如我们可以通过使用一个分号&nbsp; &nbsp;"&nbsp; ; " 来更改句子的主语一样,我们也可以通过使用另外一个object来调用这个function,从而改变context.</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> person =<span style="color: rgba(0, 0, 0, 1)"> {
   firstName   :</span>"Penelope"<span style="color: rgba(0, 0, 0, 1)">,
   lastName    :</span>"Barrymore"<span style="color: rgba(0, 0, 0, 1)">,
   showFullName:</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The "context"</span>
console.log (<span style="color: rgba(0, 0, 255, 1)">this</span>.firstName + " " + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.lastName);
}
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The "context", when invoking showFullName, is the person object, when we invoke the showFullName () method on the person object.</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> And the use of "this" inside the showFullName() method has the value of the person object,</span>
person.showFullName (); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Penelope Barrymore</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> If we invoke showFullName with a different object:</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> anotherPerson =<span style="color: rgba(0, 0, 0, 1)"> {
firstName   :</span>"Rohit"<span style="color: rgba(0, 0, 0, 1)">,
lastName    :</span>"Khan"<span style="color: rgba(0, 0, 0, 1)">
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We can use the apply method to set the "this" value explicitly—more on the apply () method later.</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> "this" gets the value of whichever object invokes the "this" Function, hence:</span>
person.showFullName.apply (anotherPerson); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Rohit Khan</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> So the context is now anotherPerson because anotherPerson invoked the person.showFullName ()method by virtue of using the apply () method</span></pre>
</div>
<h4>1. 当方法作为callback方式的传入时,如何fix住里面的this值</h4>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We have a simple object with a clickHandler method that we want to use when a button on the page is clicked</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> user =<span style="color: rgba(0, 0, 0, 1)"> {
    data:[
    {name:</span>"T. Woods", age:37<span style="color: rgba(0, 0, 0, 1)">},
    {name:</span>"P. Mickelson", age:43<span style="color: rgba(0, 0, 0, 1)">}
    ],
    clickHandler:</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (event) {
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> randomNum = ((Math.random () * 2 | 0) + 1) - 1; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> random number between 0 and 1</span>

    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This line is printing a random person's name and age from the data array</span>
    console.log (<span style="color: rgba(0, 0, 255, 1)">this</span>.data.name + " " + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.data.age);
    }
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The button is wrapped inside a jQuery $ wrapper, so it is now a jQuery object</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> And the output will be undefined because there is no data property on the button object</span>
    $ ("button").click (user.clickHandler); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Cannot read property '0' of undefined</span></pre>
</div>
<p>在上面的代码中,($('button'))自己是一个对象,我们将user.clickHandler method方法作为callback函数参数传入该jquery对象的click()方法中,我们知道user.clickHandler()中的this不再指向user对象了。this将指向user.clickMethod运行地所在的对象--因为this在user.clickHandler方法中定义。而invoking这个user.Handler方法的对象则是button object,---user.clickHandler将在button对象的click方法中被执行。</p>
<p>需要说明的是即使我们通过user.clickHandler()方式来调用(实际上我们也必须这么做,因为clickHandler本身就作为user的一个method来定义的,因此必须这么去调用), clickHandler()方法也将以button对象作为上下文去执行,也就是说this现在将指向这个button context对象($('button')).</p>
<p><span style="color: rgba(51, 51, 51, 1); font-family: &quot;Open Sans&quot;, &quot;Proxima Nova Regular&quot;, &quot;Helvetica Neue&quot;, Calibri, &quot;Droid Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: italic">到这里,我们可以下一个结论:</span></p>
<p><span style="color: rgba(51, 51, 51, 1); font-family: &quot;Open Sans&quot;, &quot;Proxima Nova Regular&quot;, &quot;Helvetica Neue&quot;, Calibri, &quot;Droid Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: italic">At this point, it should be apparent that when the context changes—when we execute a method on some other object than where the object was originally defined, the&nbsp;<em>this</em>&nbsp;keyword no longer refers to the original object where “this” was originally defined, but it now refers to the object that invokes the&nbsp;method&nbsp;where&nbsp;<em>this</em>&nbsp;was defined.</span></p>
<p><span style="color: rgba(51, 51, 51, 1); font-family: &quot;Open Sans&quot;, &quot;Proxima Nova Regular&quot;, &quot;Helvetica Neue&quot;, Calibri, &quot;Droid Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: italic">如何能解决这类问题,并且fix住this指向呢?</span></p>
<p><span style="color: rgba(51, 51, 51, 1); font-family: &quot;Open Sans&quot;, &quot;Proxima Nova Regular&quot;, &quot;Helvetica Neue&quot;, Calibri, &quot;Droid Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: italic">在上面的例子中,既然我们总是希望this.data就是指向到user object的data属性,我们可以使用Bind(),Apply()或者Call()方法去特别设定this的value.</span></p>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre> $ ("button").click (user.clickHandler); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这个clickHandler this指向button jquery对象</span>
$("button").click (user.clickHandler.bind (user)); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> P. Mickelson 43 通过bind指定这个clickHandler中的this就是指user</span></pre>
</div>
<p>&nbsp;</p>
<h3>2. 如何在一个闭包的inner function中(或匿名函数)fix住this的值</h3>
<p>正如上面提及,当我们使用一个inner method(a closure)时,this也是非常容易搞混淆的。非常重要一点是:closures闭包不能访问外部函数(outer function)的this值,因为this变量只能由函数本身来访问,而不是inner function(的this)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> user =<span style="color: rgba(0, 0, 0, 1)"> {
    tournament:</span>"The Masters"<span style="color: rgba(0, 0, 0, 1)">,
    data      :[
    {name:</span>"T. Woods", age:37<span style="color: rgba(0, 0, 0, 1)">},
    {name:</span>"P. Mickelson", age:43<span style="color: rgba(0, 0, 0, 1)">}
    ],

    clickHandler:</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the use of this.data here is fine, because "this" refers to the user object, and data is a property on the user object.</span>

    <span style="color: rgba(0, 0, 255, 1)">this</span>.data.forEach (<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (person) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> But here inside the anonymous function (that we pass to the forEach method), "this" no longer refers to the user object.</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This inner function cannot access the outer function's "this"</span>
<span style="color: rgba(0, 0, 0, 1)">   
    console.log (</span>"What is This referring to? " + <span style="color: rgba(0, 0, 255, 1)">this</span>); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"></span>
<span style="color: rgba(0, 0, 0, 1)">
    console.log (person.name </span>+ " is playing at " + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.tournament);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> T. Woods is playing at undefined</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> P. Mickelson is playing at undefined</span>
<span style="color: rgba(0, 0, 0, 1)">    })
    }

    }
    user.clickHandler(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> What is "this" referring to? </span></pre>
</div>
<p>上面代码中,由于匿名函数中的this不能访问外部函数的this,因此当在非strict mode时,this将绑定到global window对象。</p>
<p>同样地,<strong><span style="color: rgba(0, 128, 0, 1)">也可以对匿名函数调用bind来fix住this</span></strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> object =<span style="color: rgba(0, 0, 0, 1)"> {

property: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {

    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.id = 'abc'; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 'this' binds to the object</span>
<span style="color: rgba(0, 0, 0, 1)">
    aFunctionWithCallback(</span><span style="color: rgba(0, 0, 255, 1)">this</span>.id, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(data) {
      console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span>); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> null</span>
<span style="color: rgba(0, 0, 0, 1)">    });
aFunctionWithCallbackOK(</span><span style="color: rgba(0, 0, 255, 1)">this</span>.id, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(data) {
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}.bind(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">));
}
};</span></pre>
</div>
<p>&nbsp;</p>
<h3>维护在匿名函数中使用的this值方案:</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> user =<span style="color: rgba(0, 0, 0, 1)"> {
    tournament:</span>"The Masters"<span style="color: rgba(0, 0, 0, 1)">,
    data      :[
    {name:</span>"T. Woods", age:37<span style="color: rgba(0, 0, 0, 1)">},
    {name:</span>"P. Mickelson", age:43<span style="color: rgba(0, 0, 0, 1)">}
    ],
    clickHandler:</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (event) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> To capture the value of "this" when it refers to the user object, we have to set it to another variable here:</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We set the value of "this" to theUserObj variable, so we can use it later</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> that = theUserObj = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.data.forEach (<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (person) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Instead of using this.tournament, we now use theUserObj.tournament</span>
    console.log (person.name + " is playing at " +<span style="color: rgba(0, 0, 0, 1)"> theUserObj.tournament);
    })
    }
    }
    user.clickHandler();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> T. Woods is playing at The Masters</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">P. Mickelson is playing at The Masters</span></pre>
</div>
<p>3. 当method被赋值给一个变量时,如何fix住this指向</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This data variable is a global variable</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> data =<span style="color: rgba(0, 0, 0, 1)"> [
    {name:</span>"Samantha", age:12<span style="color: rgba(0, 0, 0, 1)">},
    {name:</span>"Alexis", age:14<span style="color: rgba(0, 0, 0, 1)">}
    ];

    </span><span style="color: rgba(0, 0, 255, 1)">var</span> user =<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this data variable is a property on the user object</span>
<span style="color: rgba(0, 0, 0, 1)">    data    :[
    {name:</span>"T. Woods", age:37<span style="color: rgba(0, 0, 0, 1)">},
    {name:</span>"P. Mickelson", age:43<span style="color: rgba(0, 0, 0, 1)">}
    ],
    showData:</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (event) {
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> randomNum = ((Math.random () * 2 | 0) + 1) - 1; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> random number between 0 and 1</span>

    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This line is adding a random person from the data array to the text field</span>
    console.log (<span style="color: rgba(0, 0, 255, 1)">this</span>.data.name + " " + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.data.age);
    }

    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Assign the user.showData to a variable</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> showUserData =<span style="color: rgba(0, 0, 0, 1)"> user.showData;

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> When we execute the showUserData function, the values printed to the console are from the global data array, not from the data array in the user object</span>
    <span style="color: rgba(0, 128, 0, 1)">//
</span>    showUserData (); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Samantha 12 (from the global data array)</span></pre>
</div>
<p>解决方案是特别地通过使用bind方法来指定this值</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Bind the showData method to the user object</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> showUserData =<span style="color: rgba(0, 0, 0, 1)"> user.showData.bind (user);

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Now we get the value from the user object, because the this keyword is bound to the user object</span>
    showUserData (); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> P. Mickelson 43</span></pre>
</div>
<h3>4. 当借用一个定义了this的method方法时</h3>
<p>在js开发中,借用方法是一个非常常见的实例,作为js开发者,我们一定会经常遇到。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We have two objects. One of them has a method called avg () that the other doesn't have</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> So we will borrow the (avg()) method</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> gameController =<span style="color: rgba(0, 0, 0, 1)"> {
    scores:[</span>20, 34, 55, 46, 77<span style="color: rgba(0, 0, 0, 1)">],
    avgScore:</span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
    players :[
    {name:</span>"Tommy", playerID:987, age:23<span style="color: rgba(0, 0, 0, 1)">},
    {name:</span>"Pau", playerID:87, age:33<span style="color: rgba(0, 0, 0, 1)">}
    ]
    }

    </span><span style="color: rgba(0, 0, 255, 1)">var</span> appController =<span style="color: rgba(0, 0, 0, 1)"> {
    scores:[</span>900, 845, 809, 950<span style="color: rgba(0, 0, 0, 1)">],
    avgScore:</span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
    avg   :</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {

    </span><span style="color: rgba(0, 0, 255, 1)">var</span> sumOfScores = <span style="color: rgba(0, 0, 255, 1)">this</span>.scores.reduce (<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (prev, cur, index, array) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> prev +<span style="color: rgba(0, 0, 0, 1)"> cur;
    });

    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.avgScore = sumOfScores / <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scores.length;
    }
    }
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">If we run the code below,</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the gameController.avgScore property will be set to the average score from the appController object "scores" array</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Don't run this code, for it is just for illustration; we want the appController.avgScore to remain null</span>
    gameController.avgScore = appController.avg();</pre>
</div>
<p>avg方法的"this"不再指向gameController object,它将指向到appController对象,因为该avg()方法是在appController对象上执行的。</p>
<p>解决方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Note that we are using the apply () method, so the 2nd argument has to be an array—the arguments to pass to the appController.avg () method.</span>
<span style="color: rgba(0, 0, 0, 1)">    appController.avg.apply (gameController, gameController.scores);

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The avgScore property was successfully set on the gameController object, even though we borrowed the avg () method from the appController object</span>
    console.log (gameController.avgScore); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 46.4</span>

    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> appController.avgScore is still null; it was not updated, only gameController.avgScore was updated</span>
    console.log (appController.avgScore); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> null</span></pre>
</div>
<p>gameController对象借用了appController's avg()方法,在appController.avg()中的this value会被设定为gameContrller对象,因为我们使用了apply()方法。</p>
<p>最后,需要牢记:</p>
<p><strong><span style="color: rgba(0, 128, 0, 1)">Always remember that&nbsp;<em>this</em>&nbsp;is assigned the value of the object that invoked the&nbsp;<em>this Function</em></span></strong></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    积小流以汇江海,细微做起<br><br>
来源:https://www.cnblogs.com/kidsitcn/p/10985338.html
頁: [1]
查看完整版本: 彻底理解javascript中的this指针