1 Star 0 Fork 0

ltara / ltara

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
atom.xml 508.70 KB
一键复制 编辑 原始数据 按行查看 历史
ltara 提交于 2020-10-17 16:40 . Site updated: 2020-10-17 16:40:29
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>LTARA</title>
<subtitle>[object Object]</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://ltara.gitee.io/"/>
<updated>2020-10-17T08:38:29.637Z</updated>
<id>http://ltara.gitee.io/</id>
<author>
<name>lt</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Web缓存之浏览器缓存</title>
<link href="http://ltara.gitee.io/posts/49199.html"/>
<id>http://ltara.gitee.io/posts/49199.html</id>
<published>2020-10-11T07:19:00.000Z</published>
<updated>2020-10-17T08:38:29.637Z</updated>
<content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><blockquote><p>缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。</p></blockquote><p>当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。这样带来的好处有:<strong>缓解服务器端压力,提升性能</strong>(获取资源的耗时更短了)。对于网站来说,缓存是达到高性能的重要组成部分。缓存需要合理配置,因为并不是所有资源都是永久不变的:重要的是对一个资源的缓存应截止到其下一次发生改变(即不能缓存过期的资源)。</p><p>缓存的种类有很多,其大致可归为两类:私有与共享缓存。</p><ul><li>共享缓存存储的响应能够被多个用户使用。</li><li>私有缓存只能用于单独用户,浏览器缓存属于私有缓存。</li></ul><p>当浏览器请求一个网站的时候,会加载各种各样的资源,比如:HTML文档、图片、CSS和JS等文件。对于一些不经常变得内容,浏览器会将他们保存在本地的文件中,下次访问相同网站得时候,直接加载这些资源,加速访问。</p><blockquote><p>这些被浏览器保存的文件就被称为浏览器缓存(不是指Cookie或者Localstorage)</p></blockquote><h1 id="缓存机制"><a href="#缓存机制" class="headerlink" title="缓存机制"></a>缓存机制</h1><blockquote><p>浏览器的缓存机制也就是我们说的HTTP缓存机制,其机制是根据HTTP报文的缓存字段进行的。HTTP报文见<a href="https://ltara.gitee.io/posts/335.html">计算机网络之HTTP</a></p></blockquote><h2 id="缓存字段"><a href="#缓存字段" class="headerlink" title="缓存字段"></a>缓存字段</h2><ul><li><p>通用首部字段:</p><table><thead><tr><th>字段名称</th><th>说明</th></tr></thead><tbody><tr><td>Cache-Control</td><td>控制缓存的行为</td></tr><tr><td>Pragam</td><td>HTTP/1.0时的旧社会遗留物,值为“no-cache”时禁用缓存</td></tr></tbody></table></li><li><p>请求首部字段</p><table><thead><tr><th>字段名称</th><th>说明</th></tr></thead><tbody><tr><td>If-Match</td><td>比较ETag是否一致</td></tr><tr><td>If-None-Match</td><td>比较ETag是否不一致</td></tr><tr><td>If-Modified-Since</td><td>比较资源最后更新的时间是否一致</td></tr><tr><td>If-Unmodified-Since</td><td>比较资源最后更新的时间是否不一致</td></tr></tbody></table></li><li><p>响应首部字段</p><table><thead><tr><th>字段名称</th><th>说明</th></tr></thead><tbody><tr><td>ETag</td><td>资源的匹配信息</td></tr></tbody></table></li><li><p>实体首部字段</p><table><thead><tr><th>字段名称</th><th>说明</th></tr></thead><tbody><tr><td>Expires</td><td>HTTP/1.0的遗留物,实体主体过去时间</td></tr><tr><td>Last-Modified</td><td>资源最后一次修改的时间</td></tr></tbody></table></li></ul><h2 id="缓存过程分析"><a href="#缓存过程分析" class="headerlink" title="缓存过程分析"></a>缓存过程分析</h2><p>浏览器与服务器通信的方式为<strong>请求—应答模式</strong>,即是:浏览器发起HTTP请求 — 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存字段存入浏览器缓存中。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/meG6Vo0Mevgq38cXiaLvaxNIiatrA806UA8FuX3NsJgyJWQS4RiaBIaOmRYvSNcNcibiam2SqjtV9NXwJdHGRpljZEw/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt=""></p><p>从上图中我们可以知道:</p><ul><li><strong>浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识</strong></li><li><strong>浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中</strong></li></ul><h2 id="强缓存"><a href="#强缓存" class="headerlink" title="强缓存"></a>强缓存</h2><h3 id="简述"><a href="#简述" class="headerlink" title="简述"></a>简述</h3><blockquote><p><strong>强缓存是利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期</strong>。</p></blockquote><p>强缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强缓存的情况主要有三种:</p><ul><li>缓存结果和标识存在,并且并未失效,直接获取缓存结果</li><li>缓存结果和标识存在,但已失效,进入协商缓存</li><li>缓存结果和标识不存在,从服务器请求资源</li></ul><h3 id="Expires"><a href="#Expires" class="headerlink" title="Expires"></a>Expires</h3><p><strong>Expires是HTTP 1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回,用GMT格式的字符串表示</strong>。如:Expires:Thu, 31 Dec 2037 23:55:55 GMT,包含了Expires头标签的文件,就说明浏览器对于该文件缓存具有非常大的控制权。</p><p>一个文件的Expires值是2020年的1月1日,那么就代表,在2020年1月1日之前,浏览器都可以直接使用该文件的本地缓存文件,而不必去服务器再次请求该文件,哪怕服务器文件发生了变化。</p><p>所以,<strong>Expires是优化中最理想的情况,因为它根本不会产生请求</strong>,所以后端也就无需考虑查询快慢。它的缓存原理,如下:</p><ol><li><p>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在response的header加上Expires的header;</p></li><li><p>浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来;</p></li><li><p>浏览器再请求这个资源时,<strong>先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较</strong>,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行;</p></li><li><p>如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。</p></li></ol><p>Expires是较老的强缓存管理header,<strong>由于它是服务器返回的一个绝对时间</strong>,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,<strong>比如:随意修改下客户端时间,就能影响缓存命中的结果</strong>。</p><h3 id="Cache-Control"><a href="#Cache-Control" class="headerlink" title="Cache-Control"></a>Cache-Control</h3><p>在HTTP 1.1的时候,提出了一个新的header,<strong>就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示</strong>,如:Cache-Control:max-age=315360000,它的缓存原理是:</p><ol><li><p>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在response的header加上Cache-Control的header;</p></li><li><p>浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来;</p></li><li><p>浏览器再请求这个资源时,<strong>先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期</strong>,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行;</p></li><li><p>如果缓存没有命中,浏览器直接从服务器加载资源时,<strong>Cache-Control Header在重新加载的时候会被更新</strong>。</p></li></ol><p><strong>Cache-Control描述的是一个相对时间</strong>,在进行缓存命中的时候,<strong>都是利用客户端时间进行判断</strong>,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。</p><p>这两个header可以只启用一个,也可以同时启用,<strong>当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires</strong>。</p><p>此外,还可以为 Cache-Control 指定 <code>public</code> 或 <code>private</code> 等标记:</p><ul><li><code>public</code>:表示允许客户端和代理服务器缓存</li><li><code>private</code>:表示只能被客户端缓存,中间的代理服务器不能缓存,Cache-Control默认为<code>private</code></li><li><code>no-store</code>:表示不进行任何形式的缓存</li><li><code>no-cache</code>:表示跳过强缓存,进入协商缓存</li><li><code>s-maxage</code>:这和<code>max-age</code>差不多,区别在于<code>s-maxage</code>是针对代理服务器的缓存时间</li></ul><h2 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h2><blockquote><p><strong>协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对Header来管理的</strong>。</p></blockquote><p>当浏览器对某个资源的请求没有命中强缓存,<strong>就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified的字符串</strong>。</p><h3 id="Last-Modified,If-Modified-Since"><a href="#Last-Modified,If-Modified-Since" class="headerlink" title="Last-Modified,If-Modified-Since"></a>Last-Modified,If-Modified-Since</h3><ol><li><p>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,<strong>在response的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间</strong>;</p></li><li><p>浏览器再次跟服务器请求这个资源时,<strong>在request的header上加上If-Modified-Since的header</strong>,这个header的值就是上一次请求时返回的Last-Modified的值;</p></li><li><p>服务器再次收到资源请求时,<strong>根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化</strong>,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。<strong>当服务器返回304 Not Modified的响应时,response header中不会再添加Last-Modified的header</strong>,因为既然资源没有变化,那么Last-Modified也就不会改变;</p></li><li><p>浏览器收到304的响应后,就会从缓存中加载资源;</p></li><li><p>如果协商缓存没有命中,浏览器直接从服务器加载资源时,<strong>Last-Modified Header在重新加载的时候会被更新</strong>,下次请求时,<strong>If-Modified-Since会启用上次返回的Last-Modified值</strong>。</p></li></ol><p>【Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说,<strong>在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况</strong>,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。</p><h3 id="ETag、If-None-Match"><a href="#ETag、If-None-Match" class="headerlink" title="ETag、If-None-Match"></a>ETag、If-None-Match</h3><ol><li><p>浏览器第一次跟服务器请求一个资源,<strong>服务器在返回这个资源的同时,在response的header加上ETag的header</strong>,这个header是服务器根据当前请求的资源生成的一个唯一标识,<strong>这个唯一标识是一个字符串,只要资源有变化这个串就不同</strong>,跟最后修改时间没有关系,所以能很好的补充Last-Modified的问题</p></li><li><p>浏览器再次跟服务器请求这个资源时,<strong>在request的header上加上If-None-Match的header</strong>,这个header的值就是上一次请求时返回的ETag的值:</p></li><li><p>服务器再次收到资源请求时,<strong>根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag</strong>,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,<strong>由于ETag重新生成过,response header中还会把这个ETag返回</strong>,即使这个ETag跟之前的没有变化</p></li><li><p>浏览器收到304的响应后,就会从缓存中加载资源。</p></li></ol><p>协商缓存跟强缓存不一样,强缓存不发请求到服务器,<strong>所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器</strong>,所以资源是否更新,服务器肯定知道。大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】。</p><p>如果没有协商缓存,每个到服务器的请求,就都得返回资源内容,这样服务器的性能会极差。</p><p>【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同时启用,这是为了处理Last-Modified不可靠的情况。有一种场景需要注意:</p><blockquote><p><strong>分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败</strong>;</p><p><strong>分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样</strong>。</p></blockquote><p>协商缓存需要配合强缓存使用,<strong>因为如果不启用强缓存的话,协商缓存根本没有意义</strong>。</p><h1 id="缓存位置"><a href="#缓存位置" class="headerlink" title="缓存位置"></a>缓存位置</h1><p>前面我们已经提到,当<code>强缓存</code>命中或者协商缓存中服务器返回304的时候,我们直接从缓存中获取资源。那这些资源究竟缓存在什么位置呢?</p><p>浏览器中的缓存位置一共有四种,按优先级从高到低排列分别是:</p><ul><li>Service Worker</li><li>Memory Cache</li><li>Disk Cache</li><li>Push Cache</li></ul><h2 id="Service-Worker"><a href="#Service-Worker" class="headerlink" title="Service Worker"></a>Service Worker</h2><p>Service Worker 借鉴了 Web Worker的 思路,即让 JS 运行在主线程之外,由于它脱离了浏览器的窗体,因此无法直接访问<code>DOM</code>。虽然如此,但它仍然能帮助我们完成很多有用的功能,比如<code>离线缓存</code>、<code>消息推送</code>和<code>网络代理</code>等功能。其中的<code>离线缓存</code>就是 <strong>Service Worker Cache</strong>,使用 Service Worker的话,传输协议必须为 HTTPS。</p><h2 id="Memory-Cache-和-Disk-Cache"><a href="#Memory-Cache-和-Disk-Cache" class="headerlink" title="Memory Cache 和 Disk Cache"></a>Memory Cache 和 Disk Cache</h2><ul><li><p><strong>Memory Cache</strong></p><p>指的是内存缓存,读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。内存缓存中有一块重要的缓存资源是preloader相关指令(例如&lt;link rel=”prefetch”&gt;)下载的资源。它可以一边解析js/css文件,一边网络请求下一个资源。</p></li><li><p><strong>Disk Cache</strong></p><p>存储在硬盘中的缓存,从存取效率上讲是比内存缓存慢的,但是他的优势在于存储容量和存储时长。</p></li></ul><p>好,现在问题来了,既然两者各有优劣,那浏览器如何决定将资源放进内存还是硬盘呢?主要策略如下:</p><ul><li>比较大的JS、CSS文件会直接被丢进磁盘,反之丢进内存</li><li>内存使用率比较高的时候,文件优先进入磁盘</li></ul><h2 id="Push-Cache"><a href="#Push-Cache" class="headerlink" title="Push Cache"></a>Push Cache</h2><p>即推送缓存,这是浏览器缓存的最后一道防线。它是 <code>HTTP/2</code> 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ">HTTP 缓存</a></p><p><a href="https://juejin.im/post/6844903672556552205">前端优化:浏览器缓存技术介绍</a></p><p><a href="https://mp.weixin.qq.com/s/d2zeGhUptGUGJpB5xHQbOA">彻底理解浏览器的缓存机制</a></p><p><a href="https://juejin.im/post/6844904021308735502">(1.6w字)浏览器灵魂之问,请问你能接得住几个?</a></p><p><a href="https://segmentfault.com/a/1190000020086923">HTTP缓存和浏览器的本地存储</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当 web
</summary>
<category term="浏览器" scheme="http://ltara.gitee.io/categories/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
<category term="浏览器" scheme="http://ltara.gitee.io/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
<category term="HTTP" scheme="http://ltara.gitee.io/tags/HTTP/"/>
<category term="缓存" scheme="http://ltara.gitee.io/tags/%E7%BC%93%E5%AD%98/"/>
<category term="性能优化" scheme="http://ltara.gitee.io/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
</entry>
<entry>
<title>计算机网络之HTTPS</title>
<link href="http://ltara.gitee.io/posts/40417.html"/>
<id>http://ltara.gitee.io/posts/40417.html</id>
<published>2020-10-08T07:36:46.000Z</published>
<updated>2020-10-09T15:03:21.006Z</updated>
<content type="html"><![CDATA[<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><blockquote><p><strong>HTTPS = HTTP + SSL/TLS</strong></p></blockquote><p><code>HTTPS</code> (Secure Hypertext Transfer Protocol)<strong>安全超文本传输协议</strong>,是经过SSL/TLS加密的HTTP协议,端口号为443。其设计的主要目的是,提供对网站服务器的身份认证、保护交换数据的隐私与完整性。</p><h1 id="TLS-SSL"><a href="#TLS-SSL" class="headerlink" title="TLS/SSL"></a>TLS/SSL</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><blockquote><p><code>TLS/SSL</code>是介于TCP和HTTP之间的一层安全协议</p></blockquote><blockquote><p>用<strong>非对称加密</strong>的手段传递密钥,然后用密钥进行<strong>对称加密</strong>传递数据</p></blockquote><p><code>SSL(Secure Socket Layer)</code> 1994年由 浏览器开发商Netscape公司 率先倡导研发,为数据通讯提供安全支持,开发了最初的几个版本SSL 1.0、SSL 2.0、SSL 3.0。</p><p><code>TLS(Transport LayerSecurity)</code>前身为SSL,1999年从 3.1 开始被 IETF(Internet Engineering Task Force,Internet 工程任务组)标准化并改名,发展至今已经有 TLS 1.0、TLS 1.1、TLS 1.2 三个版本。SSL3.0和TLS1.0由于存在安全漏洞,已经很少被使用到;TLS 1.3 改动会比较大,目前还在草案阶段,目前使用最广泛的是TLS 1.1、TLS 1.2;</p><h2 id="对称加密和非对称加密"><a href="#对称加密和非对称加密" class="headerlink" title="对称加密和非对称加密"></a>对称加密和非对称加密</h2><h3 id="对称加密"><a href="#对称加密" class="headerlink" title="对称加密"></a>对称加密</h3><p>对称加密,顾名思义就是<strong>加密和解密都是使用同一个密钥</strong>,常见的对称加密算法有 DES、3DES 和 AES 等。</p><ul><li>优点:算法公开、计算量小、加密速度快、加密效率高,适合加密比较大的数据。</li><li>缺点:<ol><li>交易双方需要使用相同的密钥,也就无法避免密钥的传输,而密钥在传输过程中无法保证不被截获,因此对称加密的<strong>安全性得不到保证</strong>。</li><li>每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一密钥,这会使得发收信双方所拥有的钥匙数量急剧增长,密钥管理成为双方的负担。对称加密算法在分布式网络系统上使用较为困难,主要是因为<strong>密钥管理困难,使用成本较高</strong>。</li></ol></li></ul><p><img src="https://segmentfault.com/img/bVbCz9u/view" alt=""></p><h3 id="非对称加密"><a href="#非对称加密" class="headerlink" title="非对称加密"></a>非对称加密</h3><p>非对称加密,顾名思义就是<strong>加密和解密需要使用两个不同的密钥</strong>:<strong>公钥</strong>(public key)和<strong>私钥</strong>(private key)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密;如果用私钥对数据进行加密,那么只有用对应的公钥才能解密。</p><ul><li>优点:算法公开,加密和解密使用不同的钥匙,私钥不需要通过网络进行传输,安全性很高。</li><li>缺点:计算量比较大,加密和解密速度相比对称加密慢很多,<strong>加密的内容长度不能超过公钥的长度</strong>。</li></ul><p><img src="https://segmentfault.com/img/bVbClUi/view" alt=""></p><h2 id="握手过程"><a href="#握手过程" class="headerlink" title="握手过程"></a>握手过程</h2><h3 id="RSA算法"><a href="#RSA算法" class="headerlink" title="RSA算法"></a>RSA算法</h3><ol><li><strong>“client hello”</strong>:客户端通过发送”client hello”消息向服务器发起握手请求,该消息包含了客户端所支持的 <strong>TLS 版本</strong>和<strong>密码组合</strong>以供服务器进行选择,还有一个”<strong>client random</strong>“随机字符串。</li><li><strong>“server hello”</strong>:服务器发送”server hello”消息对客户端进行回应,该消息包含了<strong>数字证书</strong>,<strong>服务器选择的密码组合</strong>和”<strong>server random</strong>“随机字符串。</li><li><strong>证书验证</strong>:客户端对服务器发来的证书进行验证,确保对方的合法身份,验证过程可以细化为以下几个步骤:<ol><li>检查数字签名</li><li>验证证书链 (这个概念下面会进行说明)</li><li>检查证书的有效期</li><li>检查证书的撤回状态 (撤回代表证书已失效)</li></ol></li><li><strong>计算“Pre-Master Secret”</strong>:客户端向服务器发送另一个随机字符串”<strong>premaster secret (预主密钥)</strong>“,这个字符串是经过服务器的公钥加密过的,只有对应的私钥才能解密。</li><li><strong>使用私钥</strong>:服务器使用私钥解密”premaster secret”。</li><li><strong>生成共享密钥</strong>:客户端和服务器均使用 client random,server random 和 premaster secret,并通过相同的算法生成相同的共享密钥 <strong>KEY</strong>。</li><li><strong>客户端就绪:</strong>客户端发送经过共享密钥 <strong>KEY</strong>加密过的”finished”信号。</li><li><strong>服务器就绪:</strong>服务器发送经过共享密钥 <strong>KEY</strong>加密过的”finished”信号。</li><li><strong>达成安全通信:</strong>握手完成,双方使用对称加密进行安全通信。</li></ol><p><img src="https://segmentfault.com/img/bVbCCMD/view" alt=""></p><h3 id="DH算法"><a href="#DH算法" class="headerlink" title="DH算法"></a>DH算法</h3><p><a href="https://www.liaoxuefeng.com/wiki/1022910821149312/1023025778520640">廖雪峰对DH算法的简单介绍</a></p><ol><li><strong>“client hello”</strong>:客户端通过发送”client hello”消息向服务器发起握手请求,该消息包含了客户端所支持的 <strong>TLS 版本</strong>和<strong>密码组合</strong>以供服务器进行选择,还有一个”<strong>client random</strong>“随机字符串。</li><li><strong>“server hello”</strong>:服务器发送”server hello”消息对客户端进行回应,该消息包含了<strong>数字证书</strong>,<strong>服务器选择的密码组合</strong>和”<strong>server random</strong>“随机字符串。</li><li><strong>证书验证</strong>:客户端对服务器发来的证书进行验证,确保对方的合法身份,验证过程可以细化为以下几个步骤:<ol><li>检查数字签名</li><li>验证证书链 (这个概念下面会进行说明)</li><li>检查证书的有效期</li><li>检查证书的撤回状态 (撤回代表证书已失效)</li></ol></li><li><strong>服务器密钥交换消息</strong>:服务器生成一对公钥和私钥,将公钥发送给客户端,服务器密钥交换完成后,服务器将发送Server Hello Done 消息。</li><li><strong>客户端密钥交换</strong>:客户端生成一对公钥和私钥,将公钥发送给服务器。</li><li><strong>计算“Pre-Master Secret”</strong>:客户端和服务端使用DH算法生成一个相同的”<strong>premaster secret (预主密钥)</strong>“。</li><li><strong>生成共享密钥</strong>:客户端和服务器均使用 client random,server random 和 premaster secret,并通过相同的算法生成相同的共享密钥 <strong>KEY</strong>。</li><li><strong>客户端就绪:</strong>客户端发送经过共享密钥 <strong>KEY</strong>加密过的”finished”信号。</li><li><strong>服务器就绪:</strong>服务器发送经过共享密钥 <strong>KEY</strong>加密过的”finished”信号。</li><li><strong>达成安全通信:</strong>握手完成,双方使用对称加密进行安全通信。</li></ol><p>使用Diffie Hellman算法进行TLS密钥交换具有优势。客户端和服务器都为每个新会话生成一个新密钥对。一旦计算出预主密钥,将立即删除客户端和服务器使用DH算法创建的私钥。这意味着私钥永远不会被窃取,确保<a href="https://en.wikipedia.org/wiki/Perfect_forward_secrecy">完美的前向保密</a>。</p><h2 id="消息验证代码(MAC)和TLS数据完整性"><a href="#消息验证代码(MAC)和TLS数据完整性" class="headerlink" title="消息验证代码(MAC)和TLS数据完整性"></a>消息验证代码(MAC)和TLS数据完整性</h2><p>TLS为客户端和服务器分配了单独的密钥,它们都来自主密钥本身,换句话说,主密钥不直接用于加密数据,而是将单独的加密密钥用于客户端和服务器。由于双方都有两个密钥,服务器用其密钥加密的数据可以由客户端轻松解密,反之亦然。</p><p>TLS还具有用于对称密钥加密的附加安全机制。</p><p>窃听者可以对传输中的加密数据进行两种可能的攻击:尝试解密数据或尝试修改数据。只要密钥安全,我们就可以认为解密基本上是不可能的,但如果是修改数据呢?客户端和服务器是怎么知道攻击者没有修改过数据呢?如上所述,TLS不仅仅是加密数据,还可以保护数据,使其免受未检测到的修改,换句话说,TLS可以检查数据的完整性。</p><p>当服务器或客户端使用主密钥加密数据时,它还会计算明文数据的校验和(哈希值),这个校验和称为<strong>消息验证代码(MAC)</strong>。然后在发送之前将MAC包含在加密数据中。密钥用于从数据中生成MAC,以确保传输过程中攻击者无法从数据中生成相同的MAC,故而MAC被称为HMAC(哈希消息认证码)。另一方面,在接收到消息时,解密器将MAC与明文分开,然后用它的密钥计算明文的校验和,并将其与接收到的MAC进行比较,如果匹配,那我们就可以得出结论:数据在传输过程中没有被篡改。</p><p>客户端和服务器必须使用相同的散列算法来创建以及验证MAC,例如密码套件<code>TLS_ECDHE_ECDSA_WITH_AES_128_GCM_ SHA256</code>中的SHA256 是用于处理HMAC的哈希函数,为了提高安全性,客户端和服务器使用MAC密钥。让我们看看这些是什么。</p><h2 id="数字证书"><a href="#数字证书" class="headerlink" title="数字证书"></a>数字证书</h2><h3 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h3><p>在非对称加密通信过程中,服务器需要将公钥发送给客户端,在这一过程中,公钥很可能会被第三方拦截并替换,然后这个第三方就可以冒充服务器与客户端进行通信,这就是传说中的“<strong>中间人攻击</strong>”(man in the middle attack)。解决此问题的方法是通过受信任的第三方交换公钥,具体做法就是服务器不直接向客户端发送公钥,而是要求受信任的第三方,也就是证书认证机构 (Certificate Authority, 简称 CA)将公钥合并到数字证书中,然后服务器会把公钥连同证书一起发送给客户端,私钥则由服务器自己保存以确保安全。</p><h3 id="证书校验"><a href="#证书校验" class="headerlink" title="证书校验"></a>证书校验</h3><p>要证明证书的真实性,通常依赖于<strong>一组</strong>受信任的第三方<strong>证书颁发机构</strong>(Certificate authorities, <strong>CA</strong>)。验证 TLS 证书有效性的方法如下:</p><ol><li><p>检查证书是否是浏览器中<strong>受信任的根证书机构</strong>颁发</p><p>证书都是上级 CA 签发的,上级的 CA 可能还有上级,直到找到根证书。</p></li><li><p>检查证书中的<strong>证书吊销列表</strong>(CRL),看证书是否已经被吊销</p><p>证书被吊销后,会被记录在证书吊销列表中,CA 会定期发布 CRL。应用程序可以根据 CRL 来检查证书是否被吊销。</p></li><li><p>通过<strong>在线证书状态协议</strong>(OCSP)检查证书是否有效</p><p>CA 会提供实时的查询接口,用来查询证书的有效性。在线实时查询会使得 TLS 握手时间延长,因为浏览器需要等待查询结束才能继续 TLS 握手。至于使用<strong>在线查询</strong>还是证书中的<strong>吊销列表</strong>,由浏览器决定。</p></li><li><p>检查证书是否过期</p></li><li><p>检查域名和证书中的域名是否一致</p></li><li><p>查询网站是否被列入了欺诈网站黑名单</p><p>这一步 IE7 会进行,IE7 会到<strong>欺诈网站数据库</strong>,查询网站是否被列入了欺诈黑名单。</p></li></ol><p>经过了以上步骤,浏览器中才会显示安全锁的标志。任意一个步骤出问题,浏览器都无法建立安全链接,并最终提示“您的链接不是私密链接”。</p><h1 id="整个HTTPS的传输过程"><a href="#整个HTTPS的传输过程" class="headerlink" title="整个HTTPS的传输过程"></a>整个HTTPS的传输过程</h1><p><img src="https://segmentfault.com/img/bVbClUl/view" alt=""></p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://www.cnblogs.com/yungyu16/p/13329297.html">SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法</a></p><p><a href="https://www.cnblogs.com/yungyu16/p/13329305.html">SSL/TLS协议详解(中)——证书颁发机构</a></p><p><a href="https://www.cnblogs.com/yungyu16/p/13329311.html">SSL/TLS协议详解(下)——TLS握手协议</a></p><p><a href="https://segmentfault.com/a/1190000021494676">HTTPS 详解一:附带最精美详尽的 HTTPS 原理图</a></p><p><a href="https://segmentfault.com/a/1190000021559557">HTTPS 详解二:SSL / TLS 工作原理和详细握手过程</a></p><p><a href="http://blog.damonare.cn/2017/12/29/SSL协议之数据加密过程详解/#more">SSL协议之数据加密过程详解</a></p><p><a href="https://www.cnblogs.com/xiaxveliang/p/13183175.html">HTTPS协议详解</a></p><p><a href="https://liuyib.github.io/2020/04/18/deep-study-ssl-tls/">深入探究 SSL/TLS 协议</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;概念&quot;&gt;&lt;a href=&quot;#概念&quot; class=&quot;headerlink&quot; title=&quot;概念&quot;&gt;&lt;/a&gt;概念&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;HTTPS = HTTP + SSL/TLS&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
<category term="计算机网络" scheme="http://ltara.gitee.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
<category term="HTTPS" scheme="http://ltara.gitee.io/tags/HTTPS/"/>
<category term="SSL/TLS" scheme="http://ltara.gitee.io/tags/SSL-TLS/"/>
</entry>
<entry>
<title>计算机网络之HTTP</title>
<link href="http://ltara.gitee.io/posts/335.html"/>
<id>http://ltara.gitee.io/posts/335.html</id>
<published>2020-10-05T07:33:27.000Z</published>
<updated>2020-10-06T14:13:14.341Z</updated>
<content type="html"><![CDATA[<h1 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><blockquote><p>HTTP协议是基于TCP/IP协议之上的应用层协议</p></blockquote><p><strong>超文本传输协议</strong>(英文:<strong>H</strong>yper<strong>T</strong>ext <strong>T</strong>ransfer <strong>P</strong>rotocol,缩写:HTTP)是一种用于<strong>分布式、协作式</strong>和<strong>超媒体信息系统</strong>的<strong>应用层协议</strong>。<code>HTTP是万维网的数据通信的基础</code>。</p><p>HTTP的发展是由蒂姆·伯纳斯-李于1989年在欧洲核子研究组织(CERN)所发起。HTTP的标准制定由万维网协会(World Wide Web Consortium,W3C)和互联网工程任务组(Internet Engineering Task Force,IETF)进行协调,最终发布了一系列的RFC,其中最著名的是1999年6月公布的 RFC 2616,定义了HTTP协议中现今广泛使用的一个版本——HTTP 1.1。</p><p>2014年12月,互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis(httpbis)工作小组将HTTP/2标准提议递交至IESG进行讨论,于2015年2月17日被批准。 HTTP/2标准于2015年5月以RFC 7540正式发表,取代HTTP 1.1成为HTTP的实现标准。</p><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>HTTP是一个<strong>客户端</strong>终端(用户)和<strong>服务器</strong>端(网站)<strong>请求</strong>和<strong>应答</strong>的标准(TCP)。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。</p><p>尽管TCP/IP协议是互联网上最流行的应用,HTTP协议中,并没有规定必须使用它或它支持的层。事实上,HTTP可以在任何互联网协议上,或其他网络上实现。HTTP假定其下层协议提供可靠的传输。因此,任何能够提供这种保证的协议都可以被其使用。因此也就是其在TCP/IP协议族使用TCP作为其传输层。</p><p>通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如”HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误消息、或者其它信息。</p><h2 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h2><p>HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。</p><h3 id="请求响应步骤"><a href="#请求响应步骤" class="headerlink" title="请求响应步骤"></a>请求响应步骤</h3><ol><li><strong>客户端连接到Web服务器</strong><br>一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个<strong>TCP套接字连接</strong>。</li><li><strong>发送HTTP请求</strong><br>通过TCP套接字,客户端向Web服务器<strong>发送一个文本的请求报文</strong>,一个请求报文由<strong>请求行、请求头部、空行和请求数据</strong>4部分组成。</li><li><strong>服务器接受请求并返回HTTP响应</strong><br>Web服务器<strong>解析请求,定位请求资源</strong>。服务器将资源复本写到TCP套接字,由客户端读取。<strong>一个响应由状态行、响应头部、空行和响应数据</strong>4部分组成。</li><li><strong>释放TCP连接</strong><br>若<strong>connection 模式为close</strong>,则<strong>服务器主动关闭TCP连接</strong>,<strong>客户端被动关闭连接</strong>,释放TCP连接;若 <code>connection 模式为keepalive</code>,则<strong>该连接会保持一段时间</strong>,在该时间内可以继续接收请求。</li><li><strong>客户端浏览器解析HTML内容</strong><br>客户端浏览器<strong>首先解析状态行</strong>,查看表明请求<strong>是否成功的状态代码</strong>。然后<strong>解析每一个响应头</strong>,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。</li></ol><h2 id="报文结构"><a href="#报文结构" class="headerlink" title="报文结构"></a>报文结构</h2><h3 id="请求报文"><a href="#请求报文" class="headerlink" title="请求报文"></a>请求报文</h3><blockquote><p>起始行 + 请求头 + 空行 + 请求体</p></blockquote><ul><li><strong>请求行</strong>:请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。</li><li><strong>请求头</strong>:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息。</li><li><strong>空行</strong>:最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。</li><li><strong>请求体</strong>:浏览器端通过http协议发送给服务器的实体数据。</li></ul><h3 id="响应报文"><a href="#响应报文" class="headerlink" title="响应报文"></a>响应报文</h3><blockquote><p>状态行 + 响应头 + 响应体</p></blockquote><ul><li><strong>状态行</strong>:格式:HTTP-Version Status-Code Reason-Phrase CRLF,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。</li><li><strong>响应头</strong>:响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。</li><li><strong>响应体</strong>:响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码。</li></ul><h2 id="URI"><a href="#URI" class="headerlink" title="URI"></a>URI</h2><p><strong>URI</strong>, 全称为(Uniform Resource Identifier), 也就是<strong>统一资源标识符</strong>,它的作用很简单,就是区分互联网上不同的资源。</p><p>但是,它并不是我们常说的<code>网址</code>,网址指的是<code>URL</code>,实际上<code>URI</code>包含了<code>URN</code>和<code>URL</code>两个部分,由于 URL 过于普及,就默认将 URI 视为 URL 了。</p><p>超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:</p><ul><li>传送协议。</li><li>层级URL标记符号(为[//],固定不变)</li><li>访问资源需要的凭证信息(可省略)</li><li>服务器。(通常为域名,有时为IP地址)</li><li>端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)</li><li>路径。(以“/”字符区别路径中的每一个目录名称)</li><li>查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&amp;”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)</li><li>片段。以“#”字符为起点</li></ul><p>以<a href="http://www.luffycity.com:80/news/index.html?id=250&amp;page=1">http://www.luffycity.com:80/news/index.html?id=250&amp;page=1</a> 为例, 其中:</p><p><code>http,是协议</code></p><p><code>www.luffycity.com,是服务器</code></p><p><code>80,是服务器上的默认网络端口号,默认不显示</code></p><p><code>/news/index.html,是路径</code></p><p><code>?id=250&amp;page=1,是查询</code></p><p>大多数网页浏览器不要求用户输入网页中“http://”的部分,因为绝大多数网页内容是超文本传输协议文件。同样,“80”是超文本传输协议文件的常用端口号,因此一般也不必写明。一般来说用户只要键入统一资源定位符的一部分(<a href="http://www.luffycity.com/news/index.html?id=250&amp;page=1">www.luffycity.com/news/index.html?id=250&amp;page=1</a> )就可以了。</p><p>由于超文本传输协议允许服务器将浏览器重定向到另一个网页地址,因此许多服务器允许用户省略网页地址中的部分,比如 www。从技术上来说这样省略后的网页地址实际上是一个不同的网页地址,浏览器本身无法决定这个新地址是否通,服务器必须完成重定向的任务。</p><h2 id="HTTP的特点和缺点"><a href="#HTTP的特点和缺点" class="headerlink" title="HTTP的特点和缺点"></a>HTTP的特点和缺点</h2><h3 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h3><ul><li><strong>灵活可扩展</strong>。主要体现在两方面:一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分割字段,其他各个部分没有严格的语法限制;另一个是传输形式的多样性,不仅可以传输文本,还可以传输图片、视频等任意数据。</li><li><strong>可靠传输</strong>。HTTP基于TCP/IP,因此把这一特性继承下来了。</li><li><strong>请求-应答</strong>。客户端请求-服务器端应答或者服务器端请求-服务器端应答都行。</li><li><strong>无状态</strong>。这里的状态指的是<strong>通信过程的上下文信息</strong>,而每次的HTTP请求都是独立、无关的,默认不需要保留状态信息。</li></ul><h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><ul><li><strong>无状态</strong>:这一点是优点还是缺点需要看场景:在长连接的场景中,需要保存大量的上下文信息,一面传输大量重复的信息,那么这时候无状态就是缺点了;但与此同时,另外一些应用只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了HTTP的优点。</li><li><strong>明文传输</strong>:协议里的报文(主要指的是头部)不适用二进制数据,而是采用文本形式。这当然为调试者提供了方便,但同时也让HTTP的报文暴露给外界,给攻击者提供了便利。</li><li><strong>对头阻塞问题</strong>:当HTTP开启长连接时,共用一个TCP连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其他请求只能处于阻塞状态,也就时著名的对头阻塞问题。</li></ul><h2 id="常见的请求方法"><a href="#常见的请求方法" class="headerlink" title="常见的请求方法"></a>常见的请求方法</h2><ul><li>GET:通常用来获取资源</li><li>POST:提交数据</li><li>HEAD:获取资源的元信息</li><li>PUT:修改数据</li><li>DELETE:删除资源</li><li>CONNECT:建立连接隧道,用于代理服务器</li><li>OPTIONS:列出可对资源实行的请求方法,用来跨域请求</li><li>TRACE:追踪请求-响应的传输路径</li></ul><h3 id="GET和POST的区别"><a href="#GET和POST的区别" class="headerlink" title="GET和POST的区别"></a>GET和POST的区别</h3><ul><li>GET请求会被浏览器主动缓存下来,留下历史记录,而POST不会</li><li>GET请求只能进行URL编码,只能接受ASCII编码,POST没有限制</li><li>GET请求的参数一般发在URL中,因此不安全,POST放在请求体中,更适合传输敏感信息</li><li>GET请求是<code>幂等</code>的,而POST不是</li><li>GET请求的参数是有长度限制的,而POST没有</li><li>GET产生一个数据包,将请求报文一次性发送出去;POST产生两个数据包,首先发送header部分,如果服务器响应100 continue,浏览器再发送body部分(火狐浏览器除外,它只发送一次)</li></ul><h3 id="Content-Type与POST请求方法提交数据的关系"><a href="#Content-Type与POST请求方法提交数据的关系" class="headerlink" title="Content-Type与POST请求方法提交数据的关系"></a>Content-Type与POST请求方法提交数据的关系</h3><table><thead><tr><th align="left">Content-Type</th><th align="left">提交的数据类型</th></tr></thead><tbody><tr><td align="left">application/x-www/form-urlencoded</td><td align="left">表单数据</td></tr><tr><td align="left">multipart/form-data</td><td align="left">表单文件上传</td></tr><tr><td align="left">Application/json</td><td align="left">序列化JSON格式数据</td></tr><tr><td align="left">Text/xml</td><td align="left">XML数据</td></tr></tbody></table><h2 id="常见的请求头"><a href="#常见的请求头" class="headerlink" title="常见的请求头"></a>常见的请求头</h2><h3 id="Accept"><a href="#Accept" class="headerlink" title="Accept"></a>Accept</h3><ul><li>Accept:指定客户端能够接受的数据格式,例如text/plain</li><li>Accept-CharSet:浏览器可以接受的字符编码集,例如chartset=utf-8</li><li>Accept-Encoding:浏览器可以接受web服务器返回内容的压缩编码类型,例如g-zip</li><li>Accept-Language:浏览器可以接受的语言,例如en,zh</li></ul><h3 id="缓存相关"><a href="#缓存相关" class="headerlink" title="缓存相关"></a>缓存相关</h3><ul><li>Date:请求发送的日期和时间,例如Thu Oct 01 2020 22:16:55 GMT+0800</li><li>Cache-Control:指定的请求和响应遵循的缓存机制,例如max-age=3600</li><li>If-Match:只有请求内容与实体匹配才有效</li><li>If-Modified-Since:如果请求的部分在指定的时间后被修改则请求成功,未被修改则会返回304状态码</li><li>If-None-Match:如果内容未改变返回304状态码,参数未服务器先前发送的Etag,与服务器回应的Etag比较是否改变</li><li>If-Range:如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag</li><li>If-unModified-Since:只有实体在指定时间后未被修改才请求成功</li></ul><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><ul><li>Connection:表示是否需要持久连接(HTTP/1.1默认进行持久连接)例如close</li><li>Cookie:HTTP发送请求时,会把保存在该域名下的所有cookie值一起发送到web服务器</li><li>Content-Length:请求的内容长度,例如120</li><li>Contetn-Type:请求的与实体对应的MIME信息,例如application/x-www-form-urlencoded</li><li>Host:请求的服务器的域名和端口号,例如<a href="http://www.abc.com:80">www.abc.com:80</a></li><li>Pragma:用来包含实现特定的指令</li><li>Proxy-Authorization:连接到代理的授权证书</li><li>Range:只请求实体的一部分,指定范围,例如bytes=500-999</li><li>Referer:先前网页的地址(如果是直接在url地址栏则没有这个字段)</li><li>TE:客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息</li><li>Upgrade:向服务器指定某种传输协议以便服务器进行转换(如果支持)</li><li>User-Agent:发出请求的用户信息</li><li>Via:通知中间网关或代理服务器地址,通信协议</li><li>Warning:关于消息实体的警告信息</li></ul><h2 id="常见的响应头"><a href="#常见的响应头" class="headerlink" title="常见的响应头"></a>常见的响应头</h2><h3 id="Content"><a href="#Content" class="headerlink" title="Content"></a>Content</h3><ul><li>Content-Encoding:web服务器支持的返回内容压缩编码类型,例如gzip</li><li>Content-Language:响应体的语言,例如en,zh</li><li>Content-Length:响应体的长度,例如348</li><li>Content-Location:请求资源可替代的备用的另一地址,例如/index.htm</li><li>Content-MD5:返回资源的MD5校验值,例如Q2hlY2sgSW50ZWdyaXR5IQ==</li><li>Content-Range:在整个返回体中本部分的子节位置,例如bytes 21010-47021/47022</li><li>Content-Type:返回内容的MIME类型,例如text/html,charse=utf8</li></ul><h3 id="缓存相关-1"><a href="#缓存相关-1" class="headerlink" title="缓存相关"></a>缓存相关</h3><ul><li>Cache-Control:告诉所有的缓存机制是否可以缓存及哪种类型</li><li>Date:原始服务器消息发出的时间</li><li>Etag:请求变量的实体标签的当前值</li><li>Expires:响应过期的日期和时间</li><li>Last-Modified:请求资源的最后修改时间</li></ul><h3 id="其他-1"><a href="#其他-1" class="headerlink" title="其他"></a>其他</h3><ul><li><p>Accept-Ranges:表明服务器是否支持指定范围请求及哪种类型的分段请求</p></li><li><p>Allow:对某网络资源的有效的请求行为,不允许则返回405</p></li><li><p>Age:从原始服务器到代理缓存形成的估算时间(以秒计,非负)</p></li><li><p>Location:用来重定向接收方到非请求URL的位置来完成请求或标识新的资源</p></li><li><p>Pragma:包括实现特定的指令,它可应用到响应链上的任何接收方</p></li><li><p>Proxy-Authenticate:它指出认证方案和可应用到代理的该URL上的参数</p></li><li><p>refresh:应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)</p></li><li><p>Retry-After:如果实体暂时不可取,通知客户端在指定时间之后再次尝试</p></li><li><p>Server:web服务器软件名称</p></li><li><p>Set-Cookie:设置Http Cookie</p></li><li><p>Trailer:指出头域在分块传输编码的尾部存在</p></li><li><p>Transfer-Encoding:文件传输编码</p></li><li><p>Vary:告诉下游代理是使用缓存响应还是从原始服务器请求</p></li><li><p>Warning:警告实体可能存在的问题</p></li><li><p>WWW-Authenticate:表明客户端请求实体应该使用的授权方案</p></li></ul><h2 id="状态码"><a href="#状态码" class="headerlink" title="状态码"></a>状态码</h2><h3 id="1xx:表示目前是协议的中间状态"><a href="#1xx:表示目前是协议的中间状态" class="headerlink" title="1xx:表示目前是协议的中间状态"></a>1xx:表示目前是协议的中间状态</h3><ul><li><strong>101 Switching Protocols</strong> 在<code>HTTP</code>升级为<code>WebSocket</code>的时候,如果服务器同意变更,就会发送状态码 101</li></ul><h3 id="2xx:表示成功状态"><a href="#2xx:表示成功状态" class="headerlink" title="2xx:表示成功状态"></a>2xx:表示成功状态</h3><ul><li><strong>200 OK</strong> 成功状态码,通常在响应体中有数据</li><li><strong>204 No Content</strong> 含义与200相同,但响应头后没有body数据</li><li><strong>206 Partial Content</strong> 成功状态码,响应体中有部分内容,使用场景为HTTP分块下载和断点续传</li></ul><h3 id="3xx:表示重定向"><a href="#3xx:表示重定向" class="headerlink" title="3xx:表示重定向"></a>3xx:表示重定向</h3><ul><li><strong>301 Moved Permanently</strong> 永久重定向,假如一个网站迁移到另一个地址了,以前的再也不用了,可以返回301,当下次访问的时候会直接访问重定向的那个地址。</li><li><strong>302 Found</strong> 临时重定向,如果一个网站只是暂时不用了,那么返回302就行了</li><li><strong>304 Not Modified</strong> 请求的资源未修改,当进行了协商缓存时会返回这个状态码</li><li><strong>307 Temporary Redirect</strong> 临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求</li></ul><h3 id="4xx:表示请求错误"><a href="#4xx:表示请求错误" class="headerlink" title="4xx:表示请求错误"></a>4xx:表示请求错误</h3><ul><li><p><strong>400 Bad Request</strong> 报文存在语法错误</p></li><li><p><strong>401 Unauthorized</strong> 发送的请求需要有通过 HTTP 认证的认证信息</p></li><li><p><strong>403 Forbidden</strong> 服务器禁止访问</p></li><li><p><strong>404 Not Found</strong> 资源未找到</p></li><li><p><strong>405 Method Not Allowed</strong> 请求方法不被服务器允许</p></li><li><p><strong>408 Request Timeout</strong> 服务器等待太长时间</p></li><li><p><strong>409 Conflict</strong> 多个请求发生冲突</p></li><li><p><strong>429 Too Many Request</strong> 客户端发送的请求过多</p></li></ul><h3 id="5xx:服务器端发生错误"><a href="#5xx:服务器端发生错误" class="headerlink" title="5xx:服务器端发生错误"></a>5xx:服务器端发生错误</h3><ul><li><p><strong>500 Internal Sever Error</strong> 服务器端在执行请求时发生了错误</p></li><li><p><strong>502 Bad Geteway</strong> 服务器自身正常,但访问的时候出错了</p></li><li><p><strong>503 Service Unavailable</strong> 服务器超载或维护,暂时无法响应服务</p></li></ul><h2 id="HTTP版本介绍"><a href="#HTTP版本介绍" class="headerlink" title="HTTP版本介绍"></a>HTTP版本介绍</h2><h3 id="HTTP-1-0"><a href="#HTTP-1-0" class="headerlink" title="HTTP/1.0"></a>HTTP/1.0</h3><p>HTTP 协议老的标准是HTTP/1.0,为了提高系统的效率,HTTP/1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。</p><h3 id="HTTP-1-1"><a href="#HTTP-1-1" class="headerlink" title="HTTP/1.1"></a>HTTP/1.1</h3><h4 id="缓存处理"><a href="#缓存处理" class="headerlink" title="缓存处理"></a>缓存处理</h4><p>在HTTP/1.0中主要使用header里的<code>If-Modified-Since</code>,<code>Expires</code>来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如<code>Etag</code>,<code>If-Unmodified-Since</code>, <code>If-Match</code>, <code>If-None-Match</code>等更多可供选择的缓存头来控制缓存策略。</p><h4 id="带宽优化及网络连接的使用"><a href="#带宽优化及网络连接的使用" class="headerlink" title="带宽优化及网络连接的使用"></a>带宽优化及网络连接的使用</h4><p>HTTP/1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP/1.1则在请求头引入了<code>Range</code>头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。</p><h4 id="错误通知的管理"><a href="#错误通知的管理" class="headerlink" title="错误通知的管理"></a>错误通知的管理</h4><p>在HTTP/1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。</p><h4 id="Host头处理"><a href="#Host头处理" class="headerlink" title="Host头处理"></a>Host头处理</h4><p>在HTTP/1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。</p><h4 id="长连接"><a href="#长连接" class="headerlink" title="长连接"></a>长连接</h4><p>HTTP/1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP/1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP/1.0每次请求都要创建连接的缺点。</p><h3 id="HTTP-2"><a href="#HTTP-2" class="headerlink" title="HTTP/2"></a>HTTP/2</h3><h4 id="头部压缩"><a href="#头部压缩" class="headerlink" title="头部压缩"></a>头部压缩</h4><ul><li>首先在服务器和客户端之间建立哈希表,将用到的字段存放在这张表中,那么在传输的时候对于之前出现过的值,只需要把<strong>索引</strong>传递给对方即可,对方拿到<strong>索引</strong>查表就行了。</li><li>其次对于整数和字符串进行<strong>哈夫曼编码</strong>,哈夫曼编码的原理就是先将所有出现的字符建立一个索引表,然后让出现次数多的字符对应的索引尽可能短,传输地时候也是传输这样地索引序列,可以达到非常高的压缩率。</li></ul><h4 id="多路复用"><a href="#多路复用" class="headerlink" title="多路复用"></a>多路复用</h4><ul><li><strong>HTTP队头阻塞</strong></li></ul><p>HTTP传输是基于<code>请求-应答</code>的模式进行的,报文必须是一发一收,但值得注意的是,里面的任务被放在一个任务队列中串行执行,一旦队首的请求处理太慢,就会阻塞后面的请求处理。</p><ul><li><strong>二进制分帧</strong></li></ul><p>HTTP/2会将报文全部转换成二进制格式,全部传输<code>01</code>串,方便及其解析。原来的<code>Headers+Body</code>的报文格式被拆分成了一个个二进制的帧,用Headers帧存放头部字段,Data帧存放请求体数据。分帧之后,服务器看到的不再是一个个完整的HTTP请求报文,而是一堆乱序的二进制帧。这些二进制帧不存在先后关系,因此也不会排队等待,也就没有了HTTP队头阻塞问题。</p><p>通信双方都可以给对方发送二进制帧,这种二进制帧的<strong>双向传输的序列</strong>,也叫做<code>流</code>。HTTP/2用<code>流</code>来在一个TCP连接上进行多个数据帧的通信,这就是多路复用的概念。</p><h4 id="服务端推送"><a href="#服务端推送" class="headerlink" title="服务端推送"></a>服务端推送</h4><p>在HTTP/2中,服务器已经不再是完全被动地接受请求,响应请求,它也能新建<code>流</code>来给客户端发送消息,当TCP连接建立后,比如浏览器请求一个HTML文件,服务器就可以在返回HTML地基础上,将HTML中引用到的其他资源文件一起返回给客户端,减少客户端地等待。</p><h3 id="HTTP-3"><a href="#HTTP-3" class="headerlink" title="HTTP/3"></a>HTTP/3</h3><p>HTTP/2使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。Google 基于 UDP 协议推出了一个的 QUIC 协议,并且使用在了 HTTP/3 上。</p><ul><li><p><strong>避免包阻塞</strong> :多个流的数据包在TCP连接上传输时,若一个流中的数据包传输出现问题,TCP需要等待该包重传后,才能继续传输其它流的数据包。但在基于UDP的QUIC协议中,不同的流之间的数据传输真正实现了相互独立互不干扰,某个流的数据包在出问题需要重传时,并不会对其他流的数据包传输产生影响。</p></li><li><p><strong>快速重启会话</strong>: 普通基于TCP的连接,是基于两端的IP和端口和协议来建立的。在网络切换场景,例如手机端切换了无线网,使用4G网络,会改变本身的ip,这就导致tcp连接必须重新创建。而QUIC协议使用特有的UUID来标记每一次连接,在网络环境发生变化的时候,只要UUID不变,就能不需要握手,继续传输数据。</p></li></ul><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://www.cnblogs.com/lopo/p/13632226.html">HTTP请求中的请求头和响应头详解</a></p><p><a href="https://juejin.im/post/6844904100035821575">(建议精读)HTTP灵魂之问,巩固你的 HTTP 知识体系</a></p><p><a href="https://www.cnblogs.com/spoem/archive/2004/01/13/12823055.html">HTTP协议(一)</a></p><p><a href="https://www.cnblogs.com/spoem/archive/2004/01/13/12827590.html">HTTP协议(二)</a></p><p><a href="https://www.cnblogs.com/fengting0913/p/13291321.html">HTTP协议详解</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;HTTP&quot;&gt;&lt;a href=&quot;#HTTP&quot; class=&quot;headerlink&quot; title=&quot;HTTP&quot;&gt;&lt;/a&gt;HTTP&lt;/h1&gt;&lt;h2 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h
</summary>
<category term="计算机网络" scheme="http://ltara.gitee.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
<category term="HTTP" scheme="http://ltara.gitee.io/tags/HTTP/"/>
</entry>
<entry>
<title>JavaScript事件循环机制</title>
<link href="http://ltara.gitee.io/posts/54318.html"/>
<id>http://ltara.gitee.io/posts/54318.html</id>
<published>2020-10-03T03:05:30.000Z</published>
<updated>2020-10-03T15:14:20.996Z</updated>
<content type="html"><![CDATA[<h1 id="事件循环(Event-Loop)"><a href="#事件循环(Event-Loop)" class="headerlink" title="事件循环(Event Loop)"></a>事件循环(Event Loop)</h1><blockquote><p>事件循环是JavaScript的执行机制 。</p></blockquote><h2 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h2><h3 id="js是单线程语言"><a href="#js是单线程语言" class="headerlink" title="js是单线程语言"></a>js是单线程语言</h3><ul><li><p>什么是单线程?</p><p>主程序只有一个线程,即同一时间片段内其只能执行单个任务。</p></li><li><p>单线程意味着什么?</p><p>单线程就意味着,所有任务都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就需要一直等着。这就会导致IO操作(耗时但cpu闲置)时造成性能浪费的问题。</p></li><li><p>如何解决单线程带来的性能问题?</p><p>答案是异步!主线程完全可以不管IO操作,暂时挂起处于等待中的任务,先运行排在后面的任务。等到IO操作返回了结果,再回过头,把挂起的任务继续执行下去。于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。</p></li></ul><h3 id="同步任务和异步任务"><a href="#同步任务和异步任务" class="headerlink" title="同步任务和异步任务"></a>同步任务和异步任务</h3><ul><li>同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。</li><li>异步任务指的是,不进入主线程、而进入”<strong>任务队列</strong>“(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。</li></ul><h3 id="任务队列(Event-Queue)"><a href="#任务队列(Event-Queue)" class="headerlink" title="任务队列(Event Queue)"></a>任务队列(Event Queue)</h3><ul><li><strong>宏任务(macrotask)</strong>:<ul><li><code>setTimeout</code></li><li><code>setInterval</code></li><li><code>setImmediate</code> (Node独有)</li><li><code>requestAnimationFrame</code> (浏览器独有)</li><li>I/O</li><li>UI rendering (浏览器独有)</li></ul></li><li><strong>微任务(microtask)</strong><ul><li><code>process.nextTick</code> (Node独有)</li><li>Promise</li><li><code>Object.observe</code></li><li><code>MutationObserver</code></li></ul></li></ul><h4 id="setTimeout-fn-0"><a href="#setTimeout-fn-0" class="headerlink" title="setTimeout(fn,0)"></a>setTimeout(fn,0)</h4><p>指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。 (关于<code>setTimeout</code>要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。)</p><h4 id="setInterval-fn-0"><a href="#setInterval-fn-0" class="headerlink" title="setInterval(fn,0)"></a>setInterval(fn,0)</h4><p>会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。每过ms秒,会有fn进入Event Queue。一旦<code>setInterval</code>的 回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了 。</p><h4 id="requestAnimationFrame"><a href="#requestAnimationFrame" class="headerlink" title="requestAnimationFrame"></a>requestAnimationFrame</h4><p>请求动画帧,是一个宏任务,html5 提供的一个专门用于请求动画的API,相比起<code>setTimeout</code>由系统决定回调函数的执行时机。60Hz的刷新频率,那么每次刷新的间隔中会执行一次回调函数,不会引起丢帧,不会卡顿。</p><h4 id="Promise与process-nextTick-callback"><a href="#Promise与process-nextTick-callback" class="headerlink" title="Promise与process.nextTick(callback)"></a>Promise与process.nextTick(callback)</h4><p><code>process.nextTick(callback)</code>:类似node.js版的”<code>setTimeout</code>”,在事件循环的下一次循环中调用 callback 回调函数。</p><p>不同类型的任务会进入对应的Event Queue,比如<code>setTimeout</code>和<code>setInterval</code>会进入相同的Event Queue。</p><h3 id="Event-Loop是谁规定的?"><a href="#Event-Loop是谁规定的?" class="headerlink" title="Event Loop是谁规定的?"></a>Event Loop是谁规定的?</h3><ul><li><strong>Engine(执行引擎):</strong> 如V8 Engine,V8 实现并提供了 ECMAScript 标准中的所有数据类型、操作符、对象和方法(注意并没有 DOM)。 <strong>Event Loop</strong> 是属于 <strong>JavaScript Runtime</strong> 的,是由宿主环境提供的(比如浏览器,node)。</li><li><strong>Runtime(执行环境):</strong> Chrome 提供了 window、DOM,而 <a href="https://nodejs.org/">Node.js</a> 则是 require、process 等等。</li></ul><p>我们知道了<strong>Event Loop</strong>在不同的宿主环境中的表现可能不同:</p><ul><li>浏览器的Event Loop是在<a href="https://www.w3.org/TR/html5/webappapis.html#event-loops">html5的规范</a>中明确定义。</li><li>NodeJS的Event Loop是基于libuv实现的。可以参考Node的<a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/">官方文档</a>以及libuv的<a href="http://docs.libuv.org/en/v1.x/design.html">官方文档</a>。</li><li>libuv已经对Event Loop做出了实现,而HTML5规范中只是定义了浏览器中Event Loop的模型,具体的实现留给了浏览器厂商。</li></ul><p>本文只讨论浏览器和Node中事件循环。</p><h2 id="JavaScript执行机制"><a href="#JavaScript执行机制" class="headerlink" title="JavaScript执行机制"></a>JavaScript执行机制</h2><blockquote><ul><li>同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入Event Table并注册函数。</li><li>当指定的事情完成时,Event Table会将这个函数移入Event Queue。</li><li>主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。</li><li>上述过程会不断重复,也就是常说的Event Loop(事件循环)。</li></ul></blockquote><p>怎么知道主线程执行栈为空?</p><p>js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。</p><h2 id="浏览器事件循环"><a href="#浏览器事件循环" class="headerlink" title="浏览器事件循环"></a>浏览器事件循环</h2><p>看一张图,理解了这张图就理解了浏览器的事件循环。<a href="https://segmentfault.com/img/remote/1460000016278118">如果看不到图,看这里</a></p><p><img src="https://segmentfault.com/img/remote/1460000016278118/view" alt=""></p><h3 id="执行规则"><a href="#执行规则" class="headerlink" title="执行规则"></a>执行规则</h3><ul><li><strong>首先在执行栈(call stack)中的内容执行完毕清空后,会在事件队列(Event queue)检查一下哪些是宏任务哪些是微任务,然后执行所有的微任务,然后执行一个宏任务,之后再次执行所有的微任务。也就是说在主线程(main thread)任务执行完毕后会把任务队列中的微任务全部执行,然后再执行一个宏任务,这个宏任务执行完再次检查队列内部的微任务,有就全部执行没有就再执行一个宏任务</strong>。</li><li><strong>JS是单线程但是浏览器是多线程</strong>。你的异步任务是浏览器开启对应的线程来执行的,最后放入JS引擎中进行执行。</li><li>所以在执行定时器、事件、ajax这些异步事件的时候是另外三个线程在执行代码,并不是JS引擎在做事情,在这些线程达到某一特定事件把任务放入JS引擎的线程中,同时GUI线程(渲染界面HTML的线程)与JS线程是互斥的,在JS引擎执行时GUI线程会被冻结、挂起。</li><li><strong>在执行微任务队列microtask queue中任务的时候,如果又产生了microtask,那么会继续添加到队列的末尾,也会在这个周期执行,直到microtask queue为空停止</strong>。</li></ul><h3 id="浏览器主线程常驻线程"><a href="#浏览器主线程常驻线程" class="headerlink" title="浏览器主线程常驻线程"></a>浏览器主线程常驻线程</h3><ul><li>GUI 渲染线程<ul><li>绘制页面,解析 HTML、CSS,构建 DOM 树,布局和绘制等</li><li>页面重绘和回流</li><li>与 JS 引擎线程互斥,也就是所谓的 JS 执行阻塞页面更新</li></ul></li><li>JS 引擎线程<ul><li>负责 JS 脚本代码的执行</li><li>负责执行准备好待执行的事件,即定时器计数结束,或异步请求成功并正确返回的事件</li><li>与 GUI 渲染线程互斥,执行时间过长将阻塞页面的渲染</li></ul></li><li>事件触发线程<ul><li>负责将准备好的事件交给 JS 引擎线程执行</li><li>多个事件加入任务队列的时候需要排队等待(JS 的单线程)</li></ul></li><li>定时器触发线程<ul><li>负责执行异步的定时器类的事件,如 <code>setTimeout</code>、<code>setInterval</code></li><li>定时器到时间之后把注册的回调加到任务队列的队尾</li></ul></li><li>HTTP 请求线程<ul><li>负责执行异步请求</li><li>主线程执行代码遇到异步请求的时候会把函数交给该线程处理,当监听到状态变更事件,如果有回调函数,该线程会把回调函数加入到任务队列的队尾等待执行</li></ul></li></ul><h3 id="一些补充"><a href="#一些补充" class="headerlink" title="一些补充"></a>一些补充</h3><ul><li><strong><code>async</code> 隐式返回 Promise 作为结果,执行完 await 之后直接跳出 <code>async</code> 函数,让出执行的所有权,当前任务的其他代码执行完之后再次获得执行权进行执行</strong>。</li><li>立即 resolve 的 Promise 对象,是在本轮”事件循环”的结束时执行,而不是在下一轮”事件循环”的开始时。</li><li>在一轮Event Loop中多次修改同一DOM,只有最后一次会进行绘制。</li><li>渲染更新(Update the rendering)会在Event Loop中的tasks和microtasks完成后进行,但并不是每轮Event Loop都会更新渲染,这取决于是否修改了DOM和浏览器觉得是否有必要在此时立即将新状态呈现给用户。如果在一帧的时间内(时间并不确定,因为浏览器每秒的帧数总在波动,16.7ms只是估算并不准确)修改了多处DOM,浏览器可能将变动积攒起来,只进行一次绘制,这是合理的。</li><li>如果希望在每轮Event Loop都即时呈现变动,可以使用<code>requestAnimationFrame</code>。</li></ul><h2 id="Node事件循环"><a href="#Node事件循环" class="headerlink" title="Node事件循环"></a>Node事件循环</h2><p>Node的事件循环是基于libuv实现,而libuv是 Node 的新跨平台抽象层,libuv使用异步IO事件驱动的编程方式,核心是提供I/O的事件循环和异步回调。libuv的API包含有时间,非阻塞的网络,异步文件操作,子进程等等。</p><p>看一张图,<a href="https://segmentfault.com/img/remote/1460000016278119">如果看不到图,看这里</a></p><p><img src="https://segmentfault.com/img/remote/1460000016278119/view" alt=""></p><blockquote><p>(1)V8引擎解析JavaScript脚本。</p><p>(2)解析后的代码,调用Node API。</p><p>(3)<a href="https://github.com/joyent/libuv">libuv库</a>负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。</p><p>(4)V8引擎再将结果返回给用户。</p></blockquote><p>当 Node.js 启动后,它会初始化事件循环,处理已提供的输入脚本(或丢入 <a href="https://nodejs.org/api/repl.html#repl_repl">REPL</a>,本文不涉及到),它可能会调用一些异步的 API、调度定时器,或者调用 <code>process.nextTick()</code>,然后开始处理事件循环。</p><p>下面的图表展示了事件循环操作顺序的简化概览。</p><pre><code> ┌───────────────────────────┐┌─&gt;│ timers ││ └─────────────┬─────────────┘│ ┌─────────────┴─────────────┐│ │ pending callbacks ││ └─────────────┬─────────────┘│ ┌─────────────┴─────────────┐│ │ idle, prepare ││ └─────────────┬─────────────┘ ┌───────────────┐│ ┌─────────────┴─────────────┐ │ incoming: ││ │ poll │&lt;─────┤ connections, ││ └─────────────┬─────────────┘ │ data, etc. ││ ┌─────────────┴─────────────┐ └───────────────┘│ │ check ││ └─────────────┬─────────────┘│ ┌─────────────┴─────────────┐└──┤ close callbacks │ └───────────────────────────┘</code></pre><h3 id="Node事件循环的6个阶段"><a href="#Node事件循环的6个阶段" class="headerlink" title="Node事件循环的6个阶段"></a>Node事件循环的6个阶段</h3><ul><li><strong>定时器(timers)</strong>:本阶段执行已经被 <code>setTimeout()</code> 和 <code>setInterval()</code> 的调度回调函数。</li><li><strong>待定回调(pending callbacks)</strong>:执行延迟到下一个循环迭代的 I/O 回调。</li><li><strong>idle, prepare</strong>:仅系统内部使用。</li><li><strong>轮询(poll)</strong>:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 <code>setImmediate()</code> 调度的之外),其余情况 node 将在适当的时候在此阻塞。</li><li><strong>检测(check)</strong>:<code>setImmediate()</code> 回调函数在这里执行。</li><li><strong>关闭的回调函数(close callbacks)</strong>:一些关闭的回调函数,如:<code>socket.on('close', ...)</code>。</li></ul><p>具体内容可以看Node<a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/">官方文档</a></p><h3 id="Node事件循环过程"><a href="#Node事件循环过程" class="headerlink" title="Node事件循环过程"></a>Node事件循环过程</h3><ol><li>执行全局Script的同步代码</li><li>执行microtask微任务</li><li>开始执行macrotask宏任务,共6个阶段,从第1个阶段开始执行相应每一个阶段macrotask中的所有任务,注意,<strong>这里是所有每个阶段宏任务队列的所有任务</strong>,在浏览器的Event Loop中是只取宏队列的第一个任务出来执行,每一个阶段的macrotask任务执行完毕后,开始执行微任务,也就是步骤2</li><li>Timers Queue -&gt; 步骤2 -&gt; I/O Queue -&gt; 步骤2 -&gt; Check Queue -&gt; 步骤2 -&gt; Close Callback Queue -&gt; 步骤2 -&gt; Timers Queue ……</li><li>这就是Node的Event Loop</li></ol><h3 id="浏览器和Node事件循环的差异"><a href="#浏览器和Node事件循环的差异" class="headerlink" title="浏览器和Node事件循环的差异"></a>浏览器和Node事件循环的差异</h3><ul><li>浏览器环境下,microtask的任务队列是每个macrotask执行完之后执行。</li><li>而在Node.js中,microtask会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务。</li></ul><h3 id="Node-gt-11-?"><a href="#Node-gt-11-?" class="headerlink" title="Node > 11 ?"></a>Node &gt; 11 ?</h3><p>观察下面代码</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></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 string">'timer1'</span><span class="token punctuation">)</span> <span class="token function">setImmediate</span><span class="token punctuation">(</span><span class="token keyword">function</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 string">'immd 1'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</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 string">'promise1'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></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 string">'timer2'</span><span class="token punctuation">)</span> <span class="token function">setImmediate</span><span class="token punctuation">(</span><span class="token keyword">function</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 string">'immd 2'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</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 string">'promise2'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在node11及以上版本打印:</p><pre class="line-numbers language-powershell"><code class="language-powershell">timer1promise1timer2promise2immd 1immd 2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 node 版本为 8.11.2 打印:</p><pre class="line-numbers language-powershell"><code class="language-powershell">timer1timer2promise1promise2immd 1immd 2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这是因为在 node11 的版本之后,也是每个 Macrotask 执行完后,就去执行 Microtask 了,和浏览器的模型一致。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html">JavaScript 运行机制详解:再谈Event Loop</a></p><p><a href="https://www.cnblogs.com/magicg/p/13644463.html">面试一定会问到的-js事件循环</a></p><p><a href="https://www.colabug.com/2020/0913/7692222/">Node.js VS 浏览器以及事件循环机制</a></p><p><a href="https://www.cnblogs.com/everlose/p/12846375.html">NodeJs 的 Event loop 事件循环机制详解</a></p><p><a href="https://segmentfault.com/a/1190000016278115">带你彻底弄懂Event Loop</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;事件循环(Event-Loop)&quot;&gt;&lt;a href=&quot;#事件循环(Event-Loop)&quot; class=&quot;headerlink&quot; title=&quot;事件循环(Event Loop)&quot;&gt;&lt;/a&gt;事件循环(Event Loop)&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;事
</summary>
<category term="JavaScript" scheme="http://ltara.gitee.io/categories/JavaScript/"/>
<category term="浏览器" scheme="http://ltara.gitee.io/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
<category term="Node" scheme="http://ltara.gitee.io/tags/Node/"/>
</entry>
<entry>
<title>Vue之组件通信</title>
<link href="http://ltara.gitee.io/posts/46969.html"/>
<id>http://ltara.gitee.io/posts/46969.html</id>
<published>2020-09-08T15:26:12.000Z</published>
<updated>2020-09-09T17:23:22.226Z</updated>
<content type="html"><![CDATA[<h1 id="组件通信"><a href="#组件通信" class="headerlink" title="组件通信"></a>组件通信</h1><h2 id="props-emit"><a href="#props-emit" class="headerlink" title="props/$emit"></a>props/$emit</h2><blockquote><p>在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。</p></blockquote><h3 id="父组件向子组件传递数据"><a href="#父组件向子组件传递数据" class="headerlink" title="父组件向子组件传递数据"></a>父组件向子组件传递数据</h3><h4 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a>代码示例</h4><p><strong>父组件</strong></p><p>在父组件里引入子组件,然后通过子组件标签属性来传递数据</p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;my-child my-message="from Parent Component"&gt;&lt;/my-child&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import MyChild from './Child'export default { components: { MyChild, }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>子组件</strong></p><p>在子组件显式地用 <code>props</code> 选项声明它预期的数据并使用</p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Child&lt;/h1&gt; &lt;h3&gt;{{ myMessage }}&lt;/h3&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { props: { myMessage: { type: String, default: '' } }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><h5 id="camelCase-vs-kebab-case"><a href="#camelCase-vs-kebab-case" class="headerlink" title="camelCase vs. kebab-case"></a>camelCase vs. kebab-case</h5><p>HTML 特性是不区分大小写的。所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名)。</p><h5 id="动态props"><a href="#动态props" class="headerlink" title="动态props"></a>动态props</h5><p>与绑定到任何普通的 HTML 特性相类似,我们可以用 <code>v-bind</code> 来动态地将 <code>prop</code> 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件:</p><pre class="line-numbers language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>my-child</span> <span class="token attr-name">:my-message</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span><span class="token punctuation">'</span>from Parent Component<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>my-child</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>以上是使用<code>v-bind</code>的简写方式。</p><p>值得注意的是,使用动态绑定的数据时,第一层引号内的数据是一个表达式:</p><pre class="line-numbers language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>my-child</span> <span class="token attr-name">my-message</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>my-child</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>my-child</span> <span class="token attr-name">:my-message</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>my-child</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>上面两个传递的数据是不同的,第一个传递的是字符串1,后面的才是传递数字1。</p><h5 id="单向数据流"><a href="#单向数据流" class="headerlink" title="单向数据流"></a>单向数据流</h5><blockquote><p>Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得难以理解。</p></blockquote><p>另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告。</p><p>在两种情况下,我们很容易忍不住想去修改 prop 中数据:</p><ol><li>Prop 作为初始值传入后,子组件想把它当作局部数据来用</li><li>Prop 作为原始数据传入,由子组件处理成其它数据输出</li></ol><p>对这两种情况,正确的应对方式是:</p><ol><li><p><strong>定义一个局部变量,并用 prop 的值初始化它:</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>myMessage <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> props<span class="token punctuation">:</span> <span class="token punctuation">{</span> myMessage<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ol><ol start="2"><li><p><strong>定义一个计算属性,处理 prop 的值并返回:</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> computed<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">message</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">'I am'</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>myMessage <span class="token punctuation">}</span> <span class="token punctuation">}</span> props<span class="token punctuation">:</span> <span class="token punctuation">{</span> myMessage<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ol><blockquote><p>注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。即便引用类型可以,也不要利用这个特性,记住一个原则:<strong>组件的数据状态在组件内部管理维护,不要在其他位置去修改它</strong>。</p></blockquote><h3 id="子组件向父组件传递数据"><a href="#子组件向父组件传递数据" class="headerlink" title="子组件向父组件传递数据"></a>子组件向父组件传递数据</h3><h4 id="代码示例-1"><a href="#代码示例-1" class="headerlink" title="代码示例"></a>代码示例</h4><p><strong>父组件</strong></p><ol><li><p><strong>在父组件中提供一个子组件内部发布的事件处理函数</strong></p></li><li><p><strong>在使用子组件的模板的标签上订阅子组件内部发布的事件</strong></p></li></ol><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;my-child @transMessage="showMessage"&gt;&lt;/my-child&gt; &lt;p&gt;{{ message }}&lt;/p&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import myChild from './Child'export default { data() { return { message: '' } }, methods: { showMessage(val) { this.message = val }, }, components: { myChild }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>子组件</strong></p><ol><li><strong>在子组件中调用 <code>$emit()</code> 方法发布一个事件</strong></li></ol><pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;Child&lt;/h2&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { mounted() { this.$emit('transMessage', 'I am from Child component') }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h2 id="parent-children"><a href="#parent-children" class="headerlink" title="$parent/$children"></a>$parent/$children</h2><h3 id="代码示例-2"><a href="#代码示例-2" class="headerlink" title="代码示例"></a>代码示例</h3><p><strong>子组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;Child&lt;/h2&gt; &lt;p&gt;{{ message }}&lt;/p&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { data() { return { message: '' } }, mounted() { // 通过 $parent 获取当前实例的父实例 this.message = this.$parent.parentMessage }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>父组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;my-child&gt;&lt;/my-child&gt; &lt;p&gt;{{ message }}&lt;/p&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import myChild from './Child'export default { data() { return { message: '', parentMessage: 'parent message' } }, mounted() { // 通过 $children 获取当前实例的直接子组件 // 获取的数据是个数组,每个子组件是该数组的一个元素 this.message = this.$children[0].childMessage }, components: { myChild }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><blockquote><p>节制地使用 <code>$parent</code> 和 <code>$children</code> - 它们的主要目的是作为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通信</p></blockquote><h2 id="provide-inject(祖先组件向后代组件传递)"><a href="#provide-inject(祖先组件向后代组件传递)" class="headerlink" title="provide/inject(祖先组件向后代组件传递)"></a>provide/inject(祖先组件向后代组件传递)</h2><blockquote><p>2.2.0 新增</p><p><code>provide</code> 和 <code>inject</code> 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。</p></blockquote><p>这对选项需要一起使用,以允许一个<strong>祖先组件</strong>向其<strong>所有子孙后代</strong>注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。</p><p><strong>父组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;my-child&gt;&lt;/my-child&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import myChild from './Child'export default { // 父组件提供一个 foo 对象 provide: { foo: { name: 'Jack', age: 18 } }, components: { myChild }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>子组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;Child&lt;/h2&gt; &lt;ul v-for="item in foo"&gt; &lt;li&gt;{{ item }}&lt;/li&gt; &lt;/ul&gt; &lt;hr&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { // 子组件注入 foo inject: ['foo'], data() { return { message: null, } }, mounted() { this.message = this.foo }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><blockquote><p>注意: 这里不论子组件嵌套有多深, 只要调用了<code>inject</code> 那么就可以注入<code>provide</code>中的数据,而不局限于只能从当前父组件的props属性中回去数据</p></blockquote><h2 id="ref-ref(子组件向父组件传递)"><a href="#ref-ref(子组件向父组件传递)" class="headerlink" title="ref/$ref(子组件向父组件传递)"></a>ref/$ref(子组件向父组件传递)</h2><blockquote><p><code>ref</code> 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 <code>$refs</code> 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例</p></blockquote><p><strong>父组件</strong></p><ol><li><strong>在使用子组件的模板的标签上通过<code>ref</code>为子组件赋予一个 ID 引用</strong></li><li><strong>通过 <code>$ref</code> 获取子组件实例</strong></li></ol><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;my-child ref="child"&gt;&lt;/my-child&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import myChild from './Child'export default { components: { myChild }, mounted() { console.log(this.$refs.child) }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>子组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;Child&lt;/h2&gt; &lt;hr&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { data() { return { message: 'child message', } },}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h2 id="attrs-listeners"><a href="#attrs-listeners" class="headerlink" title="$attrs/$listeners"></a>$attrs/$listeners</h2><p>在非直接父子组件之间的通信,如A组件下面有B组件,B组件下面有C组件,我们想将A组件的数据传递给C组件或者将C组件的数据传递给A组件,可以有以下几种方法:</p><ul><li><strong>props</strong>:使用<code>props</code>绑定来进行一级一级的信息传递,如果D组件中状态改变需要传递数据给A,使用事件系统一级级往上传递(<strong>繁琐</strong>)</li><li><strong>$parent/$children</strong>:多个<code>$parent</code>或者<code>$children</code>一层一层向上或向下传递(<strong>节制地使用这两个实例属性</strong>)</li><li><strong>eventBus</strong>:使用<code>eventBus</code>,这种情况下还是比较适合使用,但是碰到<strong>多人合作开发时,代码维护性较低, 可读性也低</strong></li><li><strong>Vuex</strong>:使用<code>Vuex</code>来进行数据管理, 但是如果仅仅是传递数据, 而不做中间处理,使用<code>Vuex</code>处理感觉有点<strong>大材小用</strong>了</li></ul><p>在<code>vue2.4</code>中,为了解决该需求,引入了<code>$attrs</code> 和<code>$listeners</code> , 新增了<code>inheritAttrs</code> 选项。</p><h3 id="祖先组件向后代组件传递"><a href="#祖先组件向后代组件传递" class="headerlink" title="祖先组件向后代组件传递"></a>祖先组件向后代组件传递</h3><p><a href="https://cn.vuejs.org/v2/api/#inheritAttrs">vue官方</a>对<code>inheritAttrs</code> 地说明:</p><blockquote><p>默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 <code>inheritAttrs</code> 到 <code>false</code>,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例 property <code>$attrs</code> 可以让这些 attribute 生效,且可以通过 <code>v-bind</code> 显性的绑定到非根元素上。</p><p>注意:这个选项<strong>不影响</strong> <code>class</code> 和 <code>style</code> 绑定。</p></blockquote><p>通俗的讲就是:<code>inheritAttrs</code> 属性默认为<code>true</code>,这时父作用域上的非props属性回退为普通的HTML attribute,如果设置为<code>false</code>,则不会回退为普通的HTML attribute,这些attribute会被子组件的实例属性 <code>$attrs</code> 收集起来。这些属性不包括<code>class</code>和<code>style</code>。</p><blockquote><p><strong><code>$attrs</code></strong>:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (<code>class</code> 和 <code>style</code> 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (<code>class</code> 和 <code>style</code> 除外),并且可以通过 <code>v-bind="$attrs"</code> 传入内部组件</p></blockquote><p><strong>A组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;A&lt;/h1&gt; &lt;com-b :a="1" :b="2" :c="3"&gt;&lt;/com-b&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import comB from './ComB'export default { data() { return { name: 'A', } }, components: { comB },}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>B组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;B&lt;/h1&gt; &lt;com-c v-bind="$attrs"&gt;&lt;/com-c&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import comC from './ComC'export default { data() { return { name: 'B', } }, components: { comC },}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>C组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;C&lt;/h1&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { inheritAttrs: false, data() { return { name: 'C', } }, props: { a: { type: Number } } mounted() { console.log(this.$attrs) // {b: 2, c: 3} }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h3 id="后代组件向祖先组件传递"><a href="#后代组件向祖先组件传递" class="headerlink" title="后代组件向祖先组件传递"></a>后代组件向祖先组件传递</h3><blockquote><p><strong><code>$listenders</code></strong>:包含了父作用域中的 (不含 <code>.native</code> 修饰器的) <code>v-on</code> 事件监听器。它可以通过 <code>v-on="$listeners"</code> 传入内部组件</p></blockquote><p><strong>A组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;my-parent @test1="showTest1" @test2="showTest2"&gt;&lt;/my-parent&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import MyParent from './views/Parent'export default { data() { return { name: 'app', } }, components: { MyParent, }, methods: { showTest1() { console.log('test1...') }, showTest2(val) { console.log('test2 ' + val) } }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>B组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;my-child v-on="$listeners"&gt;&lt;/my-child&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import myChild from './Child'export default { data() { return { name: 'parent', } }, components: { myChild }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>C组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;Child&lt;/h2&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { created() { console.log(this.$listeners) this.$emit("test1") this.$emit("test2", 'from child component') }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h2 id="eventBus"><a href="#eventBus" class="headerlink" title="eventBus"></a>eventBus</h2><p><code>eventBus</code> 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。</p><blockquote><p><code>$emit</code>和<code>$on</code>的事件必须使用一个空的 Vue 实例作为中央事件总线的实例上,才能够触发,否则会出现子组件<code>$emit</code>后父组件没有监听到函数的变化</p></blockquote><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p>首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// bus.js</span><span class="token keyword">import</span> Vue <span class="token keyword">from</span> <span class="token string">'vue'</span><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="发送事件"><a href="#发送事件" class="headerlink" title="发送事件"></a>发送事件</h3><p>假设我们有两个兄弟组件:<code>Elder-brother</code>、<code>Younger-brother</code>,我们需要在这两个兄弟组件之间传递数据</p><pre><code>&lt;template&gt; &lt;div&gt; &lt;elder-brother&gt;&lt;/elder-brother&gt; &lt;younger-brother&gt;&lt;/younger-brother&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import ElderBrother from './views/Elder-brother'import YoungerBrother from './views/Younger-brother'export default { components: { ElderBrother, YoungerBrother }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><code>Elder-brother</code>组件:</p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;elder brother&lt;/h1&gt; &lt;button @click="sendMessage"&gt;send&lt;/button&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import EventBus from '../util/bus'export default { data() { return { message: 'from Elder-brother component', } }, methods: { sendMessage() { // 利用 $emit 发布事件传递数据 EventBus.$emit('transMessage', this.message) }, },}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h3 id="接收事件"><a href="#接收事件" class="headerlink" title="接收事件"></a>接收事件</h3><p><code>Younger-brother</code>组件:</p><pre><code>&lt;template&gt; &lt;h1&gt;younger brother&lt;/h1&gt;&lt;/template&gt;&lt;script&gt;import EventBus from '../util/bus'export default { mounted() { // 利用 $on 接收事件,并执行回调函数 EventBus.$on('transMessage', (param) =&gt; { console.log(param) }) }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p>这样就完成了兄弟组件之间的数据传递。</p><h3 id="移除事件监听者"><a href="#移除事件监听者" class="headerlink" title="移除事件监听者"></a>移除事件监听者</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> eventBus <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'bus.js'</span>EventBus<span class="token punctuation">.</span><span class="token function">$off</span><span class="token punctuation">(</span><span class="token string">'transMessage'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>如果想精确移除某个事件的监听器,还需要传递一个回调函数:</p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;younger brother&lt;/h1&gt; &lt;button @click="destroy"&gt;destroy&lt;/button&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import EventBus from '../util/bus'export default { mounted() { EventBus.$on('transMessage', this.fn1) EventBus.$on('transMessage', this.fn2) }, methods: { fn1(param) { console.log(param + 'fn1') }, fn2(param) { console.log(param + 'fn2') }, destroy() { EventBus.$off('transMessage', this.fn1) } }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><h2 id="localStorage-sessionStorage"><a href="#localStorage-sessionStorage" class="headerlink" title="localStorage / sessionStorage"></a>localStorage / sessionStorage</h2><p>这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。 通过<code>window.localStorage.getItem(key)</code>获取数据 通过<code>window.localStorage.setItem(key,value)</code>存储数据</p><blockquote><p>注意用<code>JSON.parse()</code> / <code>JSON.stringify()</code> 做数据格式转换 <code>localStorage</code> / <code>sessionStorage</code>可以结合<code>vuex</code>,实现数据的持久保存,同时使用vuex解决数据和状态混乱问题。</p></blockquote><h2 id="v-model"><a href="#v-model" class="headerlink" title="v-model"></a>v-model</h2><blockquote><p>v-model只能实现父子组件之间的数据传递</p></blockquote><p><strong>父组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h1&gt;Parent&lt;/h1&gt; &lt;p&gt;{{ message }}&lt;/p&gt; &lt;my-child v-model="message"&gt;&lt;/my-child&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;import myChild from './Child'export default { data() { return { message: 'parent', } }, components: { myChild }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><p><strong>子组件</strong></p><pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;Child&lt;/h2&gt; &lt;input type="text" v-model="myMessage" @input="changeValue"&gt; &lt;/div&gt;&lt;/template&gt;&lt;script&gt;export default { props: { value: String }, data() { return { myMessage: this.value, } }, methods: { changeValue() { this.$emit('input', this.myMessage) } }}&lt;/script&gt;&lt;style&gt;&lt;/style&gt;</code></pre><ol><li>在 Parent 组件中,我们给自定义的 Child 组件实现了 v-model 绑定了 <code>message</code> 属性。此时相当于给 Child 组件传递了 value 属性和绑定了 input 事件</li><li>顺理成章,在定义的 child 组件中,可以通过 props 获取 value 属性,根据 props 单向数据流的原则,又将 value 缓存在了 data 里面的 <code>myMessage</code> 上,再在 input 上通过 <code>v-model</code> 绑定了 <code>myMessage</code> 属性和一个 <code>change</code> 事件。当 input 值变化时,就会触发 change 事件,处理 parent 组件通过 <code>v-model</code> 给 child 组件绑定的 <code>input</code> 事件,触发 <code>parent</code> 组件中 <code>message</code> 属性值的变化,完成 <code>child</code> 子组件改变 parent 组件的属性值。</li></ol><h2 id="Vuex"><a href="#Vuex" class="headerlink" title="Vuex"></a>Vuex</h2><p>还没了解<a href="https://vuex.vuejs.org/zh/">Vuex</a>,内容应该挺多的,等熟悉了再仔细总结一下。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://juejin.im/post/6844903784963899405#heading-6">Vue 组件通信方式全面详解</a></p><p><a href="https://juejin.im/post/6844903887162310669#heading-0">vue中8种组件通信方式, 值得收藏!</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;组件通信&quot;&gt;&lt;a href=&quot;#组件通信&quot; class=&quot;headerlink&quot; title=&quot;组件通信&quot;&gt;&lt;/a&gt;组件通信&lt;/h1&gt;&lt;h2 id=&quot;props-emit&quot;&gt;&lt;a href=&quot;#props-emit&quot; class=&quot;headerlink&quot; titl
</summary>
<category term="Vue" scheme="http://ltara.gitee.io/categories/Vue/"/>
<category term="Vue" scheme="http://ltara.gitee.io/tags/Vue/"/>
<category term="组件通信" scheme="http://ltara.gitee.io/tags/%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1/"/>
</entry>
<entry>
<title>CSS基础之flex/grid</title>
<link href="http://ltara.gitee.io/posts/7575.html"/>
<id>http://ltara.gitee.io/posts/7575.html</id>
<published>2020-08-23T06:45:15.000Z</published>
<updated>2020-09-06T15:27:55.198Z</updated>
<content type="html"><![CDATA[<blockquote><p>这篇文章内容来自阮一峰的<a href="http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?20200823152905#comment-last">Flex 布局教程:语法篇</a>和<a href="http://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html">CSS Grid 网格布局教程</a>,在一些微小的地方添加了一些注意事项。写这篇的目的是为了方便自己回顾,所以将图片去掉了,如果想更方便的学习推荐去阮老师的博客。</p></blockquote><h1 id="flex"><a href="#flex" class="headerlink" title="flex"></a>flex</h1><h2 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h2><p>采用 Flex 布局的元素,称为 Flex 容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称”项目”。</p><p><img src="http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071004.png" alt="img"></p><p>容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做<code>main start</code>,结束位置叫做<code>main end</code>;交叉轴的开始位置叫做<code>cross start</code>,结束位置叫做<code>cross end</code>。</p><p>项目默认沿主轴排列。单个项目占据的主轴空间叫做<code>main size</code>,占据的交叉轴空间叫做<code>cross size</code>。</p><h2 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h2><h3 id="容器属性"><a href="#容器属性" class="headerlink" title="容器属性"></a>容器属性</h3><p>以下6个属性设置在容器上。</p><blockquote><ul><li>flex-direction</li><li>flex-wrap</li><li>flex-flow</li><li>justify-content</li><li>align-items</li><li>align-content</li></ul></blockquote><h4 id="flex-direction"><a href="#flex-direction" class="headerlink" title="flex-direction"></a>flex-direction</h4><p><code>flex-direction</code>属性决定主轴的方向(即项目的排列方向)。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">flex-direction</span><span class="token punctuation">:</span> row | row-reverse | column | column-reverse<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>它可能有4个值。</p><blockquote><ul><li><code>row</code>(默认值):主轴为水平方向,起点在左端。</li><li><code>row-reverse</code>:主轴为水平方向,起点在右端。</li><li><code>column</code>:主轴为垂直方向,起点在上沿。</li><li><code>column-reverse</code>:主轴为垂直方向,起点在下沿。</li></ul></blockquote><h4 id="flex-wrap"><a href="#flex-wrap" class="headerlink" title="flex-wrap"></a>flex-wrap</h4><p>默认情况下,项目都排在一条线(又称”轴线”)上。<code>flex-wrap</code>属性定义,如果一条轴线排不下,如何换行。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.box</span></span><span class="token punctuation">{</span> <span class="token property">flex-wrap</span><span class="token punctuation">:</span> nowrap | wrap | wrap-reverse<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>它可能取三个值。</p><blockquote><ul><li><p><code>nowrap</code>(默认):不换行。</p></li><li><p><code>wrap</code>:换行,第一行在上方。</p></li><li><p><code>wrap-reverse</code>:换行,第一行在下方。</p></li></ul></blockquote><h4 id="flex-flow"><a href="#flex-flow" class="headerlink" title="flex-flow"></a>flex-flow</h4><p><code>flex-flow</code>属性是<code>flex-direction</code>属性和<code>flex-wrap</code>属性的简写形式,默认值为<code>row nowrap</code>。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">flex-flow</span><span class="token punctuation">:</span> &lt;flex-direction> || &lt;flex-wrap><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><h4 id="justify-content"><a href="#justify-content" class="headerlink" title="justify-content"></a>justify-content</h4><p><code>justify-content</code>属性定义了项目在主轴上的对齐方式。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">justify-content</span><span class="token punctuation">:</span> flex-start | flex-end | center | space-between | space-around<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。</p><blockquote><ul><li><code>flex-start</code>(默认值):左对齐</li><li><code>flex-end</code>:右对齐</li><li><code>center</code>: 居中</li><li><code>space-between</code>:两端对齐,项目之间的间隔都相等。</li><li><code>space-around</code>:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。</li></ul></blockquote><h4 id="align-items"><a href="#align-items" class="headerlink" title="align-items"></a>align-items</h4><p><code>align-items</code>属性定义项目在交叉轴上如何对齐。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">align-items</span><span class="token punctuation">:</span> flex-start | flex-end | center | baseline | stretch<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。</p><blockquote><ul><li><code>flex-start</code>:交叉轴的起点对齐。</li><li><code>flex-end</code>:交叉轴的终点对齐。</li><li><code>center</code>:交叉轴的中点对齐。</li><li><code>baseline</code>: 项目的第一行文字的基线对齐。</li><li><code>stretch</code>(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。</li></ul></blockquote><h4 id="align-content"><a href="#align-content" class="headerlink" title="align-content"></a>align-content</h4><p><code>align-content</code>属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">align-content</span><span class="token punctuation">:</span> flex-start | flex-end | center | space-between | space-around | stretch<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>该属性可能取6个值。</p><blockquote><ul><li><code>flex-start</code>:与交叉轴的起点对齐。</li><li><code>flex-end</code>:与交叉轴的终点对齐。</li><li><code>center</code>:与交叉轴的中点对齐。</li><li><code>space-between</code>:与交叉轴两端对齐,轴线之间的间隔平均分布。</li><li><code>space-around</code>:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。</li><li><code>stretch</code>(默认值):轴线占满整个交叉轴。</li></ul></blockquote><h3 id="项目属性"><a href="#项目属性" class="headerlink" title="项目属性"></a>项目属性</h3><p>以下6个属性设置在项目上。</p><blockquote><ul><li><code>order</code></li><li><code>flex-grow</code></li><li><code>flex-shrink</code></li><li><code>flex-basis</code></li><li><code>flex</code></li><li><code>align-self</code></li></ul></blockquote><h4 id="order"><a href="#order" class="headerlink" title="order"></a>order</h4><p><code>order</code>属性定义项目的排列顺序。数值越小,排列越靠前,默认为<code>0</code>。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">order</span><span class="token punctuation">:</span> &lt;integer><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><h4 id="flex-grow"><a href="#flex-grow" class="headerlink" title="flex-grow"></a>flex-grow</h4><p><code>flex-grow</code>属性定义项目的放大比例,默认为<code>0</code>,即如果存在剩余空间,也不放大。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">flex-grow</span><span class="token punctuation">:</span> &lt;number><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/* default 0 */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>如果所有项目的<code>flex-grow</code>属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的<code>flex-grow</code>属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。</p><h4 id="flex-shrink"><a href="#flex-shrink" class="headerlink" title="flex-shrink"></a>flex-shrink</h4><p><code>flex-shrink</code>属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">flex-shrink</span><span class="token punctuation">:</span> &lt;number><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/* default 1 */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>如果所有项目的<code>flex-shrink</code>属性都为1,当空间不足时,都将等比例缩小。如果一个项目的<code>flex-shrink</code>属性为0,其他项目都为1,则空间不足时,前者不缩小。</p><h4 id="flex-basis"><a href="#flex-basis" class="headerlink" title="flex-basis"></a>flex-basis</h4><p><code>flex-basis</code>属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为<code>auto</code>,即项目的本来大小。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">flex-basis</span><span class="token punctuation">:</span> &lt;length> | auto<span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/* default auto */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>它可以设为跟<code>width</code>或<code>height</code>属性一样的值(比如350px),则项目将占据固定空间。</p><h4 id="flex-1"><a href="#flex-1" class="headerlink" title="flex"></a>flex</h4><p><code>flex</code>属性是<code>flex-grow</code>, <code>flex-shrink</code> 和 <code>flex-basis</code>的简写,默认值为<code>0 1 auto</code>。后两个属性可选。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">flex</span><span class="token punctuation">:</span> none | [ &lt;<span class="token string">'flex-grow'</span>> &lt;<span class="token string">'flex-shrink'</span>>? || &lt;<span class="token string">'flex-basis'</span>> ]<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>该属性有两个快捷值:<code>auto</code> (<code>1 1 auto</code>) 和 none (<code>0 0 auto</code>)。</p><p><strong>建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值</strong>。</p><blockquote><p><strong>当使用一个或两个无单位数时, flex-basis会从auto变为0</strong>。</p></blockquote><h4 id="align-self"><a href="#align-self" class="headerlink" title="align-self"></a>align-self</h4><p><code>align-self</code>属性允许单个项目有与其他项目不一样的对齐方式,可覆盖<code>align-items</code>属性。默认值为<code>auto</code>,表示继承父元素的<code>align-items</code>属性,如果没有父元素,则等同于<code>stretch</code>。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">align-self</span><span class="token punctuation">:</span> auto | flex-start | flex-end | center | baseline | stretch<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>该属性可能取6个值,除了auto,其他都与align-items属性完全一致。</p><h2 id="布局"><a href="#布局" class="headerlink" title="布局"></a>布局</h2><p>见<a href="http://www.ruanyifeng.com/blog/2015/07/flex-examples.html">Flex 布局教程:实例篇</a></p><h1 id="grid"><a href="#grid" class="headerlink" title="grid"></a>grid</h1><h2 id="基本概念-1"><a href="#基本概念-1" class="headerlink" title="基本概念"></a>基本概念</h2><h3 id="容器和项目"><a href="#容器和项目" class="headerlink" title="容器和项目"></a>容器和项目</h3><p>采用网格布局的区域,称为”容器”(container)。容器内部采用网格定位的子元素,称为”项目”(item)。</p><blockquote><pre class="line-numbers language-markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,最外层的<code>&lt;div&gt;</code>元素就是容器,内层的三个<code>&lt;div&gt;</code>元素就是项目。</p><p>注意:项目只能是容器的顶层子元素,不包含项目的子元素,比如上面代码的<code>&lt;p&gt;</code>元素就不是项目。Grid 布局只对项目生效。</p><h3 id="行和列"><a href="#行和列" class="headerlink" title="行和列"></a>行和列</h3><p>容器里面的水平区域称为”行”(row),垂直区域称为”列”(column)。</p><h3 id="单元格"><a href="#单元格" class="headerlink" title="单元格"></a>单元格</h3><p>行和列的交叉区域,称为”单元格”(cell)。</p><p>正常情况下,<code>n</code>行和<code>m</code>列会产生<code>n x m</code>个单元格。比如,3行3列会产生9个单元格。</p><h3 id="网格线"><a href="#网格线" class="headerlink" title="网格线"></a>网格线</h3><p>划分网格的线,称为”网格线”(grid line)。水平网格线划分出行,垂直网格线划分出列。</p><p>正常情况下,<code>n</code>行有<code>n + 1</code>根水平网格线,<code>m</code>列有<code>m + 1</code>根垂直网格线,比如三行就有四根水平网格线。</p><h2 id="语法-1"><a href="#语法-1" class="headerlink" title="语法"></a>语法</h2><h3 id="容器属性-1"><a href="#容器属性-1" class="headerlink" title="容器属性"></a>容器属性</h3><h4 id="display"><a href="#display" class="headerlink" title="display"></a>display</h4><p><code>display: grid</code>指定一个容器采用网格布局。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector">div </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>默认情况下,容器元素都是块级元素,但也可以设成行内元素。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector">div </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-grid<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码指定<code>div</code>是一个行内元素,该元素内部采用网格布局。</p><blockquote><p>注意,设为网格布局以后,容器子元素(项目)的<code>float</code>、<code>display: inline-block</code>、<code>display: table-cell</code>、<code>vertical-align</code>和<code>column-*</code>等设置都将失效。</p></blockquote><h4 id="grid-template-columns,grid-template-rows"><a href="#grid-template-columns,grid-template-rows" class="headerlink" title="grid-template-columns,grid-template-rows"></a>grid-template-columns,grid-template-rows</h4><p>容器指定了网格布局以后,接着就要划分行和列。<code>grid-template-columns</code>属性定义每一列的列宽,<code>grid-template-rows</code>属性定义每一行的行高。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">100</span>px <span class="token number">100</span>px <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token number">100</span>px <span class="token number">100</span>px <span class="token number">100</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码指定了一个三行三列的网格,列宽和行高都是<code>100px</code>。</p><p>除了使用绝对单位,也可以使用百分比。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">33.33%</span> <span class="token number">33.33%</span> <span class="token number">33.33%</span><span class="token punctuation">;</span> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token number">33.33%</span> <span class="token number">33.33%</span> <span class="token number">33.33%</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p><strong>(1)repeat()</strong></p><p>有时候,重复写同样的值非常麻烦,尤其网格很多时。这时,可以使用<code>repeat()</code>函数,简化重复的值。上面的代码用<code>repeat()</code>改写如下。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span><span class="token number">3</span>, <span class="token number">33.33%</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span><span class="token number">3</span>, <span class="token number">33.33%</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p><code>repeat()</code>接受两个参数,第一个参数是重复的次数(上例是3),第二个参数是所要重复的值。</p><p><code>repeat()</code>重复某种模式也是可以的。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span><span class="token number">2</span>, <span class="token number">100</span>px <span class="token number">20</span>px <span class="token number">80</span>px<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>上面代码定义了6列,第一列和第四列的宽度为<code>100px</code>,第二列和第五列为<code>20px</code>,第三列和第六列为<code>80px</code>。</p><p><strong>(2)auto-fill 关键字</strong></p><p>有时,单元格的大小是固定的,但是容器的大小不确定。如果希望每一行(或每一列)容纳尽可能多的单元格,这时可以使用<code>auto-fill</code>关键字表示自动填充。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>auto-fill, <span class="token number">100</span>px<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码表示每列宽度<code>100px</code>,然后自动填充,直到容器不能放置更多的列。</p><p><strong>(3)fr 关键字</strong></p><p>为了方便表示比例关系,网格布局提供了<code>fr</code>关键字(fraction 的缩写,意为”片段”)。如果两列的宽度分别为<code>1fr</code>和<code>2fr</code>,就表示后者是前者的两倍。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">1</span>fr <span class="token number">1</span>fr<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码表示两个相同宽度的列。</p><p><code>fr</code>可以与绝对长度的单位结合使用,这时会非常方便。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">150</span>px <span class="token number">1</span>fr <span class="token number">2</span>fr<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码表示,第一列的宽度为150像素,第二列的宽度是第三列的一半。</p><p><strong>(4)minmax()</strong></p><p><code>minmax()</code>函数产生一个长度范围,表示长度就在这个范围之中。它接受两个参数,分别为最小值和最大值。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">1</span>fr <span class="token number">1</span>fr <span class="token function">minmax</span><span class="token punctuation">(</span><span class="token number">100</span>px, <span class="token number">1</span>fr<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>上面代码中,<code>minmax(100px, 1fr)</code>表示列宽不小于<code>100px</code>,不大于<code>1fr</code>。</p><p><strong>(5)auto 关键字</strong></p><p><code>auto</code>关键字表示由浏览器自己决定长度。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">100</span>px auto <span class="token number">100</span>px<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>上面代码中,第二列的宽度,基本上等于该列单元格的最大宽度,除非单元格内容设置了<code>min-width</code>,且这个值大于最大宽度。</p><p><strong>(6)网格线的名称</strong></p><p><code>grid-template-columns</code>属性和<code>grid-template-rows</code>属性里面,还可以使用方括号,指定每一根网格线的名字,方便以后的引用。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> [c<span class="token number">1</span>] <span class="token number">100</span>px [c<span class="token number">2</span>] <span class="token number">100</span>px [c<span class="token number">3</span>] auto [c<span class="token number">4</span>]<span class="token punctuation">;</span> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> [r<span class="token number">1</span>] <span class="token number">100</span>px [r<span class="token number">2</span>] <span class="token number">100</span>px [r<span class="token number">3</span>] auto [r<span class="token number">4</span>]<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码指定网格布局为3行 x 3列,因此有4根垂直网格线和4根水平网格线。方括号里面依次是这八根线的名字。</p><p>网格布局允许同一根线有多个名字,比如<code>[fifth-line row-5]</code>。</p><p><strong>(7)布局实例</strong></p><p><code>grid-template-columns</code>属性对于网页布局非常有用。两栏式布局只需要一行代码。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">70%</span> <span class="token number">30%</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码将左边栏设为70%,右边栏设为30%。</p><p>传统的十二网格布局,写起来也很容易。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span><span class="token number">12</span>, <span class="token number">1</span>fr<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><h4 id="grid-row-gap,grid-column-gap,grid-gap"><a href="#grid-row-gap,grid-column-gap,grid-gap" class="headerlink" title="grid-row-gap,grid-column-gap,grid-gap"></a>grid-row-gap,grid-column-gap,grid-gap</h4><p><code>grid-row-gap</code>属性设置行与行的间隔(行间距),<code>grid-column-gap</code>属性设置列与列的间隔(列间距)。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">grid-row-gap</span><span class="token punctuation">:</span> <span class="token number">20</span>px<span class="token punctuation">;</span> <span class="token property">grid-column-gap</span><span class="token punctuation">:</span> <span class="token number">20</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,<code>grid-row-gap</code>用于设置行间距,<code>grid-column-gap</code>用于设置列间距。</p><p><code>grid-gap</code>属性是<code>grid-column-gap</code>和<code>grid-row-gap</code>的合并简写形式,语法如下。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-gap</span><span class="token punctuation">:</span> &lt;grid-row-gap> &lt;grid-column-gap><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>因此,上面一段 CSS 代码等同于下面的代码。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">grid-gap</span><span class="token punctuation">:</span> <span class="token number">20</span>px <span class="token number">20</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>如果<code>grid-gap</code>省略了第二个值,浏览器认为第二个值等于第一个值。</p><blockquote><p>根据最新标准,上面三个属性名的<code>grid-</code>前缀已经删除,<code>grid-column-gap</code>和<code>grid-row-gap</code>写成<code>column-gap</code>和<code>row-gap</code>,<code>grid-gap</code>写成<code>gap</code>。</p></blockquote><h4 id="grid-template-areas"><a href="#grid-template-areas" class="headerlink" title="grid-template-areas"></a>grid-template-areas</h4><p>网格布局允许指定”区域”(area),一个区域由单个或多个单元格组成。<code>grid-template-areas</code>属性用于定义区域。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">100</span>px <span class="token number">100</span>px <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token number">100</span>px <span class="token number">100</span>px <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">'a b c'</span> <span class="token string">'d e f'</span> <span class="token string">'g h i'</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码先划分出9个单元格,然后将其定名为<code>a</code>到<code>i</code>的九个区域,分别对应这九个单元格。</p><p>多个单元格合并成一个区域的写法如下。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">'a a a'</span> <span class="token string">'b b b'</span> <span class="token string">'c c c'</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码将9个单元格分成<code>a</code>、<code>b</code>、<code>c</code>三个区域。</p><p>下面是一个布局实例。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">"header header header"</span> <span class="token string">"main main sidebar"</span> <span class="token string">"footer footer footer"</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,顶部是页眉区域<code>header</code>,底部是页脚区域<code>footer</code>,中间部分则为<code>main</code>和<code>sidebar</code>。</p><p>如果某些区域不需要利用,则使用”点”(<code>.</code>)表示。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">'a . c'</span> <span class="token string">'d . f'</span> <span class="token string">'g . i'</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,中间一列为点,表示没有用到该单元格,或者该单元格不属于任何区域。</p><blockquote><p>注意,区域的命名会影响到网格线。每个区域的起始网格线,会自动命名为<code>区域名-start</code>,终止网格线自动命名为<code>区域名-end</code>。</p><p>比如,区域名为<code>header</code>,则起始位置的水平网格线和垂直网格线叫做<code>header-start</code>,终止位置的水平网格线和垂直网格线叫做<code>header-end</code>。</p></blockquote><h4 id="grid-auto-flow"><a href="#grid-auto-flow" class="headerlink" title="grid-auto-flow"></a>grid-auto-flow</h4><p>划分网格以后,容器的子元素会按照顺序,自动放置在每一个网格。默认的放置顺序是”先行后列”,即先填满第一行,再开始放入第二行。</p><p>这个顺序由<code>grid-auto-flow</code>属性决定,默认值是<code>row</code>,即”先行后列”。也可以将它设成<code>column</code>,变成”先列后行”。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-auto-flow</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p><code>grid-auto-flow</code>属性除了设置成<code>row</code>和<code>column</code>,还可以设成<code>row dense</code>和<code>column dense</code>。这两个值主要用于,某些项目指定位置以后,剩下的项目怎么自动放置。</p><p>现在修改设置,设为<code>row dense</code>,表示”先行后列”,并且尽可能紧密填满,尽量不出现空格。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-auto-flow</span><span class="token punctuation">:</span> row dense<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>如果将设置改为<code>column dense</code>,表示”先列后行”,并且尽量填满空格。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">grid-auto-flow</span><span class="token punctuation">:</span> column dense<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><h4 id="justify-items,align-items,place-items"><a href="#justify-items,align-items,place-items" class="headerlink" title="justify-items,align-items,place-items"></a>justify-items,align-items,place-items</h4><p><code>justify-items</code>属性设置单元格内容的水平位置(左中右),<code>align-items</code>属性设置单元格内容的垂直位置(上中下)。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">justify-items</span><span class="token punctuation">:</span> start | end | center | stretch<span class="token punctuation">;</span> <span class="token property">align-items</span><span class="token punctuation">:</span> start | end | center | stretch<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>这两个属性的写法完全相同,都可以取下面这些值。</p><blockquote><ul><li>start:对齐单元格的起始边缘。</li><li>end:对齐单元格的结束边缘。</li><li>center:单元格内部居中。</li><li>stretch:拉伸,占满单元格的整个宽度(默认值)。</li></ul></blockquote><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">justify-items</span><span class="token punctuation">:</span> start<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码表示,单元格的内容左对齐。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">align-items</span><span class="token punctuation">:</span> start<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码表示,单元格的内容头部对齐。</p><p><code>place-items</code>属性是<code>align-items</code>属性和<code>justify-items</code>属性的合并简写形式。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">place-items</span><span class="token punctuation">:</span> &lt;align-items> &lt;justify-items><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>下面是一个例子。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">place-items</span><span class="token punctuation">:</span> start end<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>如果省略第二个值,则浏览器认为与第一个值相等。</p><h4 id="justify-content,align-content-,place-content"><a href="#justify-content,align-content-,place-content" class="headerlink" title="justify-content,align-content ,place-content"></a>justify-content,align-content ,place-content</h4><p><code>justify-content</code>属性是整个内容区域在容器里面的水平位置(左中右),<code>align-content</code>属性是整个内容区域的垂直位置(上中下)。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">justify-content</span><span class="token punctuation">:</span> start | end | center | stretch | space-around | space-between | space-evenly<span class="token punctuation">;</span> <span class="token property">align-content</span><span class="token punctuation">:</span> start | end | center | stretch | space-around | space-between | space-evenly<span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>这两个属性的写法完全相同,都可以取下面这些值。(下面的图都以<code>justify-content</code>属性为例,<code>align-content</code>属性的图完全一样,只是将水平方向改成垂直方向。)</p><blockquote><ul><li>start - 对齐容器的起始边框。</li><li>end - 对齐容器的结束边框。</li><li>center - 容器内部居中。</li><li>stretch - 项目大小没有指定时,拉伸占据整个网格容器。</li><li>space-around - 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与容器边框的间隔大一倍。</li><li>space-between - 项目与项目的间隔相等,项目与容器边框之间没有间隔。</li><li>space-evenly - 项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。</li></ul></blockquote><p><code>place-content</code>属性是<code>align-content</code>属性和<code>justify-content</code>属性的合并简写形式。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">place-content</span><span class="token punctuation">:</span> &lt;align-content> &lt;justify-content><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>下面是一个例子。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">place-content</span><span class="token punctuation">:</span> space-around space-evenly<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>如果省略第二个值,浏览器就会假定第二个值等于第一个值。</p><h4 id="grid-auto-columns,grid-auto-rows"><a href="#grid-auto-columns,grid-auto-rows" class="headerlink" title="grid-auto-columns,grid-auto-rows"></a>grid-auto-columns,grid-auto-rows</h4><p>有时候,一些项目的指定位置,在现有网格的外部。比如网格只有3列,但是某一个项目指定在第5行。这时,浏览器会自动生成多余的网格,以便放置项目。</p><p><code>grid-auto-columns</code>属性和<code>grid-auto-rows</code>属性用来设置,浏览器自动创建的多余网格的列宽和行高。它们的写法与<code>grid-template-columns</code>和<code>grid-template-rows</code>完全相同。如果不指定这两个属性,浏览器完全根据单元格内容的大小,决定新增网格的列宽和行高。</p><p>下面的例子里面,划分好的网格是3行 x 3列,但是,8号项目指定在第4行,9号项目指定在第5行。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.container</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token number">100</span>px <span class="token number">100</span>px <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token number">100</span>px <span class="token number">100</span>px <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码指定新增的行高统一为50px(原始的行高为100px)。</p><h4 id="grid-template,grid"><a href="#grid-template,grid" class="headerlink" title="grid-template,grid"></a>grid-template,grid</h4><p><code>grid-template</code>属性是<code>grid-template-columns</code>、<code>grid-template-rows</code>和<code>grid-template-areas</code>这三个属性的合并简写形式。</p><p><code>grid</code>属性是<code>grid-template-rows</code>、<code>grid-template-columns</code>、<code>grid-template-areas</code>、 <code>grid-auto-rows</code>、<code>grid-auto-columns</code>、<code>grid-auto-flow</code>这六个属性的合并简写形式。</p><p>从易读易写的角度考虑,还是建议不要合并属性,所以这里就不详细介绍这两个属性了。</p><h3 id="项目属性-1"><a href="#项目属性-1" class="headerlink" title="项目属性"></a>项目属性</h3><p>下面这些属性定义在项目上面。</p><h4 id="grid-column-start,grid-column-end,grid-row-start,grid-row-end"><a href="#grid-column-start,grid-column-end,grid-row-start,grid-row-end" class="headerlink" title="grid-column-start,grid-column-end,grid-row-start,grid-row-end"></a>grid-column-start,grid-column-end,grid-row-start,grid-row-end</h4><p>项目的位置是可以指定的,具体方法就是指定项目的四个边框,分别定位在哪根网格线。</p><blockquote><ul><li><code>grid-column-start</code>属性:左边框所在的垂直网格线</li><li><code>grid-column-end</code>属性:右边框所在的垂直网格线</li><li><code>grid-row-start</code>属性:上边框所在的水平网格线</li><li><code>grid-row-end</code>属性:下边框所在的水平网格线</li></ul></blockquote><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column-start</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token property">grid-column-end</span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码指定,1号项目的左边框是第二根垂直网格线,右边框是第四根垂直网格线。</p><p>只指定了1号项目的左右边框,没有指定上下边框,所以会采用默认位置,即上边框是第一根水平网格线,下边框是第二根水平网格线。</p><p>除了1号项目以外,其他项目都没有指定位置,由浏览器自动布局,这时它们的位置由容器的<code>grid-auto-flow</code>属性决定,这个属性的默认值是<code>row</code>,因此会”先行后列”进行排列。读者可以把这个属性的值分别改成<code>column</code>、<code>row dense</code>和<code>column dense</code>,看看其他项目的位置发生了怎样的变化。</p><p>下面的例子是指定四个边框位置的效果。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column-start</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token property">grid-column-end</span><span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">;</span> <span class="token property">grid-row-start</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token property">grid-row-end</span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>这四个属性的值,除了指定为第几个网格线,还可以指定为网格线的名字。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column-start</span><span class="token punctuation">:</span> header-start<span class="token punctuation">;</span> <span class="token property">grid-column-end</span><span class="token punctuation">:</span> header-end<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,左边框和右边框的位置,都指定为网格线的名字。</p><p>这四个属性的值还可以使用<code>span</code>关键字,表示”跨越”,即左右边框(上下边框)之间跨越多少个网格。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column-start</span><span class="token punctuation">:</span> span <span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码表示,1号项目的左边框距离右边框跨越2个网格。</p><p>这与下面的代码效果完全一样。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column-end</span><span class="token punctuation">:</span> span <span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>使用这四个属性,如果产生了项目的重叠,则使用<code>z-index</code>属性指定项目的重叠顺序。</p><h4 id="grid-column,grid-row"><a href="#grid-column,grid-row" class="headerlink" title="grid-column,grid-row"></a>grid-column,grid-row</h4><p><code>grid-column</code>属性是<code>grid-column-start</code>和<code>grid-column-end</code>的合并简写形式,<code>grid-row</code>属性是<code>grid-row-start</code>属性和<code>grid-row-end</code>的合并简写形式。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column</span><span class="token punctuation">:</span> &lt;start-line> / &lt;end-line><span class="token punctuation">;</span> <span class="token property">grid-row</span><span class="token punctuation">:</span> &lt;start-line> / &lt;end-line><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>下面是一个例子。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column</span><span class="token punctuation">:</span> <span class="token number">1</span> / <span class="token number">3</span><span class="token punctuation">;</span> <span class="token property">grid-row</span><span class="token punctuation">:</span> <span class="token number">1</span> / <span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">/* 等同于 */</span><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column-start</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token property">grid-column-end</span><span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">;</span> <span class="token property">grid-row-start</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token property">grid-row-end</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,项目<code>item-1</code>占据第一行,从第一根列线到第三根列线。</p><p>这两个属性之中,也可以使用<code>span</code>关键字,表示跨越多少个网格。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#b03532</span><span class="token punctuation">;</span> <span class="token property">grid-column</span><span class="token punctuation">:</span> <span class="token number">1</span> / <span class="token number">3</span><span class="token punctuation">;</span> <span class="token property">grid-row</span><span class="token punctuation">:</span> <span class="token number">1</span> / <span class="token number">3</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">/* 等同于 */</span><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#b03532</span><span class="token punctuation">;</span> <span class="token property">grid-column</span><span class="token punctuation">:</span> <span class="token number">1</span> / span <span class="token number">2</span><span class="token punctuation">;</span> <span class="token property">grid-row</span><span class="token punctuation">:</span> <span class="token number">1</span> / span <span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,项目<code>item-1</code>占据的区域,包括第一行 + 第二行、第一列 + 第二列。</p><p>斜杠以及后面的部分可以省略,默认跨越一个网格。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-column</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token property">grid-row</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,项目<code>item-1</code>占据左上角第一个网格。</p><h4 id="grid-area"><a href="#grid-area" class="headerlink" title="grid-area"></a>grid-area</h4><p><code>grid-area</code>属性指定项目放在哪一个区域。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> e<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>上面代码中,1号项目位于<code>e</code>区域。</p><p><code>grid-area</code>属性还可用作<code>grid-row-start</code>、<code>grid-column-start</code>、<code>grid-row-end</code>、<code>grid-column-end</code>的合并简写形式,直接指定项目的位置。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> &lt;row-start> / &lt;column-start> / &lt;row-end> / &lt;column-end><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p>下面是一个例子。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> <span class="token number">1</span> / <span class="token number">1</span> / <span class="token number">3</span> / <span class="token number">3</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><h4 id="justify-self,align-self,place-self"><a href="#justify-self,align-self,place-self" class="headerlink" title="justify-self,align-self,place-self"></a>justify-self,align-self,place-self</h4><p><code>justify-self</code>属性设置单元格内容的水平位置(左中右),跟<code>justify-items</code>属性的用法完全一致,但只作用于单个项目。</p><p><code>align-self</code>属性设置单元格内容的垂直位置(上中下),跟<code>align-items</code>属性的用法完全一致,也是只作用于单个项目。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item</span> </span><span class="token punctuation">{</span> <span class="token property">justify-self</span><span class="token punctuation">:</span> start | end | center | stretch<span class="token punctuation">;</span> <span class="token property">align-self</span><span class="token punctuation">:</span> start | end | center | stretch<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>这两个属性都可以取下面四个值。</p><blockquote><ul><li>start:对齐单元格的起始边缘。</li><li>end:对齐单元格的结束边缘。</li><li>center:单元格内部居中。</li><li>stretch:拉伸,占满单元格的整个宽度(默认值)。</li></ul></blockquote><p>下面是<code>justify-self: start</code>的例子。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.item-1</span> </span><span class="token punctuation">{</span> <span class="token property">justify-self</span><span class="token punctuation">:</span> start<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></blockquote><p><code>place-self</code>属性是<code>align-self</code>属性和<code>justify-self</code>属性的合并简写形式。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">place-self</span><span class="token punctuation">:</span> &lt;align-self> &lt;justify-self><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>下面是一个例子。</p><blockquote><pre class="line-numbers language-css"><code class="language-css"><span class="token property">place-self</span><span class="token punctuation">:</span> center center<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><p>如果省略第二个值,<code>place-self</code>属性会认为这两个值相等。</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;blockquote&gt;
&lt;p&gt;这篇文章内容来自阮一峰的&lt;a href=&quot;http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?20200823152905#comment-last&quot;&gt;Flex 布局教程:语法篇&lt;/a&gt;
</summary>
<category term="CSS" scheme="http://ltara.gitee.io/categories/CSS/"/>
<category term="CSS" scheme="http://ltara.gitee.io/tags/CSS/"/>
<category term="flex" scheme="http://ltara.gitee.io/tags/flex/"/>
<category term="grid" scheme="http://ltara.gitee.io/tags/grid/"/>
</entry>
<entry>
<title>CSS基础之position</title>
<link href="http://ltara.gitee.io/posts/61759.html"/>
<id>http://ltara.gitee.io/posts/61759.html</id>
<published>2020-08-22T05:46:51.000Z</published>
<updated>2020-08-22T09:00:02.101Z</updated>
<content type="html"><![CDATA[<h1 id="position"><a href="#position" class="headerlink" title="position"></a>position</h1><blockquote><p>用于指定一个元素在文档中的定位方式。<code>top</code>,<code>right</code>,<code>bottom</code> 和<code>left</code> 属性则决定了该元素的最终位置。</p></blockquote><h2 id="定位类型"><a href="#定位类型" class="headerlink" title="定位类型"></a>定位类型</h2><ul><li><strong>定位元素(positioned element)</strong>是其计算后位置属性为 <code>relative</code>, <code>absolute</code>, <code>fixed</code>或 <code>sticky</code> 的一个元素(换句话说,除<code>static</code>以外的任何东西)。</li><li><strong>相对定位元素(relatively positioned element)</strong>是计算后位置属性为 <code>relative</code>的元素。</li><li><strong>绝对定位元素(absolutely positioned element)</strong>是计算后位置属性为 <code>absolute</code> 或 <code>fixed</code> 的元素。</li><li><strong>粘性定位元素(stickily positioned element)</strong>是计算后位置属性为 <code>sticky</code> 的元素。</li></ul><h2 id="取值"><a href="#取值" class="headerlink" title="取值"></a>取值</h2><pre class="line-numbers language-html"><code class="language-html"><span class="token doctype">&lt;!DOCTYPE html></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>width<span class="token punctuation">=</span>device-width, initial-scale<span class="token punctuation">=</span>1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Document<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>./index.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style language-css"> <span class="token comment" spellcheck="true">/* 便于观察固定定位和粘性定位元素(fixed、sticky) */</span> <span class="token selector">body </span><span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">2000</span>px<span class="token punctuation">;</span> <span class="token punctuation">}</span> </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>box<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>target<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="static"><a href="#static" class="headerlink" title="static"></a>static</h3><p><strong>该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置</strong>。此时 <code>top</code>, <code>right</code>, <code>bottom</code>, <code>left</code> 和 <code>z-index</code>属性无效。</p><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">400</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">200</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">10</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token id">#target</span> </span><span class="token punctuation">{</span> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token hexcode">#f00</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="relative"><a href="#relative" class="headerlink" title="relative"></a>relative</h3><p><strong>该关键字下,元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置</strong>(因此会在此元素未添加定位时所在位置留下空白)。position:relative 对 table-*-group, table-row, table-column, table-cell, table-caption 元素无效。</p><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">400</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">200</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">10</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token id">#target</span> </span><span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/* 设置 position 属性为 relative */</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token hexcode">#f00</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>relative 是相对正常文档流的位置进行偏移,原先占据的位置依然存在,也就是说它不会影响后面元素的位置。<strong>当left和right同时存在,仅left有效,当top和bottom同时存在仅top有效</strong>。relative的偏移是<strong>基于自身元素</strong>的margin左上侧的。</p><h3 id="absolute"><a href="#absolute" class="headerlink" title="absolute"></a>absolute</h3><p><strong>元素会被移出正常文档流,并不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置</strong>。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。</p><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/* width: 400px; */</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">200</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#00f</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">10</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token id">#target</span> </span><span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/* 设置 position 属性为 absolute */</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token hexcode">#f00</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当把一个元素 position 属性设置为 absolute 的时候,会发生三件事:</p><ol><li>把该元素往 Z 轴方向移了一层,元素<strong>脱离了普通流</strong>,所以不再占据原来那层的空间,还会<strong>覆盖下层的元素</strong>。</li><li>该<strong>元素将变为块级元素</strong>,相当于给该元素设置了 display: block;(给一个内联元素,如 &lt;span&gt; ,设置 absolute 之后发现它可以设置宽高了)。</li><li><strong>如果该元素是块级元素,并且没有手动设置宽度,那么元素的宽度由原来的 width: 100%(占据一行),变为了 auto</strong>。</li></ol><p>如果父元素设定了margin,border,padding等属性,那么这个定位点将忽略padding,<strong>将会从padding开始的地方(即只从padding的左上角开始)进行定位</strong>,这与我们会想当然的以为会以margin的左上端开始定位的想法是不同的。</p><h3 id="fixed"><a href="#fixed" class="headerlink" title="fixed"></a>fixed</h3><p><strong>元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置</strong>。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。<code>fixed</code> 属性会创建新的层叠上下文。当元素祖先的 <code>transform</code>, <code>perspective</code> 或 <code>filter</code> 属性非 <code>none</code> 时,容器由视口改为该祖先。</p><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">400</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">200</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">10</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token id">#target</span> </span><span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/* 设置 position 属性为 fixed */</span> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token hexcode">#f00</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>同 position</p><h3 id="sticky"><a href="#sticky" class="headerlink" title="sticky"></a>sticky</h3><p><strong>元素根据正常文档流进行定位,然后相对它的<em>最近滚动祖先(nearest scrolling ancestor)</em>和 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_Block">containing block</a> (最近块级祖先 nearest block-level ancestor),包括table-related元素,基于<code>top</code>, <code>right</code>, <code>bottom</code>, 和 <code>left</code>的值进行偏移</strong>。偏移值不会影响任何其他元素的位置。</p><p>该值总是创建一个新的<a href="https://developer.mozilla.org/en/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context">层叠上下文(stacking context</a>)。注意,一个sticky元素会“固定”在离它最近的一个拥有“滚动机制”的祖先上(当该祖先的<code>overflow</code> 是 <code>hidden</code>, <code>scroll</code>, <code>auto</code>, 或 <code>overlay</code>时),即便这个祖先不是最近的真实可滚动祖先。这有效地抑制了任何“sticky”行为。</p><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">400</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">200</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">10</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token id">#target</span> </span><span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/* 设置 position 属性为 sticky */</span> <span class="token property">position</span><span class="token punctuation">:</span> sticky<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token hexcode">#f00</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="inherit"><a href="#inherit" class="headerlink" title="inherit"></a>inherit</h3><p><strong>规定应该从父元素继承 position 属性的值</strong>。</p><pre class="line-numbers language-css"><code class="language-css"><span class="token selector"><span class="token class">.wrapper</span> </span><span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token number">50</span>px<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">400</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">200</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#00f</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token class">.box</span> </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span>px<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">5</span>px solid <span class="token hexcode">#000</span><span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">10</span>px<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector"><span class="token id">#target</span> </span><span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/* 设置 position 属性为 inhreit 该例即为 position: absolute; */</span> <span class="token property">position</span><span class="token punctuation">:</span> inhreit<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> <span class="token hexcode">#f00</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/position">MDN-position</a></p><p><a href="https://segmentfault.com/a/1190000010699788">CSS之position详解</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;position&quot;&gt;&lt;a href=&quot;#position&quot; class=&quot;headerlink&quot; title=&quot;position&quot;&gt;&lt;/a&gt;position&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;用于指定一个元素在文档中的定位方式。&lt;code&gt;top&lt;/code
</summary>
<category term="CSS" scheme="http://ltara.gitee.io/categories/CSS/"/>
<category term="CSS" scheme="http://ltara.gitee.io/tags/CSS/"/>
</entry>
<entry>
<title>JavaScript基础之原型/继承</title>
<link href="http://ltara.gitee.io/posts/19673.html"/>
<id>http://ltara.gitee.io/posts/19673.html</id>
<published>2020-08-19T05:34:46.000Z</published>
<updated>2020-08-22T06:17:49.412Z</updated>
<content type="html"><![CDATA[<h1 id="原型"><a href="#原型" class="headerlink" title="原型"></a>原型</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><blockquote><p>所有的引用类型的数据都有 <code>__proto__</code> 这个属性,该属性即为<strong>隐式原型</strong>,所有的函数都有 <code>prototype</code> 属性,该属性即为<strong>显式原型</strong>。</p></blockquote><p>这两个属性分别是什么?有什么联系?</p><h2 id="prototype"><a href="#prototype" class="headerlink" title="prototype"></a>prototype</h2><p>我们从原型的定义上知道所有的函数都有 <code>prototype</code> 属性,输出一个函数看看:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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><span class="token punctuation">}</span>console<span class="token punctuation">.</span><span class="token function">dir</span><span class="token punctuation">(</span>Foo<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>观察输出结果,我们发现了 <code>__proto__</code> 、 <code>prototype</code> 这两个不可枚举属性,先不管 <code>__proto__</code> 这个属性,继续查看 <code>prototype</code> 属性。<code>prototype</code> 有只有两个属性:<code>__proto__</code>、<code>constructor</code>,这意味着 <code>prototype</code> 指向了一个对象,继续研究 <code>constructor</code> ,我们发现这个属性指向了函数本身,确定一下:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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><span class="token punctuation">}</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Foo<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Foo<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>好了,我们知道了 <strong><code>prototype</code> 这个属性指向一个对象,这个对象默认会拥有两个不可枚举属性 <code>__proto__</code>、<code>constructor</code> ,其中的 <code>constructor</code> 指向函数本身</strong>。</p><p>了解了 <code>prototype</code> 属性,你肯定会想去了解我们一直略过的 <code>__proto__</code> 属性,别着急我们先从构造函数说起。</p><h2 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h2><h3 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h3><p>在 JavaScript 中并没有类的概念,但有类的模拟实现,也就是我们常说的构造函数。</p><p><strong>构造函数就是一个普通的函数,只不过为了便于区分,我们将构造函数的首字母大写</strong>。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 这是一个构造函数</span><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><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 这也是一个构造函数</span><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><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>但是这样解释或许有点让人费解,<code>函数 === 构造函数</code>?</p><p>我们换一种更准确的说法:<strong>函数不是构造函数,但是当且仅当使用 new 时,函数调用会变成“构造函数调用”</strong>。</p><h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><p>构造函数有什么用?观察以下代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>weight <span class="token operator">=</span> <span class="token number">50</span><span class="token keyword">const</span> person1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">'Jack'</span><span class="token punctuation">,</span> <span class="token number">18</span><span class="token punctuation">)</span><span class="token keyword">const</span> person2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">'Mick'</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>person1<span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>person2<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>我们发现通过 <code>new</code> 操作符,实例化的对象拥有构造器属性 <code>name</code>、<code>age</code> 和原型属性 <code>weight</code> ,但是 <code>weight</code> 属性并不在实例对象中而是在实例对象的 <code>__proto__</code> 属性所指的对象中。</p><p>原型属性并不需要我们去传值,而是直接从构造函数的 <code>prototype</code> 中获取,这个操作我们称之为“继承”,“继承”的行为是在 <code>new</code> 操作符内部实现的。</p><p>使用 <code>new</code> 来调用函数,或者说发生构造函数调用时:</p><ol><li>创建一个全新的对象</li><li>将新对象连接到构造函数的 <code>prototype</code> 所指的对象</li><li>这个新对象会绑定到函数调用的 <code>this</code></li><li>这个函数没有返回其他对象,那么 <code>new</code> 表达式中的函数调用会自动返回这个新对象</li></ol><p>我们发现了一个很微妙的事情:<strong>原型属性既在构造函数的 <code>prototype</code> ,又在其实例化对象的 <code>__proto__</code> 中</strong>。</p><p>那么这两者有什么联系?<code>__proto__</code> 到底是什么?</p><h2 id="proto"><a href="#proto" class="headerlink" title="_proto_"></a>_<em>proto_</em></h2><p>我们从原型的定义上知道所有的引用类型的数据都有 <code>__proto__</code> 属性,输出一个对象看看:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">const</span> obj <span class="token operator">=</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>obj<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>观察输出结果,我们发现这个对象只拥有一个不可枚举属性 <code>__proto__</code> ,继续查看 <code>__proto__</code> 属性,发现都是一些不可枚举属性都指向了 <code>Object</code> 的方法,其中有一个属性比较特殊 <code>constructor</code>,在之前构造函数的 <code>prototype</code> 上出现了 <code>constructor</code>,那么这个 <code>constructor</code> 指向谁呢?在上面的代码中它指向了 <code>Object</code> ,这是否意味着这个“空对象”的构造函数就是 <code>Object</code> 呢?更进一步是否构造函数的 <code>prototype</code> 就是其实例化的对象的 <code>__proto__</code> 呢?</p><p>观察下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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><span class="token punctuation">}</span><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</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>Foo<span class="token punctuation">.</span>prototype <span class="token operator">===</span> foo<span class="token punctuation">.</span>__proto__<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// true</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>foo<span class="token punctuation">.</span>__proto__<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Foo<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>我们可以确定:</p><blockquote><p><code>__proto__</code>属性是该引用类型数据的构造函数的 <code>prototype</code> 属性</p></blockquote><h3 id="constructor-NaN"><a href="#constructor-NaN" class="headerlink" title="constructor"></a>constructor</h3><p>在构造函数的 <code>prototype</code> 属性中,<code>constructor</code> 指向构造函数本身;在实例对象中,其 <code>__proto__</code> 的 <code>constructor</code> 指向其构造函数,我们认为这两者是一个意思。</p><p>但真的是一个意思吗?观察下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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><span class="token punctuation">}</span>Foo<span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</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>obj<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Foo<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// false</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Object<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>看起来应该是 <code>Foo()</code> 构造了 <code>obj</code> 对象,但是实质上是 <code>Object()</code> 构造的。</p><p><code>Foo</code> 的原型被更改为一个“空对象”,那么在 <code>new</code> 这个函数的时候,创建的新对象连接到了这个“空对象”上,这个“空对象”不是函数,那么它会去找这个“空对象”的原型 <code>__proto__</code> ,这个原型是函数(<code>Object</code>),于是连接到到这个函数,<code>constructor</code> 指向这个函数。这其实就是原型链。</p><h2 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h2><blockquote><p>每个对象都拥有一个原型对象,对象的原型可能也是继承其他原型对象,一层一层的,以此类推,这种关系就是原型链。</p></blockquote><blockquote><p>所有的原型( <code>Object.prototype</code> 除外)都是对象,所以原型对象的构造函数都为 <code>Object()</code>。</p><p><code>Object.prototype</code> 的隐式原型 <code>__proto__</code> 是 <code>null</code>。即原型链的顶端为 <code>null</code>。</p></blockquote><h3 id="包装对象"><a href="#包装对象" class="headerlink" title="包装对象"></a>包装对象</h3><p>我们知道 JavaScript 中有8种数据类型:</p><ul><li>原始类型:String、Number、Boolean、Undefined、Null、BigInt、Symbol</li><li>引用类型:Object(Object、Array、Date、Math)</li></ul><p>那么这些数据都是怎么产生的,例如,<code>let a = 'Hello World'</code>,JavaScript 怎么将 <code>a</code> 声明为字符串类型,<code>a</code> 变量上的方法又是哪来的?</p><p>实际上,在我们创建一个原始数据类型时,JavaScript 底层创建一个基于此类型的包装对象的实例:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'echo'</span><span class="token keyword">var</span> name_ <span class="token operator">=</span> name<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment" spellcheck="true">// 创建 String 实例,将实例赋予变量 name</span><span class="token comment" spellcheck="true">// 在实例上调用指定的方法</span><span class="token comment" spellcheck="true">// 销毁这个实例</span><span class="token keyword">var</span> string <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">'echo'</span><span class="token punctuation">)</span><span class="token keyword">var</span> name <span class="token operator">=</span> string<span class="token keyword">var</span> name_ <span class="token operator">=</span> name<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span>string <span class="token operator">=</span> <span class="token keyword">null</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// echo</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>name_<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// ECHO</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>String、Number、Boolean属于包装对象,包装对象是一种声明周期只有一瞬的对象,创建与销毁都由底层实现。</p></blockquote><blockquote><p>Undefined、Null、BigInt、Symbol不属于包装对象。</p></blockquote><p>既然原始数据类型有包装对象,那么引用类型呢?</p><p>引用类型中,不存在所谓的包装对象,但是有各种各样的”包装类“——构造函数。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">const</span> arr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">const</span> reg <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">const</span> fn <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Function</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>obj<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// {}</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>arr<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// []</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>reg<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// /(?:)/</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// ƒ anonymous() {}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="原型链-1"><a href="#原型链-1" class="headerlink" title="原型链"></a>原型链</h3><p>这些包装对象和”包装类“本身就是构造函数,那么它们也是被 Function() 构造出来的:</p><blockquote><p>原始构造函数<code>Function()</code>扮演着创世主女娲的角色,她创造了Object()、Number()、String()、Date()、function fn(){}等第一批人类(也就是构造函数),而人类同样具备了繁衍的能力(使用new操作符),于是Number()繁衍出了数据类型数据,String()诞生了字符串,function fn(){}作为构造函数也诞生了各种各样的对象后代。</p></blockquote><p>我们可以通过如下代码论证这一点:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 所有函数对象的 __proto__ 都指向 Function.prototype,包括Function本身</span>Number<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Function<span class="token punctuation">.</span>prototype <span class="token comment" spellcheck="true">//true</span>Number<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Function <span class="token comment" spellcheck="true">//true</span>String<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Function<span class="token punctuation">.</span>prototype <span class="token comment" spellcheck="true">//true</span>String<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Function <span class="token comment" spellcheck="true">//true</span>Object<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Function<span class="token punctuation">.</span>prototype <span class="token comment" spellcheck="true">//true</span>Object<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Function <span class="token comment" spellcheck="true">//true</span>Array<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Function<span class="token punctuation">.</span>prototype <span class="token comment" spellcheck="true">//true</span>Array<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Function <span class="token comment" spellcheck="true">//true</span>Function<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Function<span class="token punctuation">.</span>prototype <span class="token comment" spellcheck="true">//true</span>Function<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Function <span class="token comment" spellcheck="true">//true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>所以当实例访问某个属性时,会先查找自己有没有,如果没有就通过 <code>__proto__</code>访问自己构造函数的 <code>prototype</code> 有没有,前面说构造函数的原型是一个对象,如果原型对象也没有,就继续顺着构造函数 <code>prototype</code> 中的<code>__proto__</code>继续查找到构造函数 <code>Object()</code> 的原型,再看有没有,如果还没有,就返回 <code>undefined</code>,因为再往上就是 <code>null</code> 了,这个过程就是我们熟知的原型链,说的再准确点,就是<code>__proto__</code> 访问过程构成了原型链。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>Object<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>a <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">)</span><span class="token punctuation">}</span>Function<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>b <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'b'</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span>f<span class="token punctuation">.</span><span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// a</span><span class="token comment" spellcheck="true">/* f 本身没有找到 a 方法,那么会去 f 的隐式原型(__proto__)上找 --> 如果还是没有,那么会去 F 的隐式原型(__proto__)上找 --> Object 的显示原型上恰好有 a 方法*/</span>f<span class="token punctuation">.</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// referenceError:b is not defined</span><span class="token comment" spellcheck="true">/* f 本身没有找到 b 方法,那么会去 f 的隐式原型(__proto__)上找 --> 如果还是没有,那么会去 F 的隐式原型(__proto__)上找 --> Object 的显式原型上还是没有 b 方法 ,由于 Object.prototype 的 __proto__ 是 null ,因此报错,没有 b 方法(b未定义)*/</span>F<span class="token punctuation">.</span><span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// a</span>F<span class="token punctuation">.</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// b</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="instanceof"><a href="#instanceof" class="headerlink" title="instanceof"></a>instanceof</h3><blockquote><p><code>instanceof</code> 会一直寻找到原型链的末端,直到找到 <code>__proto__</code> 等于 <code>prototype</code> 返回 <code>true</code> ,否则返回 <code>false</code></p></blockquote><blockquote><p><code>instanceof</code> 左边如果不是引用类型的话,会直接返回 <code>false</code></p></blockquote><blockquote><p><code>Object</code> 是由 <code>Function()</code> 构造出来的,即 <code>Object.__proto__</code> 指向 <code>Function</code></p></blockquote><p>思考下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript">Function <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment" spellcheck="true">// true</span>Function <span class="token keyword">instanceof</span> <span class="token class-name">Function</span> <span class="token comment" spellcheck="true">// true</span>Object <span class="token keyword">instanceof</span> <span class="token class-name">Function</span> <span class="token comment" spellcheck="true">// true</span>Object <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment" spellcheck="true">//true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>分别解释一下:</p><ul><li><strong><code>Function instanceof Object // true</code></strong><ol><li>去找 <code>Function</code> 的 <code>__proto__</code> 的属性,看看是否为 <code>Object</code>,发现不是</li><li>去找 <code>Function</code> 原型对象的 <code>__proto__</code> 属性,看看是否为 <code>Object</code>,发现是,返回 <code>true</code></li></ol></li><li><strong><code>Function instanceof Function // true</code></strong><ol><li>去找 <code>Function</code> 的 <code>__proto__</code> 的属性,看看是否为 <code>Function</code>,发现是,返回 <code>true</code></li></ol></li><li><strong><code>Object instanceof Function // true</code></strong><ol><li>去找 <code>Object</code> 的 <code>__proto__</code> 的属性,看看是否为 <code>Function</code>,发现是,返回 <code>true</code></li></ol></li><li><strong><code>Object instanceof Object //true</code></strong><ol><li>去找 <code>Object</code> 的 <code>__proto__</code> 的属性,看看是否为 <code>Object</code>,发现不是</li><li>去找 <code>Function</code> 原型对象的 <code>__proto__</code> 属性,看看是否为 <code>Object</code>,发现是,返回 <code>true</code></li></ol></li></ul><h1 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h1><h2 id="原型链继承"><a href="#原型链继承" class="headerlink" title="原型链继承"></a>原型链继承</h2><blockquote><p><strong>子类型的原型为父类型的一个实例对象</strong></p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 父类型</span><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>play <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</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 keyword">this</span><span class="token punctuation">.</span>setName <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><span class="token punctuation">}</span><span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 子类型</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>price<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price <span class="token keyword">this</span><span class="token punctuation">.</span>setScore <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><span class="token punctuation">}</span><span class="token punctuation">}</span>Student<span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 子类型的原型为父类型的一个实例对象</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token number">15000</span><span class="token punctuation">)</span><span class="token keyword">var</span> s2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token number">14000</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">,</span> s2<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>但这种方式实现的本质是通过将子类的原型指向了父类的实例,所以<strong>子类的实例就可以通过 <code>__proto__</code> 访问到 <code>Student.prototype</code> 也就是 <code>Person</code> 的实例,这样就可以访问到父类的私有方法,然后再通过 <code>__proto__</code> 指向父类的 <code>prototype</code> 就可以获得到父类原型上的方法</strong>。于是做到了将父类的私有、公有方法和属性都当做子类的公有属性</p><p><strong>子类继承父类的属性和方法是将父类的私有属性和公有方法都作为自己的公有属性和方法</strong>,我们都知道在操作基本数据类型的时候操作的是值,在操作引用数据类型的时候操作的是地址,如果说父类的私有属性中有引用类型的属性,那它被子类继承的时候会作为公有属性,这样子类1操作这个属性的时候,就会影响到子类2。</p><pre class="line-numbers language-javascript"><code class="language-javascript">s1<span class="token punctuation">.</span>play<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">,</span> s2<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>注意:<strong>我们需要在子类中添加新的方法或者是重写父类的方法时候,切记一定要放到替换原型的语句之后</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"111"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>price<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price <span class="token keyword">this</span><span class="token punctuation">.</span>setScore <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> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// Student.prototype.sayHello = function () {}</span><span class="token comment" spellcheck="true">// 在这里写子类的原型方法和属性是无效的,</span><span class="token comment" spellcheck="true">// 因为会改变原型的指向,所以应该放到重新指定之后</span>Student<span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span>Student<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>sayHello <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> <span class="token punctuation">}</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token number">15000</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>特点:</strong></p><ul><li>父类新增原型方法/原型属性,子类都能访问到</li><li>简单,易于实现</li></ul><p><strong>缺点:</strong></p><ul><li>无法实现多继承</li><li>来自原型对象的所有属性被所有子类实例共享</li><li>子类实例改变继承的父类实例属性或方法时会影响另一个子类实例</li><li>创建子类实例时,无法向父类构造函数传参</li><li>要想为子类新增属性和方法,必须要在<code>Student.prototype = new Person()</code> 之后执行,不能放到构造器中</li></ul><h2 id="利用构造函数继承"><a href="#利用构造函数继承" class="headerlink" title="利用构造函数继承"></a>利用构造函数继承</h2><blockquote><p><strong>在子类型构造函数中通过call()调用父类型构造函数</strong></p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>setName <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><span class="token punctuation">}</span><span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> Person<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 相当于:</span> <span class="token comment" spellcheck="true">// this.name = name</span> <span class="token comment" spellcheck="true">// this.age = age</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price<span class="token punctuation">}</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'Tom'</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">15000</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>特点:</strong></p><ul><li>解决了原型链继承中子类实例共享父类引用属性的问题</li><li>创建子类实例时,可以向父类传递参数</li><li>可以实现多继承(call多个父类对象)</li></ul><p><strong>缺点:</strong></p><ul><li>实例并不是父类的实例,只是子类的实例</li><li>只能继承父类的实例属性和方法,不能继承原型属性和方法</li><li>无法实现函数复用,每个子类都有父类实例函数的副本,影响性能</li></ul><h2 id="组合继承"><a href="#组合继承" class="headerlink" title="组合继承"></a>组合继承</h2><blockquote><p><strong>通过调用父类构造函数,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用</strong></p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>setAge <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> <span class="token punctuation">}</span><span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"111"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> Person<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span>name<span class="token punctuation">,</span>age<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 第二次调用父类构造函数</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price <span class="token keyword">this</span><span class="token punctuation">.</span>setScore <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> <span class="token punctuation">}</span><span class="token punctuation">}</span>Student<span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 第一次调用父类构造函数</span>Student<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> Student <span class="token comment" spellcheck="true">// 组合继承也是需要修复构造函数指向的</span>Student<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>sayHello <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><span class="token punctuation">}</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'Tom'</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">15000</span><span class="token punctuation">)</span><span class="token keyword">var</span> s2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'Jack'</span><span class="token punctuation">,</span> <span class="token number">22</span><span class="token punctuation">,</span> <span class="token number">14000</span><span class="token punctuation">)</span><span class="token keyword">var</span> p1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">'Mick'</span><span class="token punctuation">,</span> <span class="token number">24</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">,</span> s2<span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">.</span>constructor<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// Student</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>p1<span class="token punctuation">.</span>constructor<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// Person</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>优点</strong>:</p><ul><li>可以继承实例属性/方法,也可以继承原型属性/方法</li><li>不存在引用属性共享问题</li><li>可传参</li><li>函数可复用</li></ul><p><strong>缺点</strong>:</p><ul><li>调用了两次父类构造函数,生成了两份实例</li></ul><h3 id="改进1"><a href="#改进1" class="headerlink" title="改进1"></a>改进1</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>setAge <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> <span class="token punctuation">}</span><span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"111"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> Person<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price <span class="token keyword">this</span><span class="token punctuation">.</span>setScore <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> <span class="token punctuation">}</span><span class="token punctuation">}</span>Student<span class="token punctuation">.</span>prototype <span class="token operator">=</span> Person<span class="token punctuation">.</span>prototypeStudent<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>sayHello <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> <span class="token punctuation">}</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'Tom'</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">15000</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种方式通过父类原型和子类原型指向同一对象,子类可以继承到父类的公有方法当做自己的公有方法,而且不会初始化两次实例方法/属性,避免的组合继承的缺点。</p><p>但是这种方法无法辨别实例是子类还是父类创造的,子类和父类的构造函数指向是同一个。</p><pre class="line-numbers language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">.</span>constructor<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// Person</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这种做法甚至不算继承,只是形似继承,不需要 <code>Student</code> ,只需要将 <code>Student</code> 上的属性和方法全都写到 <code>Person</code> 上就能用 <code>Person</code> 创建任何想要的实例对象,代码还更简单一些。</p><h3 id="改进2"><a href="#改进2" class="headerlink" title="改进2"></a><span id="jump">改进2</span></h3><blockquote><p><strong>借助原型可以基于已有的对象来创建对象,<code>var B = Object.create(A)</code> 以A对象为原型,生成了B对象。B继承了A的所有属性和方法。</strong></p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"111"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> Person<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price <span class="token keyword">this</span><span class="token punctuation">.</span>setScore <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><span class="token punctuation">}</span><span class="token punctuation">}</span>Student<span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 核心代码</span>Student<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> Student <span class="token comment" spellcheck="true">// 核心代码</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'Tom'</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">15000</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1 <span class="token keyword">instanceof</span> <span class="token class-name">Student</span><span class="token punctuation">,</span> s1 <span class="token keyword">instanceof</span> <span class="token class-name">Person</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// true true</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">.</span>constructor<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// Student</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>完美解决组合继承的缺点。这样做的唯一缺点是需要创建一个新对象然后把旧对象抛弃掉,不能修改已有的默认对象。</p><p>ES6之后可以通过 <code>Object.setPrototypeOf()</code> 来修改:</p><pre class="line-numbers language-javascript"><code class="language-javascript">Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span>Student<span class="token punctuation">.</span>prototype<span class="token punctuation">,</span> Person<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token comment" spellcheck="true">// 替换下面的代码</span><span class="token comment" spellcheck="true">// Student.prototype = Object.create(Person.prototype) // 核心代码</span><span class="token comment" spellcheck="true">// Student.prototype.constructor = Student // 核心代码</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="原型式继承"><a href="#原型式继承" class="headerlink" title="原型式继承"></a>原型式继承</h2><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">object</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">function</span> <span class="token function">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> F<span class="token punctuation">.</span>prototype <span class="token operator">=</span> o <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>object()</code> 就是 <code>Object.create()</code> 的 <code>polyfill</code> 代码。<code>object()</code> 对传入其中的对象执行了一次<code>浅复制</code>,将构造函数 F 的原型直接指向传入的对象。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> person <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'Jack'</span><span class="token punctuation">,</span> friends<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'Tom'</span><span class="token punctuation">,</span> <span class="token string">'Mick'</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token keyword">var</span> anotherPerson <span class="token operator">=</span> <span class="token function">object</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>anotherPerson<span class="token punctuation">.</span>friends<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// ['Tom', 'Mick']</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种模式要去你必须有一个对象作为另一个对象的基础</p><p>在这个例子中,<code>person</code> 作为另一个对象的基础,把 <code>person</code> 传入 <code>object</code> 中,该函数就会返回一个新的对象</p><p>这个新对象将 <code>person</code> 作为原型,所以它的原型中就包含一个基本类型和一个引用类型</p><p>所以意味着如果还有另外一个对象关联了 <code>person</code>,<code>anotherPerson</code> 修改数组 <code>friends</code> 的时候,也会体现在这个对象中。</p><p><strong>缺点:</strong></p><ul><li>原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。</li><li>无法传递参数</li></ul><h2 id="寄生式继承"><a href="#寄生式继承" class="headerlink" title="寄生式继承"></a>寄生式继承</h2><p>寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">createAnother</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> clone <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 创建一个新对象</span> clone<span class="token punctuation">.</span>sayHi <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> <span class="token comment" spellcheck="true">// 添加方法</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'hi'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> clone <span class="token comment" spellcheck="true">// 返回这个对象</span><span class="token punctuation">}</span><span class="token keyword">var</span> person <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'Jack'</span><span class="token punctuation">}</span><span class="token keyword">var</span> anotherPeson <span class="token operator">=</span> <span class="token function">createAnother</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span>anotherPeson<span class="token punctuation">.</span><span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// hi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>基于 <code>person</code> 返回了一个新对象 <code>anotherPeson</code>,新对象不仅拥有了 <code>person</code> 的属性和方法,还有自己的 <code>sayHi</code> 方法</p><p>在主要考虑对象而不是自定义类型和构造函数的情况下,这是一个有用的模式</p><p><strong>缺点(同原型式继承):</strong></p><ul><li>原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。</li><li>无法传递参数</li></ul><h2 id="寄生组合式继承"><a href="#寄生组合式继承" class="headerlink" title="寄生组合式继承"></a>寄生组合式继承</h2><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">}</span>Person<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>setAge <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"111"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Student</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 继承父类实例属性</span> Person<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 自身的实例属性和方法</span> <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price <span class="token keyword">this</span><span class="token punctuation">.</span>setScore <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><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 使用寄生式继承来继承父类的原型,在将结果指定给子类型的原型</span><span class="token keyword">function</span> <span class="token function">inheritPrototype</span><span class="token punctuation">(</span>subType<span class="token punctuation">,</span> superType<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>superType<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span> prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> subType subType<span class="token punctuation">.</span>prototype <span class="token operator">=</span> prototype<span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 继承</span><span class="token function">inheritPrototype</span><span class="token punctuation">(</span>Student<span class="token punctuation">,</span> Person<span class="token punctuation">)</span><span class="token keyword">var</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'Jack'</span><span class="token punctuation">,</span> <span class="token number">18</span><span class="token punctuation">,</span> <span class="token number">15000</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用 <code>Object.create()</code> 来实现:<a href="#jump">组合继承改进2</a></p><h2 id="ES6中-class-extends-继承"><a href="#ES6中-class-extends-继承" class="headerlink" title="ES6中 class/extends 继承"></a>ES6中 class/extends 继承</h2><p>ES6 中引入了 <code>class</code> 关键字,<code>class</code> 可以通过 <code>extends</code> 关键字实现继承,还可以通过 <code>static</code> 关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。</p><p>ES5 的继承,实质是先创造子类的实例对象 <code>this</code>,然后再将父类的方法添加到 <code>this</code> 上面( <code>Parent.apply(this)</code> )。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到 <code>this</code> 上面(所以必须先调用 <code>super</code> 方法),然后再用子类的构造函数修改 <code>this</code>。</p><p><strong>需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的</strong>。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 定义父类</span><span class="token keyword">class</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 调用类的构造方法</span> <span class="token function">constructor</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 定义一般的方法</span> <span class="token function">showName</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 string">"调用父类的方法"</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>name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">let</span> p1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">'kobe'</span><span class="token punctuation">,</span> <span class="token number">39</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>p1<span class="token punctuation">)</span><span class="token comment" spellcheck="true">// 定义一个子类</span><span class="token keyword">class</span> <span class="token class-name">Student</span> <span class="token keyword">extends</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> salary<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 通过 super 调用父类的构造方法</span> <span class="token keyword">this</span><span class="token punctuation">.</span>salary <span class="token operator">=</span> salary <span class="token punctuation">}</span> <span class="token function">showName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 在子类自身定义方法</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"调用子类的方法"</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>name<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>age<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>salary<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token string">'wade'</span><span class="token punctuation">,</span> <span class="token number">38</span><span class="token punctuation">,</span> <span class="token number">1000000000</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s1<span class="token punctuation">)</span>s1<span class="token punctuation">.</span><span class="token function">showName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://www.cnblogs.com/echolun/p/12321869.html">JS 疫情宅在家,学习不能停,七千字长文助你彻底弄懂原型与原型链,武汉加油!!中国加油!!(破音)</a></p><p>《你不知道的JavaScript上卷》</p><p><a href="https://juejin.im/post/6844903924307230727">JS基础-函数、对象和原型、原型链的关系</a></p><p><a href="https://segmentfault.com/a/1190000016708006">JavaScript常见的六种继承方式</a></p><p><a href="http://caibaojian.com/6-javascript-prototype.html">JavaScript六种继承方式详解</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;原型&quot;&gt;&lt;a href=&quot;#原型&quot; class=&quot;headerlink&quot; title=&quot;原型&quot;&gt;&lt;/a&gt;原型&lt;/h1&gt;&lt;h2 id=&quot;定义&quot;&gt;&lt;a href=&quot;#定义&quot; class=&quot;headerlink&quot; title=&quot;定义&quot;&gt;&lt;/a&gt;定义&lt;/h2&gt;&lt;block
</summary>
<category term="JavaScript" scheme="http://ltara.gitee.io/categories/JavaScript/"/>
<category term="JavaScript" scheme="http://ltara.gitee.io/tags/JavaScript/"/>
</entry>
<entry>
<title>JavaScript基础之this/call/apply/bind</title>
<link href="http://ltara.gitee.io/posts/48258.html"/>
<id>http://ltara.gitee.io/posts/48258.html</id>
<published>2020-08-17T02:45:27.000Z</published>
<updated>2020-08-22T06:17:42.060Z</updated>
<content type="html"><![CDATA[<h1 id="this"><a href="#this" class="headerlink" title="this"></a>this</h1><h2 id="this-是什么"><a href="#this-是什么" class="headerlink" title="this 是什么"></a>this 是什么</h2><blockquote><p>当一个函数被调用时,会创建一个活动记录(或者称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。<code>this</code> 就是这个记录的一个属性,会在函数执行的过程中用到。</p></blockquote><p>如果对这个解释看不太懂可以去看看<a href="https://ltara.gitee.io/posts/56269.html">JavaScript基础之执行上下文</a></p><p><strong><code>this</code> 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。</strong></p><h2 id="this-值如何确定"><a href="#this-值如何确定" class="headerlink" title="this 值如何确定"></a>this 值如何确定</h2><blockquote><p><code>this</code> 永远指向最后调用它的对象。</p></blockquote><h3 id="调用位置"><a href="#调用位置" class="headerlink" title="调用位置"></a>调用位置</h3><p>在理解 this 的绑定过程之前,首先理解调用位置:<strong>调用位置就是函数在代码中被调用的位置</strong>。</p><p>确定调用位置,最重要的时分析<strong>调用栈</strong>。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// baz 的调用位置</span><span class="token keyword">function</span> <span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 当前的调用栈是 baz</span> <span class="token comment" spellcheck="true">// 因此,当前调用位置是全局作用域</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'baz'</span><span class="token punctuation">)</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// bar 的调用位置</span><span class="token punctuation">}</span><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> <span class="token comment" spellcheck="true">// 当前的调用栈是 baz -> bar</span> <span class="token comment" spellcheck="true">// 因此,当前调用位置在 baz 中</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'bar'</span><span class="token punctuation">)</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// foo 的调用位置</span><span class="token punctuation">}</span><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> <span class="token comment" spellcheck="true">// 当前的调用栈是 baz -> bar -> foo</span> <span class="token comment" spellcheck="true">// 因此,当前调用位置在 bar 中</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="绑定规则"><a href="#绑定规则" class="headerlink" title="绑定规则"></a>绑定规则</h3><h4 id="默认绑定"><a href="#默认绑定" class="headerlink" title="默认绑定"></a>默认绑定</h4><blockquote><p>对函数进行不带任何修饰的调用,即为默认绑定。</p><p>默认绑定的 <code>this</code> 指向全局对象 <code>window</code> (非严格模式)</p></blockquote><p><strong>独立的函数调用</strong>可以看作是 <code>this</code> 的默认绑定规则。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>foo()</code> 函数是直接使用不带任何修饰的函数引用进行调用的,因此只能使用<strong>默认绑定</strong>。</p><p>通过分析调用位置,我们知道 <code>foo</code> 函数是在全局作用域中运行,那么 <code>foo</code> 函数中的 <code>this</code> 绑定的就是全局对象 <code>window</code> 。</p><p>如果使用严格模式:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> <span class="token string">"use strict"</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 comment" spellcheck="true">// undefined</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>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// TypeError: Cannot read property 'a' of undefined at foo</span><span class="token punctuation">}</span><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token string">"use strict"</span><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> 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 comment" spellcheck="true">// undefined</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>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// TypeError: Cannot read property 'a' of undefined at foo</span><span class="token punctuation">}</span><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在严格模式下,默认绑定的 <code>this</code> 会指向 <code>undefined</code>。</p><blockquote><p>虽然 <code>this</code> 的绑定规则完全取决于调用位置,但是只有 <code>foo()</code> 运行在非 strict mode 下时,默认绑定才能绑定到全局对象,在严格模式下调用 <code>foo()</code> 则不影响默认绑定。</p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token string">"use strict"</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="隐式绑定"><a href="#隐式绑定" class="headerlink" title="隐式绑定"></a>隐式绑定</h4><blockquote><p>当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 <code>this</code> 绑定到这个上下文对象。</p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> foo<span class="token punctuation">:</span> foo<span class="token punctuation">}</span>obj<span class="token punctuation">.</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>foo()</code> 函数并不属于 <code>obj</code> 对象,然而,调用位置会使用 <code>obj</code> 上下文来引用函数,因此你可以说函数被 <code>obj</code> 对象“拥有”或者“包含”函数引用。当 <code>foo()</code> 被调用时,它的前面确实加上了对 <code>obj</code> 的引用,<code>this</code> 被绑定到 <code>obj</code> ,因此 <code>this.a</code> 和 <code>obj.a</code> 是一样的。</p><p>分析下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">3</span><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> 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><span class="token keyword">var</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> foo<span class="token punctuation">:</span> foo<span class="token punctuation">}</span><span class="token keyword">var</span> obj2 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> obj<span class="token punctuation">:</span> obj1<span class="token punctuation">}</span>obj2<span class="token punctuation">.</span>obj<span class="token punctuation">.</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li><strong><code>this</code> 永远指向最后调用它的对象</strong>。</li><li>如果函数调用前存在多个对象,<strong><code>this</code> 指向距离调用自己最近的对象</strong>。</li></ul><p>实际上,上面两个表达的意思是相同的,把 <code>obj2.obj.foo()</code> 按照<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence">运算符的优先级</a>分解:</p><ol><li>先运算 <code>obj2.obj</code> 结果为 <code>obj1</code></li><li><code>obj1.foo</code> (<code>foo</code>为 <code>obj1</code> 上的属性)结果为 <code>foo</code>(全局上的函数)</li><li><code>foo()</code> 执行</li></ol><p>从上面的步骤中可以看出,最后调用 <code>foo</code> 函数的对象是 <code>obj1</code>,距离 <code>foo</code> 函数最近的对象也是 <code>obj1</code>,因此 <code>foo</code> 函数里的 <code>this</code> 指向 <code>obj1</code></p><p><strong>隐式丢失</strong></p><p>思考下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> foo<span class="token punctuation">:</span> foo<span class="token punctuation">}</span><span class="token keyword">var</span> bar <span class="token operator">=</span> obj<span class="token punctuation">.</span>foo<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token string">'window'</span><span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 'window'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>虽然 <code>bar</code> 是 <code>obj.foo</code> 的一个引用,但是实际上,它引用的是 <code>foo</code> 函数本身,因此此时的 <code>bar()</code> 其实是一个不带任何修饰的函数调用,因此引用了默认绑定。</p><p>观察以下代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">function</span> <span class="token function">doFoo</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// fn 其实引用的是foo</span> <span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 调用位置</span><span class="token punctuation">}</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> foo<span class="token punctuation">:</span> foo<span class="token punctuation">}</span><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token string">'window'</span><span class="token function">doFoo</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 'window'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>我们发现,在函数回调的过程中, this 丢失了绑定对象,这种情况更微妙,但又更常见。</p><p>参数传递是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和上面的例子一样。</p><h4 id="显示绑定"><a href="#显示绑定" class="headerlink" title="显示绑定"></a>显示绑定</h4><blockquote><p>使用函数的 call(..) 和 apply(..) 方法在某个对象上强制调用函数。</p></blockquote><p>思考下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">}</span>foo<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通过 <code>foo.call(..)</code>,我们可以在调用 <code>foo</code> 时强制把它的 <code>this</code> 绑定到 <code>obj</code> 上。</p><blockquote><p>在 JavaScript中,当我们调用一个函数时,我们习惯称之为<strong>函数调用</strong>,函数处于一个被动的状态;而 <code>call</code> 与 <code>apply</code> 让函数从被动变主动,函数能主动选择自己的上下文,所以这种写法我们又称之为<strong>函数应用</strong>。</p></blockquote><p>注意,如果在使用 <code>call</code> 之类的方法改变 <code>this</code> 指向时,指向参数提供的是 <code>null</code> 或者 <code>undefined</code> ,那么 <code>this</code> 将指向全局对象。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token string">'obj'</span><span class="token punctuation">}</span><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token string">'window'</span>foo<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 'obj'</span>foo<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 'window'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol><li><strong>硬绑定</strong></li></ol><p>绑定丢失可以通过应绑定来解决,观察以下代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> 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><span class="token keyword">function</span> <span class="token function">doFoo</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// fn 其实引用的是foo</span> fn<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 调用位置</span><span class="token punctuation">}</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> foo<span class="token punctuation">:</span> foo<span class="token punctuation">}</span><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token string">'window'</span><span class="token function">doFoo</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>我们创建了函数 <code>doFoo()</code> ,并在它的内部手动调用了 <code>foo.call(obj)</code>,因此强制把 <code>foo</code> 的 <code>this</code> 绑定到了 <code>obj</code>。</p><p>硬绑定是一种非常常见的模式,所以 ES5 提供了内置的方法 <code>Function.prototype.bind</code>,它的用法如下:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>something<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>a<span class="token punctuation">,</span> something<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>a <span class="token operator">+</span> something<span class="token punctuation">}</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token keyword">var</span> bar <span class="token operator">=</span> foo<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2 3</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 5</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>bind(..)</code> 会返回一个硬编码的新函数,它会把你指定的参数设置为 <code>this</code> 的上下文并调用原始函数。</p><ol start="2"><li><strong>API 调用“上下文”</strong></li></ol><p>在 JavaScript 的 API 中部分方法也内置了显式绑定,以 <code>forEach</code> 为例:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>el<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>el<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token string">'awesome'</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 调用 foo(..)时把 this 绑定到 obj</span><span class="token punctuation">[</span><span class="token number">1</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 punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>foo<span class="token punctuation">,</span> obj<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1 awesome 2 awesome 3 awesome</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="new-绑定"><a href="#new-绑定" class="headerlink" title="new 绑定"></a>new 绑定</h4><p><code>new</code> 操作符的具体操作过程在这里不提,我们只需要知道 <strong><code>new</code> 操作符创建的新对象会绑定到其构造函数的 <code>this</code> 上</strong>。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>a <span class="token operator">=</span> a<span class="token punctuation">}</span><span class="token keyword">var</span> bar <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">foo</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>bar<span class="token punctuation">.</span>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="箭头函数绑定"><a href="#箭头函数绑定" class="headerlink" title="箭头函数绑定"></a>箭头函数绑定</h4><blockquote><p><strong>箭头函数的 <code>this</code> 继承上层执行上下文里面的 <code>this</code></strong></p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><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> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></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>a<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">var</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">2</span> <span class="token punctuation">}</span><span class="token keyword">var</span> obj2 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">3</span> <span class="token punctuation">}</span><span class="token keyword">var</span> bar <span class="token operator">=</span> foo<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span>bar<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>foo()</code> 内部创建的箭头函数会捕获调用时 <code>foo()</code> 的 <code>this</code>。由于 <code>foo()</code> 的 <code>this</code> 绑定到了 <code>obj1</code>,<code>bar</code> 的 <code>this</code> 也会绑定到 <code>obj1</code>,<strong>箭头函数的绑定无法被修改。(<code>new</code> 也不行!)</strong></p><p>思考下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'laozhang'</span><span class="token keyword">var</span> grandFather <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'laowang'</span><span class="token punctuation">,</span> father<span class="token punctuation">:</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'xiaowang'</span><span class="token punctuation">,</span> son<span class="token punctuation">:</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'xiaoming'</span><span class="token punctuation">,</span> getName<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></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>name<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> getName<span class="token punctuation">:</span> <span class="token keyword">function</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>name<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span> <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'xiaowang'</span> <span class="token keyword">var</span> son <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'xiaoming'</span><span class="token punctuation">,</span> getName1<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></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>name<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 3</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> getName2<span class="token punctuation">:</span> <span class="token keyword">function</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>name<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 4</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> son<span class="token punctuation">.</span><span class="token function">getName1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> son<span class="token punctuation">.</span><span class="token function">getName2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>grandFather<span class="token punctuation">.</span>father<span class="token punctuation">.</span>son<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span>grandFather<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码执行的结果是什么?</p><ul><li>1处为 <code>'laozhang'</code>,这里的 this 在箭头函数内,指向上层执行上下文,为全局对象 <code>window</code></li><li>2处为 <code>'laowang'</code>, 这里的 this 在普通声明函数中,指向最后一个调用它的对象(<code>grandFather</code>)</li><li>3处为 <code>'laowang'</code>,这里的 this 在箭头函数中,指向上层执行上下文,为 <code>grandFather</code> 对象</li><li>4处为 <code>'xiaoming'</code>,这里的 this 在普通声明函数中,指向最后一个调用它的对象(<code>son</code>)</li></ul><p>对执行上下文的不是很理解的话,可以看看<a href="https://ltara.gitee.io/posts/56269.html">JavaScript基础之执行上下文</a>.</p><h3 id="优先级"><a href="#优先级" class="headerlink" title="优先级"></a>优先级</h3><ul><li><p><strong>new 绑定 &gt; 隐式绑定 &gt; 默认绑定</strong></p></li><li><p><strong>显示绑定 &gt; 隐式绑定 &gt; 默认绑定</strong></p></li></ul><p><code>new</code> 和 <code>call/apply/bind</code> 无法一起使用,不存在使用场景,无需去比较它们的优先级。</p><h2 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h2><p>如果觉得上面的内容都理解了,那么可以试试 <a href="https://www.cnblogs.com/echolun/p/11969938.html">js 从两道面试题加深理解闭包与箭头函数中的this</a>。</p><h1 id="call-apply-bind"><a href="#call-apply-bind" class="headerlink" title="call/apply/bind"></a>call/apply/bind</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>在MDN中的定义:</p><p>call:</p><blockquote><p><strong><code>call()</code></strong>方法使用一个指定的 <code>this</code> 值和单独给出的一个或多个参数来调用一个函数。</p></blockquote><p>apply:</p><blockquote><p><strong><code>apply()</code></strong> 方法调用一个具有给定<code>this</code>值的函数,以及作为一个数组(或类似数组对象)提供的参数。</p></blockquote><p>bind:</p><blockquote><p><strong><code>bind()</code></strong> 方法创建一个新的函数,在 <code>bind()</code> 被调用时,这个新函数的 <code>this</code> 被指定为 <code>bind()</code> 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。</p></blockquote><h2 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h2><h3 id="call"><a href="#call" class="headerlink" title="call"></a>call</h3><pre class="line-numbers language-javascript"><code class="language-javascript">func<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>thisArg<span class="token punctuation">,</span> arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>参数:</strong></p><ul><li><strong><code>thisArg</code></strong>:可选的。在 <em><code>function</code></em> 函数运行时使用的 <code>this</code> 值。</li><li><strong><code>arg1, arg2, ...</code></strong>:指定的<strong>参数列表</strong>。(原函数所需的参数)</li></ul><p><strong>作用:</strong></p><p>改变函数的 <code>this</code> 指向。</p><p><strong>示例:</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Product</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price<span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">Food</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token punctuation">{</span> Product<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> name<span class="token punctuation">,</span> price<span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>category <span class="token operator">=</span> <span class="token string">'food'</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">new</span> <span class="token class-name">Food</span><span class="token punctuation">(</span><span class="token string">'cheese'</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 'cheese'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="apply"><a href="#apply" class="headerlink" title="apply"></a>apply</h3><pre class="line-numbers language-javascript"><code class="language-javascript">func<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>thisArg<span class="token punctuation">,</span> <span class="token punctuation">[</span>argsArray<span class="token punctuation">]</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>参数:</strong></p><ul><li><strong><code>thisArg</code></strong>:必选的。在 <em><code>func</code></em> 函数运行时使用的 <code>this</code> 值。</li><li><strong><code>argsArray</code></strong>:可选的。一个<strong>数组</strong>或者<strong>类数组对象</strong>,其中的数组元素将作为单独的参数传给 <code>func</code> 函数。</li></ul><p><strong>作用:</strong></p><p>改变函数的 <code>this</code> 指向。</p><p><strong>示例:</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">const</span> numbers <span class="token operator">=</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 keyword">const</span> max <span class="token operator">=</span> Math<span class="token punctuation">.</span>max<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> numbers<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>max<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// expected output: 7</span><span class="token keyword">const</span> min <span class="token operator">=</span> Math<span class="token punctuation">.</span>min<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> numbers<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>min<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// expected output: 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="bind"><a href="#bind" class="headerlink" title="bind"></a>bind</h3><pre class="line-numbers language-javascript"><code class="language-javascript">func<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>thisArg<span class="token punctuation">[</span><span class="token punctuation">,</span> arg1<span class="token punctuation">[</span><span class="token punctuation">,</span> arg2<span class="token punctuation">[</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>参数:</strong></p><ul><li><strong><code>thisArg</code></strong>:调用绑定函数时作为 <code>this</code> 参数传递给目标函数的值。如果使用<code>new</code>运算符构造绑定函数,则忽略该值。</li><li><strong><code>arg1, arg2, ...</code></strong>:当目标函数被调用时,被预置入绑定函数的参数列表中的参数。</li></ul><p><strong>作用:</strong></p><p>改变函数的 <code>this</code> 指向。</p><p><strong>返回值:</strong></p><p>返回一个原函数的拷贝,并拥有指定的 <strong><code>this</code></strong> 值和初始参数。</p><p><strong>示例:</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">const</span> module <span class="token operator">=</span> <span class="token punctuation">{</span> x<span class="token punctuation">:</span> <span class="token number">42</span><span class="token punctuation">,</span> getX<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span class="token keyword">const</span> unboundGetX <span class="token operator">=</span> module<span class="token punctuation">.</span>getX<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 function">unboundGetX</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// The function gets invoked at the global scope</span><span class="token comment" spellcheck="true">// expected output: undefined</span><span class="token keyword">const</span> boundGetX <span class="token operator">=</span> unboundGetX<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>module<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 function">boundGetX</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// expected output: 42</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h2><ol><li><p><code>call</code>、<code>apply</code> 与 <code>bind</code> 都用于改变 <code>this</code> 绑定,但 <code>call</code>、<code>apply</code> 在改变 <code>this</code> 指向的同时还会执行函数,而 <code>bind</code> 在改变 <code>this</code> 后是返回一个全新的 <code>boundFcuntion</code> 绑定函数,这也是为什么上方例子中 <code>bind</code> 后还加了一对括号 <code>()</code> 的原因。</p></li><li><p><code>bind</code> 属于硬绑定,返回的 <code>boundFunction</code> 的 <code>this</code> 指向无法再次通过 <code>bind</code>、<code>apply</code>或 <code>call</code> 修改;<code>call</code> 与 <code>apply</code> 的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。</p></li><li><p><code>call</code> 与 <code>apply</code> 功能完全相同,唯一不同的是 <code>call</code> 方法传递函数调用形参是以散列形式,而 <code>apply</code> 方法的形参是一个数组。在传参的情况下,<code>call</code> 的性能要高于 <code>apply</code>,因为 <code>apply</code> 在执行时还要多一步解析数组。</p></li></ol><h2 id="手动实现"><a href="#手动实现" class="headerlink" title="手动实现"></a>手动实现</h2><h3 id="call-1"><a href="#call-1" class="headerlink" title="call"></a>call</h3><pre class="line-numbers language-javascript"><code class="language-javascript">Function<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>myCall <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 判断调用对象是否为函数</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> <span class="token keyword">this</span> <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">TypeError</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> is not a function`</span></span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 判断传入的对象是否存在,默认是 window</span> context <span class="token operator">=</span> context <span class="token operator">?</span> <span class="token function">Object</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token punctuation">:</span> window <span class="token comment" spellcheck="true">// 把当前调用的函数赋值给传入对象的 context.fn</span> context<span class="token punctuation">.</span>fn <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token comment" spellcheck="true">// 声明一个数组用来保存当前调用函数的参数</span> <span class="token keyword">let</span> args <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> arguments<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> args<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">'arguments['</span><span class="token operator">+</span>i<span class="token operator">+</span><span class="token string">']'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 执行调用函数,并返回结果</span> <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token function">eval</span><span class="token punctuation">(</span><span class="token string">'context.fn('</span> <span class="token operator">+</span> args <span class="token operator">+</span> <span class="token string">')'</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 删除 fn</span> <span class="token keyword">delete</span> context<span class="token punctuation">.</span>fn <span class="token keyword">return</span> result<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="apply-1"><a href="#apply-1" class="headerlink" title="apply"></a>apply</h3><pre class="line-numbers language-javascript"><code class="language-javascript">Function<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>myApply <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 判断调用对象是否为函数</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> <span class="token keyword">this</span> <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">TypeError</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> is not a function`</span></span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 判断传入的对象是否存在,默认是 window</span> context <span class="token operator">=</span> context <span class="token operator">?</span> <span class="token function">Object</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token punctuation">:</span> window <span class="token comment" spellcheck="true">// 把当前调用的函数赋值给传入对象的 context.fn</span> context<span class="token punctuation">.</span>fn <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token comment" spellcheck="true">// 判断是否传入函数参数</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>args<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> context<span class="token punctuation">.</span><span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 判断传入的参数是否为数组</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>args <span class="token keyword">instanceof</span> <span class="token class-name">Array</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">TypeError</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>args<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> is not a array`</span></span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 执行调用函数,并返回结果</span> <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token function">eval</span><span class="token punctuation">(</span><span class="token string">'context.fn('</span><span class="token operator">+</span> args <span class="token operator">+</span><span class="token string">')'</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 删除 fn</span> <span class="token keyword">delete</span> context<span class="token punctuation">.</span>fn <span class="token keyword">return</span> result<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="bind-1"><a href="#bind-1" class="headerlink" title="bind"></a>bind</h3><pre class="line-numbers language-javascript"><code class="language-javascript">Function<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>myBind <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 判断调用对象是否为函数</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> <span class="token keyword">this</span> <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">TypeError</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> is not a function`</span></span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 获取调用函数</span> <span class="token keyword">let</span> that <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token comment" spellcheck="true">// 将调用函数的参数转成数组</span> <span class="token keyword">let</span> bindArgs <span class="token operator">=</span> Array<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>slice<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>arguments<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 声明一个新函数 Fn</span> <span class="token keyword">function</span> <span class="token function">Fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">bindFn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 将 myBind 函数的参数转成数组</span> <span class="token keyword">let</span> args <span class="token operator">=</span> Array<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>slice<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 绑定 this 指向</span> <span class="token comment" spellcheck="true">// 如果绑定的函数被 new 执行,当前函数的 this 就是当前的实例</span> that<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token keyword">instanceof</span> <span class="token class-name">bindFn</span> <span class="token operator">?</span> <span class="token keyword">this</span> <span class="token punctuation">:</span> context<span class="token punctuation">,</span> bindArgs<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// new 出来的结果可以找到原有类的原型</span> Fn<span class="token punctuation">.</span>prototype <span class="token operator">=</span> that<span class="token punctuation">.</span>prototype bindFn<span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 返回一个绑定后的函数</span> <span class="token keyword">return</span> bindFn<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p>《你不知道的JavaScript上卷》</p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function">MDN–call/apply/bind</a></p><p><a href="https://www.cnblogs.com/echolun/p/11962610.html">js 五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解</a></p><p><a href="https://juejin.im/post/6844903496253177863">this、apply、call、bind</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;this&quot;&gt;&lt;a href=&quot;#this&quot; class=&quot;headerlink&quot; title=&quot;this&quot;&gt;&lt;/a&gt;this&lt;/h1&gt;&lt;h2 id=&quot;this-是什么&quot;&gt;&lt;a href=&quot;#this-是什么&quot; class=&quot;headerlink&quot; title=&quot;t
</summary>
<category term="JavaScript" scheme="http://ltara.gitee.io/categories/JavaScript/"/>
<category term="JavaScript" scheme="http://ltara.gitee.io/tags/JavaScript/"/>
</entry>
<entry>
<title>JavaScript基础之闭包</title>
<link href="http://ltara.gitee.io/posts/31239.html"/>
<id>http://ltara.gitee.io/posts/31239.html</id>
<published>2020-08-16T02:40:39.000Z</published>
<updated>2020-08-22T06:17:56.342Z</updated>
<content type="html"><![CDATA[<h1 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>MDN定义:</p><blockquote><p>闭包是指那些能够访问自由变量的函数。</p></blockquote><p>自由变量:</p><blockquote><p>自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的的变量。</p></blockquote><p>《你不知道的JavaScript》里的定义:</p><blockquote><p>当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。</p></blockquote><h2 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h2><p>观察下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这是闭包吗?</p><p>按照我们原来的理解,这不就是作用域的查找规则嘛!我们根据对定义的理解,<code>foo</code>函数访问了<code>a</code>变量,但是<code>a</code>变量是自由变量,于是构成闭包。换一种说法,<code>foo</code>函数记住了全局作用域并进行了访问,于是产生了闭包。</p><p>从上面的代码很难理解闭包,因为我们很容易通过作用域的查找规则去理解它。</p><p>观察以下代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span> <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>a<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个代码是不是稍微容易理解了一点点,但它本质上和上面的代码相同,还是不容易观察到闭包。</p><p>于是我们再改一下:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span> <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>a<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> bar<span class="token punctuation">}</span><span class="token keyword">var</span> baz <span class="token operator">=</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这次的代码要认真分析一下了:</p><ul><li><code>foo</code>函数里声明了一个变量<code>a</code>并赋值为<code>1</code>和一个函数<code>bar</code>,<code>bar</code>函数输出变量<code>a</code>,最后<code>foo</code>函数返回<code>bar</code>函数</li><li>在全局中声明了一个变量<code>a</code>并赋值为<code>2</code>和另一个变量<code>baz</code>并将<code>foo</code>函数的返回值赋给它,然后执行<code>baz</code>函数</li></ul><p>我们惊讶地发现<code>baz</code>函数竟然输出了<code>foo</code>函数作用域内的变量<code>a</code>,按照我们对作用域和执行上下文的理解,<code>foo</code>函数执行完毕后不是应该销毁其作用域的吗,此时我们观察到了闭包。</p><p><code>bar</code>函数访问了自由变量<code>a</code>,构成闭包。这个定义感觉和上面两个例子没什么区别,太笼统了,我们用《你不知道的JavaScript》里的定义去理解:<strong><code>bar</code>函数记住并访问了它所在的词法作用域<code>foo</code>函数作用域,即使<code>bar</code>函数在全局作用域执行(实际上<code>baz</code>变量引用了<code>bar</code>函数),这时产生了闭包</strong>。</p><p>或许我们可以换一种形式观察闭包:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><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> <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span> <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>a<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">baz</span><span class="token punctuation">(</span>bar<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">baz</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>或者再换一种形式:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> fn<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> <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span> <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>a<span class="token punctuation">)</span> <span class="token punctuation">}</span> fn <span class="token operator">=</span> bar<span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>观察上面两例代码,我们可以再次通过作用域去理解闭包:</p><blockquote><p>无论通过何种手段将内部函数传递到所在词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。</p></blockquote><p>关于闭包,冴羽大大在它的<a href="https://github.com/mqyqingfeng/Blog/issues/9">深入系列之闭包</a>中提到了闭包可以从两个角度定义:</p><blockquote><ol><li>从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。</li><li>从实践角度:以下函数才算是闭包:<ol><li>即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)</li><li>在代码中引用了自由变量</li></ol></li></ol></blockquote><p>因此,MDN对于闭包的定义是如此的笼统以至于难以观察到。</p><p>如果你还是对MDN的定义耿耿于怀,那么我推荐你去看看这篇文章:<a href="https://mp.weixin.qq.com/s?__biz=MzI3NzIzMDY0NA==&amp;mid=2247491232&amp;idx=1&amp;sn=615365a3b288219d259342ea52f6cb73&amp;chksm=eb683bfbdc1fb2ed60922dde3759fe7bb2e6b7913bf598c3c9d0320c16963a46c1134f8f77ec&amp;xtrack=1&amp;scene=0&amp;subscene=92&amp;sessionid=1597544497&amp;clicktime=1597544727&amp;enterid=1597544727&amp;ascene=7&amp;devicetype=android-29&amp;version=27001139&amp;nettype=WIFI&amp;abtest_cookie=AAACAA%3D%3D&amp;lang=zh_CN&amp;exportkey=Ax4UXwOEz04xJYRceuw%2BUeA%3D&amp;pass_ticket=A%2B1zkSTXcTM2CMnF6EcUJVju%2F85GRemIh%2BVoertTDsk%3D&amp;wx_header=1">探索闭包</a>(原文:<a href="https://whatthefork.is/closure">a closure</a>)</p><h2 id="身影"><a href="#身影" class="headerlink" title="身影"></a>身影</h2><h3 id="回调函数"><a href="#回调函数" class="headerlink" title="回调函数"></a>回调函数</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">wait</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token function">timer</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>message<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token string">'Hello, closure!'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>将一个内部函数(<code>timer</code>)传递给<code>setTimeou(..)</code>。<code>timer</code>具有涵盖<code>wait(..)</code>作用域的闭包,因此还保有对变量<code>message</code>的引用。</p><blockquote><p>无论何时何地,如果将函数当作第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。</p><p>在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其它的异步(或同步)任务中,只要使用了<strong>回调函数</strong>,实际上就是在使用闭包。</p></blockquote><h3 id="IIFE——立即执行函数表达式"><a href="#IIFE——立即执行函数表达式" class="headerlink" title="IIFE——立即执行函数表达式"></a>IIFE——立即执行函数表达式</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">(</span><span class="token keyword">function</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>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>尽管IIFE本身并不是观察闭包的恰当例子,但它的确创建了闭包,并且也是最常用来创建可以被封闭起来的闭包的工具。</p><h2 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h2><p>观察下面代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">5</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> arr<span class="token punctuation">[</span>i<span class="token punctuation">]</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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> <span class="token number">5</span><span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> arr<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面代码会输出什么?</p><p>我们想要得到是5个不同的函数,每个函数分别输出0、1、2、3、4,然而结果却是输出5个<code>5</code>,我们分析造成这个结果的原因:</p><ul><li>首先是输出的5是哪来的,我们可以在全局中<code>console.log(i)</code>,发现结果为<code>5</code>,由此知道了<code>5</code>是循环结束后<code>i</code>的值,我们产生疑问:为什么在循环过程中<code>i</code>的值没有被保存下来呢?</li><li>实际上,我们陷入了一个误区:我们试图假设在循环中每个迭代在运行时都会给自己”捕获“一个<code>i</code>的副本。但是根据作用域的工作原理,实际情况是尽管循环中的5个函数是在各个迭代中分别定义的,但是它们都<strong>被封闭在一个共享的全局作用域中</strong>,因此实际上只有一个<code>i</code></li><li>因此所有函数都是共享一个<code>i</code>的引用,而在函数调用时,<code>i</code>的值已经是<code>5</code>了,所以会输出5个<code>5</code></li></ul><p>怎么解决这个问题呢?我们需要使用闭包,在循环过程中每个迭代都需要一个闭包作用域。</p><p>我们使用IIFE来尝试一下这个例子:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">5</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> arr<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span>j<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>j<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码中,循环内部使用IIFE会为每个迭代都生成一个新的作用域,使得每个函数可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。</p><blockquote><p>块作用域也能解决这个问题,但和闭包关系不大,此处略去。</p></blockquote><h2 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h2><h3 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h3><blockquote><p>单例模式是一种常见的设计模式,它保证了一个类只有一个实例。实现方法一般是先判断实例是否存在,如果存在就直接返回,否则就创建了再返回。单例模式的好处就是避免了重复实例化带来的内存开销.</p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>data <span class="token operator">=</span> <span class="token string">'singleton'</span><span class="token punctuation">}</span>Singleton<span class="token punctuation">.</span>getInstance <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> instance <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instance<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> instance <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> instance <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">var</span> sa <span class="token operator">=</span> Singleton<span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">var</span> sb <span class="token operator">=</span> Singleton<span class="token punctuation">.</span><span class="token function">getInstance</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>sa <span class="token operator">===</span> sb<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// true</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>sa<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 'singleton'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="模拟私有属性"><a href="#模拟私有属性" class="headerlink" title="模拟私有属性"></a>模拟私有属性</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">let</span> fn <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> privateCounter <span class="token operator">=</span> <span class="token number">0</span> <span class="token keyword">function</span> <span class="token function">changeBy</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span> <span class="token punctuation">{</span> privateCounter <span class="token operator">+</span><span class="token operator">=</span> val <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">changeBy</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">decrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">changeBy</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">getValue</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>privateCounter<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fn<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 0</span>fn<span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fn<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1</span>fn<span class="token punctuation">.</span><span class="token function">decrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fn<span class="token punctuation">.</span><span class="token function">decrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fn<span class="token punctuation">.</span><span class="token function">decrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fn<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// -2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在这个例子中,<code>fn</code>函数返回了3个闭包方法,除了这3个方法能够访问<code>privateCounter</code>变量和<code>changeBy</code>函数外,你无法通过其他手段操作它们。</p><h3 id="函数柯里化"><a href="#函数柯里化" class="headerlink" title="函数柯里化"></a>函数柯里化</h3><blockquote><p>柯里化(<code>currying</code>),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。</p></blockquote><ul><li>工厂函数</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">makeAdder</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>y<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>x <span class="token operator">+</span> y<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token function">makeAdder</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token function">makeAdder</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token function">a</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 7</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 12</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在这个例子中,我们利用了闭包自带执行环境的特性(即使外层作用域已销毁),仅仅使用一个形参完成了两个形参求和的操作。</p><ul><li>bind方法的实现</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript">Function<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>myBind <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>context <span class="token operator">=</span> window<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> <span class="token keyword">this</span> <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Error'</span><span class="token punctuation">)</span> <span class="token keyword">let</span> selfFunc <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token keyword">let</span> args <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>arguments<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">function</span> F <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 因为返回了一个函数,可以 new F(),所以需要判断</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token keyword">instanceof</span> <span class="token class-name">F</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">selfFunc</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">,</span> arguments<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以需要将两边的参数拼接起来</span> <span class="token keyword">return</span> selfFunc<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> args<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li>类型判断函数</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> typeOf <span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> toString <span class="token operator">=</span> Object<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>toString<span class="token punctuation">;</span> <span class="token keyword">const</span> map <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token string">'[object Boolean]'</span> <span class="token punctuation">:</span> <span class="token string">'boolean'</span><span class="token punctuation">,</span> <span class="token string">'[object Number]'</span> <span class="token punctuation">:</span> <span class="token string">'number'</span><span class="token punctuation">,</span> <span class="token string">'[object String]'</span> <span class="token punctuation">:</span> <span class="token string">'string'</span><span class="token punctuation">,</span> <span class="token string">'[object Function]'</span> <span class="token punctuation">:</span> <span class="token string">'function'</span><span class="token punctuation">,</span> <span class="token string">'[object Array]'</span> <span class="token punctuation">:</span> <span class="token string">'array'</span><span class="token punctuation">,</span> <span class="token string">'[object Date]'</span> <span class="token punctuation">:</span> <span class="token string">'date'</span><span class="token punctuation">,</span> <span class="token string">'[object RegExp]'</span> <span class="token punctuation">:</span> <span class="token string">'regExp'</span><span class="token punctuation">,</span> <span class="token string">'[object Undefined]'</span> <span class="token punctuation">:</span> <span class="token string">'undefined'</span><span class="token punctuation">,</span> <span class="token string">'[object Null]'</span> <span class="token punctuation">:</span> <span class="token string">'null'</span><span class="token punctuation">,</span> <span class="token string">'[object Object]'</span> <span class="token punctuation">:</span> <span class="token string">'object'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">return</span> map<span class="token punctuation">[</span>toString<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token operator">===</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">var</span> isNumber <span class="token operator">=</span> <span class="token function">typeOf</span><span class="token punctuation">(</span><span class="token string">'number'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">var</span> isFunction <span class="token operator">=</span> <span class="token function">typeOf</span><span class="token punctuation">(</span><span class="token string">'function'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">var</span> isRegExp <span class="token operator">=</span> <span class="token function">typeOf</span><span class="token punctuation">(</span><span class="token string">'regExp'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">isNumber</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// => true</span><span class="token function">isFunction</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// true</span><span class="token function">isRegExp</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// => false</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通过向 <code>typeOf</code> 里传入不同的类型字符串参数,就可以生成对应的类型判断函数,作为语法糖在业务代码里重复使用。</p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><h3 id="内存泄露"><a href="#内存泄露" class="headerlink" title="内存泄露"></a>内存泄露</h3><blockquote><p>由于闭包使用过度而导致的内存占用无法释放的情况,我们称之为:内存泄露。</p></blockquote><blockquote><p>内存泄露 是指当一块内存不再被应用程序使用的时候,由于某种原因,这块内存没有返还给操作系统或者内存池的现象。内存泄漏可能会导致应用程序卡顿或者崩溃。</p></blockquote><p>相比普通函数,闭包对于内存的占用比普通函数大,毕竟外层函数的自由变量无法释放。</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">bindEvent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">let</span> ele <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.ele'</span><span class="token punctuation">)</span> ele<span class="token punctuation">.</span>onclick <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>ele<span class="token punctuation">.</span>style<span class="token punctuation">.</span>color<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token function">bindEvent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在上面的例子中点击事件函数中使用到了外层函数中的DOM <code>ele</code>,导致 <code>ele</code> 始终无法释放,造成大量的内存占用。</p><p><strong>解决:</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">bindEvent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">let</span> ele <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.ele'</span><span class="token punctuation">)</span> <span class="token keyword">let</span> color <span class="token operator">=</span> ele ele<span class="token punctuation">.</span>onclick <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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>color<span class="token punctuation">)</span> <span class="token punctuation">}</span> ele <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">}</span><span class="token function">bindEvent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="闭包中的this值"><a href="#闭包中的this值" class="headerlink" title="闭包中的this值"></a>闭包中的this值</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'听风是风'</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'行星飞行'</span><span class="token punctuation">,</span> sayName<span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">function</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>name<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>obj<span class="token punctuation">.</span><span class="token function">sayName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// '听风是风'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码可以理解为以下代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'听风是风'</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'行星飞行'</span>, sayName<span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">function</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>name<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">var</span> fn <span class="token operator">=</span> obj<span class="token punctuation">.</span><span class="token function">sayName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// '听风是风'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>因此在闭包中的<code>this</code>一般指向全局对象<code>window</code>。</p><p>如果想要使用外层函数的<code>this</code>,可以:</p><ul><li>保存<code>this</code>值</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'听风是风'</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'行星飞行'</span><span class="token punctuation">,</span> sayName<span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> that <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token keyword">return</span> <span class="token keyword">function</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>that<span class="token punctuation">.</span>name<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>obj<span class="token punctuation">.</span><span class="token function">sayName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// '行星飞行'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li>使用箭头函数</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'听风是风'</span><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'行星飞行'</span><span class="token punctuation">,</span> sayName<span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></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>name<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>obj<span class="token punctuation">.</span><span class="token function">sayName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// '行星飞行'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>《你不知道的JavaScript上卷》</p><p><a href="https://github.com/mqyqingfeng/Blog/issues/9">JavaScript深入之闭包</a></p><p><a href="https://juejin.im/post/6844904165672484871#heading-14">面试官:说说作用域和闭包吧</a></p><p><a href="https://www.cnblogs.com/echolun/p/11897004.html">一篇文章看懂JS闭包,都要2020年了,你怎么能还不懂闭包?</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;闭包&quot;&gt;&lt;a href=&quot;#闭包&quot; class=&quot;headerlink&quot; title=&quot;闭包&quot;&gt;&lt;/a&gt;闭包&lt;/h1&gt;&lt;h2 id=&quot;定义&quot;&gt;&lt;a href=&quot;#定义&quot; class=&quot;headerlink&quot; title=&quot;定义&quot;&gt;&lt;/a&gt;定义&lt;/h2&gt;&lt;p&gt;MDN
</summary>
<category term="JavaScript" scheme="http://ltara.gitee.io/categories/JavaScript/"/>
<category term="JavaScript" scheme="http://ltara.gitee.io/tags/JavaScript/"/>
</entry>
<entry>
<title>JavaScript基础之作用域/作用域链</title>
<link href="http://ltara.gitee.io/posts/38149.html"/>
<id>http://ltara.gitee.io/posts/38149.html</id>
<published>2020-08-13T09:02:38.000Z</published>
<updated>2020-08-22T06:17:44.510Z</updated>
<content type="html"><![CDATA[<h1 id="作用域-作用域链"><a href="#作用域-作用域链" class="headerlink" title="作用域/作用域链"></a>作用域/作用域链</h1><h2 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><blockquote><p>作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。</p></blockquote><blockquote><p>作用域是在运行时代码中的某些特定部分中的变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。</p></blockquote><p>通过定义我们可以知道,<strong>作用域就是告诉我们变量或函数存放在哪里以及怎么去获取(访问)它们。</strong></p><h3 id="LHS查询和RHS查询"><a href="#LHS查询和RHS查询" class="headerlink" title="LHS查询和RHS查询"></a>LHS查询和RHS查询</h3><ul><li><p>LHS:赋值操作的左侧</p></li><li><p>RHS:赋值操作的右侧</p></li></ul><p>简单来说,<strong>如果查找的目的是对变量赋值,那么会使用LHS查询;如果目的是获取变量的值,那么会使用RHS查询。</strong></p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>以上代码中就存在LHS查询和RHS查询:</p><ul><li><p><code>var a = 1</code>会被分解为两步:</p><ol><li><code>var a</code>在作用域中声明变量<code>a</code>,这一步在代码执行前进行;(原理可以看看<a href="https://ltara.gitee.io/posts/56269.html">JavaScript基础之执行上下文</a>)</li><li><code>a = 1</code>会进行LHS查询,查询变量<code>a</code>,并对它进行赋值。</li></ol></li><li><p><code>console.log(a)</code>有两个RHS查询:</p><ol><li>对<code>a</code>进行RHS查询,查询<code>a</code>的值并传给<code>console.log(..)</code>;</li><li>对<code>console</code>进行RHS查询,查询<code>console</code>对象上是否有一个叫做<code>log</code>的方法。</li></ol></li></ul><p><strong>补充:</strong></p><blockquote><p>赋值操作都会导致LHS查询,<code>=</code>操作符或调用函数时出入参数的操作都会导致赋值操作。</p><p>LHS查询如果失败,会创建一个全局变量,RHS查询如果失败会报错:<code>ReferenceError</code>;如果你对查询到的值进行不合理的操作,比如试图对一个非函数类型的值进行函数调用时,会报错:<code>TypeError</code></p><p><code>ReferenceError</code>同作用域的判别失败相关,而<code>TypeError</code>则代表作用域判别成功了,但是对结果的操作时非法或不合理的</p></blockquote><h3 id="全局作用域和函数作用域"><a href="#全局作用域和函数作用域" class="headerlink" title="全局作用域和函数作用域"></a>全局作用域和函数作用域</h3><h4 id="全局作用域"><a href="#全局作用域" class="headerlink" title="全局作用域"></a>全局作用域</h4><ul><li>最外层函数和在最外层函数外面定义的变量拥有全局作用域</li><li>所有末定义直接赋值的变量自动声明为拥有全局作用域</li><li>所有<code>window</code>对象的属性和方法拥有全局作用域</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token keyword">function</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> c <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// a 为最外层函数外面定义的变量</span><span class="token comment" spellcheck="true">// b 为最外层函数</span><span class="token comment" spellcheck="true">// c 为未定义直接赋值的变量</span><span class="token comment" spellcheck="true">// window 上的属性和方法就不一一列举了</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>以上代码中的<code>a</code>,<code>b</code>,<code>c</code>都在全局作用域内,在全局作用域下的变量我们称之为<strong>全局变量</strong></p><p><strong>全局变量在程序的任何地方都能访问</strong></p><h4 id="函数作用域"><a href="#函数作用域" class="headerlink" title="函数作用域"></a>函数作用域</h4><blockquote><p>属于这个函数的全部变量都可以在整个函数的范围内使用及复用(在嵌套的作用域中也可以使用)。</p></blockquote><p>函数作用域是指声明在函数内部的变量(包括函数的形参)</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token number">1</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>以上代码中的<code>a</code>,<code>b</code>都是在函数作用域内,在函数作用域内的变量我们称之为<strong>局部变量</strong>(块级作用域的变量也是局部变量,后面谈)</p><p><strong>局部变量只能在它本身以及它内部的作用域内才能被访问</strong></p><h3 id="块作用域"><a href="#块作用域" class="headerlink" title="块作用域"></a>块作用域</h3><h4 id="在ES6之前"><a href="#在ES6之前" class="headerlink" title="在ES6之前"></a>在ES6之前</h4><p>JavaScript不支持块作用</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</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>i<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>i<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 10</span><span class="token keyword">var</span> foo <span class="token operator">=</span> <span class="token boolean">true</span><span class="token keyword">if</span> <span class="token punctuation">(</span>foo<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> bar <span class="token operator">=</span> foo <span class="token operator">*</span> <span class="token number">2</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>bar<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span class="token punctuation">}</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>bar<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码,for里的<code>i</code>变量和if块里的<code>bar</code>变量都属于外部作用域。</p><ul><li><code>with</code>是块作用域的一个例子,具体可以参考下文中的<a href="#jump">欺骗词法作用域</a></li><li><code>try/catch</code>的分句<code>catch</code>会创建一个块作用域,其中声明的变量仅在<code>catch</code>内部有效</li></ul><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token number">123</span> <span class="token comment" spellcheck="true">// 抛出一个异常</span><span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">error</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>error<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 123 能够正常的执行</span><span class="token punctuation">}</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// ReferenceError: error not found</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="ES6之后"><a href="#ES6之后" class="headerlink" title="ES6之后"></a>ES6之后</h4><p><code>let</code>和<code>const</code>关键字可以将变量绑定到任意作用域中(通常是 {..} 内部)</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</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>i<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>i<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// ReferenceError</span><span class="token keyword">var</span> foo <span class="token operator">=</span> <span class="token boolean">true</span><span class="token keyword">if</span> <span class="token punctuation">(</span>foo<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> bar <span class="token operator">=</span> foo <span class="token operator">*</span> <span class="token number">2</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>bar<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span class="token punctuation">}</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>bar<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// ReferenceError</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>由于作用域的限制,每段独立的执行代码块只能访问自己作用域和外层作用域中的变量,无法访问到内层作用域的变量。</p></blockquote><h2 id="作用域链"><a href="#作用域链" class="headerlink" title="作用域链"></a>作用域链</h2><h3 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h3><blockquote><p>当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域继续查找…一直找到全局作用域。我们把这种作用域的嵌套机制,称为作用域链。</p></blockquote><h3 id="作用域嵌套"><a href="#作用域嵌套" class="headerlink" title="作用域嵌套"></a>作用域嵌套</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> b <span class="token operator">=</span> a <span class="token operator">*</span> <span class="token number">2</span> <span class="token keyword">function</span> <span class="token function">bar</span><span class="token punctuation">(</span>c<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>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> c<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">bar</span><span class="token punctuation">(</span>b <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2, 4, 12</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在这个例子中有三个逐级嵌套的作用域,气泡表示:</p><p><img src="https://user-gold-cdn.xitu.io/2020/5/22/1723a816c2f59e4f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="作用域嵌套"></p><ol><li>包含着整个全局作用域,其中只有一个标识符:<code>foo</code></li><li>包含着<code>foo</code>所创建的作用域,其中有三个标识符:<code>a</code>、<code>bar</code>和<code>b</code></li><li>包含着<code>bar</code>所创建的作用域,其中只有一个标识符:<code>c</code></li></ol><h3 id="词法作用域"><a href="#词法作用域" class="headerlink" title="词法作用域"></a>词法作用域</h3><h4 id="定义-2"><a href="#定义-2" class="headerlink" title="定义"></a>定义</h4><blockquote><p>作用域有两种工作模型。一种是<strong>词法作用域</strong>,另一种是<strong>动态作用域</strong>,JavaScript采用的便是词法作用域。</p></blockquote><blockquote><p>词法阶段:大部分标准语言编译器的第一个工作阶段叫做词法化。词法化的过程会对源代码中的字符进行检查,如果时有状态的解析过程,还会赋予单词语义。</p></blockquote><p><strong>词法作用域就是定义在词法阶段的作用域</strong>。</p><p>换句话说,<strong>词法作用域是由你在写代码时将变量和块作用域写在哪里决定的</strong>。</p><h4 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h4><blockquote><p>作用域查找会从内部向外部逐级查找,在找到第一个匹配的标识符时停止。</p></blockquote><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> value <span class="token operator">=</span> <span class="token number">1</span><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> <span class="token keyword">var</span> value <span class="token operator">=</span> <span class="token number">2</span> <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>value<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码输出什么?</p><p>根据作用域的查找规则,结果为 <code>2</code></p><p>上面的代码很容易理解,就是按照作用域链或者说作用域气泡的方式一层层的往外部查找变量<code>value</code>,找到即停止并执行<code>console.log(..)</code>输出操作。</p><p>那么观察下面的代码:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> value <span class="token operator">=</span> <span class="token number">1</span><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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span><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> <span class="token keyword">var</span> value <span class="token operator">=</span> <span class="token number">2</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码输出什么?</p><ul><li><p>全局作用域有三个标识符:<code>value</code>,<code>foo</code>,<code>bar</code></p></li><li><p>foo函数作用域没有标识符</p></li><li><p>bar函数作用域有一个标识符:<code>value</code></p></li></ul><p>根据词法作用域的定义可知,结果为 <code>1</code>:</p><ol><li><code>bar()</code>函数执行,函数内声明了<code>value</code>变量并赋值为2,接着<code>foo()</code>函数执行</li><li><code>foo()</code>函数执行,执行<code>console.log(..)</code>方法,查找<code>value</code>变量并输出其值,<ol><li>按照作用域的查找规则,首先在<code>foo</code>函数内部查找,<code>foo</code>函数内部没有找到,则去其外部作用域查找</li><li>它的外部作用域为全局作用域,存在<code>value</code>变量,其值为<code>1</code></li></ol></li></ol><p>在这个过程中,并没有bar函数作用域的参与,可见<strong>无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定</strong>。</p><p><strong>补充:</strong></p><blockquote><p>词法作用域查找只会查找一级标识符,比如a,b,c。如果代码中引用了foo.bar.baz,词法作用域查找只会试图查找foo标识符,找到这个变量后,对象属性访问规则会分别接管对bar和baz属性的访问。</p></blockquote><h4 id="欺骗词法作用域"><a href="#欺骗词法作用域" class="headerlink" title="欺骗词法作用域"></a><span id="jump">欺骗词法作用域</span></h4><p><strong>欺骗词法作用域会导致性能下降,不要使用它们</strong></p><h5 id="eval"><a href="#eval" class="headerlink" title="eval"></a>eval</h5><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token number">2</span><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> a<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">eval</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token string">'var b = 3'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1, 3</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>eval(..)</code>调用中的<code>'var b = 3'</code>这段代码会被当做本来就在那里一样来处理。上面的代码实际上会变成下面这样:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token number">2</span><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token number">3</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 1, 3</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种做法欺骗了词法作用域的规则,使得函数声明后的作用域会被修改。</p><h5 id="with"><a href="#with" class="headerlink" title="with"></a>with</h5><ol><li><code>with</code>被当作重复引用同一个对象中的多个属性的快捷方式</li></ol><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> b<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> c<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 给obj的属性重新赋值</span>obj<span class="token punctuation">.</span>a <span class="token operator">=</span> <span class="token number">2</span>obj<span class="token punctuation">.</span>b <span class="token operator">=</span> <span class="token number">3</span>obj<span class="token punctuation">.</span>c <span class="token operator">=</span> <span class="token number">4</span><span class="token comment" spellcheck="true">//使用with</span><span class="token keyword">with</span> <span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span> a <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> b <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">,</span> c <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol start="2"><li>with可以将变量暴露到全局作用域</li></ol><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">with</span> <span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span> a <span class="token operator">=</span> <span class="token number">2</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">var</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token keyword">var</span> obj2 <span class="token operator">=</span> <span class="token punctuation">{</span> b<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token function">foo</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj1<span class="token punctuation">.</span>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2</span><span class="token function">foo</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj2<span class="token punctuation">.</span>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// undefined</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 2, a 被暴露到全局作用域上了</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个例子中创建了<code>obj1</code>和<code>obj2</code>两个对象。其中一个有<code>a</code>属性,另一个没有。<code>foo()</code>函数接受一个<code>obj</code>参数,该参数是一个引用对象,并对这个对象执行了<code>with(obj){..}</code>。在<code>with</code>块内部我们将<code>obj</code>参数引用对象的<code>a</code>属性赋值为<code>2</code>.</p><p>当我们将<code>obj1</code>传递进去,<code>a = 2</code>赋值操作找到了<code>obj1.a</code>并将<code>2</code>赋值给它;当我们将<code>obj2</code>传递进去,<code>obj2</code>并没有<code>a</code>属性,因此不会创建这个属性,<code>obj2</code>保持<code>undefined</code>,但是我们发现<code>a = 2</code>赋值操作创建了一个全局变量<code>a</code>。</p><p>实际上,<strong>with可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域,因此这个对象的属性也会被处理为定义在这个作用域中的标识符</strong>。</p><p>因此在执行<code>foo(obj2)</code>时,<code>with</code>里的<code>a = 2</code>实际上相当于未声明直接赋值的变量自动声明未全局变量,如果<code>with</code>块里改为<code>var a = 2</code>,那么这个<code>a</code>相当于声明在<code>foo</code>里的一个变量。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>《你不知道的JavaScript上卷》</p><p><a href="https://juejin.im/post/6844903797135769614#heading-4">深入理解JavaScript作用域和作用域链</a></p><p><a href="https://juejin.im/post/6844904165672484871#heading-21">面试官:说说作用域和闭包吧</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;作用域-作用域链&quot;&gt;&lt;a href=&quot;#作用域-作用域链&quot; class=&quot;headerlink&quot; title=&quot;作用域/作用域链&quot;&gt;&lt;/a&gt;作用域/作用域链&lt;/h1&gt;&lt;h2 id=&quot;作用域&quot;&gt;&lt;a href=&quot;#作用域&quot; class=&quot;headerlink&quot; ti
</summary>
<category term="JavaScript" scheme="http://ltara.gitee.io/categories/JavaScript/"/>
<category term="JavaScript" scheme="http://ltara.gitee.io/tags/JavaScript/"/>
</entry>
<entry>
<title>JavaScript基础之执行上下文</title>
<link href="http://ltara.gitee.io/posts/56269.html"/>
<id>http://ltara.gitee.io/posts/56269.html</id>
<published>2020-08-13T02:32:59.000Z</published>
<updated>2020-08-22T06:17:46.973Z</updated>
<content type="html"><![CDATA[<h1 id="执行上下文"><a href="#执行上下文" class="headerlink" title="执行上下文"></a>执行上下文</h1><h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><h3 id="执行上下文-1"><a href="#执行上下文-1" class="headerlink" title="执行上下文"></a>执行上下文</h3><p>当 <code>JS</code> 引擎解析到可执行代码片段(通常是函数调用阶段)的时候,就会先做一些执行前的准备工作,这个 <strong>“准备工作”</strong>,就叫做 <strong>“执行上下文(execution context 简称 <code>EC</code>)”</strong> 或者也可以叫做<strong>执行环境</strong>。</p><p><a href="http://ecma-international.org/ecma-262/6.0/">ES6 官方</a> 中的执行上下文定义:</p><blockquote><p>执行上下文是一种规范策略,用于跟踪ECMAScript实现对代码的运行时评估。在任何时候,最多只有一个执行上下文在实际执行代码。这被称为运行执行上下文。堆栈用于跟踪执行上下文。正在运行的执行上下文始终是这个堆栈的顶部元素。每当控制从与当前运行的执行上下文关联的可执行代码转移到与该执行上下文不关联的可执行代码时,就会创建一个新的执行上下文。新创建的执行上下文被推到堆栈上,并成为正在运行的执行上下文。</p></blockquote><h3 id="词法环境"><a href="#词法环境" class="headerlink" title="词法环境"></a>词法环境</h3><p><a href="http://ecma-international.org/ecma-262/6.0/">ES6 官方</a> 中的词法环境定义:</p><blockquote><p><strong>词法环境</strong>是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义<strong>标识符</strong>和具体变量和函数的关联。一个词法环境由环境记录器和一个可能的引用<strong>外部词法环境</strong>的空值组成。</p></blockquote><p>简单来说:</p><p>词法环境由<strong>环境记录</strong>与<strong>外部环境引入记录</strong>两个部分组成。</p><p>其中<strong>环境记录</strong>用于存储当前环境中的变量和函数声明的实际位置;<strong>外部环境引入记录</strong>很好理解,它用于保存自身环境可以访问的其它外部环境</p><h3 id="变量环境"><a href="#变量环境" class="headerlink" title="变量环境"></a>变量环境</h3><blockquote><p>执行上下文的LexicalEnvironment和VariableEnvironment组件始终是词法环境。创建执行上下文时,它的LexicalEnvironment和VariableEnvironment组件最初具有相同的值。</p></blockquote><p><strong>变量环境</strong> 它也是一个 <strong>词法环境</strong> ,所以它有着词法环境的所有特性。</p><p>在 <code>ES6</code> 中,<strong>词法环境</strong>组件和 <strong>变量环境</strong>组件的一个不同就是前者被用来存储函数声明和变量(<code>let</code> 和 <code>const</code>)绑定,而后者只用来存储 <code>var</code> 变量绑定。</p><p><a href="http://ecma-international.org/ecma-262/6.0/">ES6 官方</a> 对<code>var</code>和<code>let、const</code>的说明:</p><blockquote><p><code>var</code>语句声明了作用域为运行执行上下文的变量环境(VariableEnvironment)的变量。Var变量在实例化其包含的词法环境时被创建,在创建时被初始化为<code>undefined</code>。</p><p><code>let</code>和<code>const</code>声明定义了作用域为运行执行上下文的词法环境(LexicalEnvironment)的变量。变量是在实例化其包含的词法环境时创建的,但是在变量的<em>LexicalBinding</em>计算完成之前,不能以任何方式访问。由<em>LexicalBinding</em>和<em>Initializer</em>定义的变量在计算<em>LexicalBinding</em>时,而不是在创建变量时,被赋予<em>Initializer</em>的<em>AssignmentExpression</em>的值。如果let声明中的<em>LexicalBinding</em>没有<em>Initializer</em>,那么在对<em>LexicalBinding</em>求值时,将给变量分配未定义的值。</p></blockquote><p>这就解释了:</p><ul><li><code>var</code>为什么会存在<strong>变量声明提升</strong>:<code>var</code>声明的变量在创建时,会被初始化为undefined,并且可以访问;</li><li><code>let</code>和<code>const</code>为什么会存在<strong>暂时性死区</strong>:<code>let</code>和<code>const</code>声明的变量在创建时不会被初始化,此时不能以任何方式访问。</li></ul><h2 id="执行上下文类型"><a href="#执行上下文类型" class="headerlink" title="执行上下文类型"></a>执行上下文类型</h2><blockquote><p>执行上下文有三种:<code>全局执行上下文</code>、<code>函数执行上下文</code>、<code>Eval函数执行上下文</code></p></blockquote><h3 id="全局执行上下文"><a href="#全局执行上下文" class="headerlink" title="全局执行上下文"></a>全局执行上下文</h3><p>全局执行上下文只有一个,在客户端中,一般<code>由浏览器创建</code>,也就是window对象,可以通过<code>this</code>访问。</p><p>全局对象window上预定义了大量的方法和属性,我们在全局环境的任意处都能直接访问这些属性方法,同时window对象还是var声明的全局变量的载体。我们在全局环境中通过var创建的变量或函数,都可以通过window直接访问。</p><h3 id="函数执行上下文"><a href="#函数执行上下文" class="headerlink" title="函数执行上下文"></a>函数执行上下文</h3><p>函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;需要注意的是,<code>同一个函数被多次调用,都会创建一个新的上下文</code>。</p><h3 id="Eval函数执行上下文"><a href="#Eval函数执行上下文" class="headerlink" title="Eval函数执行上下文"></a>Eval函数执行上下文</h3><p>执行在 <code>eval</code> 函数内部的代码也会有它属于自己的执行上下文,但由于并不经常使用 <code>eval</code>,所以在这里不做分析。</p><blockquote><p><code>eval()</code>函数会修改已经存在的词法作用域,因此不推荐使用</p></blockquote><h2 id="执行上下文栈"><a href="#执行上下文栈" class="headerlink" title="执行上下文栈"></a>执行上下文栈</h2><p>当一段脚本运行起来的时候,可能会调用很多函数并产生很多函数执行上下文,那么问题来了,这些执行上下文该怎么管理呢?为了解决这个问题,<code>javascript</code> 引擎就创建了 “执行上下文栈” (<code>Execution context stack</code> 简称 <code>ECS</code>)来管理执行上下文。</p><p>顾名思义,执行上下文栈是栈结构的,因此遵循 <code>LIFO</code>(后进先出)的特性,代码执行期间创建的所有执行上下文,都会交给执行上下文栈进行管理。</p><p>当 JS 引擎开始解析脚本代码时,会首先创建一个<strong>全局执行上下文</strong>,压入栈底(这个全局执行上下文从创建一直到程序销毁,都会存在于栈的底部)。</p><p>每当引擎发现一处<strong>函数调用</strong>,就会创建一个新的<strong>函数执行上下文</strong>压入栈内,并将控制权交给该上下文,待函数执行完成后,即将该执行上下文从栈内弹出销毁,将控制权重新给到栈内上一个执行上下文。</p><h2 id="执行上下文的创建"><a href="#执行上下文的创建" class="headerlink" title="执行上下文的创建"></a>执行上下文的创建</h2><blockquote><p>执行上下文的创建主要负责三件事:<code>确定this---创建词法环境组件(LexicalEnvironment)---创建变量环境组件(VariableEnvironment)</code></p></blockquote><p>创建过程的伪代码表示:</p><pre class="line-numbers language-javascript"><code class="language-javascript">ExecutionContext <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 确定this的值</span> ThisBinding <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token keyword">this</span> value<span class="token operator">></span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 创建词法环境的组件</span> LexicalEnvironment <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 创建变量环境的组件</span> VariableEnvironment <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="全局执行上下文的创建"><a href="#全局执行上下文的创建" class="headerlink" title="全局执行上下文的创建"></a>全局执行上下文的创建</h3><p><strong>程序启动,全局上下文被创建</strong></p><ol><li><p>创建全局上下文的<strong>词法环境</strong></p><ul><li>创建<strong>对象环境记录器</strong>,它用来定义出现在全局上下文中的变量和函数的关系(<strong>负责处理 <code>let</code> 和 <code>const</code> 定义的变量</strong>)</li><li>创建<strong>外部环境引用</strong>,值为<code>null</code></li></ul></li><li><p>创建全局上下文的<strong>变量环境</strong></p><ul><li>创建<strong>对象环境记录器</strong>,它持有<strong>变量声明语句</strong>在执行上下文中创建的绑定关系(<strong>负责处理 <code>var</code> 定义的变量,初始值为 <code>undefined</code> 造成声明提升</strong>)</li><li>创建<strong>外部环境引用</strong>,值为<code>null</code></li></ul></li><li><p>确定<code>this</code>值</p><p>在浏览器中,值为全局对象<code>window</code></p></li></ol><h3 id="函数执行上下文的创建"><a href="#函数执行上下文的创建" class="headerlink" title="函数执行上下文的创建"></a>函数执行上下文的创建</h3><p><strong>函数被调用,函数上下文被创建</strong></p><ol><li><p>创建函数上下文的<strong>词法环境</strong></p><ul><li>创建<strong>声明式环境记录器</strong>,存储变量、函数和参数,它包含了一个传递给函数的 <strong><code>arguments</code></strong> 对象(此对象存储索引和参数的映射)和传递给函数的参数的 <strong>length</strong>(<strong>负责处理 <code>let</code> 和 <code>const</code> 定义的变量</strong>)</li><li>创建<strong>外部环境引用</strong>,值为全局对象或父级词法环境</li></ul></li><li><p>创建函数上下文的<strong>变量环境</strong></p><ul><li>创建<strong>声明式环境记录器</strong>,存储变量、函数和参数,它包含了一个传递给函数的 <strong><code>arguments</code></strong> 对象(此对象存储索引和参数的映射)和传递给函数的参数的 <strong>length</strong>(<strong>负责处理 <code>var</code> 定义的变量,初始值为 <code>undefined</code> 造成声明提升</strong>)</li><li>创建<strong>外部环境引用</strong>,值为全局对象或父级词法环境</li></ul></li><li><p>确定<code>this</code>值</p><p><code>this</code>的值取决于函数的调用方式,如果被一个对象调用,那么<code>this</code>指向这个对象。否则<code>this</code>一般指向全局对象<code>window</code>或者<code>undefined</code>(严格模式)</p></li></ol><h3 id="执行上下文创建过程(伪代码)"><a href="#执行上下文创建过程(伪代码)" class="headerlink" title="执行上下文创建过程(伪代码)"></a>执行上下文创建过程(伪代码)</h3><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">//全局执行上下文</span>GlobalExectionContext <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// this绑定为全局对象</span> ThisBinding<span class="token punctuation">:</span> <span class="token operator">&lt;</span>Global Object<span class="token operator">></span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 词法环境</span> LexicalEnvironment<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//环境记录</span> EnvironmentRecord<span class="token punctuation">:</span> <span class="token punctuation">{</span> Type<span class="token punctuation">:</span> <span class="token string">"Object"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 对象环境记录</span> <span class="token comment" spellcheck="true">// 标识符绑定在这里 let const创建的变量a b在这</span> a<span class="token punctuation">:</span> <span class="token operator">&lt;</span> uninitialized <span class="token operator">></span><span class="token punctuation">,</span> b<span class="token punctuation">:</span> <span class="token operator">&lt;</span> uninitialized <span class="token operator">></span><span class="token punctuation">,</span> multiply<span class="token punctuation">:</span> <span class="token operator">&lt;</span> func <span class="token operator">></span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 全局环境外部环境引入为null</span> outer<span class="token punctuation">:</span> <span class="token operator">&lt;</span><span class="token keyword">null</span><span class="token operator">></span> <span class="token punctuation">}</span><span class="token punctuation">,</span> VariableEnvironment<span class="token punctuation">:</span> <span class="token punctuation">{</span> EnvironmentRecord<span class="token punctuation">:</span> <span class="token punctuation">{</span> Type<span class="token punctuation">:</span> <span class="token string">"Object"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 对象环境记录</span> <span class="token comment" spellcheck="true">// 标识符绑定在这里 var创建的c在这</span> c<span class="token punctuation">:</span> undefined<span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 全局环境外部环境引入为null</span> outer<span class="token punctuation">:</span> <span class="token operator">&lt;</span><span class="token keyword">null</span><span class="token operator">></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token comment" spellcheck="true">// 函数执行上下文</span>FunctionExectionContext <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//由于函数是默认调用 this绑定同样是全局对象</span> ThisBinding<span class="token punctuation">:</span> <span class="token operator">&lt;</span>Global Object<span class="token operator">></span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 词法环境</span> LexicalEnvironment<span class="token punctuation">:</span> <span class="token punctuation">{</span> EnvironmentRecord<span class="token punctuation">:</span> <span class="token punctuation">{</span> Type<span class="token punctuation">:</span> <span class="token string">"Declarative"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 声明性环境记录</span> <span class="token comment" spellcheck="true">// 标识符绑定在这里 arguments对象在这</span> Arguments<span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">:</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">:</span> <span class="token number">30</span><span class="token punctuation">,</span> length<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 外部环境引入记录为&lt;/Global></span> outer<span class="token punctuation">:</span> <span class="token operator">&lt;</span>GlobalEnvironment<span class="token operator">></span> <span class="token punctuation">}</span><span class="token punctuation">,</span> VariableEnvironment<span class="token punctuation">:</span> <span class="token punctuation">{</span> EnvironmentRecord<span class="token punctuation">:</span> <span class="token punctuation">{</span> Type<span class="token punctuation">:</span> <span class="token string">"Declarative"</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 声明性环境记录</span> <span class="token comment" spellcheck="true">// 标识符绑定在这里 var创建的g在这</span> g<span class="token punctuation">:</span> undefined <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 外部环境引入记录为&lt;/Global></span> outer<span class="token punctuation">:</span> <span class="token operator">&lt;</span>GlobalEnvironment<span class="token operator">></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://juejin.im/post/6844904158957404167#heading-0">面试官:说说执行上下文吧</a></p><p><a href="https://www.cnblogs.com/echolun/p/11438363.html">一篇文章看懂JS执行上下文</a></p><p><a href="http://ecma-international.org/ecma-262/6.0/#sec-execution-contexts">ECMAScript® 2015 Language Specification</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;执行上下文&quot;&gt;&lt;a href=&quot;#执行上下文&quot; class=&quot;headerlink&quot; title=&quot;执行上下文&quot;&gt;&lt;/a&gt;执行上下文&lt;/h1&gt;&lt;h2 id=&quot;概念&quot;&gt;&lt;a href=&quot;#概念&quot; class=&quot;headerlink&quot; title=&quot;概念&quot;&gt;&lt;/a&gt;概
</summary>
<category term="JavaScript" scheme="http://ltara.gitee.io/categories/JavaScript/"/>
<category term="JavaScript" scheme="http://ltara.gitee.io/tags/JavaScript/"/>
</entry>
<entry>
<title>MongoDB的基本操作(一)</title>
<link href="http://ltara.gitee.io/posts/59980.html"/>
<id>http://ltara.gitee.io/posts/59980.html</id>
<published>2020-08-12T10:33:16.000Z</published>
<updated>2020-08-22T05:48:07.682Z</updated>
<content type="html"><![CDATA[<h1 id="MongoDB-学习之基本操作(Node)"><a href="#MongoDB-学习之基本操作(Node)" class="headerlink" title="MongoDB 学习之基本操作(Node)"></a>MongoDB 学习之基本操作(Node)</h1><h2 id="下载与安装"><a href="#下载与安装" class="headerlink" title="下载与安装"></a>下载与安装</h2><ol><li><a href="https://www.mongodb.com/try/download/community">下载地址</a></li><li>安装:全部默认下一步就行了</li><li>安装成功</li></ol><pre class="line-numbers language-shell"><code class="language-shell"># 在命令行输入以下命令mongod --version<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>成功:<br><img src="http://pic.yupoo.com/ltara/497ac094/1743fa68.png" alt="安装成功"></p><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>环境变量path的配置:</p><ol><li>复制bin目录</li></ol><blockquote><p>MongoDB默认会安装在C盘下的Program Files目录下</p></blockquote><p><img src="http://pic.yupoo.com/ltara/08b0101c/377e82b5.png" alt="复制bin目录"></p><ol start="2"><li>新建环境变量<br><img src="http://pic.yupoo.com/ltara/0d58a614/2978f956.png" alt="新建环境变量"></li></ol><h2 id="启动与关闭"><a href="#启动与关闭" class="headerlink" title="启动与关闭"></a>启动与关闭</h2><ul><li><p>启动<br>在命令行里输入<code>mongod</code>即可开启</p></li><li><p>关闭<br>在命令行里按<code>Ctrl + C</code>即可</p></li></ul><h2 id="连接和断开数据库"><a href="#连接和断开数据库" class="headerlink" title="连接和断开数据库"></a>连接和断开数据库</h2><h3 id="连接数据库"><a href="#连接数据库" class="headerlink" title="连接数据库"></a>连接数据库</h3><ol><li>在C盘目录下创建一个目录<code>/data/db</code>作为存储目录</li><li>在新的命令行窗口(数据库启动窗口不关)输入<code>mongo</code>即可</li><li>连接成功<br><img src="http://pic.yupoo.com/ltara/93bf791b/e517ac79.png" alt="连接成功"></li></ol><h3 id="断开数据库"><a href="#断开数据库" class="headerlink" title="断开数据库"></a>断开数据库</h3><p>在命令行窗口输入<code>exit</code>即可<br><img src="http://pic.yupoo.com/ltara/a22cecf1/646843b0.png" alt="断开连接"></p><h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><ul><li>数据库</li><li>集合(在MySQL中叫数据表)</li><li>文档(在MySQL中叫记录)</li></ul><p>存储结构:</p><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token punctuation">{</span> baidu<span class="token punctuation">:</span> <span class="token punctuation">{</span> user<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'zhangsan'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">18</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'zhangsan'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">18</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'zhangsan'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">18</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'zhangsan'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">18</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'zhangsan'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">18</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">//其他文档</span> <span class="token punctuation">]</span> <span class="token comment" spellcheck="true">//其他集合</span> <span class="token punctuation">}</span> taobao<span class="token punctuation">:</span> <span class="token punctuation">{</span> user<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'lisi'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">50</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'lisi'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">50</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'lisi'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">50</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'lisi'</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token number">50</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 其他文档</span> <span class="token punctuation">]</span> <span class="token comment" spellcheck="true">//其他集合</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">//其他数据库</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="基本命令"><a href="#基本命令" class="headerlink" title="基本命令"></a>基本命令</h2><ul><li><code>show dbs</code><br>查看数据库目录</li><li><code>db</code><br>当前所在数据库</li><li><code>show collections</code><br>查看当前数据库集合目录</li><li><code>use 任意数据库名称(可以不存在)</code><br>切换到指定数据库,如果不存在会新建一个数据库,但是不会立即新建,只有当该数据库中有数据时,即添加文档,才会建立</li><li><code>db.collection.insert()</code><br>添加数据(文档),<code>collection</code>代指某一集合,要使用具体的集合名称,以下<code>collection</code>同义</li><li><code>db.collection.find()</code><br>查找数据(文档)</li><li><code>db.dropDatabase()</code><br>删除当前所在数据库</li><li><code>db.collection.drop()</code><br>删除集合</li></ul><p>更多命令:<br><a href="https://www.runoob.com/mongodb/mongodb-tutorial.html">菜鸟教程</a></p><h2 id="MongoDB-在-Node-中的使用"><a href="#MongoDB-在-Node-中的使用" class="headerlink" title="MongoDB 在 Node 中的使用"></a>MongoDB 在 Node 中的使用</h2><h3 id="使用-Mongoose-第三方库来使用-MongoDB"><a href="#使用-Mongoose-第三方库来使用-MongoDB" class="headerlink" title="使用 Mongoose 第三方库来使用 MongoDB"></a>使用 Mongoose 第三方库来使用 MongoDB</h3><h4 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h4><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>mongoose<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token string">'mongodb://localhost:27017/test'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>useNewUrlParser<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> useUnifiedTopology<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> Cat <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">'Cat'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> String <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> kitty <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'Zildjian'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>kitty<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'meow'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="快速使用"><a href="#快速使用" class="headerlink" title="快速使用"></a>快速使用</h4><h5 id="配置-1"><a href="#配置-1" class="headerlink" title="配置"></a>配置</h5><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> Schema <span class="token operator">=</span> mongoose<span class="token punctuation">.</span>Schema<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//连接本机数据库</span>mongoose<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token string">'mongodb://localhost:27017/test'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> useNewUrlParser<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> useUnifiedTopology<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//设计集合结构</span><span class="token keyword">const</span> userSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> required<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> password<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> required<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> email<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//将文档结构发布为模型</span><span class="token keyword">const</span> User <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">'User'</span><span class="token punctuation">,</span> userSchema<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="增(添加数据)"><a href="#增(添加数据)" class="headerlink" title="增(添加数据)"></a>增(添加数据)</h5><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">//实例化一个User对象admin</span><span class="token keyword">const</span> admin <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin'</span><span class="token punctuation">,</span> password<span class="token punctuation">:</span> <span class="token string">'123456'</span><span class="token punctuation">,</span> email<span class="token punctuation">:</span> <span class="token string">'admin2@admin.com'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> admin2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin2'</span><span class="token punctuation">,</span> password<span class="token punctuation">:</span> <span class="token string">'1234567'</span><span class="token punctuation">,</span> email<span class="token punctuation">:</span> <span class="token string">'admin2@admin.com'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// 将admin对象存储到数据库</span>admin<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> ret<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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>ret<span class="token punctuation">,</span> <span class="token string">'成功'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="查(查询数据)"><a href="#查(查询数据)" class="headerlink" title="查(查询数据)"></a>查(查询数据)</h5><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 查询所有</span>User<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> ret<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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>ret<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//查询结果</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// 按条件查询所有</span>User<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin2'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> ret<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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>ret<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//查询结果</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// 按条件查询单个</span>User<span class="token punctuation">.</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin2'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> ret<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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>ret<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//查询结果</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="删(删除数据)"><a href="#删(删除数据)" class="headerlink" title="删(删除数据)"></a>删(删除数据)</h5><pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 删除单个数据</span>User<span class="token punctuation">.</span><span class="token function">deleteOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin2'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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 string">'删除成功'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">// 删除所有符合条件的数据</span>User<span class="token punctuation">.</span><span class="token function">deleteMany</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin2'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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 string">'删除成功'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//旧版本API</span>User<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin2'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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 string">'删除成功'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="改(更新数据)"><a href="#改(更新数据)" class="headerlink" title="改(更新数据)"></a>改(更新数据)</h5><pre class="line-numbers language-javascript"><code class="language-javascript">User<span class="token punctuation">.</span><span class="token function">updateOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span> password<span class="token punctuation">:</span> <span class="token string">'12345'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> ret<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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>ret<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//返回操作结果(成功与否)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>User<span class="token punctuation">.</span><span class="token function">findOneAndUpdate</span><span class="token punctuation">(</span><span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token string">'admin'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span> password<span class="token punctuation">:</span> <span class="token string">'123'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> ret<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<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>ret<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//返回更新之前的数据</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
&lt;h1 id=&quot;MongoDB-学习之基本操作(Node)&quot;&gt;&lt;a href=&quot;#MongoDB-学习之基本操作(Node)&quot; class=&quot;headerlink&quot; title=&quot;MongoDB 学习之基本操作(Node)&quot;&gt;&lt;/a&gt;MongoDB 学习之基本操作(Node)&lt;/
</summary>
<category term="数据库" scheme="http://ltara.gitee.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<category term="MongoDB" scheme="http://ltara.gitee.io/tags/MongoDB/"/>
</entry>
</feed>
1
https://gitee.com/ltara/ltara.git
git@gitee.com:ltara/ltara.git
ltara
ltara
ltara
master

搜索帮助