{"componentChunkName":"component---src-templates-blog-post-js","path":"/learn-about-the-problem-of-this-point/","result":{"data":{"markdownRemark":{"html":"<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">var</span> bar <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    myName<span class=\"token operator\">:</span><span class=\"token string\">\"time.geekbang.com\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">printName</span><span class=\"token operator\">:</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>myName<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>    \n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">foo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> myName <span class=\"token operator\">=</span> <span class=\"token string\">\"极客时间\"</span>\n    <span class=\"token keyword\">return</span> bar<span class=\"token punctuation\">.</span>printName\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">let</span> myName <span class=\"token operator\">=</span> <span class=\"token string\">\"极客邦\"</span>\n<span class=\"token keyword\">let</span> _printName <span class=\"token operator\">=</span> <span class=\"token function\">foo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">_printName</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nbar<span class=\"token punctuation\">.</span><span class=\"token function\">printName</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>在 printName 函数里面使用的变量 myName 是属于全局作用域下面的，所以最终打印出来的值都是“极客邦”。这是因为 JavaScript 语言的作用域链是由词法作用域决定的，而词法作用域是由代码结构来确定的。</p>\n<p>在对象内部的方法中使用对象内部的属性是一个非常普遍的需求。但是 JavaScript 的作用域机制并不支持这一点，基于这个需求，JavaScript 又搞出来另外一套 this 机制。</p>\n<p><img src=\"https://static001.geekbang.org/resource/image/b3/8d/b398610fd8060b381d33afc9b86f988d.png?wh=1142*615\" alt=\"\"></p>\n<h3>arrow function的this指向</h3>\n<p>箭头函数因为其内部 this 的值无法被改变，它与 <strong>创建</strong> 箭头函数时上下文的this指向相同</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fun</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">fun</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Window {window: Window, self: Window, document: document, name: \"\", location: Location, …}</span></code></pre></div>\n<h3>new 关键字</h3>\n<p>使用 new 关键字调用函数时，函数中的 this 指向为 JS 创建的新对象</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>name <span class=\"token operator\">=</span> <span class=\"token string\">'news'</span><span class=\"token punctuation\">;</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// func {}</span>\n<span class=\"token comment\">// func {name: 'news'}</span></code></pre></div>\n<h3>bind方法</h3>\n<p>使用 bind 方法可将函数绑定到其外部的 this</p>\n<p><strong>注意</strong></p>\n<ul>\n<li>避免使用 bind 将函数绑定到其外部的 this。使用箭头函数替代，因为这样 this 可以在函数声明就能清楚地看出来，而非在后续代码中看到。</li>\n<li>不要使用 bind 设置 this 为与父对象无关的值；这通常是出乎意料的，这也是 this 获得如此糟糕名声的原因。考虑将值作为参数传递；它更加明确，并且可以使用箭头函数</li>\n</ul>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">let</span> o <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">'bind'</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">func</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span>o<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// { name: 'bind' }</span></code></pre></div>\n<h3>apply和call方法</h3>\n<p>使用 apply 和 call 方法可将this绑定到传入函数的第一个参数。两个方法区别在于通过 apply 调用时实参是放到数组中的，而通过 call 调用时实参是逗号分隔的。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span>a<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">let</span> o <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">'bind'</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">func</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span>o<span class=\"token punctuation\">,</span><span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">func</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>o<span class=\"token punctuation\">,</span><span class=\"token punctuation\">[</span><span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// {name: \"bind\"} Arguments(5) [5, 6, 2, 3, 7, callee: ƒ, Symbol(Symbol.iterator): ƒ]</span>\n<span class=\"token comment\">// {name: \"bind\"} Arguments(5) [5, 6, 2, 3, 7, callee: ƒ, Symbol(Symbol.iterator): ƒ]</span></code></pre></div>\n<h3>函数作为对象的成员变量被调用</h3>\n<p>函数作为对象的成员变量被调用时，this的指向为调用函数的对象</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">let</span> obj <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">'obj'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">func</span><span class=\"token operator\">:</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n\nobj<span class=\"token punctuation\">.</span><span class=\"token function\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// {name: \"obj\", func: ƒ}</span></code></pre></div>\n<p>当函数没有作为<strong>方法</strong>被调用，而是被赋值给另一个变量时，则根据那个变量来判断this指向</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">let</span> obj <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">'obj'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">func</span><span class=\"token operator\">:</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">let</span> func <span class=\"token operator\">=</span> obj<span class=\"token punctuation\">.</span>func<span class=\"token punctuation\">;</span>\n<span class=\"token function\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// Window {window: Window, self: Window, document: document, name: \"\", location: Location, …}</span></code></pre></div>\n<h3>浏览器和node环境下</h3>\n<p>在浏览器和node环境下全局this的指向会有所不同</p>\n<ul>\n<li>在浏览器里，this 指向 Window。</li>\n<li>在 Node.js 里，this指向 Global</li>\n</ul>\n<h3>this 的设计缺陷以及应对方案</h3>\n<ul>\n<li>嵌套函数中的 this 不会从外层函数中继承</li>\n</ul>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">var</span> myObj <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  name <span class=\"token operator\">:</span> <span class=\"token string\">\"极客时间\"</span><span class=\"token punctuation\">,</span> \n  <span class=\"token function-variable function\">showThis</span><span class=\"token operator\">:</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">function</span> <span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span>\n    <span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\nmyObj<span class=\"token punctuation\">.</span><span class=\"token function\">showThis</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<ul>\n<li>\n<p>普通函数中的 this 默认指向全局对象。</p>\n<ul>\n<li>\n<p>不过这个设计也是一种缺陷，因为在实际工作中，我们并不希望函数执行上下文中的 this 默认指向全局对象，因为这样会打破数据的边界，造成一些误操作。如果要让函数执行上下文中的 this 指向某个对象，最好的方式是通过 call 方法来显示调用。</p>\n</li>\n<li>\n<p>这个问题可以通过设置 JavaScript 的“严格模式”来解决。在严格模式下，默认执行一个函数，其函数的执行上下文中的 this 值是 undefined，这就解决上面的问题了。</p>\n</li>\n</ul>\n</li>\n</ul>\n<!-- 关于this的指向问题，我通过查阅资料和阅读文章总结了判断this指向的几个优先级, 箭头函数 =》new关键字 =》 bind方法 =》apply和call方法 =》 函数作为对象的成员变量被调用，按照优先级来判断this，多个规则并用取优先级高的那个规则来判断 -->\n<!-- \n### 非/严格模式\n在**严格模式**下，this 可以是 undefined 或 null。而在**非严格模式**下，如果 this 指向是 undefined 或 null，那么 this 会指向全局对象。\n\n```js\n'use strict';\nlet obj = {\n    name: 'obj',\n    func: function (){\n        console.log(this)\n    }\n};\n\nlet func = obj.func;\nfunc();\n\n// undefined\n``` -->","timeToRead":3,"frontmatter":{"title":"一文搞懂this指向问题","date":"January 02, 2021","spoiler":null},"fields":{"slug":"/learn-about-the-problem-of-this-point/"}}},"pageContext":{"slug":"/learn-about-the-problem-of-this-point/","previous":{"fields":{"slug":"/classic-topic-what-happened-from-URL-input-to-page-rendering/index2/"},"frontmatter":{"title":"经典题目：从输入URL到页面呈现发生了什么"}},"next":{"fields":{"slug":"/js-implicit-conversion/"},"frontmatter":{"title":"js隐式转换"}}}},"staticQueryHashes":["3649515864","63159454"]}