<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Ian Gao</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <icon>https://ig505gi.github.io/blog/icon.png</icon>
  <id>https://ig505gi.github.io/blog/</id>
  <link href="https://ig505gi.github.io/blog/" rel="alternate"/>
  <link href="https://ig505gi.github.io/blog/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Ian Gao</rights>
  <subtitle>Ian Gao 的技术博客，记录 AI、全栈、工程实践与生活思考。</subtitle>
  <title>Ian Gao's Blog</title>
  <updated>2026-05-29T11:55:00.723Z</updated>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="Computer Science" scheme="https://ig505gi.github.io/blog/tags/Computer-Science/"/>
    <category term="Full Stack" scheme="https://ig505gi.github.io/blog/tags/Full-Stack/"/>
    <category term="Network" scheme="https://ig505gi.github.io/blog/tags/Network/"/>
    <content>
      <![CDATA[<h2 id="浏览器是多进程的"><a href="#浏览器是多进程的" class="headerlink" title="浏览器是多进程的"></a>浏览器是多进程的</h2><h3 id="浏览器都包含哪些进程？"><a href="#浏览器都包含哪些进程？" class="headerlink" title="浏览器都包含哪些进程？"></a>浏览器都包含哪些进程？</h3><ul><li>Browser进程：浏览器的主进程（负责协调、主控），只有一个。作用有<ul><li>负责浏览器界面显示，与用户交互。如前进，后退等</li><li>负责各个页面的管理，创建和销毁其他进程</li><li>将Renderer进程得到的内存中的Bitmap，绘制到用户界面上</li><li>网络资源的管理，下载等</li></ul></li></ul><ul><li>第三方插件进程：每种类型的插件对应一个进程，仅当使用该插件时才创建</li><li>GPU进程：最多一个，用于3D绘制等</li><li>浏览器渲染进程（浏览器内核）（Renderer进程，内部是多线程的）：默认每个Tab页面一个进程，互不影响。</li></ul><h4 id="2-1-浏览器多进程的优势"><a href="#2-1-浏览器多进程的优势" class="headerlink" title="2.1. 浏览器多进程的优势"></a>2.1. 浏览器多进程的优势</h4><ul><li>避免单个page crash影响整个浏览器</li><li>避免第三方插件crash影响整个浏览器</li><li>多进程充分利用多核优势</li><li>方便使用沙盒模型隔离插件等进程，提高浏览器稳定性</li></ul><h4 id="2-2-重点是浏览器内核（渲染进程）"><a href="#2-2-重点是浏览器内核（渲染进程）" class="headerlink" title="2.2. 重点是浏览器内核（渲染进程）"></a>2.2. 重点是浏览器内核（渲染进程）</h4><p><em><strong>浏览器的渲染进程是多线程的</strong></em></p><ul><li><ol><li>GUI渲染线程</li></ol><ul><li>负责渲染浏览器界面，解析HTML，CSS，构建DOM树和RenderObject树，布局和绘制等。</li><li>当界面需要重绘（Repaint）或由于某种操作引发回流(reflow)时，该线程就会执行</li><li>注意，<strong>GUI渲染线程与JS引擎线程是互斥的</strong>，当JS引擎执行时GUI线程会被挂起（相当于被冻结了），GUI更新会被保存在一个队列中<strong>等到JS引擎空闲时</strong>立即被执行。<ul><li><ol start="2"><li>JS引擎线程</li></ol><ul><li>也称为JS内核，负责处理Javascript脚本程序。（例如V8引擎）</li><li>JS引擎线程负责解析Javascript脚本，运行代码。</li><li>JS引擎一直等待着任务队列中任务的到来，然后加以处理，一个Tab页（renderer进程）中无论什么时候都只有一个JS线程在运行JS程序</li><li>同样注意，<strong>GUI渲染线程与JS引擎线程是互斥的</strong>，所以如果JS执行的时间过长，这样就会造成页面的渲染不连贯，导致页面渲染加载阻塞。</li></ul></li><li><ol start="3"><li>事件触发线程</li></ol><ul><li>归属于浏览器而不是JS引擎，用来控制事件循环（可以理解，JS引擎自己都忙不过来，需要浏览器另开线程协助）</li><li>当JS引擎执行代码块如setTimeOut时（也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等），会将对应任务添加到事件线程中</li><li>当对应的事件符合触发条件被触发时，该线程会把事件添加到待处理队列的队尾，等待JS引擎的处理</li><li>注意，由于JS的单线程关系，所以这些待处理队列中的事件都得排队等待JS引擎处理（当JS引擎空闲时才会去执行）</li></ul></li><li><ol start="4"><li>定时触发器线程</li></ol><ul><li>传说中的<code>setInterval</code>与<code>setTimeout</code>所在线程</li><li>浏览器定时计数器并不是由JavaScript引擎计数的,（因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确）<ul><li>因此通过单独线程来计时并触发定时（计时完毕后，添加到事件队列中，等待JS引擎空闲后执行）</li><li>注意，W3C在HTML标准中规定，规定要求setTimeout中低于4ms的时间间隔算为4ms。</li></ul></li></ul></li><li><ol start="5"><li>异步http请求线程</li></ol><ul><li>在XMLHttpRequest在连接后是通过浏览器新开一个线程请求</li><li>将检测到状态变更时，如果设置有回调函数，异步线程就<strong>产生状态变更事件</strong>，将这个回调再放入事件队列中。再由JavaScript引擎执行。</li></ul></li></ul></li></ul></li></ul><h4 id="2-3-Browser进程和浏览器内核（Renderer进程）的通信过程"><a href="#2-3-Browser进程和浏览器内核（Renderer进程）的通信过程" class="headerlink" title="2.3. Browser进程和浏览器内核（Renderer进程）的通信过程"></a>2.3. Browser进程和浏览器内核（Renderer进程）的通信过程</h4><p><img src="/blog/posts/%E4%BB%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%88%B0JS%E5%8D%95%E7%BA%BF%E7%A8%8B/browser%E9%80%9A%E4%BF%A1.png" alt="browser通信"></p><h2 id="梳理浏览器内核中线程之间的关系"><a href="#梳理浏览器内核中线程之间的关系" class="headerlink" title="梳理浏览器内核中线程之间的关系"></a>梳理浏览器内核中线程之间的关系</h2><h4 id="GUI渲染线程与JS引擎线程互斥"><a href="#GUI渲染线程与JS引擎线程互斥" class="headerlink" title="GUI渲染线程与JS引擎线程互斥"></a>GUI渲染线程与JS引擎线程互斥</h4><h4 id="JS阻塞页面加载"><a href="#JS阻塞页面加载" class="headerlink" title="JS阻塞页面加载"></a>JS阻塞页面加载</h4><h4 id="WebWorker，JS的多线程？"><a href="#WebWorker，JS的多线程？" class="headerlink" title="WebWorker，JS的多线程？"></a>WebWorker，JS的多线程？</h4><ul><li>创建Worker时，JS引擎向浏览器申请开一个子线程（子线程是浏览器开的，完全受主线程控制，而且不能操作DOM）</li><li>JS引擎线程与worker线程间通过特定的方式通信（postMessage API，需要通过序列化对象来与线程交互特定的数据）</li></ul><h4 id="WebWorker与SharedWorker"><a href="#WebWorker与SharedWorker" class="headerlink" title="WebWorker与SharedWorker"></a>WebWorker与SharedWorker</h4><ul><li>WebWorker只属于某个页面，不会和其他页面的Render进程（浏览器内核进程）共享</li><li>SharedWorker是浏览器所有页面共享的，不能采用与Worker同样的方式实现，因为它不隶属于某个Render进程，可以为多个Render进程共享使用</li></ul><h2 id="简单梳理下浏览器渲染流程"><a href="#简单梳理下浏览器渲染流程" class="headerlink" title="简单梳理下浏览器渲染流程"></a>简单梳理下浏览器渲染流程</h2><p>浏览器器内核拿到内容后，渲染大概可以划分成以下几个步骤：</p><ol><li>解析html建立dom树</li><li>解析css构建render树（将CSS代码解析成树形的数据结构，然后结合DOM合并成render树）</li><li>布局render树（Layout&#x2F;reflow），负责各元素尺寸、位置的计算</li><li>绘制render树（paint），绘制页面像素信息</li><li>浏览器会将各层的信息发送给GPU，GPU会将各层合成（composite），显示在屏幕上。</li></ol><p>所有详细步骤都已经略去，渲染完毕后就是<code>load</code>事件了，之后就是自己的JS逻辑处理了</p><h4 id="load事件与DOMContentLoaded事件的先后"><a href="#load事件与DOMContentLoaded事件的先后" class="headerlink" title="load事件与DOMContentLoaded事件的先后"></a>load事件与DOMContentLoaded事件的先后</h4><h4 id="css加载是否会阻塞dom树渲染？"><a href="#css加载是否会阻塞dom树渲染？" class="headerlink" title="css加载是否会阻塞dom树渲染？"></a>css加载是否会阻塞dom树渲染？</h4><h4 id="普通图层和复合图层"><a href="#普通图层和复合图层" class="headerlink" title="普通图层和复合图层"></a>普通图层和复合图层</h4><h2 id="从Event-Loop谈JS的运行机制"><a href="#从Event-Loop谈JS的运行机制" class="headerlink" title="从Event Loop谈JS的运行机制"></a>从Event Loop谈JS的运行机制</h2><ul><li>JS分为同步任务和异步任务</li><li>同步任务都在主线程上执行，形成一个<code>执行栈</code></li><li>主线程之外，<strong>事件触发线程</strong>管理着一个<code>任务队列</code>，只要异步任务有了运行结果，就在<code>任务队列</code>之中放置一个事件。</li><li>一旦<code>执行栈</code>中的所有同步任务执行完毕（此时JS引擎空闲），系统就会读取<code>任务队列</code>，将可运行的异步任务添加到可执行栈中，开始执行。</li></ul><p><img src="/blog/posts/%E4%BB%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%88%B0JS%E5%8D%95%E7%BA%BF%E7%A8%8B/EventLoop.png" alt="EventLoop"></p><h4 id="单独说说定时器"><a href="#单独说说定时器" class="headerlink" title="单独说说定时器"></a>单独说说定时器</h4><ul><li>为什么要单独的定时器线程？因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确，因此很有必要单独开一个线程用来计时。</li><li>什么时候会用到定时器线程？<strong>当使用<code>setTimeout</code>或<code>setInterval</code>时</strong>，它需要定时器线程计时，计时完成后就会将特定的事件推入事件队列中。</li></ul><h2 id="事件循环进阶：macrotask与microtask"><a href="#事件循环进阶：macrotask与microtask" class="headerlink" title="事件循环进阶：macrotask与microtask"></a>事件循环进阶：macrotask与microtask</h2><p>除了广义的同步任务和异步任务，我们对任务有更精细的定义：</p><ul><li>macro-task(宏任务)：包括整体代码script，setTimeout，setInterval</li><li>micro-task(微任务)：Promise，process.nextTick</li></ul><p>不同类型的任务会进入对应的Event Queue；事件循环的顺序，决定js代码的执行顺序。进入整体代码(宏任务)后，开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始，找到其中一个任务队列执行完毕，再执行所有的微任务。</p><p>（PS:这里process.nextTick有问题，运行环境不一样可能产生不一样的结果，还可能：并不是放在微任务后，而是宏任务执行完后，立马去执行这个回调，再去查询微任务）</p><p><img src="/blog/posts/%E4%BB%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%88%B0JS%E5%8D%95%E7%BA%BF%E7%A8%8B/%E5%AE%8F%E4%BB%BB%E5%8A%A1%E5%92%8C%E5%BE%AE%E4%BB%BB%E5%8A%A1.png" alt="宏任务和微任务.tiff"></p>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/%E4%BB%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%88%B0JS%E5%8D%95%E7%BA%BF%E7%A8%8B/</id>
    <link href="https://ig505gi.github.io/blog/posts/%E4%BB%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%88%B0JS%E5%8D%95%E7%BA%BF%E7%A8%8B/"/>
    <published>2023-01-06T19:00:09.000Z</published>
    <summary>深入理解浏览器多进程架构与 JavaScript 单线程模型，包括事件循环、宏任务与微任务机制。</summary>
    <title>从浏览器多进程到JS单线程</title>
    <updated>2026-05-29T11:55:00.723Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="Coursera" scheme="https://ig505gi.github.io/blog/tags/Coursera/"/>
    <category term="Computer Science" scheme="https://ig505gi.github.io/blog/tags/Computer-Science/"/>
    <category term="Full Stack" scheme="https://ig505gi.github.io/blog/tags/Full-Stack/"/>
    <category term="Network" scheme="https://ig505gi.github.io/blog/tags/Network/"/>
    <content>
      <![CDATA[<h1 id="Chapter1-整体认知"><a href="#Chapter1-整体认知" class="headerlink" title="Chapter1 整体认知"></a>Chapter1 整体认知</h1><h2 id="1-1-Internet"><a href="#1-1-Internet" class="headerlink" title="1.1 Internet"></a>1.1 Internet</h2><h2 id="1-1-1-分层"><a href="#1-1-1-分层" class="headerlink" title="1.1.1 分层"></a>1.1.1 分层</h2><p>应用层<br>传输层<br>网络层<br>数据链路层<br>物理层</p><span id="more"></span><h2 id="1-1-2-计算机网络"><a href="#1-1-2-计算机网络" class="headerlink" title="1.1.2 计算机网络"></a>1.1.2 计算机网络</h2><p>路由器是工作在网络层的，交换机是工作在链路层的</p><p>网络节点有：1.路由器或交换机等交换设备 2.主机及其运行的程序<br>P2P(Point to Point) 是网络中的一个交换设备到另一个交换设备<br>E2E(End to End) 是网络中每一个终端(PC&#x2F;手机&#x2F;Server等)到另一个终端</p><p>point在计算机网络中用<code>圆圈</code>表示，End用<code>方块</code>表示</p><p>接入链路：终端接入的<br>主干链路：路由器等的链路</p><h2 id="1-1-3-协议"><a href="#1-1-3-协议" class="headerlink" title="1.1.3 协议"></a>1.1.3 协议</h2><p>PDU 协议数据单元<br>在不同的层名字不同，在应用层叫message，在传输层叫segment</p><h2 id="从服务角度看计算机网络"><a href="#从服务角度看计算机网络" class="headerlink" title="从服务角度看计算机网络"></a>从服务角度看计算机网络</h2><p>使用通信设施进行通信的分布式应用<br>由两部分，一部分是应用进程，只认识其他的应用进程。<br>第二部分是基础设施，在应用之下以及负责传输的所有基础设施。</p><p>通信基础设施为apps提供编程接口</p><h2 id="1-2-网络边缘"><a href="#1-2-网络边缘" class="headerlink" title="1.2 网络边缘"></a>1.2 网络边缘</h2><p>边缘、核心和接入，三大块<br>上一节中End就是边缘edge，Point是核心core，把pc等边缘设备接入整个网络核心，叫接入access</p><p>【待整理】<br>CS模式<br>P2P模式 peer2peer<br>面向链接<br>有链接</p><p>tcp<br>可靠<br>流量控制<br>拥塞控制</p><h2 id="1-3-网络核心"><a href="#1-3-网络核心" class="headerlink" title="1.3 网络核心"></a>1.3 网络核心</h2><ul><li><p>电路交换，适合电话，不适合计算机<br>建立连接时间长<br>独享线路，每个线路的带宽分成piece(片)，频分，时分，波分<br>容易浪费资源</p></li><li><p>分组交换<br>带宽不分片，全用<br>在每个节点都会存储，然后转发<br>每一跳，需要存储时间，排队时间</p></li></ul><p>问题：排队延迟和丢失，就是常说的丢包</p><p>关键功能：路由和核心</p><p>统计多路复用</p><p>分组交换分类：<br>数据报网络：无连接，每个数据包都有主机的完整信息<br>虚电路网络：每个交换节点要维持链接状态</p><p>接入<br>共享网络</p><p>住宅接入 modem 调制解调器<br>利用已有的电话线<br>线缆接入<br>无线接入</p><p>互联网结构，网络的网络 ISP<br>全局的ISP，<br>ICP 会自己建立数据中心，接入就近的ISP</p><p>节点处理延时，微秒级别<br>排队延时，取决于拥塞程度<br>传输延时：从节点把比特发出到链路上的延时，微秒到毫秒级别<br>传播延时：物理传播延时，几微秒到几百毫秒</p><p>ICMP</p><p>SAP 服务访问点</p><p>DU 数据单元</p><h1 id="Chapter2-应用层"><a href="#Chapter2-应用层" class="headerlink" title="Chapter2 应用层"></a>Chapter2 应用层</h1><h2 id="2-1-应用层原理"><a href="#2-1-应用层原理" class="headerlink" title="2.1 应用层原理"></a>2.1 应用层原理</h2><p>模式：C&#x2F;S模式，p2p模式，混合模式</p><ul><li>C&#x2F;S模式：不平等、资源在服务器、性能断崖式下降、可扩展性差</li><li>p2p: 平等、性能平滑、管理困难</li></ul><p>原理：</p><ol><li>标识、寻址<br>必须要有唯一的标识符，有ip地址和端口号，才能找到进行进程间的通信</li><li>传输层的服务<br>UDP和TCP，必须每次都要带上源ip，源port和目标ip，目标port。<br>因此socket API解决这个麻烦：用TCP socket时，返回一个整型，代表以上四个信息的四元组；应用层的应用再进行通信时，只用这个整型即可。<br>目的：1.便于管理 2.使穿过层间的信息量最少</li></ol><p>UDP socket：只保存源ip和源port，二元组</p><p>TCP和UDP都不加密，都不安全</p><ol start="3"><li>定义自己的协议<br>报文类型、语法、语义、规则</li></ol><p>传输层提供的服务：</p><ul><li>数据丢失率</li><li>延迟</li><li>吞吐量</li><li>安全性</li></ul><p>例子：SSL：应用层协议，利用TCP，加密的协议；如https就是跑在SSL之上</p><h2 id="2-2-web和http"><a href="#2-2-web和http" class="headerlink" title="2.2 web和http"></a>2.2 web和http</h2><h3 id="1-请求格式："><a href="#1-请求格式：" class="headerlink" title="1. 请求格式："></a>1. 请求格式：</h3><p>prot:&#x2F;&#x2F;user:<a href="mailto:&#x70;&#115;&#x77;&#x40;&#119;&#119;&#x77;&#46;&#115;&#111;&#x6d;&#x65;&#83;&#99;&#104;&#x6f;&#111;&#x6c;&#46;&#x65;&#100;&#x75;">psw@www.someSchool.edu</a>&#x2F;someDept&#x2F;pic.gif:port<br>[协议]:&#x2F;&#x2F;[用户口令]@[主机名]&#x2F;[路径名]:[端口]</p><h3 id="2-基本原理"><a href="#2-基本原理" class="headerlink" title="2. 基本原理"></a>2. 基本原理</h3><ul><li>基于TCP，TCP向上层提供字节流的服务，http协议需要自己维护报文与报文之间的界限<br>举个例子：客户端的http给下层的tcp传输了两个15k的包；在服务端，tcp传输给了http一个30k的包，http协议需要自己分成两个15k。</li><li>一开始需要基于TCP建立连接，默认端口80</li><li>http是无状态的</li><li>RTT：往返时间。一个小分组从客户端到服务器，在回来的时间。</li></ul><h3 id="3-1-0和1-1的区别"><a href="#3-1-0和1-1的区别" class="headerlink" title="3. 1.0和1.1的区别"></a>3. 1.0和1.1的区别</h3><ul><li>http1.0:非持久，每次传输一个对象，HTTP关闭TCP连接，因此每传输一个对象都要2个RTT。</li><li>http1.1:持久，连接不关;有流水和非流水型的。</li></ul><h3 id="4-报文"><a href="#4-报文" class="headerlink" title="4. 报文"></a>4. 报文</h3><p>编码是ASCII</p><ul><li>请求 GET&#x2F;POST&#x2F;HEAD PUT&#x2F;DELETE(网络管理员使用)</li><li>响应 响应码</li></ul><h3 id="5-cookie"><a href="#5-cookie" class="headerlink" title="5.cookie"></a>5.cookie</h3><p>保存在客户端，有过期时间（已经理解，不解释了）</p><h3 id="6-web缓存"><a href="#6-web缓存" class="headerlink" title="6. web缓存"></a>6. web缓存</h3><ul><li>可以通过局域网建立缓存服务器，通常由ISP安装</li><li>优点：客户会快，而且减少用户访问远程服务器的压力</li><li>大家搜索的东西需要有趋同性，这样更容易命中缓存</li></ul><h2 id="2-3-FTP"><a href="#2-3-FTP" class="headerlink" title="2.3 FTP"></a>2.3 FTP</h2><ul><li>文件传输协议</li><li>ftp服务器知名端口：21</li><li>控制连接：客户端请求服务器21号端口，身份确认，请求控制连接浏览远程目录，请求下载某个文件</li><li>数据连接：服务端请求客户端的20号端口，发送数据</li></ul><h2 id="2-4-Email"><a href="#2-4-Email" class="headerlink" title="2.4 Email"></a>2.4 Email</h2><h3 id="3个主要组成部分"><a href="#3个主要组成部分" class="headerlink" title="3个主要组成部分"></a>3个主要组成部分</h3><ul><li>用户代理：邮件是从发送服务器到目标服务器，所以用户操作是代理</li><li>邮件服务器</li><li>简单邮件传输协议： SMTP</li></ul><h3 id="SMTP"><a href="#SMTP" class="headerlink" title="SMTP"></a>SMTP</h3><ul><li>使用TCP，端口25</li><li>从发送方服务器到接收方服务器</li><li>握手，传输报文，关闭</li><li>命令：ASCII文本；响应：状态码和状态信息</li><li>报文必须是7位ASCII码</li><li>邮件中有多个对象，附件等；http只有一个</li><li>内容必须是ASCII码，怎么编码中文等？<br>报文首部声明MIME，多媒体编码。比如base64</li></ul><h3 id="拉取协议"><a href="#拉取协议" class="headerlink" title="拉取协议"></a>拉取协议</h3><ul><li>POP3：邮局访问协议</li><li>IMAP: Internet邮件访问协议，比POP3更复杂</li><li>HTTP： hotmail等</li></ul>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <link href="https://ig505gi.github.io/blog/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <published>2022-08-22T19:00:09.000Z</published>
    <summary>计算机网络基础课程笔记，涵盖 Internet 分层架构、TCP/IP 协议栈等核心概念。</summary>
    <title>计算机网络-学习笔记</title>
    <updated>2026-05-29T11:55:00.726Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="Coursera" scheme="https://ig505gi.github.io/blog/tags/Coursera/"/>
    <category term="Computer Science" scheme="https://ig505gi.github.io/blog/tags/Computer-Science/"/>
    <category term="Full Stack" scheme="https://ig505gi.github.io/blog/tags/Full-Stack/"/>
    <content>
      <![CDATA[<p>简介：Coursera网课笔记，已经在Part1用硬件造出了电脑，准备实现OS、VM、编译器、高级编程语言。<br>课程地址：<a href="https://www.coursera.org/learn/nand2tetris2">https://www.coursera.org/learn/nand2tetris2</a><br>作业仓库：<a href="https://github.com/ig505gi/Nand2Tetris-homework">https://github.com/ig505gi/Nand2Tetris-homework</a></p><p><img src="/blog/posts/Nand2Tetris-Part2/week1/overview.png" alt="课程概览，part2为上面的软件部分"></p><span id="more"></span><h1 id="Week1-Overview和VM-part1"><a href="#Week1-Overview和VM-part1" class="headerlink" title="Week1 Overview和VM part1"></a><strong>Week1 Overview和VM part1</strong></h1><h2 id="0-Overview"><a href="#0-Overview" class="headerlink" title="0. Overview"></a>0. <strong>Overview</strong></h2><p>软件层面，我们如何从高级编程语言一步步转化为机器语言<br><img src="/blog/posts/Nand2Tetris-Part2/week1/%E4%BB%8E%E9%AB%98%E7%BA%A7%E5%88%B0%E4%BD%8E%E7%BA%A7.png" alt="高级编程语言到机器语言"><br>本周的内容主要是VM的实现part1</p><h3 id="0-1-VM-Overview"><a href="#0-1-VM-Overview" class="headerlink" title="0.1 VM Overview"></a>0.1 VM Overview</h3><p>VM中应该有四种命令：算术&#x2F;逻辑、内存访问、分支、函数<br><img src="/blog/posts/Nand2Tetris-Part2/week1/VM-part1.png" alt="VM part1和part2"><br>本周为part1，作为project7，只会涉及算术&#x2F;逻辑、内存访问；而分支和函数则在week2作为part2，在project8中实现。</p><h2 id="1-Stack-Memory-Segment-concept"><a href="#1-Stack-Memory-Segment-concept" class="headerlink" title="1. Stack &amp; Memory Segment concept"></a>1. <strong>Stack &amp; Memory Segment concept</strong></h2><h3 id="1-1-Stack"><a href="#1-1-Stack" class="headerlink" title="1.1 Stack"></a>1.1 Stack</h3><p>在VM中，重要的对象stack，stack除了我们所熟知的push和pop方法，还有一些可以简化操作的方法<br><img src="/blog/posts/Nand2Tetris-Part2/week1/stack-function.png" alt="Stack的方法"></p><ul><li><code>add</code>: 将栈顶的两个值相加，并放回栈顶</li><li><code>neg</code>: 对栈顶的值求负，并放回栈顶</li><li><code>eq</code>: 比较栈顶的两个值，将是否相等的布尔值放回栈顶</li><li><code>or</code>: 对栈顶的两个值进行逻辑or的操作，并放回栈顶</li></ul><p>以此类推。。（图中eq应该是x&#x3D;&#x3D;y）<br><img src="/blog/posts/Nand2Tetris-Part2/week1/stack-function2.png" alt="Stack的方法2"></p><h3 id="1-2-Memory-Segment"><a href="#1-2-Memory-Segment" class="headerlink" title="1.2 Memory Segment"></a>1.2 Memory Segment</h3><p>在内存中存放的值，会分不同的片段，push和pop主要是在stack和segment之间来回通信的操作。<br><img src="/blog/posts/Nand2Tetris-Part2/week1/memory-segment.png" alt="Memory Segment语法"><br>一共有8种Segment：local, argument, static, constant, this, that, pointer, temp</p><h2 id="2-VM-Implement"><a href="#2-VM-Implement" class="headerlink" title="2. VM Implement"></a>2. <strong>VM Implement</strong></h2><h3 id="2-1-指针：如何从VM-code到Machine-code"><a href="#2-1-指针：如何从VM-code到Machine-code" class="headerlink" title="2.1 指针：如何从VM code到Machine code"></a>2.1 指针：如何从VM code到Machine code</h3><p>Memory Segment和stack的push和pop方法，都属于抽象的概念。如何转化为我们的机器语言和RAM交互逻辑？<br>要借助指针的概念：<br><img src="/blog/posts/Nand2Tetris-Part2/week1/pointer.png" alt="指针的基本思想"></p><h3 id="2-2-stack-Implement"><a href="#2-2-stack-Implement" class="headerlink" title="2.2 stack Implement"></a>2.2 stack Implement</h3><p>抽象的stack machine：从push到指针再到Hack语言</p><ul><li>注意：stack的指针SP，一直指向栈顶。</li></ul><p><img src="/blog/posts/Nand2Tetris-Part2/week1/stack-machine.png" alt="stack machine"></p><h3 id="2-3-local-argument-this-that-Segment-Implement"><a href="#2-3-local-argument-this-that-Segment-Implement" class="headerlink" title="2.3 local&#x2F;argument&#x2F;this&#x2F;that Segment Implement"></a>2.3 local&#x2F;argument&#x2F;this&#x2F;that Segment Implement</h3><p>这四个segment的作用和分工：</p><ul><li>方法的本地变量或入参放在local和argument中</li><li>方法可访问到的对象和数组放在this或that中；</li><li>注意，与stack不同:local&#x2F;argument&#x2F;this&#x2F;that的指针, 一直指向base。</li></ul><p><img src="/blog/posts/Nand2Tetris-Part2/week1/local.png" alt="local实现"><br>图中的pop local 2用hack assembly实现：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">// 必须借助其他内存空间才能完成</span><br><span class="line">@2 // Addr = LCL + 2</span><br><span class="line">D=A</span><br><span class="line">@LCL</span><br><span class="line">D=M+D</span><br><span class="line">@R13</span><br><span class="line">M=D</span><br><span class="line">@SP // SP--, D=*SP</span><br><span class="line">AM=M-1</span><br><span class="line">D=M</span><br><span class="line">@R13 // *Addr = D</span><br><span class="line">A=M</span><br><span class="line">M=D</span><br></pre></td></tr></table></figure><p>push local 5实现：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">@5 // addr = LCL + 5</span><br><span class="line">D=A</span><br><span class="line">@LCL</span><br><span class="line">A=M+D</span><br><span class="line">D=M</span><br><span class="line">@SP // *SP = *addr</span><br><span class="line">A=M</span><br><span class="line">M=D</span><br><span class="line">@SP // SP++</span><br><span class="line">M=M+1</span><br></pre></td></tr></table></figure><p>argument&#x2F;this&#x2F;that和local的实现是一致的</p><h3 id="2-4-constant-Segment-Implement"><a href="#2-4-constant-Segment-Implement" class="headerlink" title="2.4 constant Segment Implement"></a>2.4 constant Segment Implement</h3><p>constant没有pop操作，也在内存里没有对应的地址<br><code>push constant i</code> 转化为 <code>*SP = i, SP++</code></p><h3 id="2-5-static-Segment-Implement"><a href="#2-5-static-Segment-Implement" class="headerlink" title="2.5 static Segment Implement"></a>2.5 static Segment Implement</h3><ul><li>static因为要保证去全局唯一，比较复杂，加上文件前缀；16~256</li><li>文件名+.+数字 确定一个唯一的Label</li></ul><p><img src="/blog/posts/Nand2Tetris-Part2/week1/static%E5%AE%9E%E7%8E%B0.png" alt="static实现"></p><h3 id="2-6-temp-Segment-Implement"><a href="#2-6-temp-Segment-Implement" class="headerlink" title="2.6 temp Segment Implement"></a>2.6 temp Segment Implement</h3><ul><li>当编译高级语言的时候，可能会需要一些变量暂时存储，我们的VM只提供8个暂时变量 5~12，实现上几乎和local等是一样的。</li><li>不同点是LCL是指针，<code>@LCL</code>是指针操作，要拿出存在LCL位置的值在加上index；而temp是<code>@5</code>（5是temp的base），然后<code>5+index</code>。</li></ul><h3 id="2-7-pointer-Segment-Implement"><a href="#2-7-pointer-Segment-Implement" class="headerlink" title="2.7 pointer Segment Implement"></a>2.7 pointer Segment Implement</h3><ul><li>注意：跟其他的不同点，该操作的是this或that指针，而其他操作的都是指针指向地址的值</li><li>pointer为什么需要：之后写编译器的时候才会解释<br><img src="/blog/posts/Nand2Tetris-Part2/week1/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81pointer.png" alt="为什么要pointer segment"><br>pointer的值只有0或1，当0的时候就是访问this，当1的时候，访问that<br><img src="/blog/posts/Nand2Tetris-Part2/week1/pointer-segment%E5%AE%9E%E7%8E%B0.png" alt="pointer segment实现"></li></ul><h3 id="2-8-Hack平台的约定-标准"><a href="#2-8-Hack平台的约定-标准" class="headerlink" title="2.8 Hack平台的约定&#x2F;标准"></a>2.8 Hack平台的约定&#x2F;标准</h3><p>这些segment在哪里，在Hack平台都是约定好的<br><img src="/blog/posts/Nand2Tetris-Part2/week1/%E6%A0%87%E5%87%86VMmapping.png" alt="Hack平台的标准VM Mapping"><br>用约定好的标识符<br><img src="/blog/posts/Nand2Tetris-Part2/week1/%E6%A0%87%E5%87%86VMmapping2.png" alt="Hack平台的标准VM Mapping的标识符"></p><h2 id="3-VM-translator"><a href="#3-VM-translator" class="headerlink" title="3 VM translator"></a>3 <strong>VM translator</strong></h2><p>可以用python或者java等语言去写，主要架构如下：<br><img src="/blog/posts/Nand2Tetris-Part2/week1/translator%E6%9E%B6%E6%9E%84.png" alt="VM translator架构"><br>其中parser的主要API：<br><img src="/blog/posts/Nand2Tetris-Part2/week1/parser%E7%B1%BBAPI.png" alt="Parser的API"><br><img src="/blog/posts/Nand2Tetris-Part2/week1/parser%E7%B1%BBAPI2.png" alt="Parser的API补充"><br>codeWriter的主要API：<br><img src="/blog/posts/Nand2Tetris-Part2/week1/codeWriter%E7%B1%BBAPI.png" alt="CodeWriter的API"></p><h2 id="4-project7"><a href="#4-project7" class="headerlink" title="4. project7"></a>4. <strong>project7</strong></h2><p>code now!</p><h1 id="Week2-VM-part2"><a href="#Week2-VM-part2" class="headerlink" title="Week2 VM part2"></a><strong>Week2 VM part2</strong></h1><h2 id="0-overview"><a href="#0-overview" class="headerlink" title="0. overview"></a>0. <strong>overview</strong></h2><p>涉及算术&#x2F;逻辑、内存访问已经在week1学习。接下来要学习的有以下知识：</p><ul><li>Branching</li><li>Functions</li><li>Function call-and-return</li><li>Dynamic memory management</li><li>Stack processing</li><li>Pointers</li></ul><p>然后就完成了VM的实现。</p><h2 id="1-branching"><a href="#1-branching" class="headerlink" title="1. branching"></a>1. <strong>branching</strong></h2><p>branching和assembly中的branch几乎一样。<br><img src="/blog/posts/Nand2Tetris-Part2/week2/branching.png" alt="branching"></p><h2 id="2-functions-重点"><a href="#2-functions-重点" class="headerlink" title="2. functions(重点)"></a>2. <strong>functions(重点)</strong></h2><h2 id="2-1-overview-abstraction"><a href="#2-1-overview-abstraction" class="headerlink" title="2.1 overview&#x2F;abstraction"></a>2.1 overview&#x2F;abstraction</h2><p>function：在其他地方也有叫method，一个意思。<br>一个函数从高阶语言编译成vm伪代码，再到vm代码<br><img src="/blog/posts/Nand2Tetris-Part2/week2/function-overview.png" alt="function overview"></p><h2 id="2-2-caller-and-callee"><a href="#2-2-caller-and-callee" class="headerlink" title="2.2 caller and callee"></a>2.2 caller and callee</h2><p>左侧的main函数是caller，右侧mult函数是callee，在每个函数内，他们视角中的stack和内存segment是不同的。<br><img src="/blog/posts/Nand2Tetris-Part2/week2/caller-and-callee.png" alt="caller and callee"><br>抽象的说一个call命令，应该做什么；一个return命令，应该做什么<br><img src="/blog/posts/Nand2Tetris-Part2/week2/call-and-return.png" alt="call and return"><br>在一个函数内，也是通过stack和memory segement之间的push&#x2F;pop操作来进行计算的，那么，当caller调用了callee的时候，caller的状态必须保存下来，运行完callee后，恢复caller的状态，接着运行caller。</p><h2 id="2-3-implement"><a href="#2-3-implement" class="headerlink" title="2.3 implement"></a>2.3 implement</h2><p>caller的视角<br><img src="/blog/posts/Nand2Tetris-Part2/week2/caller-view.png" alt="caller的视角"><br>callee的视角<br><img src="/blog/posts/Nand2Tetris-Part2/week2/callee-view.png" alt="callee的视角"><br>vm代码<br><img src="/blog/posts/Nand2Tetris-Part2/week2/vm-view.png" alt="vm代码中实现相关语句"></p><h3 id="2-3-1-handling-call"><a href="#2-3-1-handling-call" class="headerlink" title="2.3.1 handling call"></a>2.3.1 handling call</h3><p><img src="/blog/posts/Nand2Tetris-Part2/week2/handling-call.png" alt="处理call语句"></p><h3 id="2-3-2-handling-function"><a href="#2-3-2-handling-function" class="headerlink" title="2.3.2 handling function"></a>2.3.2 handling function</h3><p><img src="/blog/posts/Nand2Tetris-Part2/week2/handling-function.png" alt="处理function语句"></p><h3 id="2-3-3-handling-return"><a href="#2-3-3-handling-return" class="headerlink" title="2.3.3 handling return"></a>2.3.3 handling return</h3><p><img src="/blog/posts/Nand2Tetris-Part2/week2/handling-return.png" alt="处理return语句"></p><h2 id="3-hack平台"><a href="#3-hack平台" class="headerlink" title="3. hack平台"></a>3. <strong>hack平台</strong></h2><p>hack平台的编译和翻译概览<br><img src="/blog/posts/Nand2Tetris-Part2/week2/hack-vm-compilation.png" alt="jack -&gt; vm -&gt; asm"><br>Hack平台的约定</p><ul><li>需要有个Main.vm文件；其中一个函数叫main</li><li>电脑开启 运行Sys.init</li><li>Sys.init会call Main.main，然后进入无限循环</li><li>约定最开始的两条机器语言指令是：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// 这两行应该卸载hack ROM中，最开始的两行</span><br><span class="line">sp=256</span><br><span class="line">Call Sys.init</span><br></pre></td></tr></table></figure><p>目前为止，VM中的特殊字符一共有如下这么多：<br><img src="/blog/posts/Nand2Tetris-Part2/week2/vm-special-symbols.png" alt="VM中的特殊字符"></p><h2 id="4-具体代码实现"><a href="#4-具体代码实现" class="headerlink" title="4.具体代码实现"></a>4.<strong>具体代码实现</strong></h2><p>还是三个模块Main、Parser和CodeWriter。其中CodeWriter需要增加一些方法，Parser需要补充一些逻辑。<br><img src="/blog/posts/Nand2Tetris-Part2/week2/code-writer.png" alt="CodeWriter补充方法"></p><p>project8: code now!</p><h2 id="5-问答"><a href="#5-问答" class="headerlink" title="5.问答"></a>5.问答</h2><p>因为java先转VM再转机器语言(two tier compiler)，<br>而C++直接转机器语言，因此快（但微软也开发了C++的two tier compiler）<br>java转化为机器语言1000行，C++只需要300行，差不多效率是3倍</p><h1 id="Week3"><a href="#Week3" class="headerlink" title="**Week3 **"></a>**Week3 **</h1><p>【待整理】<br>Main.main程序入口<br>2<br>面向对象<br>3<br>链表<br>4<br>语法<br>5<br>数据类型</p><p>6<br>class<br>OS的目的<br>OS API 8个</p><p>7<br>method fucntion constructor的区别<br>变量类型<br>expression和subroutine<br>JACK语言的特殊点</p><p>8<br>jack app demo</p><h1 id="Week4"><a href="#Week4" class="headerlink" title="**Week4 **"></a>**Week4 **</h1>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/Nand2Tetris-Part2/</id>
    <link href="https://ig505gi.github.io/blog/posts/Nand2Tetris-Part2/"/>
    <published>2022-07-10T16:28:17.000Z</published>
    <summary>Coursera 课程笔记，在硬件基础上实现操作系统、虚拟机、编译器和高级编程语言。</summary>
    <title>Nand2Tetris-Part2</title>
    <updated>2026-05-29T11:55:00.663Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="Coursera" scheme="https://ig505gi.github.io/blog/tags/Coursera/"/>
    <category term="Computer Science" scheme="https://ig505gi.github.io/blog/tags/Computer-Science/"/>
    <category term="Full Stack" scheme="https://ig505gi.github.io/blog/tags/Full-Stack/"/>
    <content>
      <![CDATA[<p>简介：Coursera网课笔记，从一个门电路开始，搭建一台电脑。<br>课程地址：<a href="https://www.coursera.org/learn/build-a-computer">https://www.coursera.org/learn/build-a-computer</a><br>作业仓库：<a href="https://github.com/ig505gi/Nand2Tetris-homework">https://github.com/ig505gi/Nand2Tetris-homework</a></p><p><img src="/blog/posts/Nand2Tetris-Part1/week1/overview.png" alt="课程概览，part1为下面的硬件部分"></p><span id="more"></span><h2 id="Week1-布尔运算和逻辑门"><a href="#Week1-布尔运算和逻辑门" class="headerlink" title="Week1 布尔运算和逻辑门"></a>Week1 布尔运算和逻辑门</h2><h3 id="1-1-运算法则"><a href="#1-1-运算法则" class="headerlink" title="1.1 运算法则"></a>1.1 运算法则</h3><p><img src="/blog/posts/Nand2Tetris-Part1/week1/booleanIdentities.png" alt="布尔运算法则"></p><h3 id="1-2-NAND"><a href="#1-2-NAND" class="headerlink" title="1.2 NAND"></a>1.2 NAND</h3><p>任何一个布尔函数都可以用只含有AND和NOT操作符的表达式来表示；而NAND是NOT和AND的结合，因此：<br><code>任何一个布尔函数都可以用只含有NAND操作符的表达式来表示</code><br><img src="/blog/posts/Nand2Tetris-Part1/week1/NAND.png" alt="NAND的证明"></p><h3 id="1-3-XOR"><a href="#1-3-XOR" class="headerlink" title="1.3 XOR"></a>1.3 XOR</h3><p><img src="/blog/posts/Nand2Tetris-Part1/week1/XOR.png" alt="XOR真值表和门电路图"><br><img src="/blog/posts/Nand2Tetris-Part1/week1/XOR-HDL.png" alt="XOR的HDL实现"><br>ps: HDL: Hardware Design Language</p><h3 id="1-4-Multiplexor和Demultiplexor"><a href="#1-4-Multiplexor和Demultiplexor" class="headerlink" title="1.4 Multiplexor和Demultiplexor"></a>1.4 Multiplexor和Demultiplexor</h3><p>HDL中的subBuses：<br><img src="/blog/posts/Nand2Tetris-Part1/week1/subBuses.png" alt="subBuses"><br>Multiplexor非常有用，可以从多条数据中选出其中的一条<br><img src="/blog/posts/Nand2Tetris-Part1/week1/Multiplexor.png" alt="Multiplexor"><br>Demultiplexor可以从一条数据中按照规律解析成多条数据<br><img src="/blog/posts/Nand2Tetris-Part1/week1/Demultiplexor.png" alt="Demultiplexor"></p><h2 id="Week2-加法和算术逻辑单元ALU"><a href="#Week2-加法和算术逻辑单元ALU" class="headerlink" title="Week2 加法和算术逻辑单元ALU"></a>Week2 加法和算术逻辑单元ALU</h2><h3 id="2-1-半加器和加法器"><a href="#2-1-半加器和加法器" class="headerlink" title="2.1 半加器和加法器"></a>2.1 半加器和加法器</h3><h4 id="半加器真值表："><a href="#半加器真值表：" class="headerlink" title="半加器真值表："></a>半加器真值表：</h4><table><thead><tr><th>输入输出</th><th>a</th><th>b</th><th>sum</th><th>carry</th></tr></thead><tbody><tr><td>值</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>值</td><td>0</td><td>1</td><td>1</td><td>0</td></tr><tr><td>值</td><td>1</td><td>0</td><td>1</td><td>0</td></tr><tr><td>值</td><td>1</td><td>1</td><td>0</td><td>1</td></tr></tbody></table><h4 id="全加器真值表和接口"><a href="#全加器真值表和接口" class="headerlink" title="全加器真值表和接口"></a>全加器真值表和接口</h4><p><img src="/blog/posts/Nand2Tetris-Part1/week2/fullAdder.png" alt="全加器"></p><h3 id="2-2-负数和补位"><a href="#2-2-负数和补位" class="headerlink" title="2.2 负数和补位"></a>2.2 负数和补位</h3><p>从左起第一位为0的为正数，为1的为负数，x+(-x)&#x3D;0，如果不考虑进了一位，也就是2^n<br><img src="/blog/posts/Nand2Tetris-Part1/week2/negative.png" alt="负数的计算"></p><h3 id="2-3-ALU"><a href="#2-3-ALU" class="headerlink" title="2.3 ALU"></a>2.3 ALU</h3><p>CPU里的核心计算单元，该项目里设计的比较简单<br><img src="/blog/posts/Nand2Tetris-Part1/week2/ALU.png" alt="ALU概览"><br><img src="/blog/posts/Nand2Tetris-Part1/week2/ALU%E7%9C%9F%E5%80%BC%E8%A1%A8.png" alt="ALU真值表"></p><h3 id="Week3-内存"><a href="#Week3-内存" class="headerlink" title="Week3 内存"></a>Week3 内存</h3><h3 id="3-1-Sequential-Logic-DFF"><a href="#3-1-Sequential-Logic-DFF" class="headerlink" title="3.1 Sequential Logic &amp; DFF"></a>3.1 Sequential Logic &amp; DFF</h3><p>序列逻辑可以在一个位置存放所有的信息(这句怎么理解？)<br><img src="/blog/posts/Nand2Tetris-Part1/week3/SequentialLogic.png" alt="Sequential Logic"><br>DFF: Data Filp Flop<br>特点：1.自己需要保存一个状态，2.该状态取决于上一个时间unit的输入<br><img src="/blog/posts/Nand2Tetris-Part1/week3/DFF.png" alt="DFF"></p><h3 id="3-2-Register寄存器"><a href="#3-2-Register寄存器" class="headerlink" title="3.2 Register寄存器"></a>3.2 Register寄存器</h3><h4 id="1-Bit-Register"><a href="#1-Bit-Register" class="headerlink" title="1-Bit Register"></a>1-Bit Register</h4><p>内部用一个DFF和一个Mux可以实现。<br><img src="/blog/posts/Nand2Tetris-Part1/week3/1-bitRegister.png" alt="1-Bit Register"><br>16个1-Bit Register组合在一起成为一个16位寄存器</p><h3 id="3-3-RAM"><a href="#3-3-RAM" class="headerlink" title="3.3 RAM"></a>3.3 RAM</h3><p>Register堆在一起组成RAM，图中的小三角代表是一个Sequential chip<br><img src="/blog/posts/Nand2Tetris-Part1/week3/RAM.png" alt="RAM"><br>读：设置address为i就好<br>写：设置address为i，in为v，load为1</p><h4 id="RAM16K"><a href="#RAM16K" class="headerlink" title="RAM16K"></a>RAM16K</h4><p>我们的电脑内存是16K的，如何用HDL来写？<br>首先实现RAM8.png，tips:<br><img src="/blog/posts/Nand2Tetris-Part1/week3/RAM8.png" alt="RAM8实现tips"><br>8个16bit-Register组成RAM8,8个RAM8组成RAM64。。。。。<br><img src="/blog/posts/Nand2Tetris-Part1/week3/RAM16K.png" alt="RAM16K实现"></p><h3 id="3-4-Counter"><a href="#3-4-Counter" class="headerlink" title="3.4 Counter"></a>3.4 Counter</h3><p>计数器，内部用DFF实现，每过一个时间单位，能自动加一<br><img src="/blog/posts/Nand2Tetris-Part1/week3/Counter.png" alt="Counter"></p><h2 id="Week4-机器语言"><a href="#Week4-机器语言" class="headerlink" title="Week4 机器语言"></a>Week4 机器语言</h2><h3 id="4-1-Machine-Language-Overview"><a href="#4-1-Machine-Language-Overview" class="headerlink" title="4.1 Machine Language Overview"></a>4.1 Machine Language Overview</h3><p>一门机器语言应该有哪些成分？操作符，逻辑运算，流程控制，寻址功能，IO。</p><ul><li>如果硬件复杂，可以实现乘除法，机器语言可以包含乘除法，如果硬件简单，可以只有加法；</li><li>同样，如果硬件复杂，可以设计负数，浮点型数据类型。如果硬件简单，只有正数</li></ul><p>ps: 这里强调tradeoff，一些功能到底设计在硬件层面还是软件层面，由设计者决定。</p><h3 id="4-2-Hack-Computer"><a href="#4-2-Hack-Computer" class="headerlink" title="4.2 Hack Computer"></a>4.2 Hack Computer</h3><p>Hack电脑认识的机器语言叫Hack语言，由两位教授设计的。</p><h4 id="硬件"><a href="#硬件" class="headerlink" title="硬件"></a>硬件</h4><p><img src="/blog/posts/Nand2Tetris-Part1/week4/Hardware.png" alt="Hack电脑的硬件部分"></p><h4 id="软件"><a href="#软件" class="headerlink" title="软件"></a>软件</h4><p><img src="/blog/posts/Nand2Tetris-Part1/week4/Software.png" alt="Hack电脑的软件部分"></p><h4 id="控制和寄存器"><a href="#控制和寄存器" class="headerlink" title="控制和寄存器"></a>控制和寄存器</h4><ul><li>ROM中加载了Hack语言</li><li>reset按钮一按，程序开始执行</li></ul><p>三个寄存器用来实现Hack语言：A M D，目前先不用管CPU里如何设计，第五章会讲<br><img src="/blog/posts/Nand2Tetris-Part1/week4/registers.png" alt="Hack电脑中的Register"></p><h3 id="4-3-Hack-Language"><a href="#4-3-Hack-Language" class="headerlink" title="4.3 Hack Language"></a>4.3 Hack Language</h3><p>汇编语言时人能看懂的语言，转化成二进制才是机器认识的机器语言<br>Hack语言含有A命令和C命令</p><h4 id="A命令"><a href="#A命令" class="headerlink" title="A命令"></a>A命令</h4><p><img src="/blog/posts/Nand2Tetris-Part1/week4/A1.png" alt="A命令定义"><br><img src="/blog/posts/Nand2Tetris-Part1/week4/A2.png" alt="A命令转化二进制"></p><h4 id="C命令"><a href="#C命令" class="headerlink" title="C命令"></a>C命令</h4><p><img src="/blog/posts/Nand2Tetris-Part1/week4/C1.png" alt="C命令定义"><br><img src="/blog/posts/Nand2Tetris-Part1/week4/C2.png" alt="C命令转化二进制"></p><h3 id="4-3-IO"><a href="#4-3-IO" class="headerlink" title="4.3 IO"></a>4.3 IO</h3><h4 id="屏幕"><a href="#屏幕" class="headerlink" title="屏幕"></a>屏幕</h4><p>RAM中有一块专门管屏幕的内存screen memory map，hack电脑用512X256像素的黑白屏<br><img src="/blog/posts/Nand2Tetris-Part1/week4/Screen.png" alt="屏幕和RAM"></p><h3 id="键盘"><a href="#键盘" class="headerlink" title="键盘"></a>键盘</h3><p>RAM中有一个16bit-Register管键盘<br><img src="/blog/posts/Nand2Tetris-Part1/week4/KBD.png" alt="键盘和RAM"><br><img src="/blog/posts/Nand2Tetris-Part1/week4/KBD-character-set.png" alt="键盘上的键对应的code"></p><h3 id="4-4-Hack-program"><a href="#4-4-Hack-program" class="headerlink" title="4.4 Hack program"></a>4.4 Hack program</h3><p>如果程序不终止，有人在ROM后面写入了恶意命令，就会被执行。<br>如何终止程序：写一个无限循环即可</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// line 6</span><br><span class="line">@6</span><br><span class="line">0;JMP</span><br></pre></td></tr></table></figure><h4 id="内置标识符"><a href="#内置标识符" class="headerlink" title="内置标识符"></a>内置标识符</h4><p>为了方便编程，内置了一些标识符<br><img src="/blog/posts/Nand2Tetris-Part1/week4/builtInSymbols.png" alt="内置标识符"></p><h4 id="分支"><a href="#分支" class="headerlink" title="分支"></a>分支</h4><p>使语言更具有可读性，一般为大写字母<br><img src="/blog/posts/Nand2Tetris-Part1/week4/Branch.png" alt="分支"></p><h4 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h4><p>也是更具有可读性，不用在其他地方声明，一般为小写字母，而分支需要在其他地方声明<br><img src="/blog/posts/Nand2Tetris-Part1/week4/Variable.png" alt="变量"></p><h4 id="迭代"><a href="#迭代" class="headerlink" title="迭代"></a>迭代</h4><p>用以上功能实现迭代的功能<br><img src="/blog/posts/Nand2Tetris-Part1/week4/Iteration.png" alt="迭代"></p><h4 id="指针"><a href="#指针" class="headerlink" title="指针"></a>指针</h4><p>A寄存器实际上就是一个指针的功能<br><img src="/blog/posts/Nand2Tetris-Part1/week4/Points.png" alt="指针"></p><h2 id="Week5-电脑架构"><a href="#Week5-电脑架构" class="headerlink" title="Week5 电脑架构"></a>Week5 电脑架构</h2><h3 id="5-1-整体架构图"><a href="#5-1-整体架构图" class="headerlink" title="5.1 整体架构图"></a>5.1 整体架构图</h3><p><img src="/blog/posts/Nand2Tetris-Part1/week5/%E6%9E%B6%E6%9E%84%E5%9B%BE1.png" alt="架构图"><br><img src="/blog/posts/Nand2Tetris-Part1/week5/%E6%9E%B6%E6%9E%84%E5%9B%BE2.png" alt="架构图细节"></p><h3 id="5-2-CPU内部具体实现"><a href="#5-2-CPU内部具体实现" class="headerlink" title="5.2 CPU内部具体实现"></a>5.2 CPU内部具体实现</h3><p>非常优雅，值得细品<br><img src="/blog/posts/Nand2Tetris-Part1/week5/CPU%E5%AE%9E%E7%8E%B0.png" alt="CPU实现"></p><h2 id="Week6-汇编器Assembler"><a href="#Week6-汇编器Assembler" class="headerlink" title="Week6 汇编器Assembler"></a>Week6 汇编器Assembler</h2><p>用汇编器将汇编语言变成机器语言，可以用高等语言去写，前提是有其他电脑被造出来了。<br>如果没有电脑，只能手工去费时费力的去转化。</p><p>汇编器的架构：<br><img src="/blog/posts/Nand2Tetris-Part1/week6/Assembler.png" alt="汇编器架构"></p>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/Nand2Tetris-Part1/</id>
    <link href="https://ig505gi.github.io/blog/posts/Nand2Tetris-Part1/"/>
    <published>2022-06-18T12:05:49.000Z</published>
    <summary>Coursera 课程笔记，从与非门电路开始，逐步搭建一台完整的计算机。</summary>
    <title>Nand2Tetris Part1</title>
    <updated>2026-05-29T11:55:00.602Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="JavaScript" scheme="https://ig505gi.github.io/blog/tags/JavaScript/"/>
    <category term="Fronted" scheme="https://ig505gi.github.io/blog/tags/Fronted/"/>
    <content>
      <![CDATA[<h2 id="Chapter-1-7-JS的基础知识"><a href="#Chapter-1-7-JS的基础知识" class="headerlink" title="Chapter 1-7 JS的基础知识"></a>Chapter 1-7 JS的基础知识</h2><h3 id="1-JS的历史"><a href="#1-JS的历史" class="headerlink" title="1 JS的历史"></a>1 JS的历史</h3><ul><li>JS包括ECMA、DOM、BOM<br>…</li></ul><h3 id="2-HTML"><a href="#2-HTML" class="headerlink" title="2 HTML"></a>2 HTML</h3><ul><li>HTML的发展，HTML中如何用JS<br>…</li></ul><span id="more"></span><h3 id="3-语法"><a href="#3-语法" class="headerlink" title="3 语法"></a>3 语法</h3><ul><li>Number类型的上下限<br>…</li></ul><h3 id="4-变量、作用域"><a href="#4-变量、作用域" class="headerlink" title="4 变量、作用域"></a>4 变量、作用域</h3><ul><li>执行环境是什么，「没有块级作用域」</li><li>垃圾回收、接触引用</li></ul><h3 id="5-引用类型"><a href="#5-引用类型" class="headerlink" title="5 引用类型"></a>5 引用类型</h3><h3 id="6-面向对象"><a href="#6-面向对象" class="headerlink" title="6 面向对象"></a>6 面向对象</h3><ul><li>设计模式：工厂模式、继承模式</li><li>原型链</li></ul><h3 id="7-私有变量"><a href="#7-私有变量" class="headerlink" title="7 私有变量"></a>7 私有变量</h3><ul><li>闭包</li><li>模仿块级作用域</li><li>模块模式 &amp; 单例模式</li></ul><h2 id="Chapter-8-BOM"><a href="#Chapter-8-BOM" class="headerlink" title="Chapter 8 BOM"></a>Chapter 8 BOM</h2><h3 id="1-window对象"><a href="#1-window对象" class="headerlink" title="1 window对象"></a>1 window对象</h3><ul><li>frame框架，每一层都有个window对象，top指向最高层级的window</li><li>窗口位置</li><li>窗口大小</li><li>导航和打开窗口</li><li>间歇调用和超时调用(setTimeout&#x2F;setInterval)</li><li>系统对话框(alert&#x2F;prompt&#x2F;confirm&#x2F;print&#x2F;find)</li></ul><h3 id="2-location对象"><a href="#2-location对象" class="headerlink" title="2 location对象"></a>2 location对象</h3><ul><li>既是windows的对象，又是document的对象</li><li>location的属性：<br><img src="/blog/posts/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/location%E5%B1%9E%E6%80%A7.png" alt="location属性"></li><li>每次修改location的属性(hash除外)页面都会以新url重新加载</li><li>replace方法不能生成历史记录，也不能返回刚刚的网页</li></ul><h3 id="3-navigator对象"><a href="#3-navigator对象" class="headerlink" title="3 navigator对象"></a>3 navigator对象</h3><ul><li>有属性浏览器版本信息、名称、插件、环境等</li><li>注册处理程序<code>registerContentHandler()</code>和<code>registerProtocolHandler()</code>方法，跟RSS和在线电子邮件有关</li></ul><h3 id="4-screen对象"><a href="#4-screen对象" class="headerlink" title="4 screen对象"></a>4 screen对象</h3><p>能够拿到一些屏幕的硬件信息：PPI、像素、刷新率等，对于编程意义不大</p><h3 id="5-history对象"><a href="#5-history对象" class="headerlink" title="5 history对象"></a>5 history对象</h3><p>安全的：开发者是不能知道用户访问过那些网页的<br>方法：go&#x2F;back&#x2F;forward</p><h2 id="Chapter-10-12-DOM"><a href="#Chapter-10-12-DOM" class="headerlink" title="Chapter 10-12 DOM"></a>Chapter 10-12 DOM</h2><p>DOM（文档对象类型）是针对HTML和XML文档的一个API。<br>DOM 可以将任何HTML或XML文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型。如下多种：</p><h3 id="1-节点层次"><a href="#1-节点层次" class="headerlink" title="1 节点层次"></a>1 节点层次</h3><h4 id="1-1-Node类型"><a href="#1-1-Node类型" class="headerlink" title="1.1 Node类型"></a>1.1 Node类型</h4><ul><li>其它节点都继承自Node类型，每个节点都有一个<code>nodeType</code>的属性，记录了其类型</li><li>每个节点都有这样或那样的关系。每个节点都有一个<code>childNodes</code>属性，其中保存着一个<code>NodeList</code>对象</li><li>每个节点都有一个<code>parentNode</code>属性，该属性指向文档树中的父节点<br><img src="/blog/posts/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/Node%E7%B1%BB%E5%9E%8B%E6%9F%A5%E8%AF%A2%E5%B1%9E%E6%80%A7.png" alt="Node类型查询属性"></li><li>常用方法<code>appendChild()</code>。用于向<code>childNodes</code>列表的末尾添加一个节点。</li><li>常用方法<code>insertBefore()</code>。这个方法接受两个参数:要插入的节点和作为参照的节点。</li><li>常用方法<code>replaceChild()</code>。这个方法接受两个参数:要插入的节点和要替换的节点。</li><li>其他方法<code>cloneNode()</code>可以深浅复制；<code>normalize()</code>处理文档树中的文本节点。</li></ul><h4 id="1-2-Document类型"><a href="#1-2-Document类型" class="headerlink" title="1.2 Document类型"></a>1.2 Document类型</h4><ul><li>document对象是HTMLDocument(继承自Document类型)的一个实例，表示整个HTML页面。</li><li>document对象是Window对象的一个属性，可以将其作为全局属性访问。</li><li>document对象的属性<code>nodeType</code>值为9。</li><li>document对象的属性<code>documentElement</code>、<code>firstChild</code>和<code>childNodes[0]</code>的值相同，都指向&lt;html&gt;元素。</li><li>document作为HTMLDocument实例，有一些不同于Document类型的属性：<code>title</code>、<code>URL</code>、<code>domain</code>、<code>referrer</code>。其中title和domain是可以设置的，domain只能修改三级域名(www)</li><li>Document类型都有的方法：<code>getElementById()</code>和<code>getElementsByTagName()</code>;HTMLDocument类型独有的方法是<code>getElementsByName()</code>。</li><li>document有一些集合可以方便拿到一些HTMLCollection：<code>document.links</code>拿到所有带href的&lt;a&gt;元素、<code>document.forms</code>拿到所有&lt;form&gt;元素、<code>document.images</code>拿到所有&lt;img&gt;元素</li><li><code>document.implementation.hasFeature(&quot;XML&quot;, &quot;1.0&quot;)</code> 可以检测DOM的功能及版本号</li><li>document对象具有输出流写入网页的功能：<code>write()</code>、<code>writeln()</code>、<code>open()</code>、<code>close()</code></li></ul><h4 id="1-3-Element类型"><a href="#1-3-Element类型" class="headerlink" title="1.3 Element类型"></a>1.3 Element类型</h4><ul><li>所有的HTML元素都是HTMLElement类型，其继承自Element类型，添加了一些Element没有的属性：id、titile、className等；可以直接像这样<code>div.id</code><strong>通过属性访问</strong>对其属性赋值和取值</li><li><code>getAttribute()</code>、<code>setAttribute()</code>、<code>removeAttribute()</code>是三个常用的方法</li><li>H5规范规定，自定义特性需要有<code>data-</code>前缀；<strong>通过getAttribute方法</strong>可以拿到自定义特性的值；除了IE可以直接用<code>div.myAttr</code>这样<strong>通过属性访问</strong>拿到特性的值，其他浏览器都<strong>不可以</strong></li><li>style属性：<strong>通过getAttribute方法</strong>拿到的是CSS文本，<strong>通过属性访问</strong>拿到的是对象</li><li>onClick属性：<strong>通过getAttribute方法</strong>拿到的是响应代码的字符串，<strong>通过属性访问</strong>拿到的是JS函数</li><li>通过setAttribute()方法设置的特性，统一会变成小写</li><li>想要遍历元素的特性，可以使用attributes属性<code>element.attributes</code></li><li><code>var div = document.createElement(&quot;div&quot;);</code></li></ul><h4 id="1-4-Text类型"><a href="#1-4-Text类型" class="headerlink" title="1.4 Text类型"></a>1.4 Text类型</h4><ul><li>文本节点，parentNode是一个Element类型；通过<code>nodeValue</code>属性或<code>data</code>属性访问文本内容，是一致的</li><li>常用方法：<code>appendData()</code> <code>deleteData()</code> <code>insertData()</code> <code>replaceData()</code> <code>splitText()</code> <code>substringData()</code></li><li><code>div.firstChild.nodeValue = &#39;aa&lt;s&gt;&#39;;</code> 输出的结果是转义过的，是<code>aa&amp;lt;s&amp;gt;</code></li><li><code>document.createTextNode()</code>创建新文本节点，但是如果不指定其父元素，在页面上是看不到的；</li></ul><h4 id="1-5-1-9-其它类型"><a href="#1-5-1-9-其它类型" class="headerlink" title="1.5-1.9 其它类型"></a>1.5-1.9 其它类型</h4><ul><li>Comment类型：parentNode可能是Document或Element类型；有除了splitText以外的Text类型的全部方法</li><li>CDATASection：类型只针对基于XML的文档</li><li>DocumentType类型：在Web浏览器中并不常用，仅有 Firefox、Safari 和 Opera 支持它</li><li>DocumentFragment类型：只有它在文档中没有对应的标记，可以当做一个仓库。比如如果向文档中渲染三个连续的元素，先放到仓库里，可以避免重复渲染三次</li><li>Attr类型：不是DOM文档树的一部分，是元素的特性</li></ul><h3 id="2-DOM扩展"><a href="#2-DOM扩展" class="headerlink" title="2 DOM扩展"></a>2 DOM扩展</h3><h4 id="2-1-选择符API"><a href="#2-1-选择符API" class="headerlink" title="2.1 选择符API"></a>2.1 选择符API</h4><ul><li>querySelector()</li><li>querySelectorAll()</li><li>msMatchesSelector()</li></ul><p>…</p><h3 id="3-DOM2和DOM3"><a href="#3-DOM2和DOM3" class="headerlink" title="3 DOM2和DOM3"></a>3 DOM2和DOM3</h3><p>DOM1主要定义的是HTML和XML文档的底层结构</p><h4 id="DOM2的模块"><a href="#DOM2的模块" class="headerlink" title="DOM2的模块"></a>DOM2的模块</h4><ul><li>DOM2级核心(DOM Level 2 Core):在1级核心基础上构建，为节点添加了更多方法和属性。</li><li>DOM2级视图(DOM Level 2 Views):为文档定义了基于样式信息的不同视图。</li><li>DOM2级事件(DOM Level 2 Events):说明了如何使用事件与 DOM 文档交互。</li><li>DOM2级样式(DOM Level 2 Style):定义了如何以编程方式来访问和改变 CSS 样式信息。</li><li>DOM2级遍历和范围(DOM Level 2 Traversal and Range):引入了遍历 DOM 文档和选择其特定部分的新接口。</li><li>DOM2级HTML(DOM Level 2 HTML):在1级HTML基础上构建，添加了更多属性、方法和新接口。</li></ul><p>…</p><h2 id="Chapter-13-事件"><a href="#Chapter-13-事件" class="headerlink" title="Chapter 13 事件"></a>Chapter 13 事件</h2><p>JS和HTML之间的交互是通过事件实现的。</p><h3 id="1-事件流"><a href="#1-事件流" class="headerlink" title="1.事件流"></a>1.事件流</h3><h4 id="1-1-事件冒泡和事件捕获"><a href="#1-1-事件冒泡和事件捕获" class="headerlink" title="1.1 事件冒泡和事件捕获"></a>1.1 事件冒泡和事件捕获</h4><ul><li><strong>事件流</strong>描述的是从页面中接收事件的顺序。但有意思的是，IE和 Netscape开发团队居然提出了差不多是完全相反的事件流的概念。IE的事件流是事件冒泡流，而Netscape Communicator的事件流是事件捕获流。</li><li><strong>事件冒泡</strong>从div到body到html到document，自下而上。</li><li><strong>事件捕获</strong>从document到html到body到div，自上而下。</li><li>由于老版本的浏览器不支持，因此很少有人使用事件捕获。我们也建议读者放心地使用事件冒泡，在有特殊需要时再使用事件捕获。</li></ul><h3 id="1-2-DOM事件流"><a href="#1-2-DOM事件流" class="headerlink" title="1.2 DOM事件流"></a>1.2 DOM事件流</h3><p>捕获和冒泡都有<br><img src="/blog/posts/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/DOM%E4%BA%8B%E4%BB%B6%E6%B5%81.png" alt="DOM事件流"><br>三个阶段：</p><ul><li>事件捕获阶段</li><li>处于目标阶段</li><li>事件冒泡阶段</li></ul><h3 id="2-事件处理程序"><a href="#2-事件处理程序" class="headerlink" title="2 事件处理程序"></a>2 事件处理程序</h3><h4 id="2-1-html事件处理程序"><a href="#2-1-html事件处理程序" class="headerlink" title="2.1 html事件处理程序"></a>2.1 html事件处理程序</h4><p>html的onXXX同名属性，传入一段JS代码，就能执行，而且能访问到其他地方的脚本中的变量，以及局部变量event，和this(该元素)</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;button&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Click Me&quot;</span> <span class="attr">onclick</span>=<span class="string">&quot;alert(event.type);alert(this.value);&quot;</span> &gt;</span></span><br></pre></td></tr></table></figure><p>事件处理程序中的代码在执行时，有权访问全局作用域中的任何代码。</p><h4 id="2-2-DOM0级事件处理程序"><a href="#2-2-DOM0级事件处理程序" class="headerlink" title="2.2 DOM0级事件处理程序"></a>2.2 DOM0级事件处理程序</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> btn = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;myBtn&quot;</span>);</span><br><span class="line">btn.<span class="property">onclick</span> = <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">id</span>); <span class="comment">// &quot;myBtn&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-3-DOM2级事件处理程序"><a href="#2-3-DOM2级事件处理程序" class="headerlink" title="2.3 DOM2级事件处理程序"></a>2.3 DOM2级事件处理程序</h4><p>“DOM2级事件”定义了两个方法，用于处理指定和删除事件处理程序的操作:<code>addEventListener()</code> 和 <code>removeEventListener()</code><br>接受三个参数：</p><ul><li>处理的事件名</li><li>事件处理程序的函数</li><li>布尔值：如果是true，表示在捕获阶段处理，反之则在冒泡阶段</li></ul><p>并且可添加多个事件处理程序</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 添加</span></span><br><span class="line"><span class="keyword">var</span> btn = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;myBtn&quot;</span>);</span><br><span class="line">btn.<span class="title function_">addEventListener</span>(<span class="string">&quot;click&quot;</span>, <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">id</span>); <span class="comment">// &quot;myBtn&quot;</span></span><br><span class="line">&#125;, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除</span></span><br><span class="line"><span class="keyword">var</span> btn = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;myBtn&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> handler = <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">id</span>); <span class="comment">// &quot;myBtn&quot;</span></span><br><span class="line">&#125;;</span><br><span class="line">btn.<span class="title function_">addEventListener</span>(<span class="string">&quot;click&quot;</span>, handler, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">btn.<span class="title function_">removeEventListener</span>(<span class="string">&quot;click&quot;</span>, handler, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure><h3 id="3-事件对象"><a href="#3-事件对象" class="headerlink" title="3 事件对象"></a>3 事件对象</h3><p>TODO 有哪些属性</p><h3 id="4-事件类型"><a href="#4-事件类型" class="headerlink" title="4 事件类型"></a>4 事件类型</h3><p>非常多类型，很详细，有需要可以去查询</p><h3 id="5-内存和性能"><a href="#5-内存和性能" class="headerlink" title="5 内存和性能"></a>5 内存和性能</h3><ul><li>对“事件处理程序过多”问题的解决方案就是<strong>事件委托</strong><br>添加一个事件处理程序，根据target的属性去做不同的事情。这样只会添加一次。</li><li>移除事件处理程序</li></ul><h3 id="6-模拟事件"><a href="#6-模拟事件" class="headerlink" title="6 模拟事件"></a>6 模拟事件</h3><p>利用JS 在任意时刻触发事件，<br>DOM中：document对象上使用createEvent()方法创建event对象</p>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/</id>
    <link href="https://ig505gi.github.io/blog/posts/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/"/>
    <published>2022-03-21T22:04:08.000Z</published>
    <summary>《JavaScript 高级程序设计》读书笔记，涵盖 JS 基础、DOM、BOM、事件、Ajax 等核心内容。</summary>
    <title>JavaScript高级程序设计</title>
    <updated>2026-05-29T11:55:00.600Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="JavaScript" scheme="https://ig505gi.github.io/blog/tags/JavaScript/"/>
    <category term="Fronted" scheme="https://ig505gi.github.io/blog/tags/Fronted/"/>
    <category term="React" scheme="https://ig505gi.github.io/blog/tags/React/"/>
    <content>
      <![CDATA[<h2 id="1-初探React源码"><a href="#1-初探React源码" class="headerlink" title="1. 初探React源码"></a>1. 初探React源码</h2><p>React源码的组织结构<br><img src="/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter3-React%E6%BA%90%E7%A0%81/react%E7%9B%AE%E5%BD%95.jpg" alt="react目录"></p><span id="more"></span><p>而renderers目录结构是这样的<br><img src="/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter3-React%E6%BA%90%E7%A0%81/react-renderers%E7%9B%AE%E5%BD%95.jpg" alt="react-renderers目录"></p><p>client是客户端操作dom的一些方法；server是服务端渲染的实现和方法；event包含有一套自定义的事件插件系统；作为React核心的reconciler，称为协调器，管理组件的实现、生命周期、setState、DOM diff算法;管理Virtual DOM</p><h2 id="2-Virtual-DOM-模型"><a href="#2-Virtual-DOM-模型" class="headerlink" title="2. Virtual DOM 模型"></a>2. Virtual DOM 模型</h2><h3 id="ReactNode"><a href="#ReactNode" class="headerlink" title="ReactNode"></a>ReactNode</h3><p>Virtual DOM的节点叫作ReactNode</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">type <span class="title class_">ReactNode</span> = <span class="title class_">ReactElement</span> | <span class="title class_">ReactFragment</span> | <span class="title class_">ReactText</span>;</span><br><span class="line">type <span class="title class_">ReactElement</span> = <span class="title class_">ReactComponentElement</span> | <span class="title class_">ReactDOMElement</span>;</span><br></pre></td></tr></table></figure><p>&#x2F;&#x2F; 差一些</p><h3 id="ReactDOMComponent"><a href="#ReactDOMComponent" class="headerlink" title="ReactDOMComponent"></a>ReactDOMComponent</h3><ol><li>属性的更新，包括更新样式、更新属性、处理事件等;</li><li>子节点的更新，包括更新内容、更新子节点，此部分涉及diff算法。</li></ol><p>&#x2F;&#x2F; 差很多</p>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter3-React%E6%BA%90%E7%A0%81/</id>
    <link href="https://ig505gi.github.io/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter3-React%E6%BA%90%E7%A0%81/"/>
    <published>2022-02-21T08:47:40.000Z</published>
    <summary>《深入 React 技术栈》React 源码分析，探索 React 框架内部的组织结构和实现原理。</summary>
    <title>深入React技术栈-Chapter3-React源码</title>
    <updated>2026-05-29T11:55:00.725Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="JavaScript" scheme="https://ig505gi.github.io/blog/tags/JavaScript/"/>
    <category term="Fronted" scheme="https://ig505gi.github.io/blog/tags/Fronted/"/>
    <category term="React" scheme="https://ig505gi.github.io/blog/tags/React/"/>
    <content>
      <![CDATA[<h2 id="1-React基础"><a href="#1-React基础" class="headerlink" title="1.React基础"></a>1.React基础</h2><h3 id="React生命周期"><a href="#React生命周期" class="headerlink" title="React生命周期"></a>React生命周期</h3><p><img src="/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter1-2/react-lifecircle.jpg" alt="react生命周期"></p><span id="more"></span><h3 id="无状态组件"><a href="#无状态组件" class="headerlink" title="无状态组件"></a>无状态组件</h3><ol><li>无状态组件挂载时只是函数的调用，没有创建实例。</li><li>因此findDOMNode和refs都不能用，同时建议，这两个方法尽量别用，会打破封装性。</li><li>在能用无状态组件的情况下，尽量用无状态组件</li></ol><h3 id="React之外的DOM操作"><a href="#React之外的DOM操作" class="headerlink" title="React之外的DOM操作"></a>React之外的DOM操作</h3><p>尽量不用DOM操作为了保证封装性，但有时有一些操作没办法避免</p><ol><li>H5的video的play和input的forcus</li><li>点击其他区域合上该组件</li><li>计算DOM的尺寸<br>自己的理解：用hooks解决以上2，3问题，保证了一定的封装性？</li></ol><h2 id="2-事件"><a href="#2-事件" class="headerlink" title="2.事件"></a>2.事件</h2><h3 id="合成事件的实现机制"><a href="#合成事件的实现机制" class="headerlink" title="合成事件的实现机制"></a>合成事件的实现机制</h3><p>事件委派和自动绑定</p><ol><li>事件委派：有一个统一的事件监听层，并不会把事件处理函数直接绑定到真实的节点上。</li><li>自动绑定：React组件中，每个方法的上下文都会指向该组件的实例，即this自动指向当前组件。</li></ol><h3 id="三种手动绑定方法"><a href="#三种手动绑定方法" class="headerlink" title="三种手动绑定方法"></a>三种手动绑定方法</h3><p>如果用ES6 classes或者纯函数，就没自动绑定了，需要手动绑定：</p><p>1.bind方法绑定</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.handleClick.bind(this,</span> &#x27;<span class="attr">tes</span>&#x27;)&#125;&gt;</span>Test<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br></pre></td></tr></table></figure><p>2.构造器内声明<br>class中的方法不用箭头函数，就必须绑定</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在constructor中：</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">handleClick</span> = <span class="variable language_">this</span>.<span class="property">handleClick</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在 class中</span></span><br><span class="line"><span class="title function_">handleClick</span>(<span class="params">e</span>) &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>3.箭头函数<br>如果使用箭头函数，可以直接这样：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">App</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Component</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="title function_">handleClick</span> = (<span class="params">e</span>) =&gt; &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(e);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.handleClick&#125;</span>&gt;</span>Test<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="React合成事件的限制"><a href="#React合成事件的限制" class="headerlink" title="React合成事件的限制"></a>React合成事件的限制</h3><p>React 的合成事件系统只是原生 DOM 事件系统的一个子集。它仅仅实现了 DOM Level 3 的事件接口，并且统一了浏览器间的兼容问题。有些事件 React 并没有实现，或者受某些 限制没办法去实现，比如 window 的 resize 事件</p><h3 id="React合成事件和JS原生事件的对比"><a href="#React合成事件和JS原生事件的对比" class="headerlink" title="React合成事件和JS原生事件的对比"></a>React合成事件和JS原生事件的对比</h3><h4 id="1-事件传播与阻止事件传播"><a href="#1-事件传播与阻止事件传播" class="headerlink" title="1. 事件传播与阻止事件传播"></a>1. 事件传播与阻止事件传播</h4><p>原生DOM事件三阶段：1.事件捕获 2.事件处理 3.事件冒泡</p><p>而React没有实现事件捕获，因为其在开发中意义不大 (?)</p><h4 id="2-事件类型"><a href="#2-事件类型" class="headerlink" title="2.事件类型"></a>2.事件类型</h4><p>React 合成事件的事件类型是 JavaScript 原生事件类型的一个子集</p><h4 id="3-事件绑定方式"><a href="#3-事件绑定方式" class="headerlink" title="3.事件绑定方式"></a>3.事件绑定方式</h4><p>不多说了</p><h4 id="4-事件对象"><a href="#4-事件对象" class="headerlink" title="4.事件对象"></a>4.事件对象</h4><p>原生 DOM 事件对象在 W3C 标准和 IE 标准下存在着差异。在低版本的 IE 浏览器中，只能使用 window.event 来获取事件对象。而在 React 合成事件系统中，不存在这种兼容性问题，在事件处理函数中可以得到一个合成事件对象。</p><h2 id="3-表单"><a href="#3-表单" class="headerlink" title="3.表单"></a>3.表单</h2><h3 id="受控组件"><a href="#受控组件" class="headerlink" title="受控组件"></a>受控组件</h3><p>React 受控组件更新 state 的流程:</p><p>(1) 可以通过在初始 state 中设置表单的默认值。</p><p>(2) 每当表单的值发生变化时，调用 onChange 事件处理器。</p><p>(3) 事件处理器通过合成事件对象 e 拿到改变后的状态，并更新应用的 state。</p><p>(4) setState 触发视图的重新渲染，完成表单组件值的更新。</p><h2 id="4-CSS"><a href="#4-CSS" class="headerlink" title="4.CSS"></a>4.CSS</h2><h3 id="CSS模块化"><a href="#CSS模块化" class="headerlink" title="CSS模块化"></a>CSS模块化</h3><h4 id="CSS模块化的问题"><a href="#CSS模块化的问题" class="headerlink" title="CSS模块化的问题"></a>CSS模块化的问题</h4><ol><li><code>全局污染</code>:CSS 使用全局选择器机制来设置样式，优点是方便重写样式。缺点是所有的 样式都是全局生效，样式可能被错误覆盖，因此产生了非常丑陋的 !important，甚至 inline !important 和复杂的选择器权重计数表1 ，提高犯错概率和使用成本。Web Components 标准中的 Shadow DOM 能彻底解决这个问题，但它把样式彻底局部化，造成 外部无法重写样式，损失了灵活性。</li><li><code>命名混乱</code>:由于全局污染的问题，多人协同开发时为了避免样式冲突，选择器越来越复 杂，容易形成不同的命名风格，很难统一。样式变多后，命名将更加混乱。</li><li><code>依赖管理不彻底</code>:组件应该相互独立，引入一个组件时，应该只引入它所需要的 CSS 样 式。现在的做法是除了要引入 JavaScript，还要再引入它的 CSS，而且 Saas&#x2F;Less 很难实现 对每个组件都编译出单独的 CSS，引入所有模块的 CSS 又造成浪费。JavaScript 的模块化 已经非常成熟，如果能让 JavaScript 来管理 CSS 依赖是很好的解决办法，而 webpack 的 css-loader 提供了这种能力。</li><li><code>无法共享变量</code>:复杂组件要使用 JavaScript 和 CSS 来共同处理样式，就会造成有些变量 在 JavaScript 和 CSS 中冗余，而预编译语言不能提供跨 JavaScript 和 CSS 共享变量的这种 能力。</li><li><code>代码压缩不彻底</code>:由于移动端网络的不确定性，现代工程项目对 CSS 压缩的要求已经到 了变态的程度。很多压缩工具为了节省一个字节,会把 16px 转成 1pc，但是这对非常长的 类名却无能为力</li></ol><h4 id="CSS与JS变量共享"><a href="#CSS与JS变量共享" class="headerlink" title="CSS与JS变量共享"></a>CSS与JS变量共享</h4><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* config.scss */</span> </span><br><span class="line">$primary-<span class="attribute">color</span>: <span class="number">#f40</span>;</span><br><span class="line">:export &#123;</span><br><span class="line">  primaryColor: $primary-color;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* app.js */</span></span><br><span class="line"><span class="keyword">import</span> style <span class="keyword">from</span> <span class="string">&#x27;config.scss&#x27;</span>;</span><br><span class="line"><span class="comment">// 会输出 #F40 </span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(style.<span class="property">primaryColor</span>);</span><br></pre></td></tr></table></figure><h4 id="css-Modules使用技巧"><a href="#css-Modules使用技巧" class="headerlink" title="css Modules使用技巧"></a>css Modules使用技巧</h4><ol><li>不适用选择器，只用class名来定义样式</li><li>不层叠多个class，只用一个class把所有样式定义好</li><li>通过composes组合来实现复用</li><li>不嵌套</li></ol><p>如果在style文件中使用id选择器、伪类和标签选择器，不会被转换或者加前缀</p><h4 id="react-css-modules库"><a href="#react-css-modules库" class="headerlink" title="react-css-modules库"></a>react-css-modules库</h4><p>可以避免重复输入styles.**</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; <span class="title class_">Component</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> classNames <span class="keyword">from</span> <span class="string">&#x27;classnames&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">CSSModules</span> <span class="keyword">from</span> <span class="string">&#x27;react-css-modules&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">&#x27;./dialog.css&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dialog</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Component</span> &#123; </span><br><span class="line">  <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> cx = <span class="title function_">classNames</span>(&#123;</span><br><span class="line">      <span class="attr">confirm</span>: !<span class="variable language_">this</span>.<span class="property">state</span>.<span class="property">disabled</span>, </span><br><span class="line">      <span class="attr">disabledConfirm</span>: <span class="variable language_">this</span>.<span class="property">state</span>.<span class="property">disabled</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">styleName</span>=<span class="string">&quot;root&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">a</span> <span class="attr">styleName</span>=<span class="string">&#123;cx&#125;</span>&gt;</span>Confirm<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        // ... </span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">    ); </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">CSSModules</span>(<span class="title class_">Dialog</span>, styles);</span><br></pre></td></tr></table></figure><p>使用 CSS Modules，容易使用 :global 去解决特殊情况，使用 react-css-modules 可写成 <div className="global-css" styleName="local-module"></div>，这种形式轻松对应全局和局部;</p><h2 id="5-React组件"><a href="#5-React组件" class="headerlink" title="5.React组件"></a>5.React组件</h2><h3 id="组件通信"><a href="#组件通信" class="headerlink" title="组件通信"></a>组件通信</h3><ul><li>父向子通信：属性</li><li>子向父通信：回调</li><li>跨级通信：在父组件中定义ChildContext,子组件可以直接用this.context拿到对应的值；context可以存在很多级</li><li>无嵌套关系通信：使用Events模块的事件订阅和发布来实现</li></ul><h3 id="组件间抽象"><a href="#组件间抽象" class="headerlink" title="组件间抽象"></a>组件间抽象</h3><h4 id="mixin"><a href="#mixin" class="headerlink" title="mixin"></a>mixin</h4><h5 id="JS中的mixin"><a href="#JS中的mixin" class="headerlink" title="JS中的mixin"></a>JS中的mixin</h5><p>mixin是为了弥补一些老的OOP语言不能实现多重继承的缺点，而采用的另外一个方法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// TODO 有空自己实现一下</span></span><br></pre></td></tr></table></figure><h5 id="React中的mixin"><a href="#React中的mixin" class="headerlink" title="React中的mixin"></a>React中的mixin</h5><ol><li>适用于createClass的情况。。比较老了，不再细讲</li><li>使用ES6 Classes和decorator，用法：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; <span class="title class_">Component</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;React&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; mixin &#125; <span class="keyword">from</span> <span class="string">&#x27;core-decorators&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">PureRender</span> = &#123; </span><br><span class="line">  <span class="title function_">shouldComponentUpdate</span>(<span class="params"></span>) &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">Theme</span> = &#123; </span><br><span class="line">  <span class="title function_">setTheme</span>(<span class="params"></span>) &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line">@<span class="title function_">mixin</span>(<span class="title class_">PureRender</span>, <span class="title class_">Theme</span>)</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyComponent</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Component</span> &#123;</span><br><span class="line">  <span class="title function_">render</span>(<span class="params"></span>) &#123;&#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是mixin也有命名冲突、破坏原组件的封装性等问题</p><h4 id="高阶组件"><a href="#高阶组件" class="headerlink" title="高阶组件"></a>高阶组件</h4><p>解释：React组件被包裹，高阶组件会返回一个增强的React组件。<br>实现高阶组件有两种方式：属性代理和反向继承</p><h5 id="属性代理"><a href="#属性代理" class="headerlink" title="属性代理"></a>属性代理</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; <span class="title class_">Component</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;React&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">MyContainer</span> = (<span class="params">WrappedComponent</span>) =&gt; </span><br><span class="line">  <span class="keyword">class</span> <span class="title class_">extends</span> <span class="title class_">Component</span> &#123;</span><br><span class="line">    <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; /&gt;</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>当然，也可以使用装饰器语法。<br>调用顺序<br>didmount→HOC didmount→(HOCs didmount)→(HOCs will unmount)→HOC will unmount→unmount</p><h5 id="反向继承"><a href="#反向继承" class="headerlink" title="反向继承"></a>反向继承</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">MyContainer</span> = (<span class="params">WrappedComponent</span>) =&gt; </span><br><span class="line">  <span class="keyword">class</span> <span class="title class_">extends</span> <span class="title class_">WrappedComponent</span> &#123;</span><br><span class="line">    <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="variable language_">super</span>.<span class="title function_">render</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>特点：渲染劫持，控制state<br>(大部分高阶组件都应该限制读取或增加state，防止混乱)</p><h4 id="组合式组件"><a href="#组合式组件" class="headerlink" title="组合式组件"></a>组合式组件</h4><p><img src="/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter1-2/compose.jpg" alt="组合式组件"><br>组合式的方式意图打破这种关联，寻求单元化，通过颗粒度更细的 基础组件与抽象组件共有交互与业务逻辑的高阶组件，使组件更灵活，更易扩展，也使我们能够 完成对于基础组件的自由支配。</p><h3 id="组件性能优化"><a href="#组件性能优化" class="headerlink" title="组件性能优化"></a>组件性能优化</h3><h4 id="纯函数的定义"><a href="#纯函数的定义" class="headerlink" title="纯函数的定义"></a>纯函数的定义</h4><ol><li>给定相同的输入，总返回相同的输出</li><li>过程没有副作用</li><li>没有额外的状态依赖<br>理解：<br>1.好理解。2.入参有个对象，函数内是该对象的引用，对其进行改变，就是所谓的产生了副作用。可以用immutable的概念去解决，用lodash的cloneDeep。3.在方法内不使用共享变量，内部所有变量只在方法的生命周期内存活，拒绝闭包？</li></ol><h4 id="PureRender"><a href="#PureRender" class="headerlink" title="PureRender"></a>PureRender</h4><p>重新实现shouldComponentUpdate方法，对props和state做浅比较，值对比引用(深比较实在太昂贵了)<br>以下类型，无论如何都会触发pureRender的重渲染：</p><ol><li>props直接设置为对象或数组</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;<span class="title class_">Account</span> style=&#123;&#123;<span class="attr">color</span>: <span class="string">&#x27;black&#x27;</span>&#125;&#125; /&gt;</span><br></pre></td></tr></table></figure><p>因为每次都是重新生成了一个新对象，数组同理；解决：先定义一个变量即可。<br>2. 设置props方法并通过事件绑定在元素上</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;input onChange=&#123;<span class="variable language_">this</span>.<span class="property">handleChange</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)&#125; /&gt;</span><br></pre></td></tr></table></figure><p>每次会重新生成新函数对象(箭头函数也同理？)。解决：bind放在constructor内<br>3. 设置子组件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;<span class="title class_">Item</span>&gt;</span><br><span class="line">  <span class="language-xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Arcthur<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">Item</span>/&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 翻译后 每次都生成了新对象</span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">Item</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"><span class="attr">children</span>=<span class="string">&#123;React.createElement(</span>&#x27;<span class="attr">span</span>&#x27;, &#123;&#125;, &#x27;<span class="attr">Arcthur</span>&#x27;)&#125;</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">/&gt;</span></span></span><br></pre></td></tr></table></figure><p>解决：使用Item组件的父组件也要pureRender</p><h4 id="Immutable"><a href="#Immutable" class="headerlink" title="Immutable"></a>Immutable</h4><p>引用赋值的问题：容易改变，复杂系统有隐患。<br>深拷贝：浪费内存</p><ol><li>Immutable Data<br>Immutable Data被创建后，不能更改。如果对原数据修改，会生成一个新的Immutable对象。<br>Immutable.js库就是这样的理念</li><li>Immutable的优点<br>复杂度降低；节省了内存，因为节点是共享的；重做&#x2F;撤销、复制&#x2F;粘贴都可以实现，老数据都存着呢；并发安全，对于JS来说好像无关紧要，单线程。。；拥抱函数式编程，其本就是函数式编程的概念。</li><li>缺点<br>api还是和原生map array有区别</li><li>Immutable 与 PureRender<br>用immutable的<code>is</code>去改造shouldComponentUpdate即可</li></ol><h4 id="key"><a href="#key" class="headerlink" title="key"></a>key</h4><p><code>Array&lt;Element&gt;</code>渲染时，每一个item都要唯一的key，切不建议使用index，建议使用id或shortid</p><h4 id="性能检测工具react-addons-perf"><a href="#性能检测工具react-addons-perf" class="headerlink" title="性能检测工具react-addons-perf"></a>性能检测工具react-addons-perf</h4><p>使用<code>Perf.start()</code>和<code>Perf.stop()</code>分析</p><h2 id="6-动画"><a href="#6-动画" class="headerlink" title="6.动画"></a>6.动画</h2><p>后续添加</p><h2 id="7-自动化测试"><a href="#7-自动化测试" class="headerlink" title="7.自动化测试"></a>7.自动化测试</h2><p>后续添加</p>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter1-2/</id>
    <link href="https://ig505gi.github.io/blog/posts/%E6%B7%B1%E5%85%A5React%E6%8A%80%E6%9C%AF%E6%A0%88-Chapter1-2/"/>
    <published>2021-11-29T22:36:03.000Z</published>
    <summary>《深入 React 技术栈》读书笔记，涵盖 React 组件生命周期、状态管理和 JSX 等基础知识。</summary>
    <title>深入React技术栈-Chapter1-2</title>
    <updated>2026-05-29T11:55:00.724Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="JavaScript" scheme="https://ig505gi.github.io/blog/tags/JavaScript/"/>
    <category term="Fronted" scheme="https://ig505gi.github.io/blog/tags/Fronted/"/>
    <content>
      <![CDATA[<h2 id="chapter1-让自己习惯JavaScript"><a href="#chapter1-让自己习惯JavaScript" class="headerlink" title="chapter1 让自己习惯JavaScript"></a>chapter1 让自己习惯JavaScript</h2><h3 id="1-ES版本问题及strict-mode"><a href="#1-ES版本问题及strict-mode" class="headerlink" title="1.ES版本问题及strict mode"></a>1.ES版本问题及strict mode</h3><p>ES5引入了严格模式，使用字符串字面量作为指令是为了向前兼容，ES3看到<code>&quot;use strict&quot;</code>会直接丢掉。<br>为了解决严格模式和非严格模式的文件打包在一起的问题，可以用IIFE(Immmediately Invoked Function Expression)来解决</p><span id="more"></span><h3 id="2-number是浮点型"><a href="#2-number是浮点型" class="headerlink" title="2.number是浮点型"></a>2.number是浮点型</h3><p>JS中的数字只有<code>number</code>一种类型，是符合IEEE754制定的64位编码数字—-doubles<br><code>number</code>的位运算会隐式地转为32位整数，然后计算。</p><h3 id="3-当心隐式的强制转换"><a href="#3-当心隐式的强制转换" class="headerlink" title="3. 当心隐式的强制转换"></a>3. 当心隐式的强制转换</h3><p>很多JS代码不会报错，而是会做强制的转换</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">3</span> + <span class="literal">true</span> <span class="comment">// 4</span></span><br><span class="line"><span class="string">&quot;hello&quot;</span> + <span class="string">&quot; world&quot;</span> <span class="comment">// &quot;hello world&quot;</span></span><br><span class="line"><span class="number">2</span> + <span class="string">&quot;3&quot;</span> <span class="comment">// &quot;23&quot;</span></span><br><span class="line"><span class="string">&quot;17&quot;</span> * <span class="string">&quot;5&quot;</span> <span class="comment">// 85</span></span><br><span class="line"><span class="string">&quot;8&quot;</span> | <span class="string">&quot;1&quot;</span> <span class="comment">// 9</span></span><br></pre></td></tr></table></figure><p>尽量避免使用<code>valueOf()</code></p><h4 id="3-1-真值运算-truthines"><a href="#3-1-真值运算-truthines" class="headerlink" title="3.1 真值运算(truthines)"></a>3.1 真值运算(truthines)</h4><p>JS有7个假值：<br><code>false</code>&#x2F;<code>0</code>&#x2F;<code>-0</code>&#x2F;<code>&quot;&quot;</code>&#x2F;<code>null</code>&#x2F;<code>NaN</code>&#x2F;<code>undefined</code></p><h3 id="4-原始类型优于封装对象"><a href="#4-原始类型优于封装对象" class="headerlink" title="4.原始类型优于封装对象"></a>4.原始类型优于封装对象</h3><p>JS的5个原始类型<br><code>boolean/number/string/null/undefined</code></p><p>封装对象的方法，原始类型也可以使用，例如toUpperCase是String的原型对象的方法，而原始字符串可以直接调用：<br><code>&quot;hello&quot;.toUpperCase(); // &quot;HELLO&quot;</code><br>这种隐式封装，每次调用都会产生一个新的String对象，因此对其设置属性是不起作用的。</p><h3 id="5-避免对混合类型使用-运算符"><a href="#5-避免对混合类型使用-运算符" class="headerlink" title="5.避免对混合类型使用&#x3D;&#x3D;运算符"></a>5.避免对混合类型使用&#x3D;&#x3D;运算符</h3><p>当两个参数属于同一类型时，&#x3D;&#x3D;和&#x3D;&#x3D;&#x3D;是没有区别的<br>当使用&#x3D;&#x3D;对不同的类型比较时，有强制类型转换规则，记忆比较麻烦，因此最好避免<br><img src="/blog/posts/Effective-javascript/%E5%BC%BA%E5%88%B6%E8%BD%AC%E6%8D%A2%E8%A7%84%E5%88%99.jpg" alt="强制转换规则"></p><h3 id="6-了解分号插入的局限"><a href="#6-了解分号插入的局限" class="headerlink" title="6.了解分号插入的局限"></a>6.了解分号插入的局限</h3><p>JS有自动分号插入技术，因此不用写分号。<br>分号插入的规则：</p><ol><li>分号仅在}标记之前、一个或多个换行之后和程序输入的结尾被插入</li><li>分号仅在随后的输入标记不能解析时插入</li></ol><h3 id="7-视字符串为16位的代码单元序列"><a href="#7-视字符串为16位的代码单元序列" class="headerlink" title="7.视字符串为16位的代码单元序列"></a>7.视字符串为16位的代码单元序列</h3><p><img src="/blog/posts/Effective-javascript/%E7%A0%81%E5%8D%95%E5%85%83%E5%A4%A7%E4%BA%8E%E7%AD%89%E4%BA%8E2.jpg" alt="码单元大于等于2"><br>因此，一个以上的音乐符号，在字符串中的长度是2</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;∮ clef&quot;</span>.<span class="title function_">charCodeAt</span>(<span class="number">0</span>); <span class="comment">// 55348(0xd834)</span></span><br><span class="line"><span class="string">&quot;∮ clef&quot;</span>.<span class="title function_">charCodeAt</span>(<span class="number">0</span>); <span class="comment">// 56606(0xdd1e)</span></span><br><span class="line"><span class="string">&quot;∮ clef&quot;</span>.<span class="title function_">charAt</span>(<span class="number">1</span>) === <span class="string">&quot; &quot;</span>; <span class="comment">// false</span></span><br><span class="line"><span class="string">&quot;∮ clef&quot;</span>.<span class="title function_">charAt</span>(<span class="number">2</span>) === <span class="string">&quot; &quot;</span>; <span class="comment">// true</span></span><br></pre></td></tr></table></figure><h2 id="chapter2-变量作用域"><a href="#chapter2-变量作用域" class="headerlink" title="chapter2 变量作用域"></a>chapter2 变量作用域</h2><h3 id="8-尽量少用全局对象"><a href="#8-尽量少用全局对象" class="headerlink" title="8.尽量少用全局对象"></a>8.尽量少用全局对象</h3><p>简单理解：容易冲突</p><h3 id="9-始终声明局部变量"><a href="#9-始终声明局部变量" class="headerlink" title="9.始终声明局部变量"></a>9.始终声明局部变量</h3><p>虽然js不声明变量不会报错，但是这个变量直接变成了全局变量，要避免</p><h3 id="10-避免使用with"><a href="#10-避免使用with" class="headerlink" title="10.避免使用with"></a>10.避免使用with</h3><p>with可以避免对对象的重复引用，但是不可靠性大大增加，避免使用</p><h3 id="11-熟练掌握闭包"><a href="#11-熟练掌握闭包" class="headerlink" title="11.熟练掌握闭包"></a>11.熟练掌握闭包</h3><p>事实1. JS允许你引用在当前函数以外定义的变量<br>事实2. 即使外部函数已经返回，当前函数仍然可以引用在外部函数所定义的变量<br>事实3. 闭包可以更新外部的值</p><p>原理:JS的函数值在内部存储它们可能会引用的定义在其封闭作用域的变量。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">box</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> val = <span class="literal">undefined</span>;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">newVal</span>) &#123; val = newVal; &#125;,</span><br><span class="line">        <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="keyword">return</span> val; &#125;,</span><br><span class="line">        <span class="attr">type</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="keyword">return</span> <span class="keyword">typeof</span> val; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> v = <span class="title function_">box</span>();</span><br><span class="line">b.<span class="title function_">type</span>(); <span class="comment">// &quot;undefined&quot;</span></span><br><span class="line">b.<span class="title function_">set</span>(<span class="number">98.6</span>);</span><br><span class="line">b.<span class="title function_">get</span>(); <span class="comment">// 98.6</span></span><br><span class="line">b.<span class="title function_">type</span>(); <span class="comment">// &quot;number&quot;</span></span><br></pre></td></tr></table></figure><p>这基本就是一个Class了，ES6的Class编译之后好像就是这样？</p><h3 id="12-理解变量声明提升"><a href="#12-理解变量声明提升" class="headerlink" title="12.理解变量声明提升"></a>12.理解变量声明提升</h3><p>JS不支持块级作用域，而是函数级作用域<br>变量提升：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        <span class="keyword">var</span> x=<span class="comment">/*...*/</span>;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 会转化为以下：</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> x;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        x=<span class="comment">/*...*/</span>;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>异常处理的catch看似块级作用域，但是感觉更像函数，因此是个例外</p><h3 id="13-使用立即调用的函数表达式创建局部作用域"><a href="#13-使用立即调用的函数表达式创建局部作用域" class="headerlink" title="13.使用立即调用的函数表达式创建局部作用域"></a>13.使用立即调用的函数表达式创建局部作用域</h3><p>闭包通过<code>引用</code>而不是<code>值</code>捕获它们的外部变量</p><p>好好理解下面三段代码：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">wrapElements</span>(<span class="params">a</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> result = [], i, n;</span><br><span class="line">  <span class="keyword">for</span>(i = <span class="number">0</span>, n = a.<span class="property">length</span>; i &lt; n; i++) &#123;</span><br><span class="line">    result[i] = <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="keyword">return</span> a[i]; &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> wrapped = <span class="title function_">wrapElements</span>([<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>]);</span><br><span class="line"><span class="keyword">var</span> f = wrapped[<span class="number">0</span>];</span><br><span class="line"><span class="title function_">f</span>(); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">wrapElements</span>(<span class="params">a</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> result = [];</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>, n = a.<span class="property">length</span>; i &lt; n; i++) &#123;</span><br><span class="line">    result[i] = <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="keyword">return</span> a[i]; &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> wrapped = <span class="title function_">wrapElements</span>([<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>]);</span><br><span class="line"><span class="keyword">var</span> f = wrapped[<span class="number">0</span>];</span><br><span class="line"><span class="title function_">f</span>(); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p>这个版本更具有欺骗性，但是由于变量提升，没有块级作用域，i仍然只有一个槽(slot)<br>想实现的效果：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">wrapElements</span>(<span class="params">a</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> result = [], i, n;</span><br><span class="line">  <span class="keyword">for</span>(i = <span class="number">0</span>, n = a.<span class="property">length</span>; i &lt; n; i++) &#123;</span><br><span class="line">    (<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="keyword">var</span> j = i;</span><br><span class="line">      result[i] = <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="keyword">return</span> a[j]; &#125;;</span><br><span class="line">    &#125;)()</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> wrapped = <span class="title function_">wrapElements</span>([<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>]);</span><br><span class="line"><span class="keyword">var</span> f = wrapped[<span class="number">0</span>];</span><br><span class="line"><span class="title function_">f</span>(); <span class="comment">// 10</span></span><br></pre></td></tr></table></figure><h3 id="14-当心命名函数表达式笨拙的作用域"><a href="#14-当心命名函数表达式笨拙的作用域" class="headerlink" title="14.当心命名函数表达式笨拙的作用域"></a>14.当心命名函数表达式笨拙的作用域</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> f = <span class="keyword">function</span> <span class="title function_">abs</span>(<span class="params">a</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> a &lt; <span class="number">0</span> ? -a : a;</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">f</span>(-<span class="number">2</span>); <span class="comment">// 2</span></span><br><span class="line"><span class="title function_">abs</span>(-<span class="number">3</span>); <span class="comment">// wrong 并没有abs这个变量</span></span><br><span class="line"><span class="comment">// 但是有些JS环境会产生abs，甚至分配多余的内存，都是不符合规范的</span></span><br></pre></td></tr></table></figure><p>命名函数表达式的作用域在E3中会表示为一个对象，从而导致跟with语句一样的问题，作用域会受到<code>Object.prototype</code>的属性变化。<br>在E5中修复了这个问题</p><h3 id="15-当心局部块函数生命笨拙的作用域"><a href="#15-当心局部块函数生命笨拙的作用域" class="headerlink" title="15.当心局部块函数生命笨拙的作用域"></a>15.当心局部块函数生命笨拙的作用域</h3><p>函数中可以嵌套函数，但是如果再把函数声明放在if语句块中呢？<br>在不同的JS运行环境中，可能有不同的解释，因此避免使用</p><h3 id="16-避免使用eval创建局部变量"><a href="#16-避免使用eval创建局部变量" class="headerlink" title="16.避免使用eval创建局部变量"></a>16.避免使用eval创建局部变量</h3><p>eval很强大，但使用起来有安全性，避免如下使用方式：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params">src</span>) &#123;</span><br><span class="line">  <span class="built_in">eval</span>(src);</span><br><span class="line">  <span class="keyword">return</span> y;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">test</span>(<span class="string">&quot;var y = &#x27;local&#x27;;&quot;</span>);</span><br></pre></td></tr></table></figure><p>推荐如下，这样不会对函数内部造成影响：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params">src</span>) &#123;</span><br><span class="line">  (<span class="keyword">function</span>(<span class="params"></span>) &#123;<span class="built_in">eval</span>(src);&#125;)()</span><br><span class="line">  <span class="keyword">return</span> y;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">test</span>(<span class="string">&quot;var y = &#x27;local&#x27;;&quot;</span>);</span><br></pre></td></tr></table></figure><h3 id="17-间接调用eval函数优于直接调用"><a href="#17-间接调用eval函数优于直接调用" class="headerlink" title="17.间接调用eval函数优于直接调用"></a>17.间接调用eval函数优于直接调用</h3><p>大多数函数只能访问定义它们所在的作用域，而eval函数具有访问调用它那时的整个作用域的能力。 （有点绕）</p><p>直接调用和间接调用：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> x = <span class="string">&#x27;global&#x27;</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> x = <span class="string">&#x27;local&#x27;</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">eval</span>(<span class="string">&quot;x&quot;</span>); <span class="comment">// 直接调用</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">test</span>(); <span class="comment">// &quot;local&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> x = <span class="string">&#x27;global&#x27;</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> x = <span class="string">&#x27;local&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> f = <span class="built_in">eval</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">f</span>(<span class="string">&quot;x&quot;</span>); <span class="comment">// 间接调用</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">test</span>(); <span class="comment">// &quot;local&quot;</span></span><br></pre></td></tr></table></figure><p>例子是有了，但是不太明白为什么后者优于前者</p><p>间接调用还有另外一种方式<code>(0, eval)(src)</code></p><h2 id="chapter3-使用函数"><a href="#chapter3-使用函数" class="headerlink" title="chapter3 使用函数"></a>chapter3 使用函数</h2><h3 id="18-理解函数调用、方法调用及构造函数调用"><a href="#18-理解函数调用、方法调用及构造函数调用" class="headerlink" title="18.理解函数调用、方法调用及构造函数调用"></a>18.理解函数调用、方法调用及构造函数调用</h3><p>在面向对象中，三种概念是不同的。但是在JS中，只是单个构造对象的不同使用模式。<br><code>方法就是特定对象调用的函数</code><br>如果纯函数中使用this，会默认使用全局对象，但ES5中规定的严格模式不会这么做，会报错。<br>纯函数作为构造函数使用：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">User</span>(<span class="params">name, age</span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用new操作符能识别构造函数</span></span><br><span class="line"><span class="keyword">const</span> person = <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&#x27;Jack&#x27;</span>, <span class="number">18</span>);</span><br><span class="line">person.<span class="property">name</span>; <span class="comment">// &#x27;Jack&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="19-熟练掌握高阶函数"><a href="#19-熟练掌握高阶函数" class="headerlink" title="19. 熟练掌握高阶函数"></a>19. 熟练掌握高阶函数</h3><ul><li>高阶函数是那些将函数作为参数或返回值的函数</li><li>熟悉掌握现有库中的高阶函数</li><li>学会发现可以被高阶函数所取代的常见的编码模式</li></ul><h3 id="20-使用call方法来自定义接收者来调用方法"><a href="#20-使用call方法来自定义接收者来调用方法" class="headerlink" title="20. 使用call方法来自定义接收者来调用方法"></a>20. 使用call方法来自定义接收者来调用方法</h3><p>需要自定义接收者来调用函数时，也就是说<code>想改变函数中的this</code></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 不推荐的方法：</span></span><br><span class="line">obj.<span class="property">temp</span> = f;</span><br><span class="line"><span class="title function_">f</span>(arg1, arg2);</span><br><span class="line"><span class="keyword">delete</span> obj.<span class="property">temp</span></span><br><span class="line"><span class="comment">// 万一obj就有temp呢？</span></span><br></pre></td></tr></table></figure><p>call方法支持传入一个接收者来调用函数，相当于改变了this</p><h3 id="21-使用apply方法通过不同数量的参数调用函数"><a href="#21-使用apply方法通过不同数量的参数调用函数" class="headerlink" title="21. 使用apply方法通过不同数量的参数调用函数"></a>21. 使用apply方法通过不同数量的参数调用函数</h3><p>当一个函数可接受可变参数时，用apply传入数组</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> scores = <span class="title function_">getAllScores</span>(); <span class="comment">// 返回一个数组</span></span><br><span class="line"><span class="comment">// average可接受可变参数</span></span><br><span class="line">average.<span class="title function_">apply</span>(<span class="literal">null</span>, scores);</span><br></pre></td></tr></table></figure><h3 id="22-使用arguments创建可变参数的函数"><a href="#22-使用arguments创建可变参数的函数" class="headerlink" title="22. 使用arguments创建可变参数的函数"></a>22. 使用arguments创建可变参数的函数</h3><p>如果提供一个便利的可变参数的函数，同时最好提供一个需要显式指定数组的固定参数版本，很容易实现：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">average</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="comment">// average可接受可变参数</span></span><br><span class="line">  <span class="title function_">averageOfArray</span>(<span class="variable language_">arguments</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="23-永远不要修改arguments对象"><a href="#23-永远不要修改arguments对象" class="headerlink" title="23. 永远不要修改arguments对象"></a>23. 永远不要修改arguments对象</h3><p>如下实现：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">callMethod</span>(<span class="params">obj, method</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> args = [].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>, <span class="number">2</span>);</span><br><span class="line">  retrun obj[method].<span class="title function_">apply</span>(obj, args);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">  <span class="attr">add</span>: <span class="keyword">function</span>(<span class="params">x + y</span>) &#123; <span class="keyword">return</span> x + y; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">callMethod</span>(obj, <span class="string">&#x27;add&#x27;</span>, <span class="number">17</span>, <span class="number">25</span>);</span><br></pre></td></tr></table></figure><h3 id="24-使用变量保存arguments对象的引用"><a href="#24-使用变量保存arguments对象的引用" class="headerlink" title="24.使用变量保存arguments对象的引用"></a>24.使用变量保存arguments对象的引用</h3><p>主要是注意函数嵌套时，arguments会变，要用外层的arguments时，需要先保存引用</p><h3 id="25-使用bind方法提取具有确定接收者的方法"><a href="#25-使用bind方法提取具有确定接收者的方法" class="headerlink" title="25.使用bind方法提取具有确定接收者的方法"></a>25.使用bind方法提取具有确定接收者的方法</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> buffer = &#123;</span><br><span class="line">  <span class="attr">entries</span>: [],</span><br><span class="line">  <span class="attr">add</span>: <span class="keyword">function</span>(<span class="params">s</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">entries</span>.<span class="title function_">push</span>(s);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// wrong</span></span><br><span class="line"><span class="keyword">var</span> source = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">source.<span class="title function_">forEach</span>(buffer.<span class="property">add</span>); <span class="comment">// entries undefined</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// correct</span></span><br><span class="line">source.<span class="title function_">forEach</span>(buffer.<span class="property">add</span>.<span class="title function_">bind</span>(buffer));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意！！</span></span><br><span class="line">buffer.<span class="property">add</span> === buffer.<span class="property">add</span>.<span class="title function_">bind</span>(buffer); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p>bind是安全的，不会修改add方法，bind方法创建了一个新的方法，里面this指向的buffer</p><h3 id="26-使用bind函数进行函数柯里化"><a href="#26-使用bind函数进行函数柯里化" class="headerlink" title="26.使用bind函数进行函数柯里化"></a>26.使用bind函数进行函数柯里化</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">simpleURL</span>(<span class="params">protocol, domain, path</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> protocol + <span class="string">&#x27;://&#x27;</span> + domin + <span class="string">&#x27;/&#x27;</span> + path;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//优化前</span></span><br><span class="line"><span class="keyword">var</span> urls = paths.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">path</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">simpleURL</span>(<span class="string">&#x27;http&#x27;</span>, siteDomain, path);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 优化后</span></span><br><span class="line"><span class="keyword">var</span> urls = paths.<span class="title function_">map</span>(simpleURL.<span class="title function_">bind</span>(<span class="literal">null</span>, <span class="string">&#x27;http&#x27;</span>, siteDomain));</span><br></pre></td></tr></table></figure><h3 id="27-使用闭包而不是字符串封装代码"><a href="#27-使用闭包而不是字符串封装代码" class="headerlink" title="27.使用闭包而不是字符串封装代码"></a>27.使用闭包而不是字符串封装代码</h3><p>这个正常人也不会用eval…</p><h3 id="28-不要信赖函数对象的toString-方法"><a href="#28-不要信赖函数对象的toString-方法" class="headerlink" title="28.不要信赖函数对象的toString()方法"></a>28.不要信赖函数对象的toString()方法</h3><p>如题，因为在不同JS引擎中结果不同</p><h3 id="29-避免使用非标准的栈检查工具"><a href="#29-避免使用非标准的栈检查工具" class="headerlink" title="29.避免使用非标准的栈检查工具"></a>29.避免使用非标准的栈检查工具</h3><p>arguments.caller虽然能告诉你函数的调用者，如果用它来实现栈检查工具，在函数调用本身的情况下，会陷入无限循环</p><h2 id="Chapter4-对象和原型"><a href="#Chapter4-对象和原型" class="headerlink" title="Chapter4 对象和原型"></a>Chapter4 对象和原型</h2><p>虽然JS支持继承，但不像传统语言，继承不是基于类，而是基于原型。</p><h3 id="30-31-32-prototype-getPrototypeOf-proto"><a href="#30-31-32-prototype-getPrototypeOf-proto" class="headerlink" title="30.31.32.prototype&#x2F;getPrototypeOf()&#x2F;__proto__"></a>30.31.32.prototype&#x2F;getPrototypeOf()&#x2F;__proto__</h3><p>它们是对立但相关的访问器</p><ul><li><code>C.prototype</code>用于建立由new C()c创建的对象的原型</li><li><code>Object.getPrototypeOf(obj)</code>是ES5中用来获取obj对象的原型对象的标准方法</li><li><code>obj.__proto__</code>是获取obj对象的原型对象的非标准方法</li></ul><p>因此，尽量用<code>getPrototypeof()</code>,尽量不去修改<code>__proto__</code></p><h3 id="33-使构造函数与New操作符无关"><a href="#33-使构造函数与New操作符无关" class="headerlink" title="33.使构造函数与New操作符无关"></a>33.使构造函数与New操作符无关</h3><p>如果不使用new而是直接：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> u = <span class="title class_">User</span>(<span class="string">&#x27;Jack&#x27;</span>, <span class="string">&#x27;18&#x27;</span>);</span><br></pre></td></tr></table></figure><p>如果内部用了严格模式，并给this.name赋值，会报错<br>所以内部函数要考虑到这种情况，判断this是否是undefined，主动new一个</p><h3 id="34-在原型中储存方法"><a href="#34-在原型中储存方法" class="headerlink" title="34. 在原型中储存方法"></a>34. 在原型中储存方法</h3><p>如果在对象中储存方法，每个实例都会生成副本，而在原型中，只有一个</p><h3 id="35-闭包存储私有变量"><a href="#35-闭包存储私有变量" class="headerlink" title="35. 闭包存储私有变量"></a>35. 闭包存储私有变量</h3><p>对象中的属性很容易通过this获取，如果有私有变量可以利用闭包存储</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">User</span>(<span class="params">name, passwordHash</span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">toString</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;User &#x27;</span> + name;</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">checkPassword</span> = <span class="keyword">function</span>(<span class="params">password</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title class_">Hash</span>(password) === passwordHash;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是会牺牲一些，如34的问题，不能将该方法存在原型中。</p><h3 id="36-只将实例状态保存在实例对象中"><a href="#36-只将实例状态保存在实例对象中" class="headerlink" title="36. 只将实例状态保存在实例对象中"></a>36. 只将实例状态保存在实例对象中</h3><p>别保存在原型中，不然所有实例会共享，显而易见</p><h3 id="37-this变量的隐式绑定问题"><a href="#37-this变量的隐式绑定问题" class="headerlink" title="37. this变量的隐式绑定问题"></a>37. this变量的隐式绑定问题</h3><p>经常遇到，this的作用域是由最近的封闭函数确定，解决该办法有三种方法</p><ol><li><code>arr.map((v) =&gt; this.outerFunc(v), this)</code> 有很多函数接受第二个参数绑定this</li><li><code>var self = this; arr.map(...</code> 在外部把this保存一下</li><li><code>arr.map((v) =&gt; this.outerFunc(v).bind(this))</code> 使用bind</li></ol><h3 id="38-在子类中的构造函数调用父类的构造函数"><a href="#38-在子类中的构造函数调用父类的构造函数" class="headerlink" title="38. 在子类中的构造函数调用父类的构造函数"></a>38. 在子类中的构造函数调用父类的构造函数</h3><p>假设飞船SpaceShip是游戏中Actor的子类，</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">SpaceShip</span>(<span class="params">scene, x, y</span>) &#123;</span><br><span class="line">  <span class="title class_">Actor</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, scene, x, y);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">point</span> = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>先调用Actor的构造函数，使Actor的实例属性添加到新对象中<br>后续作为一个正确的子类，SpaceShip的原型必须继承Actor.prototype</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✔️right</span></span><br><span class="line"><span class="title class_">SpaceShip</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Object</span>.<span class="title function_">create</span>(<span class="title class_">Actor</span>.<span class="property"><span class="keyword">prototype</span></span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ❌ wrong</span></span><br><span class="line"><span class="title class_">SpaceShip</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">Actor</span>();</span><br></pre></td></tr></table></figure><p>上述正确做法避免调用了Actor的构造函数。</p><h3 id="39-不要重用父类的属性名"><a href="#39-不要重用父类的属性名" class="headerlink" title="39.不要重用父类的属性名"></a>39.不要重用父类的属性名</h3><p>如题 理解很简单</p><h3 id="40-避免继承标准类"><a href="#40-避免继承标准类" class="headerlink" title="40.避免继承标准类"></a>40.避免继承标准类</h3><p>如果继承标准类，其行为会被特殊的内部标记属性[[Class]]破坏<br>如果child继承了array，<br>child是object，不会有array的行为</p><h3 id="41-将原型视为实现细节"><a href="#41-将原型视为实现细节" class="headerlink" title="41.将原型视为实现细节"></a>41.将原型视为实现细节</h3><p>对象是接口，原型是实现 (？如何理解)</p><h3 id="42-避免使用轻率的猴子补丁"><a href="#42-避免使用轻率的猴子补丁" class="headerlink" title="42.避免使用轻率的猴子补丁"></a>42.避免使用轻率的猴子补丁</h3><p>猴子补丁是指，假如Array缺少一个split方法，那么直接<code>Array.prototype.split = ...</code>创建一个方法。。</p><p>如果非要用，可以考虑封装在一个function中，调用给函数的代码才会修改原型</p><h2 id="Chapter5-数组和字典"><a href="#Chapter5-数组和字典" class="headerlink" title="Chapter5 数组和字典"></a>Chapter5 数组和字典</h2><h3 id="43-使用Object的直接实例构造轻量级字典"><a href="#43-使用Object的直接实例构造轻量级字典" class="headerlink" title="43.使用Object的直接实例构造轻量级字典"></a>43.使用Object的直接实例构造轻量级字典</h3><p>其实就是常用的做法，把一个Object直接当成字典来用<br>但是文中指出了避免对其原型上增加方法，不然会导致错误(原型污染)</p><h3 id="44-使用null原型防止原型污染"><a href="#44-使用null原型防止原型污染" class="headerlink" title="44.使用null原型防止原型污染"></a>44.使用null原型防止原型污染</h3><p>用Object.create(null)创建空对象，不容易被污染（？）<br>还是会在属性查找的时候，出现污染现象</p><h3 id="45-使用hasOwnProperty避免原型污染"><a href="#45-使用hasOwnProperty避免原型污染" class="headerlink" title="45.使用hasOwnProperty避免原型污染"></a>45.使用hasOwnProperty避免原型污染</h3><p>这样不会拿到加在原型上的方法<br>更严谨的：你不知道对象上的hasOwnProperty是否被覆盖了，可以这么办</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> hasOwn =&#123;&#125;.<span class="property">hasOwnProperty</span>;</span><br><span class="line">hasOwn.<span class="title function_">call</span>(obj, <span class="string">&#x27;Alice&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="46-使用数组而不要使用字典来储存有序集合"><a href="#46-使用数组而不要使用字典来储存有序集合" class="headerlink" title="46.使用数组而不要使用字典来储存有序集合"></a>46.使用数组而不要使用字典来储存有序集合</h3><p>很简单明了，对象的key是无序的</p><h3 id="47-绝不要在Object-prototype中增加可枚举的属性"><a href="#47-绝不要在Object-prototype中增加可枚举的属性" class="headerlink" title="47.绝不要在Object.prototype中增加可枚举的属性"></a>47.绝不要在Object.prototype中增加可枚举的属性</h3><p>如果想增加方法给原型，会污染for…in方法，<br>以下方式可以解决这个问题：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>, <span class="string">&#x27;allKeys&#x27;</span>, &#123;</span><br><span class="line">  <span class="attr">value</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> result = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> <span class="variable language_">this</span>) &#123;</span><br><span class="line">      result.<span class="title function_">push</span>(key);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="attr">enumerable</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="attr">configurable</span>: <span class="literal">true</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="48-避免在枚举之间修改对象"><a href="#48-避免在枚举之间修改对象" class="headerlink" title="48.避免在枚举之间修改对象"></a>48.避免在枚举之间修改对象</h3><ul><li>使用for…in的时候一定不要修改可枚举对象，会出现意外情况。</li><li>可改用for循环或者while</li><li>为了在一个可变的枚举对象中遍历，可以考虑再用一个额外的数组去保存信息</li></ul><h3 id="49-数组迭代优先使用for循环而不是for…in"><a href="#49-数组迭代优先使用for循环而不是for…in" class="headerlink" title="49.数组迭代优先使用for循环而不是for…in"></a>49.数组迭代优先使用for循环而不是for…in</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> scores = [<span class="number">98</span>, <span class="number">74</span>, <span class="number">85</span>, <span class="number">77</span>, <span class="number">93</span>, <span class="number">100</span>, <span class="number">89</span>];</span><br><span class="line"><span class="keyword">var</span> total = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> score <span class="keyword">in</span> scores) &#123;</span><br><span class="line">    total += score;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> mean = total / scores.<span class="property">length</span>;</span><br><span class="line">mean; <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>第一层❌：以上代码看似求平均值，得出88；<br>第二层❌：for…in拿的是index，所以是0+1+2。。。total&#x3D;21,mean&#x3D;3;<br>实际✔️：for…in拿的确实是index，但是是string，所以得到了total&#x3D;’0123456’, 强制类型转化为了123456&#x2F;7,得到17636.57….</p><p>解决：用普通for循环即可</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 更好的代码</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>, n = scores.<span class="property">length</span>; i &lt; n; i++) &#123;...&#125;</span><br></pre></td></tr></table></figure><p>提前计算出n，避免长度变化的影响</p><h3 id="50-迭代方法优于循环"><a href="#50-迭代方法优于循环" class="headerlink" title="50.迭代方法优于循环"></a>50.迭代方法优于循环</h3><ul><li>因为自己写for循环很容易写错初始条件，尽量用数组的forEach,map,filter,every,some等迭代方法。</li><li>同样的，对自己定义的抽象对象，也可以尽量实现迭代方法去遍历。</li></ul><h3 id="51-在类数组对象中复用数组方法"><a href="#51-在类数组对象中复用数组方法" class="headerlink" title="51.在类数组对象中复用数组方法"></a>51.在类数组对象中复用数组方法</h3><p>举个例子：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">highlight</span>(<span class="params"></span>) &#123;</span><br><span class="line">    [].<span class="property">forEach</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>, <span class="keyword">function</span>(<span class="params">widget</span>)&#123;</span><br><span class="line">        widget.<span class="title function_">setBackground</span>(<span class="string">&#x27;yellow&#x27;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>像arguments、DOM中的Nodelist，都没有继承JS的Array原型，但是是类数组</p><p>类数组定义：<br><img src="/blog/posts/Effective-javascript/%E7%B1%BB%E6%95%B0%E7%BB%84%E5%AE%9A%E4%B9%89.png" alt="类数组定义"><br>这个例子不满足第二条：<code>var arraylike = {0: &#39;a&#39;, 1: &#39;b&#39;, length: 2}</code><br>因为，length不能自动变化</p><p>但是Array的几乎所有方法和length变化无关，因此arraylike可以用，除了concat</p><h3 id="52-数组字面量优于数组构造函数"><a href="#52-数组字面量优于数组构造函数" class="headerlink" title="52.数组字面量优于数组构造函数"></a>52.数组字面量优于数组构造函数</h3><p>使用这种字面量语法：<code>var list = [1, 2, 3]</code><br>优于这种构造函数语法：<code>var list = new Array(1, 2, 3)</code></p><p>因为Array可能有人修改，可能有人包装过。。。<br>(说得有道理，但是不常见)</p><h2 id="Chapter6-库和API设计"><a href="#Chapter6-库和API设计" class="headerlink" title="Chapter6 库和API设计"></a>Chapter6 库和API设计</h2><h3 id="53-保持一致的约定"><a href="#53-保持一致的约定" class="headerlink" title="53.保持一致的约定"></a>53.保持一致的约定</h3><ul><li>变量命名和函数签名尽量一致</li><li>参数顺序尽量一致，比如先宽度，后高度</li><li>优秀的库都有完善的文档，如果文档保持一致，用户面对一些常见的任务就不用不停得看文档</li></ul><h3 id="54-将undefined看作“没有值”"><a href="#54-将undefined看作“没有值”" class="headerlink" title="54.将undefined看作“没有值”"></a>54.将undefined看作“没有值”</h3><ul><li>避免使用undefined表示某一非特定值</li><li>不要用undefined表示特定标志</li><li>提供的默认参数用undefined检查，而不是arguments.length</li><li>在允许0、NaN、’’存在的情况，避免用真值测试(width || 300)</li></ul><h3 id="55-接受关键字参数的选项对象"><a href="#55-接受关键字参数的选项对象" class="headerlink" title="55.接受关键字参数的选项对象"></a>55.接受关键字参数的选项对象</h3><p>多参数虽然简单，但参数多的时候难以记忆:<br><code>new Person(18, &#39;Bob&#39;)</code><br>选项对象，不用记忆顺序：<br><code>new Person({age: 18, name: &#39;Bob&#39;})</code></p><ul><li>用选项对象使API可读性提高，减少记忆</li><li>一般所有的选项对象都是可选的</li><li>用extend函数(lodash.merge也可以吧？)抽象从选项对象中取值的逻辑</li></ul><h3 id="56-避免不必要的状态"><a href="#56-避免不必要的状态" class="headerlink" title="56.避免不必要的状态"></a>56.避免不必要的状态</h3><p>函数内部不保存状态，输入参数相同时，输出也相同。个人理解也就是纯函数。<br>更加模块化、减少不同部分的影响、使代码更易于阅读。<br>一个著名的有状态的API是Web的Canvas库，一开始学的时候确实费劲。。</p><h3 id="57-使用结构类型设计灵活的接口"><a href="#57-使用结构类型设计灵活的接口" class="headerlink" title="57.使用结构类型设计灵活的接口"></a>57.使用结构类型设计灵活的接口</h3><p>举个例子，以下代码是我们将实现的功能：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Wiki.formats.MEDIAWIKI为格式化器</span></span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title class_">Wiki</span>(<span class="title class_">Wiki</span>.<span class="property">formats</span>.<span class="property">MEDIAWIKI</span>);</span><br><span class="line"><span class="comment">// Wiki将格式化函数存储在了wiki实例对象的内部</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Wiki</span>(<span class="params">format</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">format</span> = format;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 每次渲染页面时，会调用Wiki的displayPage方法，内部实现如下：</span></span><br><span class="line"><span class="title class_">Wiki</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">displayPage</span> = <span class="keyword">function</span>(<span class="params">source</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> page = <span class="variable language_">this</span>.<span class="title function_">format</span>(source);</span><br><span class="line">    <span class="keyword">var</span> title = page.<span class="title function_">getTitle</span>();</span><br><span class="line">    <span class="keyword">var</span> author = page.<span class="title function_">getAuthor</span>();</span><br><span class="line">    <span class="keyword">var</span> output = page.<span class="title function_">toHTML</span>();</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="方法一：使用类去实现格式化器"><a href="#方法一：使用类去实现格式化器" class="headerlink" title="方法一：使用类去实现格式化器"></a>方法一：使用类去实现格式化器</h4><p>创建一个Page基类，再实现一个继承Page的MWPage类(其他格式也都为Page的子类)，然后MEDIAWIKI则是一个返回MWPage实例的一个“工厂函数”，代码如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">MWPage</span>(<span class="params">source</span>) &#123;</span><br><span class="line">    <span class="title class_">Page</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, source);</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// MWPage extends Page</span></span><br><span class="line"><span class="title class_">MWPage</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Object</span>.<span class="title function_">create</span>(<span class="title class_">Page</span>.<span class="property"><span class="keyword">prototype</span></span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">MWPage</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getTitle</span> = <span class="comment">/* ... */</span>;</span><br><span class="line"><span class="title class_">MWPage</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getAuthor</span> = <span class="comment">/* ... */</span>;</span><br><span class="line"><span class="title class_">MWPage</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toHTML</span> = <span class="comment">/* ... */</span>;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Wiki</span>.<span class="property">formats</span>.<span class="property">MEDIAWIKI</span> = <span class="keyword">function</span>(<span class="params">source</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MWPage</span>(source);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>确实是OOP的做法，但问题是调用的3个方法都是自己实现的，并未从Page继承任何有用的代码。</p><h4 id="方法二：结构类型"><a href="#方法二：结构类型" class="headerlink" title="方法二：结构类型"></a>方法二：结构类型</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Wiki</span>.<span class="property">formats</span>.<span class="property">MEDIAWIKI</span> = <span class="keyword">function</span>(<span class="params">source</span>) &#123;</span><br><span class="line">  <span class="comment">// ... </span></span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    <span class="attr">getTitle</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="comment">/* ... */</span> &#125;,</span><br><span class="line">    <span class="attr">getAuthor</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="comment">/* ... */</span> &#125;,</span><br><span class="line">    <span class="attr">toHTML</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="comment">/* ... */</span> &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>只要displayPage方法结构正确，具有预期的<code>getTitle</code>、<code>getAuthor</code>、<code>toHTML</code>方法，那么这个格式化器就OK；比PAGE类更加灵活。</p><p>这种接口有时候称为结构类型或鸭子类型，优雅的轻量的编程模式。</p><h3 id="58-区分数组对象和类数组对象"><a href="#58-区分数组对象和类数组对象" class="headerlink" title="58.区分数组对象和类数组对象"></a>58.区分数组对象和类数组对象</h3><p>有这么一个方法，支持入参为 一个数组或一个对象，也就是实现了方法的重载。那么就必须在方法内部去判断这个入参是数组还是对象，这时候就要考虑类数组的概念了（51条），类数组是一个对象但是可以被视为数组，所以如何区分？因此引出了如下规则：</p><ul><li>API绝不应该重载与其他类型有重叠的类型<br>不完美的方案：<code>if (x instanceof Array)</code>。跨frame通信时，会有多个Array的原型副本，是不同的。<br>更好的方案：<code>if (Array.isArray(x))</code>。</li></ul><h3 id="59-避免过度的强制转换"><a href="#59-避免过度的强制转换" class="headerlink" title="59.避免过度的强制转换"></a>59.避免过度的强制转换</h3><p>如题所示，本人几乎不去用强制类型转换。</p><ul><li>避免强制类转换和重载的混用</li><li>考虑防御性地监视非预期的输入(对入参进行类型判断)<br>如果用TS多了，这种问题基本上不会有。。</li></ul><h3 id="60-支持方法链"><a href="#60-支持方法链" class="headerlink" title="60.支持方法链"></a>60.支持方法链</h3><p>无状态的API的好的例子：String的<code>replace</code>方法、Array的<code>map</code>、<code>filter</code>方法。<br>有状态的API也值得支持，在更新对象时返回this。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">element.<span class="title function_">setBackgroundColor</span>(<span class="string">&quot;yellow&quot;</span>)</span><br><span class="line">       .<span class="title function_">setColor</span>(<span class="string">&quot;red&quot;</span>)</span><br><span class="line">       .<span class="title function_">setFontWeight</span>(<span class="string">&quot;bold&quot;</span>);</span><br></pre></td></tr></table></figure><h2 id="Chapter7-并发"><a href="#Chapter7-并发" class="headerlink" title="Chapter7 并发"></a>Chapter7 并发</h2><h3 id="61-不要阻塞I-O事件队列"><a href="#61-不要阻塞I-O事件队列" class="headerlink" title="61.不要阻塞I&#x2F;O事件队列"></a>61.不要阻塞I&#x2F;O事件队列</h3><ul><li>主要是用异步函数和回调去避免阻塞。</li><li>JavaScript并发地接收事件，但会使用一个事件队列按序地处理程序</li><li>最重要的原则：<code>绝不要在应用程序事件队列中使用阻塞I/O的API</code></li></ul><h3 id="62-在异步序列中使用嵌套或命名的回调函数"><a href="#62-在异步序列中使用嵌套或命名的回调函数" class="headerlink" title="62.在异步序列中使用嵌套或命名的回调函数"></a>62.在异步序列中使用嵌套或命名的回调函数</h3><ul><li>使用嵌套或者命名的回调函数按顺序地执行多个异步操作。（合理的处理回调地狱。。）</li><li>尝试在过多的嵌套的回调函数和尴尬的命名的非嵌套回调函数之间取得平衡。</li><li>避免将可被并行执行的操作顺序化</li></ul><h3 id="63-当心丢弃错误"><a href="#63-当心丢弃错误" class="headerlink" title="63.当心丢弃错误"></a>63.当心丢弃错误</h3><ul><li>写共享的错误处理函数来避免复制和粘贴错误处理代码</li><li>确保异步操作的所有错误情况都处理了，避免丢弃错误</li></ul><h3 id="64-对异步循环使用递归"><a href="#64-对异步循环使用递归" class="headerlink" title="64.对异步循环使用递归"></a>64.对异步循环使用递归</h3><p>想要实现的效果：一个文件下载完，继续下另一个，但给的是一个urls。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">downloadOneAsync</span>(<span class="params">urls, onsuccess, onfailure</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> n = urls.<span class="property">length</span>;</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">tryNextURL</span>(<span class="params">i</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &gt;= n) &#123;</span><br><span class="line">      <span class="title function_">onfailure</span>(<span class="string">&quot;all downloads failed&quot;</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">downloadAsync</span>(urls[i], onsuccess, <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="title function_">tryNextURL</span>(i + <span class="number">1</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="title function_">tryNextURL</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>循环不能异步</li><li>使用递归在时间循环的单独轮次中执行迭代</li><li><code>在回调函数中执行递归，不会导致栈溢出</code></li></ul><h3 id="65-不要在计算时阻塞事件队列"><a href="#65-不要在计算时阻塞事件队列" class="headerlink" title="65.不要在计算时阻塞事件队列"></a>65.不要在计算时阻塞事件队列</h3><p>JS代码在进行长时间计算时，会使web页面无响应。</p><h4 id="方案1：Worker-API"><a href="#方案1：Worker-API" class="headerlink" title="方案1：Worker API"></a>方案1：Worker API</h4><p>有一个技术可以解决Web平台的Worker API，以下场景，比如一个要搜索大量可移动距离的人工智能游戏，如果在单线程环境下，会卡顿无响应。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="comment">// 产生一个新的线程独立的事件队列的并发执行线程。</span></span><br><span class="line"><span class="keyword">var</span> ai = <span class="keyword">new</span> <span class="title class_">Worker</span>(<span class="string">&quot;ai.js&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 主线程需要ai的数据时，通过postMessage的方式交互</span></span><br><span class="line"><span class="keyword">var</span> userMove = <span class="comment">/* ... */</span>;</span><br><span class="line"><span class="comment">// 需要重新计算用户移动</span></span><br><span class="line">ai.<span class="title function_">postMessage</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123;</span><br><span class="line">  <span class="attr">userMove</span>: userMove</span><br><span class="line">&#125;));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 处理worker线程的响应</span></span><br><span class="line">ai.<span class="property">onMessage</span> = <span class="keyword">function</span>(<span class="params">event</span>) &#123;</span><br><span class="line">  <span class="title function_">excuteMove</span>(<span class="title class_">JSON</span>.<span class="title function_">parse</span>(event.<span class="property">data</span>).<span class="property">computerMove</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>AI的代码</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">self.<span class="property">onMessage</span> = <span class="keyword">function</span>(<span class="params">event</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> userMove = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(event.<span class="property">data</span>).<span class="property">userMove</span>;</span><br><span class="line">  <span class="keyword">var</span> computerMove = <span class="title function_">computeNextMove</span>(userMove);</span><br><span class="line">  self.<span class="title function_">postMessage</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123;</span><br><span class="line">    <span class="attr">computerMove</span>: computerMove</span><br><span class="line">  &#125;));</span><br><span class="line"></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">computeNextMove</span>(<span class="params">userMove</span>) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="方案2：分解算法。组成可管理的工作块"><a href="#方案2：分解算法。组成可管理的工作块" class="headerlink" title="方案2：分解算法。组成可管理的工作块"></a>方案2：分解算法。组成可管理的工作块</h4><p>搜索社交网络图的算法，改造前的算法while循环代价过高，会阻塞。如果用方案1，还要复制整个网络图的状态或在worker中存储网络图的状态，传递消息来更新和查询网络。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 改造前</span></span><br><span class="line"><span class="title class_">Member</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">inNetwork</span> = <span class="keyword">function</span>(<span class="params">other</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> visited = &#123;&#125;;</span><br><span class="line">  <span class="keyword">var</span> worklist = [<span class="variable language_">this</span>];</span><br><span class="line">  <span class="keyword">while</span> (worklist.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> member = worklist.<span class="title function_">pop</span>();</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">if</span> (member === other) &#123; <span class="comment">// found</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 改造后</span></span><br><span class="line"><span class="title class_">Member</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">inNetwork</span> = <span class="keyword">function</span>(<span class="params">other, callback</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> visited =&#123;&#125;;</span><br><span class="line">  <span class="keyword">var</span> worklist = [<span class="variable language_">this</span>];</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">next</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (worklist.<span class="property">length</span> === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="title function_">callback</span>(<span class="literal">false</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> member = worklist.<span class="title function_">pop</span>();</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">if</span> (member === other) &#123; <span class="comment">// found</span></span><br><span class="line">      <span class="title function_">callback</span>(<span class="literal">true</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="built_in">setTimeout</span>(next, <span class="number">0</span>); <span class="comment">// schedule the next iteration</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">setTimeout</span>(next, <span class="number">0</span>); <span class="comment">// schedule the first iteration</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>利用回调函数拿到异步查询的结果，查询过程中不会阻塞。<br>但如果在一个<code>应用程序事件队列</code>中只执行算法的一个迭代，有点杀鸡用牛刀，可以稍微调整下，在next函数中加一个循环，<code>每进行10次循环再setTimeout(next, 0)</code></p><h3 id="66-使用计数器来执行并行操作"><a href="#66-使用计数器来执行并行操作" class="headerlink" title="66.使用计数器来执行并行操作"></a>66.使用计数器来执行并行操作</h3><p>并行发生的事件，回调函数的执行顺序是未知的，注意返回结果的顺序。<br>解决：用数组的index去保存结果(JS不会报index出边界的错误。。)，用计数器保证所有成功的时候回调。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">downloadAllAsync</span>(<span class="params">urls, onsuccess, onerror</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> pending = urls.<span class="property">length</span>;</span><br><span class="line">  <span class="keyword">var</span> results = [];</span><br><span class="line">  <span class="keyword">if</span> (pending === <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(onsuccess.<span class="title function_">bind</span>(<span class="literal">null</span>, result), <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  urls.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">url, i</span>) &#123;</span><br><span class="line">    <span class="title function_">downloadAsync</span>(url, <span class="keyword">function</span>(<span class="params">text</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (result) &#123;</span><br><span class="line">        result[i] = text;</span><br><span class="line">        pending--;</span><br><span class="line">        <span class="keyword">if</span> (pending === <span class="number">0</span>) &#123;</span><br><span class="line">          <span class="title function_">onsuccess</span>(result);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;, <span class="keyword">function</span>(<span class="params">error</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (result) &#123;</span><br><span class="line">        result = <span class="literal">null</span>;</span><br><span class="line">        <span class="title function_">onerror</span>(error);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="67-绝不要同步地调用异步的回调函数"><a href="#67-绝不要同步地调用异步的回调函数" class="headerlink" title="67.绝不要同步地调用异步的回调函数"></a>67.绝不要同步地调用异步的回调函数</h3><ul><li>即使可以立即得到数据，也绝不要同步地调用异步回调函数。</li><li>同步地调用异步的回调函数扰乱了预期的操作序列，并可能导致意想不到的交错代码。</li><li>同步地调用异步回调函数可能导致栈溢出或错误地处理异常</li><li>使用异步的API，比如setTimeout去调度异步回调函数</li></ul><h3 id="68-使用promise模式清洁异步逻辑"><a href="#68-使用promise模式清洁异步逻辑" class="headerlink" title="68.使用promise模式清洁异步逻辑"></a>68.使用promise模式清洁异步逻辑</h3><p>promise经常用，就不细说了</p><ul><li>promise代表最终值，即并行操作完成时最终产生的结果</li><li>使用promise组合不同的并行操作</li><li>使用promise模式的API避免数据竞争</li><li>在要求有意的竞争条件时使用select(早期的API吧，后期有race)</li></ul>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/Effective-javascript/</id>
    <link href="https://ig505gi.github.io/blog/posts/Effective-javascript/"/>
    <published>2021-05-15T11:23:19.000Z</published>
    <summary>《Effective JavaScript》读书笔记，涵盖 JS 严格模式、类型转换、函数调用等核心知识点。</summary>
    <title>Effective javascript</title>
    <updated>2026-05-29T11:55:00.599Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="Mooc" scheme="https://ig505gi.github.io/blog/tags/Mooc/"/>
    <category term="Java" scheme="https://ig505gi.github.io/blog/tags/Java/"/>
    <category term="OOP" scheme="https://ig505gi.github.io/blog/tags/OOP/"/>
    <category term="Backend" scheme="https://ig505gi.github.io/blog/tags/Backend/"/>
    <content>
      <![CDATA[<p>简介：慕课网课笔记，主要讲述Java的面向对象思想。用对象思考，用类来写作<br>课程地址：<a href="https://www.icourse163.org/course/ZJU-1001542001?tid=1003687002">https://www.icourse163.org/course/ZJU-1001542001?tid=1003687002</a><br>作业仓库：<a href="https://github.com/ig505gi/JAVA-Course">https://github.com/ig505gi/JAVA-Course</a></p><h1 id="Part1"><a href="#Part1" class="headerlink" title="Part1"></a>Part1</h1><h2 id="1-类的定义"><a href="#1-类的定义" class="headerlink" title="1.类的定义"></a>1.类的定义</h2><span id="more"></span><h3 id="1-1-对象、函数"><a href="#1-1-对象、函数" class="headerlink" title="1.1 对象、函数"></a>1.1 对象、函数</h3><p>对象 &#x3D; 属性 + 服务<br>数据：属性，状态<br>操作：函数<br>操作是包含着数据的，把数据和对数据的操作放在一起，数据不对外面公开–&gt;<strong>封装</strong></p><h3 id="1-2-this"><a href="#1-2-this" class="headerlink" title="1.2 this"></a>1.2 this</h3><p>在成员函数内部可以用this调用其他成员函数</p><h3 id="1-3-成员变量-本地变量"><a href="#1-3-成员变量-本地变量" class="headerlink" title="1.3 成员变量&amp;本地变量"></a>1.3 成员变量&amp;本地变量</h3><p>本地变量：生存期和作用域都在函数内部，定义在函数内部<br>成员变量：生存期是对象的生成期，作用域是类中的成员函数，定义在类中</p><h3 id="1-4-构造函数"><a href="#1-4-构造函数" class="headerlink" title="1.4 构造函数"></a>1.4 构造函数</h3><p>构造函数与类名相同，没有返回类型，<br><strong>重载</strong>：可以有多个构造函数，根据参数不同调用不同的构造函数<br>创建对象的时候，先调用构造函数，再初始化成员变量，再进入构造函数内部。</p><h3 id="1-5-toString"><a href="#1-5-toString" class="headerlink" title="1.5 toString()"></a>1.5 toString()</h3><p>可以用于任何一个类中，使类的输出变成想要的结果</p><h2 id="2-对象交互"><a href="#2-对象交互" class="headerlink" title="2 对象交互"></a>2 对象交互</h2><h3 id="2-1-访问属性"><a href="#2-1-访问属性" class="headerlink" title="2.1 访问属性"></a>2.1 访问属性</h3><p><strong>private</strong><br>私有成员是类的私有，不是对象的私有，可以在类中访问不同对象的私有变量。<br><strong>friendly</strong><br>如果前面不加访问属性，就是friendly，在同一个包里可以使用  </p><h3 id="2-2-类变量"><a href="#2-2-类变量" class="headerlink" title="2.2 类变量"></a>2.2 类变量</h3><p><strong>static</strong> 静态变量，是类的变量，不是对象的变量，可以用”类.“去访问<br>同样，函数前加 static，函数是类函数。<br><strong>static函数只能调用static函数，只能访问static变量</strong></p><h3 id="2-3-public-编译单元"><a href="#2-3-public-编译单元" class="headerlink" title="2.3  public&amp;编译单元"></a>2.3  public&amp;编译单元</h3><p>public 类的定义加上public，该类一定在以该类命名的java文件中，如：<br><code>public class test{}</code> 一定在test.java中<br>一个 **.java是一个编译单元，里面只能有一个public类，并且和文件名相同</p><h2 id="3-包"><a href="#3-包" class="headerlink" title="3 包"></a>3 包</h2><h3 id="3-1"><a href="#3-1" class="headerlink" title="3.1"></a>3.1</h3><p>在一个包pac下面的的每个java文件一定有一行<code>package pac</code><br>包中类的调用方式：<br><img src="/blog/posts/Java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1-%E6%85%95%E8%AF%BE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/package.png" alt="package"></p><h2 id="4-对象容器"><a href="#4-对象容器" class="headerlink" title="4 对象容器"></a>4 对象容器</h2><h3 id="4-1-定义"><a href="#4-1-定义" class="headerlink" title="4.1 定义"></a>4.1 定义</h3><p><code>ArrayList&lt;String&gt; notes = new ArrayList&lt;String&gt;();</code><br>定义了一个存放String的ArrayList，ArrayList是一种范型类，就是一种容器<br>容器的输出带着方括号</p><h3 id="4-2-对象数组"><a href="#4-2-对象数组" class="headerlink" title="4.2 对象数组"></a>4.2 对象数组</h3><p>对象数组的每一个元素都是对象的管理者，而非对象本身<br>不像int数组，for-each循环的时候，不能对每个元素赋值，<br>对象数组和对象容器都可以在for-each循环中，对对象的成员变量赋值。</p><h3 id="4-3-set定义"><a href="#4-3-set定义" class="headerlink" title="4.3 set定义"></a>4.3 set定义</h3><p><code>HashSet&lt;String&gt; notes = new HashSet&lt;String&gt;();</code><br>HashSet没有重复元素，乱序</p><h3 id="4-4-Hash-类似python中字典"><a href="#4-4-Hash-类似python中字典" class="headerlink" title="4.4 Hash(类似python中字典)"></a>4.4 Hash(类似python中字典)</h3><p><code>HashMap&lt;Integer, String&gt; hm = new Hashmap&lt;Integer, String&gt;();</code><br>需要用Integer，int的包裹类型，都是类的名字<br><code>hm.put(5, &quot;five&quot;)</code> 用于往HashMap的对象中放元素<br>重复key进行put，value会覆盖</p><h2 id="5-继承与多态"><a href="#5-继承与多态" class="headerlink" title="5.继承与多态"></a>5.继承与多态</h2><h3 id="5-1-定义"><a href="#5-1-定义" class="headerlink" title="5.1 定义"></a>5.1 定义</h3><p><code>class &lt;子类名字&gt; extends &lt;父类名字&gt; {}</code>  </p><h3 id="5-2-父子类的关系"><a href="#5-2-父子类的关系" class="headerlink" title="5.2 父子类的关系"></a>5.2 父子类的关系</h3><table><thead><tr><th>父类成员访问属性</th><th>在父类中的含义</th><th>在子类中的含义</th></tr></thead><tbody><tr><td>public</td><td>对所有人开放</td><td>对所有人开放</td></tr><tr><td>protected</td><td>只有包内其它类、自己和子类可以访问</td><td>只有包内其它类、自己和子类可以访问</td></tr><tr><td>缺省</td><td>只有包内其它类可以访问</td><td>如果子类与父类在同一个包内：只有包内其它类可以访问，否则：相当于private，不能访问</td></tr><tr><td>private</td><td>只有自己可以访问</td><td>不能访问</td></tr></tbody></table><p>在构造一个子类的对象时，父类的构造方法也是会被调用的，而且<strong>父类的构造方法在子类的构造方法之前被调用</strong><br>如果我们试图重新定义一个在父类中<strong>已经存在</strong>的成员变量，那么我们是在定义一个与父类的成员变量<strong>完全无关</strong>的变量，<br>在子类中我们可以访问这个定义在子类中的变量，在父类的方法中访问父类的那个。尽管它们<strong>同名但是互不影响</strong></p><h3 id="5-3-super"><a href="#5-3-super" class="headerlink" title="5.3 super"></a>5.3 super</h3><p><code>super()</code>在构造函数第一行执行，进入父类的构造函数，可以重载，调用不同的构造函数<br>在子类中使用父类的函数，<code>super.parentsFunction()</code>  </p><h3 id="5-4-多态变量和向上造型"><a href="#5-4-多态变量和向上造型" class="headerlink" title="5.4 多态变量和向上造型"></a>5.4 多态变量和向上造型</h3><p>JAVA中的变量都是多态变量，比如一个放水果的变量，可以放苹果，也可以重新放橘子，但是不能放打印机<br>当把一个子类对象赋给父类变量时，发生了*<em>向上造型</em></p><p>例子：CD是Item的子类  </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Item item = new Item();</span><br><span class="line">CD cd = new CD();</span><br><span class="line">item = cd；</span><br><span class="line">CD cc = item; // 不对，编译不通过</span><br><span class="line">CD cc = (CD)item; // 将Item类变量item当做CD类型去给cc变量赋值</span><br></pre></td></tr></table></figure><p>用括号加类型放在变量前面的操作就叫做<strong>造型</strong><br>造型和（int）转换类型不同的！！一个是转换类型，一个是造型<br>造型时，有可能出现<strong>ClassCastException的错误</strong></p><h3 id="5-5-绑定"><a href="#5-5-绑定" class="headerlink" title="5.5 绑定"></a>5.5 绑定</h3><p>当调用一个方法时，究竟应该调用哪个方法，这件事情叫做绑定。<br>绑定有两种：一种是早绑定，又称静态绑定，这种绑定在编译的时候就确定了，根据变量的<strong>声明类型</strong>决定；<br>另一种是晚绑定，即动态绑定。动态绑定在根据变量的<strong>动态类型</strong>决定。<br>Java缺省使用动态绑定。</p><h3 id="5-6-override"><a href="#5-6-override" class="headerlink" title="5.6 override"></a>5.6 override</h3><p><strong>override 覆盖</strong><br>通过父类的对象调用存在覆盖关系的函数时，会调用变量但是所管理的对象的类的函数<br>利用IDE进行override时 在函数上会有<code>@Override</code>字段，<br>如果该覆盖函数与父类的访问属性、返回类型、名称、参数表<strong>有不同，就会报错</strong></p><h2 id="6-抽象类"><a href="#6-抽象类" class="headerlink" title="6 抽象类"></a>6 抽象类</h2><h3 id="6-1-定义"><a href="#6-1-定义" class="headerlink" title="6.1 定义"></a>6.1 定义</h3><p><code>public class abstract shape{     public abstract void draw(Graphcs g); }</code><br>我们没有想过要真正实现一个shape类的对象, 但是要保存一些图形共有的属性。<br>抽象类不能实例化  </p><p>子类中的函数<strong>必须</strong>全都覆盖抽象父类中的抽象函数，这叫做<strong>实现</strong><br>如果没有全部实现，子类也是抽象类</p><h2 id="7-接口"><a href="#7-接口" class="headerlink" title="7. 接口"></a>7. 接口</h2><h3 id="7-1-定义"><a href="#7-1-定义" class="headerlink" title="7.1 定义"></a>7.1 定义</h3><p>声明 <code>public interface 接口名</code><br>接口是纯抽象类<br>所有的成员函数都是抽象函数，所有的成员变量声明都是 <code>public static final</code><br>接口规定了长什么样，但不管里面是什么</p><h3 id="7-2-类实现"><a href="#7-2-类实现" class="headerlink" title="7.2 类实现"></a>7.2 类实现</h3><p>如何<strong>声明</strong><br><code>public class Fox extands Animal implements Cell {}</code><br>定义了一个 Fox类，继承自 Animal类， 实现了 Cell接口</p><h3 id="7-3-接口变量-自己命名"><a href="#7-3-接口变量-自己命名" class="headerlink" title="7.3 接口变量(自己命名)"></a>7.3 接口变量(自己命名)</h3><p><code>Cell c = new Fox();</code><br>一个Cell接口的变量可以赋于任何一个实现了Cell接口的类  </p><h3 id="7-4-使用规则"><a href="#7-4-使用规则" class="headerlink" title="7.4 使用规则"></a>7.4 使用规则</h3><p>（1）接口是一种特殊的类，在使用上一样<br>（2）类可以实现很多接口<br>（3）接口可以继承接口，但不能继承类<br>（4）接口不能实现接口  </p><h2 id="8-内部类"><a href="#8-内部类" class="headerlink" title="8. 内部类"></a>8. 内部类</h2><p>在一个A类的内部定义了一个B类，我就称这个B类为内部类<br>内部类B<strong>可以访问A类的成员函数、成员变量</strong>，<br>如果定义在A的外部，如果B想访问A类的成员，需要将A类实例化（<strong>很麻烦</strong>）</p><p>内部类也有可能<strong>在函数内</strong>(如下文中的匿名类)<br>在函数内不能访问本地变量，只能访问有final修饰的本地变量</p><h2 id="9-匿名类"><a href="#9-匿名类" class="headerlink" title="9. 匿名类"></a>9. 匿名类</h2><p><img src="/blog/posts/Java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1-%E6%85%95%E8%AF%BE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%8C%BF%E5%90%8D%E7%B1%BB.png" alt="匿名类"><br>这里定义的就是一个匿名类  </p><p>在new对象的时候给出的类的定义形成了匿名类，<br>匿名类可以实现某接口，也可以继承自某个父类，  </p><p><em>Swing框架的消息机制广泛运用匿名类</em></p><h2 id="10-final"><a href="#10-final" class="headerlink" title="10.final"></a>10.final</h2><p>inal关键字是我们经常使用的关键字之一，它的用法有很多，但是并不是每一种用法都值得我们去广泛使用。它的主要用法有以下四种：  </p><pre><code>1.用来修饰数据，包括成员变量和局部变量，该变量只能被赋值一次且它的值无法被改变。对于成员变量来讲，我们必须在声明时或者构造方法中对它赋值； 2.用来修饰方法参数，表示在变量的生存期中它的值不能被改变；3.修饰方法，表示该方法无法被重写；4.修饰类，表示该类无法被继承。</code></pre><p>上面的四种方法中，第三种和第四种方法需要谨慎使用，因为在大多数情况下，如果是仅仅为了一点设计上的考虑，我们并不需要使用final来修饰方法和类。</p><h1 id="Part2"><a href="#Part2" class="headerlink" title="Part2"></a>Part2</h1><h2 id="1-一些设计原则"><a href="#1-一些设计原则" class="headerlink" title="1.一些设计原则"></a>1.一些设计原则</h2><h3 id="1-1-消除代码复制"><a href="#1-1-消除代码复制" class="headerlink" title="1.1. 消除代码复制"></a>1.1. 消除代码复制</h3><h3 id="1-2-封装"><a href="#1-2-封装" class="headerlink" title="1.2. 封装"></a>1.2. 封装</h3><p>用封装来降低耦合</p><h3 id="1-3-可扩展性"><a href="#1-3-可扩展性" class="headerlink" title="1.3. 可扩展性"></a>1.3. 可扩展性</h3><p>用 框架+数据 提高扩展性<br>方法：i）命令的解析是否可以脱离if-else<br>     ii）定义一个Handler类来处理命令<br>     iii）用Hash表来保存Handler和命令的关系</p><h3 id="1-4-接口的引入"><a href="#1-4-接口的引入" class="headerlink" title="1.4. 接口的引入"></a>1.4. 接口的引入</h3><p>设计程序时，先定义接口，再实现类<br>任何需要在函数间传入传出的一定是接口而不是具体的类</p><h2 id="2-设计思想"><a href="#2-设计思想" class="headerlink" title="2.设计思想"></a>2.设计思想</h2><h3 id="2-1-控制反转"><a href="#2-1-控制反转" class="headerlink" title="2.1. 控制反转"></a>2.1. 控制反转</h3><p><code>[图丢失]</code><br>Jbutton类想要实现按下去，调用step()的操作，但是我们不能在Jbutton类中再定义，是原本的框架。<br>但是Jbutton类实现了ActionListener接口，并且有addActionListener函数。<br>ActionListener接口中只有一个actionPerfomed函数， btnStep实例执行addActionListener函数后，就将actionPerfomed函数注册给了btnStep<br>当btnStep被按下去的时候，就会执行外部override的actionPerfomed函数。  </p><h3 id="2-2-MVC"><a href="#2-2-MVC" class="headerlink" title="2.2. MVC"></a>2.2. MVC</h3><p>View-Model-Control<br>View 和 Control不直接有关系，通过Model(模型、数据)来连接</p><h2 id="3-加载类"><a href="#3-加载类" class="headerlink" title="3.加载类"></a>3.加载类</h2><p><a href="https://www.cnblogs.com/doit8791/p/5820037.html">https://www.cnblogs.com/doit8791/p/5820037.html</a></p><p>ps：</p><ul><li>在18年学习的，但是未发布在个人博客，2022年整理并同步到个人博客</li></ul>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/Java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1-%E6%85%95%E8%AF%BE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <link href="https://ig505gi.github.io/blog/posts/Java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1-%E6%85%95%E8%AF%BE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <published>2018-12-25T20:28:23.000Z</published>
    <summary>慕课网 Java 面向对象课程笔记，学习用对象思考、用类来组织代码的编程思想。</summary>
    <title>Java面向对象-慕课学习笔记</title>
    <updated>2026-05-29T11:55:00.601Z</updated>
  </entry>
  <entry>
    <author>
      <name>Ian Gao</name>
    </author>
    <category term="学习笔记" scheme="https://ig505gi.github.io/blog/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="Mooc" scheme="https://ig505gi.github.io/blog/tags/Mooc/"/>
    <category term="Java" scheme="https://ig505gi.github.io/blog/tags/Java/"/>
    <category term="Backend" scheme="https://ig505gi.github.io/blog/tags/Backend/"/>
    <content>
      <![CDATA[<p>简介：慕课网课笔记，从零学习Java语法<br>课程地址：<a href="https://www.icourse163.org/course/ZJU-1001541001?tid=1003042007">https://www.icourse163.org/course/ZJU-1001541001?tid=1003042007</a><br>作业仓库：<a href="https://github.com/ig505gi/JAVA-Course">https://github.com/ig505gi/JAVA-Course</a></p><h2 id="1-IDE使用技巧"><a href="#1-IDE使用技巧" class="headerlink" title="1.IDE使用技巧"></a>1.IDE使用技巧</h2><p>Alt+&#x2F; 补全</p><p>control+&#x2F; 注释某一行 （mac下 是 command）</p><span id="more"></span><p>shift+↑ 选中光标移动行</p><p>eclipse右上角可以切换java模式和debug模式</p><p>debug时，step over是跳过函数，step into是进入函数</p><h2 id="2-输出和输入"><a href="#2-输出和输入" class="headerlink" title="2.输出和输入"></a>2.输出和输入</h2><h3 id="2-1-string与数字类型转化demo"><a href="#2-1-string与数字类型转化demo" class="headerlink" title="2.1 string与数字类型转化demo"></a>2.1 string与数字类型转化demo</h3><p><code>system.out.println(2+3+“=2+3=”+2+3)；</code></p><p>控制台输出：<br><code>5=2+3=23</code></p><h3 id="2-2-格式化输出"><a href="#2-2-格式化输出" class="headerlink" title="2.2 格式化输出"></a>2.2 格式化输出</h3><p>println输出回车</p><p>print不输出回车</p><p>输出两位有效数字<br><code>System.out.printf(&quot;%.2f&quot;, 7.128741)</code></p><h3 id="2-2-输入"><a href="#2-2-输入" class="headerlink" title="2.2 输入"></a>2.2 输入</h3><p><code>Scanner in = new Scanner(System.in);</code></p><p><code>in.next()</code> –&gt;读入一个单词，结束的标志位空格(包括tab和换行)</p><p><code>in.nextLine()</code> –&gt;读入一整行</p><p><code>in.nextInt()</code> –&gt; 读入一个整型</p><h2 id="3-定义常量"><a href="#3-定义常量" class="headerlink" title="3.定义常量"></a>3.定义常量</h2><p>final</p><h2 id="4-结合关系及优先级"><a href="#4-结合关系及优先级" class="headerlink" title="4.结合关系及优先级"></a>4.结合关系及优先级</h2><p>一般从左向右，而赋值运算从右向左</p><p>判断优先级很低，最后做</p><h2 id="5-强制类型转换"><a href="#5-强制类型转换" class="headerlink" title="5.强制类型转换"></a>5.强制类型转换</h2><p><code>System.out.println((int)(170.239));</code></p><h2 id="6-最大的数"><a href="#6-最大的数" class="headerlink" title="6.最大的数"></a>6.最大的数</h2><p>20！不能算，最大到2e31-1</p><h2 id="7-循环"><a href="#7-循环" class="headerlink" title="7.循环"></a>7.循环</h2><h3 id="7-1-for"><a href="#7-1-for" class="headerlink" title="7.1 for"></a>7.1 for</h3><p>for循环的（；；）每个分号之间都可以空着，也可以用逗号链接多个句子，是唯一一处可以用逗号链接构成长句子的地方</p><h3 id="7-2-break-continue"><a href="#7-2-break-continue" class="headerlink" title="7.2 break&#x2F;continue"></a>7.2 break&#x2F;continue</h3><p>要离开多重循环的时候，可以在需要的for前加上一个限定词 如OUT</p><p>然后在多重循环里面 break OUT；即可结束多重循环</p><p>continue 同理也可以这样使用</p><h3 id="7-3-for-each循环"><a href="#7-3-for-each循环" class="headerlink" title="7.3 for-each循环"></a>7.3 for-each循环</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">int[] nums = new int[2]</span><br><span class="line">for(int item : nums)&#123; item = 1;&#125;</span><br></pre></td></tr></table></figure><p>理解：1.for-each循环的格式与C#不同，要记住。 2。取出item赋值不会改变原数组的值，只有nums[i]调用赋值才能改变</p><h2 id="8-数组"><a href="#8-数组" class="headerlink" title="8. 数组"></a>8. 数组</h2><h3 id="8-1-数组索引、长度"><a href="#8-1-数组索引、长度" class="headerlink" title="8.1 数组索引、长度"></a>8.1 数组索引、长度</h3><p>编译器不检查数组的索引是否有效，只有运行的时候才会出错超出索引范围</p><p>获取num[10]数组的长度：<code>num.length</code></p><h3 id="8-2-数组变量、数组复制"><a href="#8-2-数组变量、数组复制" class="headerlink" title="8.2 数组变量、数组复制"></a>8.2 数组变量、数组复制</h3><p><code>int[] b = a</code> 该行代码使得b指向了a所指向的数组，改变a[i]的同时，b[i]也会变，反之也会变</p><p>b称之为数组变量，如果比较数组变量，比较的是两个数组变量是否管理一个数组</p><h2 id="9-字符串"><a href="#9-字符串" class="headerlink" title="9.字符串"></a>9.字符串</h2><h3 id="9-1-char"><a href="#9-1-char" class="headerlink" title="9.1 char"></a>9.1 char</h3><p>char类型是单个字符</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">char ch = &#x27;a&#x27;;</span><br><span class="line">char c = &#x27;字&#x27;;</span><br></pre></td></tr></table></figure><p>java用的Unicode编码，汉字也是一个字符</p><p><code>ch++</code> –&gt; <code>b</code></p><p>char类型加1得到ASCII码表后面的一个字符</p><p><code>char ch=&#39;\u0041&#39;</code> &#x3D;&#x3D; <code>char ch=65</code> &#x3D;&#x3D; <code>char ch=&#39;A&#39;</code></p><p>代表16进制的ASCII码值，即65 –&gt;A</p><p>字符可以做加减法，得到的值是int</p><p>字符可以比较大小，比较的是ASCII值</p><h3 id="9-2-逃逸字符"><a href="#9-2-逃逸字符" class="headerlink" title="9.2 逃逸字符"></a>9.2 逃逸字符</h3><p><code>\b</code> 回退一格，在Eclipse里面不会执行，需要到console才行。并且只是光标回退一格，接下来的输出从这里开始，不会删除只会被覆盖</p><p><code>\t</code> 到下一个表格位</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(&quot;123\tabc&quot;)</span><br><span class="line">System.out.println(&quot;12\tabc&quot;)</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">123     abc</span><br><span class="line">12      abc</span><br></pre></td></tr></table></figure><p><code>\r</code> 回车 <code>\n</code> 换行，来源老式打印机，现在使用可能差不多？</p><h2 id="10-包裹类型"><a href="#10-包裹类型" class="headerlink" title="10.包裹类型"></a>10.包裹类型</h2><p>int –&gt; Integer, char –&gt; Character</p><p>包裹类型的意义：可以用点运算符，eg：</p><p><code>Integer.Max_VALUE</code> 得到int型的最大范围</p><p><code>Character.toLowerCase(&#39;A&#39;)</code> –&gt; a, 可以使用许多方法</p><h2 id="11-String"><a href="#11-String" class="headerlink" title="11.String"></a>11.String</h2><h3 id="11-1-定义"><a href="#11-1-定义" class="headerlink" title="11.1 定义"></a>11.1 定义</h3><p>注意：String 的S是大写，和其他类型不一样，而是和包裹类型一样</p><p><code>String s = new String(&quot;Hello world&quot;)</code></p><h3 id="11-2-比较"><a href="#11-2-比较" class="headerlink" title="11.2 比较"></a>11.2 比较</h3><p>String的定义和数组一样需要用new，也就说明了和数组一样定义的是管理者，还不是所有者</p><p>因此，<code>s == &quot;Hello world&quot;</code>返回的是false</p><p>需要用 <code>s.equals(&quot;Hello world&quot;)</code> 返回的是true</p><h3 id="11-3-索引"><a href="#11-3-索引" class="headerlink" title="11.3 索引"></a>11.3 索引</h3><p>用<code>s.length()</code> 可以获取长度</p><p>遍历的时候跟数组一样 <code>s[index]</code>  但是不能用for-each循环</p><h3 id="11-4-常用操作"><a href="#11-4-常用操作" class="headerlink" title="11.4 常用操作"></a>11.4 常用操作</h3><p><code>s.compareTo(&quot;Hello worlf&quot;)</code> –&gt; 返回 <code>-2</code> 一个字符一个字符比较，返回比它小多少</p><p><code>s.indexOf(&#39;e&#39;)</code> –&gt; 返回 <code>1</code></p><p><code>s.indexOf(&quot;ell&quot;)</code> –&gt; 返回 <code>1</code> 开始的index</p><p><code>s.indexOf(&#39;a&#39;)</code> –&gt; 返回 <code>-1</code></p><p><code>s.trim()</code> 去掉两边的空格</p><p><code>s.startsWith(t)</code> 是否以t开始</p><p><code>s.replace(c1,c2)</code> 用c2代替c1</p><p><code>s.toLowerCase()</code> 变成小写</p><h3 id="11-5-StringBuffer"><a href="#11-5-StringBuffer" class="headerlink" title="11.5 StringBuffer"></a>11.5 StringBuffer</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">String re = &quot;&quot;</span><br><span class="line">re += &quot;first&quot;</span><br><span class="line">re += &quot;second&quot;</span><br></pre></td></tr></table></figure><p>这样的代码对系统开销很大,因为String是一种不可以修改的对象，每一次“+&#x3D;”操作都会产生一个String对象<br><em><strong>优化</strong></em></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">StringBuffer sb = new StringBuffer()；</span><br><span class="line">sb.append(&quot;first&quot;);</span><br><span class="line">sb.append(&quot;second&quot;);</span><br><span class="line">String re = sb.toString();</span><br></pre></td></tr></table></figure><p>ps：</p><ul><li>主要记录基本语法和之前学过的语言不太一样的知识点，或者容易忘记的知识点</li><li>网课老师为浙大翁恺教授，绝对男神！！不仅会教Java，还会教一些很Geek的tips，很实用！</li><li>在18年学习的，但是未发布在个人博客，2022年整理并同步到个人博客</li></ul>]]>
    </content>
    <id>https://ig505gi.github.io/blog/posts/%E9%9B%B6%E5%9F%BA%E7%A1%80Java-%E6%85%95%E8%AF%BE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <link href="https://ig505gi.github.io/blog/posts/%E9%9B%B6%E5%9F%BA%E7%A1%80Java-%E6%85%95%E8%AF%BE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <published>2018-12-24T14:53:23.000Z</published>
    <summary>慕课网零基础 Java 入门课程笔记，从零开始学习 Java 语法和面向对象基础。</summary>
    <title>零基础Java-慕课学习笔记</title>
    <updated>2026-05-29T11:55:00.726Z</updated>
  </entry>
</feed>
