{"action":"opened","issue":{"url":"https://api.github.com/repos/ccforward/cc/issues/12","labels_url":"https://api.github.com/repos/ccforward/cc/issues/12/labels{/name}","comments_url":"https://api.github.com/repos/ccforward/cc/issues/12/comments","events_url":"https://api.github.com/repos/ccforward/cc/issues/12/events","html_url":"https://github.com/ccforward/cc/issues/12","id":56215514,"number":12,"title":"11.[译] 详解javascript的this","user":{"login":"ccforward","id":2782664,"avatar_url":"https://avatars.githubusercontent.com/u/2782664?v=3","gravatar_id":"","url":"https://api.github.com/users/ccforward","html_url":"https://github.com/ccforward","followers_url":"https://api.github.com/users/ccforward/followers","following_url":"https://api.github.com/users/ccforward/following{/other_user}","gists_url":"https://api.github.com/users/ccforward/gists{/gist_id}","starred_url":"https://api.github.com/users/ccforward/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ccforward/subscriptions","organizations_url":"https://api.github.com/users/ccforward/orgs","repos_url":"https://api.github.com/users/ccforward/repos","events_url":"https://api.github.com/users/ccforward/events{/privacy}","received_events_url":"https://api.github.com/users/ccforward/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-02-02T09:53:28Z","updated_at":"2015-02-02T09:53:28Z","closed_at":null,"body":"<div id=\"cnblogs_post_body\" class=\"cnblogs-markdown\"><blockquote>\r\n<p><code>this</code> 虐我千百遍，看完此文效立见！不得不说，这篇文章的总结很地道很全面，适合收藏之用。<br>原文：<a href=\"http://bjorn.tipling.com/all-this\" target=\"_blank\">all this</a></p>\r\n</blockquote>\r\n<p>习惯了高级语言的你或许觉得JavaScript中的<code>this</code>跟Java这些面向对象语言相似，保存了实体属性的一些值。其实不然。将它视作<a href=\"http://harrypotter.wikia.com/wiki/Boggart\" target=\"_blank\">幻影魔神</a>比较恰当，手提一个装满未知符文的<a href=\"http://harrypotter.wikia.com/wiki/Undetectable_Extension_Charm\" target=\"_blank\">灵龛</a>。</p>\r\n<p>以下内容我希望广大同行们能够了解。全是掏箱底的干货，其中大部分占用了我很多时间才掌握。</p>\r\n<!-- more -->\r\n\r\n\r\n<h1 id=\"this\">全局<code>this</code></h1>\r\n<p>浏览器宿主的全局环境中，<code>this</code>指的是<code>window</code>对象。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span> === <span class=\"hljs-built_in\">window</span>); <span class=\"hljs-comment\">//true</span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/hL9emz4m/\" target=\"_blank\">示例</a></p>\r\n<p>浏览器中在全局环境下，使用<code>var</code>声明变量其实就是赋值给<code>this</code>或<code>window</code>。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n    <span class=\"hljs-keyword\">var</span> foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-built_in\">window</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/1/\" target=\"_blank\">示例</a></p>\r\n<p>任何情况下，创建变量时没有使用<code>var</code>或者<code>let</code>(ECMAScript 6)，也是在操作全局<code>this</code>。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n    foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">testThis</span><span class=\"hljs-params\">()</span> </span>{\r\n      foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n    }\r\n\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n    testThis();\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"foo\"</span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/3/\" target=\"_blank\">示例</a></p>\r\n<p>Node命令行（REPL）中，<code>this</code>是全局命名空间。可以通过<code>global</code>来访问。</p>\r\n<pre class=\"bash\"><code class=\" hljs \">&gt; this\r\n{ ArrayBuffer: [Function: ArrayBuffer],\r\n  Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: <span class=\"hljs-number\">1</span> },\r\n  Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: <span class=\"hljs-number\">1</span> },\r\n  ...\r\n&gt; global === this\r\n<span class=\"hljs-literal\">true</span></code></pre>\r\n<p>在Node环境里执行的JS脚本中，<code>this</code>其实是个空对象，有别于<code>global</code>。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>);\r\n<span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span> === global);</code></pre>\r\n<pre class=\"bash\"><code class=\" hljs \">$ node test.js\r\n{}\r\n<span class=\"hljs-literal\">false</span></code></pre>\r\n<p>当尝试在Node中执行JS脚本时，脚本中全局作用域中的<code>var</code>并不会将变量赋值给全局<code>this</code>，这与在浏览器中是不一样的。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-keyword\">var</span> foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n<span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);</code></pre>\r\n<pre class=\"bash\"><code class=\" hljs \">$ node test.js\r\nundefined</code></pre>\r\n<p>...但在命令行里进行求值却会赋值到<code>this</code>身上。</p>\r\n<pre class=\"bash\"><code class=\" hljs \">&gt; var foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n&gt; this.foo\r\nbar\r\n&gt; global.foo\r\nbar</code></pre>\r\n<p>在Node里执行的脚本中，创建变量时没带<code>var</code>或<code>let</code>关键字，会赋值给全局的<code>global</code>但不是<code>this</code>（译注：上面已经提到<code>this</code>和<code>global</code>不是同一个对象，所以这里就不奇怪了）。</p>\r\n<pre class=\"js\"><code class=\" hljs \">foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n<span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n<span class=\"hljs-built_in\">console</span>.log(global.foo);</code></pre>\r\n<pre class=\"bash\"><code class=\" hljs \">$ node test.js\r\nundefined\r\nbar</code></pre>\r\n<p>但在Node命令行里，就会赋值给两者了。</p>\r\n<blockquote>\r\n<p>译注：简单来说，Node脚本中<code>global</code>和<code>this</code>是区别对待的，而Node命令行中，两者可等效为同一对象。</p>\r\n</blockquote>\r\n<h1 id=\"this\">函数或方法里的<code>this</code></h1>\r\n<p>除了DOM的事件回调或者提供了执行上下文（后面会提到）的情况，函数正常被调用（不带<code>new</code>）时，里面的<code>this</code>指向的是全局作用域。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n    foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">testThis</span><span class=\"hljs-params\">()</span> </span>{\r\n      <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n    }\r\n\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n    testThis();\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"foo\"</span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/4/\" target=\"_blank\">示例</a></p>\r\n<pre class=\"js\"><code class=\" hljs \">foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">testThis</span> <span class=\"hljs-params\">()</span> </span>{\r\n  <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n}\r\n\r\n<span class=\"hljs-built_in\">console</span>.log(global.foo);\r\ntestThis();\r\n<span class=\"hljs-built_in\">console</span>.log(global.foo);</code></pre>\r\n<pre class=\"bash\"><code class=\" hljs \">$ node test.js\r\nbar\r\nfoo</code></pre>\r\n<p>还有个例外，就是使用了<code>\"use strict\";</code>。此时<code>this</code>是<code>undefined</code>。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n    foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">testThis</span><span class=\"hljs-params\">()</span> </span>{\r\n<span class=\"hljs-pi\">      \"use strict\"</span>;\r\n      <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n    }\r\n\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n    testThis();  <span class=\"hljs-comment\">//Uncaught TypeError: Cannot set property 'foo' of undefined </span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/51/\" target=\"_blank\">示例</a></p>\r\n<p>当用调用函数时使用了<code>new</code>关键字，此刻<code>this</code>指代一个新的上下文，不再指向全局<code>this</code>。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n    foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">testThis</span><span class=\"hljs-params\">()</span> </span>{\r\n      <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n    }\r\n\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n    <span class=\"hljs-keyword\">new</span> testThis();\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">new</span> testThis().foo); <span class=\"hljs-comment\">//logs \"foo\"</span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/6/\" target=\"_blank\">示例</a></p>\r\n<p>通常我将这个新的上下文称作实例。</p>\r\n<h1 id=\"this\">原型中的<code>this</code></h1>\r\n<p>函数创建后其实以一个函数对象的形式存在着。既然是对象，则自动获得了一个叫做<code>prototype</code>的属性，可以自由地对这个属性进行赋值。当配合<code>new</code>关键字来调用一个函数创建实例后，此刻便能直接访问到原型身上的值。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing(); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n<span class=\"hljs-built_in\">console</span>.log(thing.foo);  <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/8/\" target=\"_blank\">示例</a></p>\r\n<p>当通过<code>new</code>的方式创建了多个实例后，他们会共用一个原型。比如，每个实例的<code>this.foo</code>都返回相同的值，直到<code>this.foo</code>被重写。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\nThing.prototype.setFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">(newFoo)</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.foo = newFoo;\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing1 = <span class=\"hljs-keyword\">new</span> Thing();\r\n<span class=\"hljs-keyword\">var</span> thing2 = <span class=\"hljs-keyword\">new</span> Thing();\r\n\r\nthing1.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\nthing2.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n\r\nthing1.setFoo(<span class=\"hljs-string\">\"foo\"</span>);\r\nthing1.logFoo(); <span class=\"hljs-comment\">//logs \"foo\";</span>\r\nthing2.logFoo(); <span class=\"hljs-comment\">//logs \"bar\";</span>\r\n\r\nthing2.foo = <span class=\"hljs-string\">\"foobar\"</span>;\r\nthing1.logFoo(); <span class=\"hljs-comment\">//logs \"foo\";</span>\r\nthing2.logFoo(); <span class=\"hljs-comment\">//logs \"foobar\";</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/9/\" target=\"_blank\">示例</a></p>\r\n<p>在实例中，<code>this</code>是个特殊的对象，而<code>this</code>自身其实只是个关键字。你可以把<code>this</code>想象成在实例中获取原型值的一种途径，同时对<code>this</code>赋值又会覆盖原型上的值。完全可以将新增的值从原型中删除从而将原型还原为初始状态。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\nThing.prototype.setFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">(newFoo)</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.foo = newFoo;\r\n}\r\nThing.prototype.deleteFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">delete</span> <span class=\"hljs-keyword\">this</span>.foo;\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.setFoo(<span class=\"hljs-string\">\"foo\"</span>);\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"foo\";</span>\r\nthing.deleteFoo();\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\";</span>\r\nthing.foo = <span class=\"hljs-string\">\"foobar\"</span>;\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"foobar\";</span>\r\n<span class=\"hljs-keyword\">delete</span> thing.foo;\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\";</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/11/\" target=\"_blank\">示例</a></p>\r\n<p>...或者不通过实例，直接操作函数的原型。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo, Thing.prototype.foo);\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"foo bar\";</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/12/\" target=\"_blank\">示例</a></p>\r\n<p>同一函数创建的所有实例均共享一个原型。如果你给原型赋值了一个数组，那么所有实例都能获取到这个数组。除非你在某个实例中对其进行了重写，实事上是进行了覆盖。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.things = [];\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing1 = <span class=\"hljs-keyword\">new</span> Thing();\r\n<span class=\"hljs-keyword\">var</span> thing2 = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing1.things.push(<span class=\"hljs-string\">\"foo\"</span>);\r\n<span class=\"hljs-built_in\">console</span>.log(thing2.things); <span class=\"hljs-comment\">//logs [\"foo\"]</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/13/\" target=\"_blank\">示例</a></p>\r\n<p>通常上面的做法是不正确的（译注：改变<code>thing1</code>的同时也影响了<code>thing2</code>）。如果你想每个实例互不影响，那么请在函数里创建这些值，而不是在原型上。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.things = [];\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing1 = <span class=\"hljs-keyword\">new</span> Thing();\r\n<span class=\"hljs-keyword\">var</span> thing2 = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing1.things.push(<span class=\"hljs-string\">\"foo\"</span>);\r\n<span class=\"hljs-built_in\">console</span>.log(thing1.things); <span class=\"hljs-comment\">//logs [\"foo\"]</span>\r\n<span class=\"hljs-built_in\">console</span>.log(thing2.things); <span class=\"hljs-comment\">//logs []</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/14/\" target=\"_blank\">示例</a></p>\r\n<p>多个函数可以形成原型链，这样<code>this</code>便会在原型链上逐步往上找直到找到你想引用的值。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing1</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing1.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing2</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing2.prototype = <span class=\"hljs-keyword\">new</span> Thing1();\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing2();\r\n<span class=\"hljs-built_in\">console</span>.log(thing.foo); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/15/\" target=\"_blank\">示例</a></p>\r\n<p>很多人便是利用这个特性在JS中模拟经典的对象继承。</p>\r\n<p>注意原型链底层函数中对<code>this</code>的操作会覆盖上层的值。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing1</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing1.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing2</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n}\r\nThing2.prototype = <span class=\"hljs-keyword\">new</span> Thing1();\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing3</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing3.prototype = <span class=\"hljs-keyword\">new</span> Thing2();\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing3();\r\n<span class=\"hljs-built_in\">console</span>.log(thing.foo); <span class=\"hljs-comment\">//logs \"foo\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/16/\" target=\"_blank\">示例</a></p>\r\n<p>我习惯将赋值到原型上的函数称作方法。上面某些地方便使用了方法这样的字眼，比如<code>logFoo</code>方法。这些方法中的<code>this</code>同样具有在原型链上查找引用的魔力。通常将最初用来创建实例的函数称作构造函数。</p>\r\n<p>原型链方法中的<code>this</code>是从实例中的<code>this</code>开始住上查找整个原型链的。也就是说，如果原型链中某个地方直接对<code>this</code>进行赋值覆盖了某个变量，那么我们拿到 的是覆盖后的值。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing1</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing1.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing1.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing2</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n}\r\nThing2.prototype = <span class=\"hljs-keyword\">new</span> Thing1();\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing2();\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"foo\";</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/17/\" target=\"_blank\">示例</a></p>\r\n<p>在JavaScript中，函数可以嵌套函数，也就是你可以在函数里面继续定义函数。但内层函数是通过闭包获取外层函数里定义的变量值的，而不是直接继承<code>this</code>。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">var</span> info = <span class=\"hljs-string\">\"attempting to log this.foo:\"</span>;\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doIt</span><span class=\"hljs-params\">()</span> </span>{\r\n        <span class=\"hljs-built_in\">console</span>.log(info, <span class=\"hljs-keyword\">this</span>.foo);\r\n    }\r\n    doIt();\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo();  <span class=\"hljs-comment\">//logs \"attempting to log this.foo: undefined\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/21/\" target=\"_blank\">示例</a></p>\r\n<p>上面示例中，<code>doIt</code> 函数中的<code>this</code>指代是全局作用域或者是<code>undefined</code>如果使用了<code>\"use strict\";</code>声明的话。对于很多新手来说，理解这点是非常头疼的。</p>\r\n<p>还有更奇葩的。把实例的方法作为参数传递时，实例是不会跟着过去的。也就是说，此时方法中的<code>this</code>在调用时指向的是全局<code>this</code>或者是<code>undefined</code>在声明了<code>\"use strict\";</code>时。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{  \r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);   \r\n}\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doIt</span><span class=\"hljs-params\">(method)</span> </span>{\r\n    method();\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\ndoIt(thing.logFoo); <span class=\"hljs-comment\">//logs undefined</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/22/\" target=\"_blank\">示例</a></p>\r\n<p>所以很多人习惯将<code>this</code>缓存起来，用个叫<code>self</code>或者其他什么的变量来保存，以将外层与内层的<code>this</code>区分开来。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">var</span> self = <span class=\"hljs-keyword\">this</span>;\r\n    <span class=\"hljs-keyword\">var</span> info = <span class=\"hljs-string\">\"attempting to log this.foo:\"</span>;\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doIt</span><span class=\"hljs-params\">()</span> </span>{\r\n        <span class=\"hljs-built_in\">console</span>.log(info, self.foo);\r\n    }\r\n    doIt();\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo();  <span class=\"hljs-comment\">//logs \"attempting to log this.foo: bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/23/\" target=\"_blank\">示例</a></p>\r\n<p>...但上面的方式不是万能的，在将方法做为参数传递时，就不起作用了。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{ \r\n    <span class=\"hljs-keyword\">var</span> self = <span class=\"hljs-keyword\">this</span>;\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doIt</span><span class=\"hljs-params\">()</span> </span>{\r\n        <span class=\"hljs-built_in\">console</span>.log(self.foo);\r\n    }\r\n    doIt();\r\n}\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doItIndirectly</span><span class=\"hljs-params\">(method)</span> </span>{\r\n    method();\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\ndoItIndirectly(thing.logFoo); <span class=\"hljs-comment\">//logs undefined</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/24/\" target=\"_blank\">示例</a></p>\r\n<p>解决方法就是传递的时候使用<code>bind</code>方法显示指明上下文，<code>bind</code>方法是所有函数或方法都具有的。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{ \r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doIt</span><span class=\"hljs-params\">(method)</span> </span>{\r\n    method();\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\ndoIt(thing.logFoo.bind(thing)); <span class=\"hljs-comment\">//logs bar</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/25/\" target=\"_blank\">示例</a></p>\r\n<p>同时也可以使用<code>apply</code>或<code>call</code> 来调用该方法或函数，让它在一个新的上下文中执行。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{ \r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doIt</span><span class=\"hljs-params\">()</span> </span>{\r\n        <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n    }\r\n    doIt.apply(<span class=\"hljs-keyword\">this</span>);\r\n}\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">doItIndirectly</span><span class=\"hljs-params\">(method)</span> </span>{\r\n    method();\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\ndoItIndirectly(thing.logFoo.bind(thing)); <span class=\"hljs-comment\">//logs bar</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/26/\" target=\"_blank\">示例</a></p>\r\n<p>使用<code>bind</code>可以任意改变函数或方法的执行上下文，即使它没有被绑定到一个实例的原型上。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">logFoo</span><span class=\"hljs-params\">(aStr)</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(aStr, <span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nlogFoo.bind(thing)(<span class=\"hljs-string\">\"using bind\"</span>); <span class=\"hljs-comment\">//logs \"using bind bar\"</span>\r\nlogFoo.apply(thing, [<span class=\"hljs-string\">\"using apply\"</span>]); <span class=\"hljs-comment\">//logs \"using apply bar\"</span>\r\nlogFoo.call(thing, <span class=\"hljs-string\">\"using call\"</span>); <span class=\"hljs-comment\">//logs \"using call bar\"</span>\r\nlogFoo(<span class=\"hljs-string\">\"using nothing\"</span>); <span class=\"hljs-comment\">//logs \"using nothing undefined\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/27/\" target=\"_blank\">示例</a></p>\r\n<p>避免在构造函数中返回作何东西，因为返回的东西可能覆盖本来该返回的实例。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">return</span> {};\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo(); <span class=\"hljs-comment\">//Uncaught TypeError: undefined is not a function</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/28/\" target=\"_blank\">示例</a></p>\r\n<p>但，如果你在构造函数里返回的是个原始值比如字符串或者数字什么的，上面的错误就不会发生了，返回语句将被忽略。所以最好别在一个将要通过<code>new</code>来调用的构造函数中返回作何东西，即使你是清醒的。如果你想实现工厂模式，那么请用一个函数来创建实例，并且不通过<code>new</code>来调用。当然这只是个人建议。</p>\r\n<p>诚然，你也可以使用<code>Object.create</code>从而避免使用<code>new</code>。这样也能创建一个实例。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing =  <span class=\"hljs-built_in\">Object</span>.create(Thing.prototype);\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/29/\" target=\"_blank\">示例</a></p>\r\n<p>这种方式不会调用该构造函数。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n\r\n<span class=\"hljs-keyword\">var</span> thing =  <span class=\"hljs-built_in\">Object</span>.create(Thing.prototype);\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/30/\" target=\"_blank\">示例</a></p>\r\n<p>正因为<code>Object.create</code>没有调用构造函数，这在当你想实现一个继承时是非常有用的，随后你可能想要重写构造函数。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing1</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n}\r\nThing1.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing2</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n    Thing1.apply(<span class=\"hljs-keyword\">this</span>);\r\n    <span class=\"hljs-keyword\">this</span>.logFoo(); <span class=\"hljs-comment\">//logs \"foo\"</span>\r\n}\r\nThing2.prototype = <span class=\"hljs-built_in\">Object</span>.create(Thing1.prototype);\r\nThing2.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing2();</code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/32/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"this\">对象中的<code>this</code></h1>\r\n<p>可以在对象的任何方法中使用<code>this</code>来访问该对象的属性。这与用<code>new</code>得到的实例是不一样的。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-keyword\">var</span> obj = {\r\n    foo: <span class=\"hljs-string\">\"bar\"</span>,\r\n    logFoo: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n        <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n    }\r\n};\r\n\r\nobj.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/33/\" target=\"_blank\">示例</a></p>\r\n<p>注意这里并没有使用<code>new</code>，也没有用<code>Object.create</code>，更没有函数的调用来创建对象。也可以将函数绑定到对象，就好像这个对象是一个实例一样。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-keyword\">var</span> obj = {\r\n    foo: <span class=\"hljs-string\">\"bar\"</span>\r\n};\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">logFoo</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n}\r\n\r\nlogFoo.apply(obj); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/34/\" target=\"_blank\">示例</a></p>\r\n<p>此时使用<code>this</code>没有向上查找原型链的复杂工序。通过<code>this</code>所拿到的只是该对象身上的属性而以。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-keyword\">var</span> obj = {\r\n    foo: <span class=\"hljs-string\">\"bar\"</span>,\r\n    deeper: {\r\n        logFoo: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n            <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.foo);\r\n        }\r\n    }\r\n};\r\n\r\nobj.deeper.logFoo(); <span class=\"hljs-comment\">//logs undefined</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/35/\" target=\"_blank\">示例</a></p>\r\n<p>也可以不通过<code>this</code>，直接访问对象的属性。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-keyword\">var</span> obj = {\r\n    foo: <span class=\"hljs-string\">\"bar\"</span>,\r\n    deeper: {\r\n        logFoo: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n            <span class=\"hljs-built_in\">console</span>.log(obj.foo);\r\n        }\r\n    }\r\n};\r\n\r\nobj.deeper.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/36/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"dom-this\">DOM 事件回调中的<code>this</code></h1>\r\n<p>在DOM事件的处理函数中，<code>this</code>指代的是被绑定该事件的DOM元素。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Listener</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"foo\"</span>).addEventListener(<span class=\"hljs-string\">\"click\"</span>,\r\n       <span class=\"hljs-keyword\">this</span>.handleClick);\r\n}\r\nListener.prototype.handleClick = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">(event)</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>); <span class=\"hljs-comment\">//logs \"&lt;div id=\"foo\"&gt;&lt;/div&gt;\"</span>\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> listener = <span class=\"hljs-keyword\">new</span> Listener();\r\n<span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"foo\"</span>).click();</code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/37/\" target=\"_blank\">示例</a></p>\r\n<p>...除非你通过<code>bind</code>人为改变了事件处理器的执行上下文。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Listener</span><span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"foo\"</span>).addEventListener(<span class=\"hljs-string\">\"click\"</span>, \r\n        <span class=\"hljs-keyword\">this</span>.handleClick.bind(<span class=\"hljs-keyword\">this</span>));\r\n}\r\nListener.prototype.handleClick = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">(event)</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>); <span class=\"hljs-comment\">//logs Listener {handleClick: function}</span>\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> listener = <span class=\"hljs-keyword\">new</span> Listener();\r\n<span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"foo\"</span>).click();</code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/38/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"htmlthis\">HTML中的<code>this</code></h1>\r\n<p>HTML标签的属性中是可能写JS的，这种情况下<code>this</code>指代该HTML元素。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">div</span> <span class=\"hljs-attribute\">id</span>=<span class=\"hljs-value\">\"foo\"</span> <span class=\"hljs-attribute\">onclick</span>=<span class=\"hljs-value\">\"console.log(this);\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">div</span>&gt;</span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n<span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"foo\"</span>).click(); <span class=\"hljs-comment\">//logs &lt;div id=\"foo\"...</span>\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/40/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"this\">重写<code>this</code></h1>\r\n<p>无法重写<code>this</code>，因为它是一个关键字。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">test</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">var</span> <span class=\"hljs-keyword\">this</span> = {};  <span class=\"hljs-comment\">// Uncaught SyntaxError: Unexpected token this </span>\r\n}</code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/41/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"evalthis\"><code>eval</code>中的<code>this</code></h1>\r\n<p><code>eval</code> 中也可以正确获取当前的 <code>this</code>。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span> <span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">eval</span>(<span class=\"hljs-string\">\"console.log(this.foo)\"</span>); <span class=\"hljs-comment\">//logs \"bar\"</span>\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo();</code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/43/\" target=\"_blank\">示例</a></p>\r\n<p>这里存在安全隐患。最好的办法就是避免使用<code>eval</code>。</p>\r\n<p>使用<code>Function</code>关键字创建的函数也可以获取<code>this</code>：</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span> <span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-keyword\">new</span> <span class=\"hljs-built_in\">Function</span>(<span class=\"hljs-string\">\"console.log(this.foo);\"</span>);\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo(); <span class=\"hljs-comment\">//logs \"bar\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/44/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"withthis\">使用<code>with</code>时的<code>this</code></h1>\r\n<p>使用<code>with</code>可以将<code>this</code>人为添加到当前执行环境中而不需要显示地引用<code>this</code>。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span> <span class=\"hljs-params\">()</span> </span>{\r\n}\r\nThing.prototype.foo = <span class=\"hljs-string\">\"bar\"</span>;\r\nThing.prototype.logFoo = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">with</span> (<span class=\"hljs-keyword\">this</span>) {\r\n        <span class=\"hljs-built_in\">console</span>.log(foo);\r\n        foo = <span class=\"hljs-string\">\"foo\"</span>;\r\n    }\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing();\r\nthing.logFoo(); <span class=\"hljs-comment\">// logs \"bar\"</span>\r\n<span class=\"hljs-built_in\">console</span>.log(thing.foo); <span class=\"hljs-comment\">// logs \"foo\"</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/45/\" target=\"_blank\">示例</a></p>\r\n<p>正如很多人认为的那样，使用<code>with</code>是不好的，因为会产生歧义。</p>\r\n<h1 id=\"jquerythis\">jQuery中的<code>this</code></h1>\r\n<p>一如HTML DOM元素的事件回调，jQuery库中大多地方的<code>this</code>也是指代的DOM元素。页面上的事件回调和一些便利的静态方法比如<code>$.each</code> 都是这样的。</p>\r\n<pre class=\"html\"><code class=\" hljs \"><span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">div</span> <span class=\"hljs-attribute\">class</span>=<span class=\"hljs-value\">\"foo bar1\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">div</span>&gt;</span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">div</span> <span class=\"hljs-attribute\">class</span>=<span class=\"hljs-value\">\"foo bar2\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">div</span>&gt;</span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-title\">script</span> <span class=\"hljs-attribute\">type</span>=<span class=\"hljs-value\">\"text/javascript\"</span>&gt;</span><span class=\"javascript\">\r\n$(<span class=\"hljs-string\">\".foo\"</span>).each(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>); <span class=\"hljs-comment\">//logs &lt;div class=\"foo...</span>\r\n});\r\n$(<span class=\"hljs-string\">\".foo\"</span>).on(<span class=\"hljs-string\">\"click\"</span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>); <span class=\"hljs-comment\">//logs &lt;div class=\"foo...</span>\r\n});\r\n$(<span class=\"hljs-string\">\".foo\"</span>).each(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">()</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.click();\r\n});\r\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-title\">script</span>&gt;</span></code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/47/\" target=\"_blank\">示例</a></p>\r\n<h1 id=\"-this\">传递 <code>this</code></h1>\r\n<p>如果你用过<a href=\"http://underscorejs.org/\" target=\"_blank\">underscore.js</a>或者<a href=\"http://lodash.com/\" target=\"_blank\">lo-dash</a>你便知道，这两个库中很多方法你可以传递一个参数来显示指定执行的上下文。比如<code>_.each</code>。自ECMAScript 5 标准后，一些原生的JS方法也允许传递上下文，比如<code>forEach</code>。事实上，上文提到的<code>bind</code>，<code>apply</code>还有<code>call</code> 已经给我们手动指定函数执行上下文的能力了。</p>\r\n<pre class=\"js\"><code class=\" hljs \"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">Thing</span><span class=\"hljs-params\">(type)</span> </span>{\r\n    <span class=\"hljs-keyword\">this</span>.type = type;\r\n}\r\nThing.prototype.log = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">(thing)</span> </span>{\r\n    <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-keyword\">this</span>.type, thing);\r\n}\r\nThing.prototype.logThings = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">(arr)</span> </span>{\r\n   arr.forEach(<span class=\"hljs-keyword\">this</span>.log, <span class=\"hljs-keyword\">this</span>); <span class=\"hljs-comment\">// logs \"fruit apples...\"</span>\r\n   _.each(arr, <span class=\"hljs-keyword\">this</span>.log, <span class=\"hljs-keyword\">this</span>); <span class=\"hljs-comment\">//logs \"fruit apples...\"</span>\r\n}\r\n\r\n<span class=\"hljs-keyword\">var</span> thing = <span class=\"hljs-keyword\">new</span> Thing(<span class=\"hljs-string\">\"fruit\"</span>);\r\nthing.logThings([<span class=\"hljs-string\">\"apples\"</span>, <span class=\"hljs-string\">\"oranges\"</span>, <span class=\"hljs-string\">\"strawberries\"</span>, <span class=\"hljs-string\">\"bananas\"</span>]);</code></pre>\r\n<p><a href=\"http://jsfiddle.net/btipling/25xkmho7/50/\" target=\"_blank\">示例</a></p>\r\n<p>这样可以使得代码简洁些，不用层层嵌套<code>bind</code>，也不用不断地缓存<code>this</code>。</p>\r\n<p>一些编程语言上手很简单，比如<a href=\"http://golang.org/\" target=\"_blank\">Go语言手册</a>可以被快速读完。然后你差不多就掌握这门语言了，只是在实战时会有些小的问题或陷阱在等着你。</p>\r\n<p>而JavaScript不是这样的。手册难读。非常多缺陷在里面，以至于人们抽离出了<code>它好的部分</code>（<a href=\"http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742\" target=\"_blank\">The Good Parts</a>）。最好的文档可能是<a href=\"https://developer.mozilla.org/en-US/\" target=\"_blank\">MDN</a>上的了。所以我建议你看看他上面关于<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this\" target=\"_blank\"><code>this</code></a>的介绍，并且始终在搜索JS相关问题时加上\"mdn\" 来获得最好的文档资料。静态代码检查也是个不错的工具，比如<a href=\"http://www.jshint.com/\" target=\"_blank\">jshint</a>。</p>\r\n<p>欢迎勘误及讨论，<script type=\"text/javascript\">\r\n<!--\r\nh='&#98;&#106;&#x6f;&#114;&#110;&#116;&#x69;&#112;&#108;&#x69;&#110;&#x67;';a='&#64;';n='&#x6211;&#30340;&#25512;&#x7279;';e=n+a+h;\r\ndocument.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\">'+e+'<\\/'+'a'+'>');\r\n// -->\r\n</script><a href=\"mailto:我的推特@bjorntipling\" target=\"_blank\">我的推特@bjorntipling</a><noscript>&amp;#x6211;&amp;#30340;&amp;#25512;&amp;#x7279;&amp;#32;&amp;#x61;&amp;#116;&amp;#32;&amp;#98;&amp;#106;&amp;#x6f;&amp;#114;&amp;#110;&amp;#116;&amp;#x69;&amp;#112;&amp;#108;&amp;#x69;&amp;#110;&amp;#x67;</noscript>。</p><a class=\"fastCom\" id=\"fastCom\" title=\"快速评论\" href=\"#comment_form_container\"></a></div>"}}