<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾javascript推荐</title>
    <link>https://itindex.net/categories/javascript</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/categories/javascript</link>
    </image>
    <item>
      <title>8行javascript录制屏幕</title>
      <link>https://itindex.net/detail/62493-javascript-%E5%B1%8F%E5%B9%95</link>
      <description>&lt;p&gt;看到个骚操作，玩了一下，写了个 bookmarklet&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;
navigator.mediaDevices.getDisplayMedia({video:true}).then(function(stream){
  const mr=new MediaRecorder(stream,{mimeType:&amp;quot;video/webm;codecs=h264,opus&amp;quot;});
  mr.ondataavailable=function(ev){
    const a=document.createElement(&amp;apos;a&amp;apos;);a.href=URL.createObjectURL(ev.data);
    a.download =&amp;apos;screen_capture.webm&amp;apos;;a.click();
  }; // will be called when .stop() with ev.data as a Blob
  mr.start();
})
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这段代码也是很多WebRTC共享白板的实现原理。看起来也很简单，而且不需要额外的权限确认。&lt;/p&gt;
 &lt;p&gt;如果不需要另存为一个名字，则可以直接   &lt;code&gt;location.assign()&lt;/code&gt; 又节约几个字。不知道还有没有办法继续节约代码量？&lt;/p&gt;
 &lt;p&gt;只是很好奇为啥chrome不支持 .mp4 封装。偏袒自家的 webm ？&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>stdout</category>
      <guid isPermaLink="true">https://itindex.net/detail/62493-javascript-%E5%B1%8F%E5%B9%95</guid>
      <pubDate>Fri, 18 Nov 2022 18:50:05 CST</pubDate>
    </item>
    <item>
      <title>Meta开源JavaScript内存泄漏监测工具MemLab</title>
      <link>https://itindex.net/detail/62431-meta-%E5%BC%80%E6%BA%90-javascript</link>
      <description>&lt;h1&gt;一、MemLab简介&lt;/h1&gt;
 &lt;p&gt;上周，Facebook母公司Meta 宣布了开源 MemLab，一个基于 Chromium 的浏览器的 JavaScript 应用程序内存泄漏监测工具。同时，Facebook 技术团队指出：“应用程序的性能和功能正确性问题通常会被用户立即留意到。然而内存泄漏却不一样，它不容易被立即察觉，但它每次都会吃掉一大块内存，使得整个网络会话的响应变得非常慢。”&lt;/p&gt;
 &lt;p&gt;为了帮助开发人员解决这个问题，Meta 构建了MemLab，它可以自动进行内存泄漏检测并更容易找到泄漏的根本原因。据官方公告称，Meta 内部使用它成功地控制了不可持续的内存增长，并识别了产品和基础设施中的内存泄漏和内存优化机会。目前，Meta 已经在 GitHub 上开源了 MemLab。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/313eb4b7b54646729f524cba974bba76~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Facebook在 2020 年被重新设计为单页应用程序 (SPA)，该应用程序的大部分渲染和导航使用客户端 JavaScript。而 Meta 的大多数其他流行网络应用程序都使用了类似的架构来构建，包括 Instagram 和 Workplace。&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;虽然这种架构使其能够提供更快的用户交互、更好的开发人员体验和更像应用程序的感觉，但在客户端维护 Web 应用程序状态会使有效管理客户端内存变得更加复杂。且内存泄漏的后果在单页应用程序（SPA）中更为严重，因为用户可能会在较长时间内持续与页面交互，而 MemLab 就是专为这种场景设计的。&lt;/p&gt;
 &lt;p&gt;在许多情况下，JavaScript 可能会泄漏内存。比如，Facebook 工程师 Liang Gong 和 Glenn Conner 就在公告中谈到，当你向 Chrome 控制台发送一个对象时，Chrome 会对其进行隐藏引用，以防止它被收集。另外，auth0 工程师 Sebastian Peyrott 也曾谈到，其他可能出现泄漏或未绑定内存增长的情况则与意外使用全局变量、忘记计时器或回调以及 DOM 外引用有关。&lt;/p&gt;
 &lt;p&gt;虽然 Chrome 开发者工具提供了检查 JavaScript 代码的内存行为的基本手段，比如时间线视图和配置文件视图，但这并不直接，也不能自动化。相反，MemLab 则可以很容易地集成到 CI/CD 管道中，Gong 和 Conner 介绍道。&lt;/p&gt;
 &lt;h1&gt;二、工作原理&lt;/h1&gt;
 &lt;p&gt;MemLab 的工作原理是通过预定义的测试场景运行 headless 浏览器并对 JavaScript heap snapshots 进行差异分析来发现内存泄漏。要达到这一目的，需要经过如下几步：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;导航到页面并返回；&lt;/li&gt;
  &lt;li&gt;查找未释放的对象；&lt;/li&gt;
  &lt;li&gt;显示泄露追踪结果。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;据悉，MemLab 使用了一个名为“Puppeteer”的 Node.js 库。它可以控制 Google Chrome 或其它基于 Chromium 内核打造的浏览器，且默认情况下以 headless 模式运行（方便命令行交互）。&lt;/p&gt;
 &lt;p&gt;Facebook 工程师解释称，MemLab 的工作方式就是导航到一个页面、然后离开。正常情况下，可预计该页面分配的大部分内存也将被释放。但若没有被释放，则意味其存在极高的内存泄露可能性。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac9e8c69dfaa46159bbd708057629eeb~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;
 
我们知道，React 使用存储在树结构中、被称作 Fibers 的对象，来表示内存中的浏览器文档对象模型（DOM）。据该团队所述，这可能是存在“巨大内存泄露”的一个主要原因。拥有强连接图的缺点很是显著，若有任何外部引用指向图的任何部分，就无法对整个图开展垃圾回收。&lt;/p&gt;
 &lt;p&gt;对于浏览器内存泄漏检测，MemLab 需要开发人员提供的唯一输入是一个测试场景文件，该文件定义了如何通过 overriding Puppeteer API 和 CSS 选择器的三个回调来与网页进行交互。MemLab 会自动对 JavaScript heap 进行差异化处理，完善内存泄漏，并对结果进行汇总。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="MemLab-figure-2-FINAL.gif" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/23c3fb7b966f4962a8b714443a1009a4~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;MemLab 的另一特性，就是提供了 JavaScript 堆的图形视图、启用了用于检查堆快照的 API 。这意味着开发者能够编写开展内存断言的测试，例如声明某个对象将不再存在于内存中。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d36a6ba054a54b2d89063576a4fe1a87~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;此外还有一个用于查找重复字符串实例的工具，在某个案例中，团队发现字符串占用了 70% 的堆、且其中半数至少有一个重复的实例。包括 Chrome、Edge、Firefox 在内的浏览器，都有附带内存检查工具。但正如以为开发者在 Hacker News 上吐槽的那样，这些开发工具难以在调试过程中揪出内存泄露的问题。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2964251ed4bf4e80ac212fd7e8f41a06~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;
最后，MemLab 的另一项强大功能，就是可以在测试期间作为命令过程的一部分而运行。这意味着如果代码中引入了严重的泄露，开发者们也能够在投入生产环境前加以捕获。&lt;/p&gt;
 &lt;p&gt;除了内存泄漏检测之外，MemLab还包括一组用于查找内存优化机会的内置CLI命令和api，并提供如下的功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;堆内容分解&lt;/li&gt;
  &lt;li&gt;监测单个对象的内存使用情况&lt;/li&gt;
  &lt;li&gt;查找重复的字符串实例&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;比如，监测浏览内存泄漏部分UI。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4384bd59c304abb96b1fcd27f92c7e3~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;跟踪UI内存泄漏的整个链路。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df39db4945bf47109ddffc4c6e03e8f2~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h1&gt;三、基本使用&lt;/h1&gt;
 &lt;h2&gt;3.1 安装与使用&lt;/h2&gt;
 &lt;p&gt;首先，需要全局安装MemLab插件，安装的命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;npm install -g memlab
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;例如下面是找到谷歌Maps中的内存泄漏的例子，我妈可以创建一个场景文件来定义如何与谷歌Maps进行交互，比如将其命名为test-google-maps.js。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function url() {
  return &amp;apos;https://www.google.com/maps/@37.386427,-122.0428214,11z&amp;apos;;
}


async function action(page) {
  await page.click(&amp;apos;button[aria-label=&amp;quot;Hotels&amp;quot;]&amp;apos;);
}


async function back(page) {
  await page.click(&amp;apos;[aria-label=&amp;quot;Clear search&amp;quot;]&amp;apos;);
}


module.exports = {action, back, url};
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;现在使用下面的命令运行上面的js代码, 当memlab与web页面进行交互时就会运行内置的泄漏检测器检测内存泄漏。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab run --scenario test-google-maps.js
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;执行结束之后，Memlab就会打印内存泄漏结果，显示每个泄漏对象集群的一个代表性保留跟踪。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;MemLab found 46 leak(s)
--Similar leaks in this run: 4--
--Retained size of leaked objects: 8.3MB--
[Window] (native) @35847 [8.3MB]
  --20 (element)---&amp;gt;  [InternalNode] (native) @130981728 [8.3MB]
  --8 (element)---&amp;gt;  [InternalNode] (native) @130980288 [8.3MB]
  --1 (element)---&amp;gt;  [EventListener] (native) @131009888 [8.3MB]
  --1 (element)---&amp;gt;  [V8EventListener] (native) @224808192 [8.3MB]
  --1 (element)---&amp;gt;  [eventHandler] (closure) @168079 [8.3MB]
  --context (internal)---&amp;gt;  [&amp;lt;function scope&amp;gt;] (object) @181905 [8.3MB]
  --bigArray (variable)---&amp;gt;  [Array] (object) @182925 [8.3MB]
  --elements (internal)---&amp;gt;  [(object elements)] (array) @182929 [8.3MB]
...
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;接着，我们就可以通过这些捕获的跟踪信息定位到里面的方法。&lt;/p&gt;
 &lt;p&gt;当然，我没也可以使用Memlab查看基于从Chromium、Hermes、memlab或任何node.js或electronic .js程序中获取的单个JavaScript堆快照检测到的内存问题。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab view-heap --snapshot &amp;lt;PATH TO .heapsnapshot FILE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;然后，我没可以使用对象的id，比如node-id @28173来精确定位特定的堆对象。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e795fdc3752e4984b70dc9cd7e0bbd67~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;当然，Memlab也支持自定义的检漏器，自定义检漏器时需要在场景文件中添加一个  &lt;a href="https://facebookincubator.github.io/memlab/docs/api/interfaces/core_src.IScenario/#-optional-beforeleakfilter-initleakfiltercallback"&gt;filterLeak文档&lt;/a&gt;。对于目标交互分配的每个未释放的堆对象(节点)将调用filterLeak。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function filterLeak(node, heap) {
  // ... your leak detector logic
  // return true to mark the node as a memory leak
};
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;heap是最终JavaScript堆快照的图形表示。&lt;/p&gt;
 &lt;h2&gt;3.2 堆分析与研究&lt;/h2&gt;
 &lt;p&gt;除了检测内存泄露意外，Memlab还提供了很多其他有用的命令，比如查看某个对象在运行的交互过程中的整个链路。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab analyze unbound-object
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;获取V8/hermes .heapsnapshot文件。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab analyze unbound-object --snapshot-dir &amp;lt;DIR_OF_SNAPSHOT_FILES&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;使用memlab analyze查看所有内置内存分析。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab trace --node-id &amp;lt;HEAP_OBJECT_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;3.3 Memlab API&lt;/h2&gt;
 &lt;p&gt;Memlab的npm包支持在浏览器中启动端到端运行并检测内存泄漏。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;const memlab = require(&amp;apos;memlab&amp;apos;);


const scenario = {
    url: () =&amp;gt; &amp;apos;https://www.google.com/maps/@37.386427,-122.0428214,11z&amp;apos;,
    action: async (page) =&amp;gt; await page.click(&amp;apos;button[aria-label=&amp;quot;Hotels&amp;quot;]&amp;apos;),
    back: async (page) =&amp;gt; await page.click(&amp;apos;[aria-label=&amp;quot;Clear search&amp;quot;]&amp;apos;),
}
memlab.run({scenario});
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;3.4 内存断言&lt;/h2&gt;
 &lt;p&gt;Memlab支持在Node.js程序中进行Jest测试，也可以使用图视图API来获得其自身状态的堆图视图，执行自内存检查，并编写各种内存断言。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;import type {IHeapSnapshot} from &amp;apos;@memlab/core&amp;apos;;
import {config, takeNodeMinimalHeap, tagObject} from &amp;apos;@memlab/core&amp;apos;;
test(&amp;apos;memory test&amp;apos;, async () =&amp;gt; {
  config.muteConsole = true;
  const o1 = {};
  let o2 = {};
  tagObject(o1, &amp;apos;memlab-mark-1&amp;apos;);
  tagObject(o2, &amp;apos;memlab-mark-2&amp;apos;);
  o2 = null;
  const heap: IHeapSnapshot = await takeNodeMinimalHeap();
   //断言函数
  expect(heap.hasObjectWithTag(&amp;apos;memlab-mark-1&amp;apos;)).toBe(true);
  //断言函数
  expect(heap.hasObjectWithTag(&amp;apos;memlab-mark-2&amp;apos;)).toBe(false);
}, 30000);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;附件：  &lt;a href="https://github.com/facebookincubator/memlab"&gt;https://github.com/facebookincubator/memlab&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62431-meta-%E5%BC%80%E6%BA%90-javascript</guid>
      <pubDate>Thu, 22 Sep 2022 11:26:54 CST</pubDate>
    </item>
    <item>
      <title>面向微前端，谈谈 JavaScript 隔离沙箱机制的古往今来</title>
      <link>https://itindex.net/detail/62300-%E5%89%8D%E7%AB%AF-javascript-%E9%9A%94%E7%A6%BB</link>
      <description>&lt;h2&gt;前言&lt;/h2&gt;

 &lt;p&gt;随着微前端的不断发展、被更多的团队采用，越来越多开始对沙箱这个概念有所了解。  &lt;strong&gt;沙箱，即 sandbox，意指一个允许你独立运行程序的虚拟环境，沙箱可以隔离当前执行的环境作用域和外部的其他作用域，外界无法修改该环境内任何信息，沙箱内的东西单独运行，环境间相互不受影响。&lt;/strong&gt;本文计划谈谈微前端的 JavaScript 隔离，即沙箱机制的古往今来。&lt;/p&gt;

 &lt;p&gt;要实现一个 JavaScript 沙箱，可以有很多种分类方式，比如按照具体的实现方式来区分，就至少包含如下：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;基于 Proxy 快照存储 + window 修改的实现&lt;/li&gt;
    &lt;li&gt;基于 Proxy 代理拦截 + window 激活/卸载的实现&lt;/li&gt;
    &lt;li&gt;基于普通对象快照存储的 window 属性 diff 实现&lt;/li&gt;
    &lt;li&gt;基于 iframe + 消息通信的实现&lt;/li&gt;
    &lt;li&gt;基于 ShadowRealm 提案的实现&lt;/li&gt;
    &lt;li&gt;基于 with + eval 的简单实现&lt;/li&gt;
    &lt;li&gt;……&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;  &lt;img alt="" src="https://hijiangtao.github.io/assets/in-post/2022-06-11-JavaScript-Sandbox-Mechanism-and-Its-History-1.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;p&gt;本文主要考虑沙箱机制在实现时所用到的主要 Web 技术，计划大致分为四类实现方案分别介绍，结合之下，本文目录如下：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;前言&lt;/li&gt;
    &lt;li&gt;基于 Proxy 实现的沙箱机制
       &lt;ol&gt;
          &lt;li&gt;简要谈谈 Proxy API&lt;/li&gt;
          &lt;li&gt;基于 Proxy 的沙箱实现考虑&lt;/li&gt;
          &lt;li&gt;结合微前端框架 qiankun 介绍两类沙箱实现&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
    &lt;li&gt;基于属性 diff 实现的沙箱机制&lt;/li&gt;
    &lt;li&gt;基于 iframe 实现的沙箱机制
       &lt;ol&gt;
          &lt;li&gt;基于 Proxy 及 diff 的沙箱机制边界考虑&lt;/li&gt;
          &lt;li&gt;利用 iframe 实现沙箱机制的几点思考&lt;/li&gt;
          &lt;li&gt;一段 iframe 沙箱的示例代码&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
    &lt;li&gt;各类沙箱机制对比&lt;/li&gt;
    &lt;li&gt;基于 ES 提案 ShadowRealm API 介绍
       &lt;ol&gt;
          &lt;li&gt;什么是 JavaScript 的运行环境实例&lt;/li&gt;
          &lt;li&gt;ShadowRealm API 简介&lt;/li&gt;
          &lt;li&gt;ShadowRealm 的错误捕获与更多应用场景&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
    &lt;li&gt;总结&lt;/li&gt;
    &lt;li&gt;参考&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;希望通过我自己的项目实践、阅读代码、提案梳理等方式对 JavaScript 隔离（沙箱机制）进行系统整理，其中会结合一些开源框架的实现来辅助解读，但不会针对微前端框架深入介绍，也不会就某一个沙箱机制的具体细节实现（比如如何构建闭包环境、属性读取、DOM 操作等众多边界处理）进行剖析。&lt;/p&gt;

 &lt;p&gt;如果你想了解关于 CSS 样式隔离的内容可以搜索 Shadow DOM 相关内容进一步查阅；如果你想了解微前端的主子应用加载、运行机制，可以参考 single-spa 文档、qiankun 文档、ShadowRealm 提案等内容；如果你想了解文中涉及的一些概念与 API 用法可以在 MDN 进行搜索查阅，大部分均有对应介绍。&lt;/p&gt;

 &lt;p&gt;本文在撰写中尽力保证文章的思路流畅和通俗易懂，但由于个人正从事基于微前端方案的开发，可能有些概念会潜意识认为所有读者均已了解，未能详尽每个涉及名词的统一处理或解释，此处针对一些通用的概念进行铺垫：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;   &lt;strong&gt;主应用&lt;/strong&gt;：在微前端方案中，区分主子应用，主应用通常负责全局资源的加载、隔离、控制运行，用户登陆信息等全局状态的管理等等，也被称为基座、微前端全局环境等；&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;子应用&lt;/strong&gt;：微前端方案中可以独立加载运行的一个 Web 应用，通常需要一个完备的隔离环境供其加载，文中提到的沙箱激活/卸载也是为其服务，也称微应用；&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;沙箱&lt;/strong&gt;：意指一个允许你独立运行程序的虚拟环境，沙箱可以隔离当前执行的环境作用域和外部的其他作用域，外界无法修改该环境内任何信息，沙箱内的东西单独运行，环境间相互不受影响，英文对应 sandbox，此名词常与 JavaScript 隔离一起使用；&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;qiankun&lt;/strong&gt;：一款开源方案，基于    &lt;a href="https://github.com/CanopyTax/single-spa"&gt;single-spa&lt;/a&gt; 的   &lt;a href="https://micro-frontends.org/"&gt;微前端&lt;/a&gt;实现库；&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;以下开始正文。&lt;/p&gt;

 &lt;h2&gt;基于 Proxy 的沙箱机制&lt;/h2&gt;

 &lt;p&gt;Proxy 是当下做 JavaScript 隔离用到的最主要的手段之一，接下来我们详细说说基于 Proxy 的沙箱机制。&lt;/p&gt;

 &lt;h3&gt;简要谈谈 Proxy API&lt;/h3&gt;

 &lt;p&gt;  &lt;strong&gt;Proxy 是一个标准 Web API，在 ES6 版本中被推出，这个对象可以用于创建一个对象的代理，从而实现基本操作的拦截和自定义（如属性查找、赋值、枚举、函数调用等）&lt;/strong&gt;，我们可以通过一个简单的例子来解释说明 Proxy 的作用：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;const handler = {
    get: function(obj, prop) {
        return prop in obj ? obj[prop] : 37;
    }
};

const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

console.log(p.a, p.b);      // 1, undefined
console.log(&amp;apos;c&amp;apos; in p, p.c); // false, 37
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;在上例中，我们定义了一个 handler，其中包含一个 get 拦截器，它的作用是在属性查找时，如果对象对应属性不存在时返回数值 37，此后我们通过 Proxy 对一个空对象进行了代理，分别打印了其中的 a、b、c 属性，可以发现，其中 c 属性由于不存在而返回了 37。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="https://hijiangtao.github.io/assets/in-post/2022-06-11-JavaScript-Sandbox-Mechanism-and-Its-History-3.jpeg"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h3&gt;基于 Proxy 的沙箱实现考虑&lt;/h3&gt;

 &lt;p&gt;既然 Proxy 可以用于代理对象，那么我们同样可以用其代理 window——Web 应用运行中最重要的上下文环境。每个 Web 应用都会与 window 交互，无数的 API 也同样挂靠在 window 上，要达到允许独立运行的微前端环境，首先需要 window 隔开。&lt;/p&gt;

 &lt;p&gt;在采用 Proxy 作为沙箱机制方案时，主要还是基于 get、set、has、getOwnPropertyDescriptor 等关键拦截器对 window 进行代理拦截（如下如有涉及代码，我们主要关注 get 与 set 两类拦截器）。为了让沙箱的代理拦截完备，除了 window 外，我们通常都需要关注几方面，比如一些难以代理（或者说没必要代理）的 Web API，如 Array、Number、Promise 等，此外还需要保证通过 with、eval、new Function 等方式执行的代码作用域不会逃逸，动态加载的 JavaScript 代码也算一个。&lt;/p&gt;

 &lt;p&gt;谈到这里，我们首先看看通过 Proxy 进行属性查找时的一些处理逻辑。除了在拦截器中进行一些常规的无需拦截 case 判断外，还需要对 Symbol.unscopables 属性 get 拦截器的返回值做些定义，以方便 with 等方式下代码的执行作用域正常处理，如下是个简单的例子：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;const unscopables = {
  Array: true,
  Object: true,
  String: true,
  Promise: true,
  requestAnimationFrame: true,
  ...
};

// ...

{
  get: (target: FakeWindow, p: PropertyKey): any =&amp;gt; {
    // Symbol.unscopables 属性
    if (p === Symbol.unscopables) return unscopables;

    // 无需拦截的 Web API
    if (p === &amp;apos;eval&amp;apos;) {
      return eval;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;如上代码中，关于 eval 的拦截判断很好理解，这里我们停下简要介绍一下一个概念：   &lt;code&gt;Symbol.unscopables&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;  &lt;code&gt;Symbol.unscopables&lt;/code&gt; 属性，指用于指定对象值，其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。当我们在   &lt;code&gt;unscopables&lt;/code&gt; 对象上将属性设置为 true，将使其   &lt;em&gt;unscopable&lt;/em&gt; 并且因此该属性也将不会在词法环境变量中出现。我们来看一个简单例子，以了解其效果：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;const object1 = {
  property1: 42
};

object1[Symbol.unscopables] = {
  property1: true
};

with (object1) {
  console.log(property1);
  // expected output: Error: property1 is not defined
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;  &lt;em&gt;注：在微前端环境下，通常需要对一些全局变量与属性进行更全面的梳理，此处可以参考 qiankun 的实现    &lt;a href="https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/proxySandbox.ts#L255"&gt;https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/proxySandbox.ts#L255&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

 &lt;h3&gt;结合微前端框架 qiankun 介绍两类沙箱实现&lt;/h3&gt;

 &lt;p&gt;微前端框架 qiankun 中一共存在三类沙箱，基于 Proxy 实现方式不同以及是否支持多实例，可以分为两类：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;支持子应用单实例沙箱（LegacySandbox）&lt;/li&gt;
    &lt;li&gt;支持子应用多实例沙箱（ProxySandbox）&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;当我们只针对全局运行环境进行代理赋值记录，而不从中取值，那么这样的沙箱只是作为我们记录变化的一种手段，而实际操作仍在主应用运行环境中对 window 进行了读写，因此这类沙箱也只能支持单实例模式，qiankun 在实现上将其命名为 LegacySandbox。&lt;/p&gt;

 &lt;p&gt;我们先假设我们的沙箱实现上包含这几个变量（此处以 qiankun 实现为例）：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;/** 沙箱期间新增的全局变量 */
private addedPropsMapInSandbox = new Map&amp;lt;PropertyKey, any&amp;gt;();

/** 沙箱期间更新的全局变量 */
private modifiedPropsOriginalValueMapInSandbox = new Map&amp;lt;PropertyKey, any&amp;gt;();

/** 持续记录更新的(新增和修改的)全局变量的 map，用于在任意时刻做 snapshot */
private currentUpdatedPropsValueMap = new Map&amp;lt;PropertyKey, any&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;这类沙箱的激活与卸载思路可以通过如下两个函数代码解释。首先是激活函数，当沙箱被激活时，我们通过曾经记录好的更新过的全局变量（也可以称为快照）来还原子应用所需要的沙箱环境（即上下文）：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;active() {
  if (!this.sandboxRunning) {
    this.currentUpdatedPropsValueMap.forEach(
       (v, p) =&amp;gt; this.setWindowProp(p, v)
    );
  }

  this.sandboxRunning = true;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;等到需要卸载时，沙箱需要做两件事，一是将子应用运行时修改过的全局变量还原，另一个是删除子应用运行时新增的全局变量：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;inactive() {
  this.modifiedPropsOriginalValueMapInSandbox.forEach(
    (v, p) =&amp;gt; this.setWindowProp(p, v)
  );
  
  this.addedPropsMapInSandbox.forEach(
    (_, p) =&amp;gt; this.setWindowProp(p, undefined, true)
  );

  this.sandboxRunning = false;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;  &lt;em&gt;注：详尽代码可以参考 qiankun 实现    &lt;a href="https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/legacy/sandbox.ts#L51-L73"&gt;https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/legacy/sandbox.ts#L51-L73&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;如上所述，LegacySandbox 的思路在于虽然建立了沙箱代理，但在子应用运行过程中，所有的赋值仍旧会直接操作 window 对象，代理所做的事情就是记录变化（形成快照）；而针对激活和卸载，沙箱会在激活时还原子应用的状态，而卸载时还原主应用的状态，以此达到沙箱隔离的目的。&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;LegacySandbox 由于会修改 window 对象，在多个实例运行时肯定会存在冲突，因此，该沙箱模式只能在单实例场景下使用，而当我们需要同时起多个实例时，ProxySandbox 便登场了。&lt;/p&gt;

 &lt;p&gt;ProxySandbox 的方案是同时用 Proxy 给子应用运行环境做了 get 与 set 拦截。沙箱在初始构造时建立一个状态池，当应用操作 window 时，赋值通过 set 拦截器将变量写入状态池，而取值也是从状态池中优先寻找对应属性。由于状态池与子应用绑定，那么运行多个子应用，便可以产生多个相互独立的沙箱环境。&lt;/p&gt;

 &lt;p&gt;由于取值赋值均在建立的状态池上操作，因此，在第一种沙箱环境下激活和卸载需要做的工作，这里也就不需要了。关于状态池的设计，可以参考代码   &lt;a href="https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/proxySandbox.ts#L81"&gt;https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/proxySandbox.ts#L81&lt;/a&gt;&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="https://hijiangtao.github.io/assets/in-post/2022-06-11-JavaScript-Sandbox-Mechanism-and-Its-History-4.jpeg"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h2&gt;基于属性 diff 的沙箱机制&lt;/h2&gt;

 &lt;p&gt;由于 Proxy 为 ES6 引入的 API，在不支持 ES6 的环境下，我们可以通过一类原始的方式来实现所要的沙箱，即利用普通对象针对 window 属性值构建快照，用于环境的存储与恢复，并在应用卸载时对 window 对象修改做 diff 用于子应用环境的更新保存。在 qiankun 中也有该降级方案，被称为 SnapshotSandbox。当然，这类沙箱同样也不能支持多实例运行，原因也相同。&lt;/p&gt;

 &lt;p&gt;这类方案的主要思路与 LegacySandbox 有些类似，同样主要分为激活与卸载两个部分的操作。&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;// iter 为一个遍历对象属性的方法

active() {
  // 记录当前快照
  this.windowSnapshot = {} as Window;
  iter(window, (prop) =&amp;gt; {
    this.windowSnapshot[prop] = window[prop];
  });

  // 恢复之前的变更
  Object.keys(this.modifyPropsMap).forEach((p: any) =&amp;gt; {
    window[p] = this.modifyPropsMap[p];
  });

  this.sandboxRunning = true;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;在激活时首先将 window 属性遍历存储起来（作为还原 window 所需的快照），然后在 window 上恢复子应用所需的属性变更，是的，直接修改 window 对象。&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;inactive() {
  this.modifyPropsMap = {};

  iter(window, (prop) =&amp;gt; {
    if (window[prop] !== this.windowSnapshot[prop]) {
      // 记录变更，恢复环境
      this.modifyPropsMap[prop] = window[prop];
      window[prop] = this.windowSnapshot[prop];
    }
  });

  this.sandboxRunning = false;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;而等到卸载时，将此时 window 上所包含的属性遍历存储起来（作为以后还原子应用所需的快照），然后从先前保存的 window 对象中将环境恢复。&lt;/p&gt;

 &lt;p&gt;由于未使用到 Proxy，且只利用 Object 的操作来实现，这个沙箱机制是三类机制中最简单的一种。&lt;/p&gt;

 &lt;p&gt;  &lt;em&gt;注：SnapshotSandbox 参考代码    &lt;a href="https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/snapshotSandbox.ts"&gt;https://github.com/umijs/qiankun/blob/dbbc9acdb0733b3ab28e0470c969d65b57653ff0/src/sandbox/snapshotSandbox.ts&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

 &lt;h2&gt;基于 iframe 的沙箱机制&lt;/h2&gt;

 &lt;h3&gt;基于 Proxy 及 diff 的沙箱机制边界考虑&lt;/h3&gt;

 &lt;p&gt;不论是基于 Proxy 还是 diff，其沙箱机制的方案都是通过模拟和代理来实现一个环境隔离的沙箱，只是所有 API 不同。由于是模拟，因此不可避免的在使用中需要考虑一些边界 case，我们简单来看两个问题。首先看一段代码：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;var foo = &amp;quot;hello&amp;quot;;

function foo() {}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;如上代码大家都很熟悉，在无沙箱环境下两种写法可以自动提升为   &lt;code&gt;[window.foo](http://window.foo)&lt;/code&gt;，但是 Proxy 沙箱下这类代码就需要注意，由于代码执行作用域发生了变更，所以生效的环境不再是全局 window，这时通过 proxy 的 get 拦截器大概率就会返回 undefined，于是便会产生疑问“我本地运行是有值的，为什么到微前端里就 undefined 了呢？”，对于后者，诸如 qiankun 框架中可以通过 window.proxy 获取对应上下文来取值达到目的，但前者由于限制，必须显式的定义为 window.foo 否则无法获取。&lt;/p&gt;

 &lt;p&gt;对于不了解微前端框架的同学来说，这无疑会增加了解成本。对于同类问题，我们再看一个问题描述：&lt;/p&gt;

 &lt;blockquote&gt;
    &lt;p&gt;我的子应用新建了一个 iframe 来做些 JavaScript 逻辑，但在里面通过 window.parent.xxx 无法获取子应用 window 上的全局变量？
但这个变量实际上是存在的，我在子应用中可以把它打印出来的。&lt;/p&gt;

&lt;/blockquote&gt;

 &lt;p&gt;造成这个问题的原因类似，由于 iframe 中的 JavaScript 不在沙箱里执行，会读到外面真实的 window 上。而当你在子应用中定义了一个全局变量，方法是在沙箱里面拦截定义的，也就是方法实现写在沙箱里、方法调用读在沙箱外。解决方法有两种：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;把变量做白名单处理，强制写在外面真实的 window 上；&lt;/li&gt;
    &lt;li&gt;在 iframe 中用 window.parent.proxy 来获取对应的变量；&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;以上所述的问题源自模拟，既然是模拟那么就可能存在难以抹平的边界情况，那么有没有更好一些的解决方案呢，iframe 虽然有那么多缺点，但他就是浏览器原生提供的一个隔离环境呢，有可能吗？&lt;/p&gt;

 &lt;p&gt;常规思路下，大家想到的 iframe 都是在页面内起一个 iframe 元素，然后将需要加载的 url 填入进行加载，由于体验上的割裂，这种方式并不为大家认可，这也是为什么基于 Proxy 和 diff 的沙箱机制被提出的原因。&lt;/p&gt;

 &lt;p&gt;让我们再想想，iframe 都有什么优点？&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;使用简单，一个 url 即可，不需要其他微前端方案那样手动写入很多钩子以适配在微前端环境中的运行；&lt;/li&gt;
    &lt;li&gt;利用浏览器的设计，可以实现样式、DOM、JavaScript 代码执行的完美隔离；&lt;/li&gt;
    &lt;li&gt;页面原则上可以起无数多个 iframe 标签来加载应用，所以可以实现多应用共存；&lt;/li&gt;
    &lt;li&gt;通过 iframe 实现的沙箱可以绕过 eval 执行的限制，比如当我们的代码中使用了原生 es modules 的写法时（eval 中不支持    &lt;code&gt;import()&lt;/code&gt;），如果不做转译，代码便会抛出异常；&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;基于这个思路，如果我们不用 iframe 来加载应用，而是只将其作为一个 JavaScript 运行环境，问题是不是就解决了？&lt;/p&gt;

 &lt;h3&gt;利用 iframe 实现沙箱机制的几点思考&lt;/h3&gt;

 &lt;p&gt;我们知道，iframe 标签可以创造一个独立的浏览器级别的运行环境，该环境与主环境隔离，并有自己的 window 上下文；在通信机制上，也可以利用 postMessage 等 API 与宿主环境进行通信。具体来说，在执行 JavaScript 代码上，我们不需要做什么处理，但是要让 iframe 成为符合我们要求的沙箱，还需要重新设计。其中，和沙箱机制有关的几点包含：&lt;/p&gt;

 &lt;ul&gt;
    &lt;li&gt;   &lt;strong&gt;应用间运行时隔离；&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;应用间通信；&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;路由劫持；&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;我们一一来看看。  &lt;strong&gt;首先，是对运行环境的代理与隔离&lt;/strong&gt;，这也是大多数沙箱必备的基础之一。由于利用了 iframe，所以我们几乎不用担心 JavaScript 的代码运行会给沙箱外环境带来什么影响，因为在 iframe 中运行的 JavaScript 代码都是直接操作 iframe 的 window 上下文，但这里却需要考虑另一方面：如何将一些必要的操作传递出沙箱，因此也需要用到 Proxy 来做一些共享，比如路由、DOM操作等，这涉及到 location、history 等对象。通过将主应用环境下的对象透传给 iframe 中 JavaScript 使用，可以保证子应用在执行操作时，返回前进等操作可以同步到浏览器 top level 层面。此外，对于动态执行的 JavaScript 脚本（比如动态增加一个 script 元素），也需要单独考虑限制作用域，以使 script 中代码在执行时可以对应上具体的全局环境，这里可以通过为 script 包裹一层以锁定作用域内的部分全局变量取值：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;const scriptInstance = document.createElement(&amp;apos;script&amp;apos;);
const script = `(function(window, self, document, location, history) {
    ${scriptString}\n
  }).bind(window.proxyWindow)(
    window.proxyWindow,
    window.proxyWindow,
    window.proxyShadowDom,
    window.proxyLocation,
    window.proxyHistory,
  );`;

scriptInstance.text = script;
document.head.appendChild(scriptInstance);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;其他方面，由于上文已经提到过关于 Proxy 对 get/set 拦截器的实现，本部分不再赘述。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;刚刚提到的 DOM 操作，我们在这里多做一些介绍。&lt;/strong&gt;当 JavaScript 操作 DOM 时，我们肯定需要让其中的操作透传到 iframe 外部进行实现，因为 iframe 里面我们不构建 DOM。如果想在隔离方案上一步到位，这里可以使用 Shadow DOM 作为样式隔离的方案，来构建子应用渲染所需的 DOM 结构，而回到 DOM 操作本身，依旧是通过 Proxy 对 iframe document 进行拦截和替换来实现的，这里依据你的样式隔离方案，来决定 document 究竟是指向主应用中的 Shadow DOM Root 节点，还是其他代理的 document 对象。此外，诸如 MutationObserver 这类的操作也需要通过代理保证在主应用上进行。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;其次，再说说通信。&lt;/strong&gt;一个完备的微前端方案需要考虑主子应用间的通信（与沙箱的通信），这样才可以对框架内的的全局状态或者子应用状态进行感知与响应，我们从同域 iframe 环境看起。&lt;/p&gt;

 &lt;p&gt;通过如下代码我们可以构建一个同域的 iframe 元素，此时，iframe 内外通信并不会存在障碍，通过各自 window 便能方便的获取对应属性值；因为是同域环境，从中取出对应的  &lt;code&gt;contentWindow&lt;/code&gt;便可以对 iframe 内容属性进行随意读取，而与此同时还与外部环境隔离。&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;const iframe = document.createElement(&amp;apos;iframe&amp;apos;,{url:&amp;apos;about:blank&amp;apos;});
document.body.appendChild(iframe);
const sandboxGlobal = iframe.contentWindow;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;而如果要单独构建通信机制，也可以利用自定义 props、event 等方式实现，或者通过 Web API 诸如 postMessage 或者 BroadcastChannel 来实现，关于此部分我在曾经的一篇文章中稍有提及，感兴趣的话可以查看《  &lt;strong&gt;   &lt;a href="https://hijiangtao.github.io/2021/04/13/Service-Worker-Practical-Notes/"&gt;Service Worker 实践指南&lt;/a&gt;&lt;/strong&gt;》。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;说回路由状态&lt;/strong&gt;，要保证 JavaScript 沙箱环境内与主应用路由状态保持一致，我们有两种实现方案：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;让 JavaScript 沙箱内路由变更操作在主应用环境生效；&lt;/li&gt;
    &lt;li&gt;同步沙箱内路由变化至主应用环境；&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;其中，针对第一种情况，我们需要做的是将诸如 location 、history 等变量代理到沙箱环境中，在这种情况下，因为我们不关心 iframe 自身的路由变化，便可以自由设置 src 属性，比如   &lt;code&gt;about:blank&lt;/code&gt; 的方式来构建 iframe，而在沙箱实现上我们可以通过前述的 Proxy 来拦截实现。&lt;/p&gt;

 &lt;p&gt;但稍微考虑下实际生产环境便会发现，第一种情况存在的限制较多，最基本的便是对沙箱内网络请求发送的处理，所以这就需要我们考虑第二种情况的实现，在这种操作下，我们的路由变化会同步到 iframe 上下文，所以我们需要针对 iframe 路由添加一个监听器，在监听到变化时处理主应用的路由，以实现两者路由同步。当然，这种情况下，我们需要针对主应用所在域名设计一个 iframe 的同域方案，比如同一域名+自定义 path 或者 hash 的实现就很简单易懂，这样也不存在跨域限制，此处不再展开。&lt;/p&gt;

 &lt;h3&gt;一段 iframe 沙箱的示例代码&lt;/h3&gt;

 &lt;p&gt;以下简单写了一个 iframe 沙箱的实现伪代码，核心依旧在 window 隔离与共享对象的处理上，主要的实现手段依旧是完善 Proxy 的 get/set 拦截器：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;class SandboxWindow {
    constructor(context, frameWindow) {
        return new Proxy(frameWindow, {
            get(target, name) {
                if (name in context) {
                    return context[name];
                } else if(typeof target[name] === &amp;apos;function&amp;apos; &amp;amp;&amp;amp; /^[a-z]/.test(name) ){
                    return target[name].bind &amp;amp;&amp;amp; target[name].bind(target);
                } else {
                    return target[name];
                }
            },
            set(target, name, value) {
                if (name in context) {
                    return context[name] = value;
                }
                target[name] = value;
            }
        })
    }
}

// 需要全局共享的变量
const context = { 
    document: window.document, 
    history: window.history, 
    location: window.location,
}

// 创建 iframe
const userInputUrl = &amp;apos;&amp;apos;;
const iframe = document.createElement(&amp;apos;iframe&amp;apos;,{url: userInputUrl});
document.body.appendChild(iframe);
const sandboxGlobal = iframe.contentWindow;

// 创建沙箱
const newSandboxWindow = new SandboxWindow(context, sandboxGlobal); 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;但需要注意的是，iframe 方案下，JavaScript 沙箱只是其中一部分，还需要通过完备的 HTML/JavaScript 代码拆分等方案辅助达到微前端环境的目的，这部分实现可参考 kuitos 的开源库   &lt;a href="https://github.com/kuitos/import-html-entry"&gt;import-html-entry&lt;/a&gt;；同样的，之前的几类沙箱方案也需要考虑与这些方案组合。&lt;/p&gt;

 &lt;p&gt;  &lt;em&gt;注：在实现上，如果需要区分 iframe 与主应用环境，可以通过代码    &lt;code&gt;window.parent !== window&lt;/code&gt; 进行判断。&lt;/em&gt;&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="https://hijiangtao.github.io/assets/in-post/2022-06-11-JavaScript-Sandbox-Mechanism-and-Its-History-6.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h2&gt;各类沙箱机制对比&lt;/h2&gt;

 &lt;p&gt;通过对比 Proxy 的两类实现、属性 diff 的一种实现以及 iframe 实现方案，可以发现几类沙箱的主要特点在于（以下部分方案用 qiankun 中对三类沙箱的命名方式作为沙箱机制名称）&lt;/p&gt;

 &lt;table&gt;
  
      &lt;tr&gt;
         &lt;th&gt; &lt;/th&gt;
         &lt;th&gt;多实例运行&lt;/th&gt;
         &lt;th&gt;语法兼容&lt;/th&gt;
         &lt;th&gt;不污染全局环境（主应用）&lt;/th&gt;
    &lt;/tr&gt;
  
  
      &lt;tr&gt;
         &lt;td&gt;LegacySanbox&lt;/td&gt;
         &lt;td&gt;❌&lt;/td&gt;
         &lt;td&gt;❌&lt;/td&gt;
         &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
      &lt;tr&gt;
         &lt;td&gt;ProxySandbox&lt;/td&gt;
         &lt;td&gt;✅&lt;/td&gt;
         &lt;td&gt;❌&lt;/td&gt;
         &lt;td&gt;✅&lt;/td&gt;
    &lt;/tr&gt;
      &lt;tr&gt;
         &lt;td&gt;SnapshotSandbox&lt;/td&gt;
         &lt;td&gt;❌&lt;/td&gt;
         &lt;td&gt;✅&lt;/td&gt;
         &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
      &lt;tr&gt;
         &lt;td&gt;iframe&lt;/td&gt;
         &lt;td&gt;✅&lt;/td&gt;
         &lt;td&gt;✅&lt;/td&gt;
         &lt;td&gt;✅&lt;/td&gt;
    &lt;/tr&gt;
  
&lt;/table&gt;

 &lt;h2&gt;基于 ES 提案   &lt;strong&gt;ShadowRealm 实现&lt;/strong&gt;&lt;/h2&gt;

 &lt;p&gt;  &lt;strong&gt;ShadowRealm 是一个 ECMAScript 标准提案，旨在创建一个独立的全局环境，它的全局对象包含自己的内建函数与对象（未绑定到全局变量的标准对象，如 Object.prototype 的初始值），有自己独立的作用域&lt;/strong&gt;，方案当前处于 stage 3 阶段。提案地址   &lt;a href="https://github.com/tc39/proposal-shadowrealm"&gt;https://github.com/tc39/proposal-shadowrealm&lt;/a&gt;&lt;/p&gt;

 &lt;h3&gt;什么是 JavaScript 的运行环境实例&lt;/h3&gt;

 &lt;p&gt;谈及提案之前，我们简单来看看什么是 Realm，下面是 Alex 附上的一个例子：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;&amp;lt;body&amp;gt;
  &amp;lt;iframe&amp;gt;
  &amp;lt;/iframe&amp;gt;
  &amp;lt;script&amp;gt;
    const win = frames[0].window;
    console.assert(win.globalThis !== globalThis); // (A)
    console.assert(win.Array !== Array); // (B)
  &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;在前面 iframe 沙箱机制中我们也有介绍，由于每个   &lt;code&gt;iframe&lt;/code&gt; 都有一个独立的运行环境，于是在执行时，当前 html 中的全局对象肯定与   &lt;code&gt;iframe&lt;/code&gt;的全局对象不相同（A），类似的，全局对象上的   &lt;code&gt;Array&lt;/code&gt;与   &lt;code&gt;iframe&lt;/code&gt; 中获取到的   &lt;code&gt;Array&lt;/code&gt; 也不同（B）。&lt;/p&gt;

 &lt;p&gt;这就是 realm，一个 JavaScript 运行环境（JavaScript platform）实例：包含其所必须的全局环境及内建函数等。&lt;/p&gt;

 &lt;h3&gt;ShadowRealm API 简介&lt;/h3&gt;

 &lt;p&gt;ShadowRealm API 由一个包含如下函数签名的类实现：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;declare class ShadowRealm {
  constructor();
  evaluate(sourceText: string): PrimitiveValueOrCallable;
  importValue(specifier: string, bindingName: string): Promise&amp;lt;PrimitiveValueOrCallable&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;  &lt;strong&gt;每个    &lt;code&gt;ShadowRealm&lt;/code&gt; 实例都有自己独立的运行环境实例，在 realm 中，提案提供了两种方法让我们来执行运行环境实例中的 JavaScript 代码：&lt;/strong&gt;&lt;/p&gt;

 &lt;ul&gt;
    &lt;li&gt;   &lt;code&gt;.evaluate()&lt;/code&gt;：同步执行代码字符串，类似    &lt;code&gt;eval()&lt;/code&gt;。&lt;/li&gt;
    &lt;li&gt;   &lt;code&gt;.importValue()&lt;/code&gt;：返回一个    &lt;code&gt;Promise&lt;/code&gt; 对象，异步执行代码字符串。&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;通过 evaluate 执行代码与 eval 类似，比如：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;const sr = new ShadowRealm();
console.assert(
  sr.evaluate(`&amp;apos;ab&amp;apos; + &amp;apos;cd&amp;apos;`) === &amp;apos;abcd&amp;apos;
);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;但存在一些细微的差别，比如执行作用域、调用方式以及传值类型等。例如，如果   &lt;code&gt;.evaluate()&lt;/code&gt; 返回一个函数，则该函数会被包装，这样我们就可以从外部调用它，而逻辑在 ShadowRealm 中运行，我们可以通过观察下面的 console.assert 来效果：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;globalThis.realm = &amp;apos;incubator realm&amp;apos;;

const sr = new ShadowRealm();
sr.evaluate(`globalThis.realm = &amp;apos;child realm&amp;apos;`);

const wrappedFunc = sr.evaluate(`() =&amp;gt; globalThis.realm`);
console.assert(wrappedFunc() === &amp;apos;child realm&amp;apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;说到另一个 API   &lt;code&gt;.importValue()&lt;/code&gt;，我们可以利用它导入一个外部模块，它会通过一个 Promise 异步返回其执行内容，和   &lt;code&gt;.evalute()&lt;/code&gt;函数一样，这个函数被包装，以允许我们在外部调用，而实际代码在 ShadowRealm 中执行，我们可以看看下面这个例子，很好的解释了这个 API 的功能：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;// main.js
const sr = new ShadowRealm();
const wrappedSum = await sr.importValue(&amp;apos;./my-module.js&amp;apos;, &amp;apos;sum&amp;apos;);
console.assert(wrappedSum(&amp;apos;hi&amp;apos;, &amp;apos; &amp;apos;, &amp;apos;folks&amp;apos;, &amp;apos;!&amp;apos;) === &amp;apos;hi folks!&amp;apos;);

// my-module.js
export function sum(...values) {
  return values.reduce((prev, value) =&amp;gt; prev + value);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;h3&gt;ShadowRealm 的错误捕获与更多应用场景&lt;/h3&gt;

 &lt;p&gt;ShadowRealm API 提案暂未针对错误捕获做详细设计，整体看上去比较简洁，因为这些在未来还有可能变化，以下为 Alex 针对当前提案下代码执行错误给出的两个例子，可以看出其中并不包含错误的原始调用堆栈等：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;&amp;gt; new ShadowRealm().evaluate(&amp;apos;someFunc(&amp;apos;)
SyntaxError: Unexpected end of script

&amp;gt; new ShadowRealm().evaluate(`throw new RangeError(&amp;apos;The message&amp;apos;)`)
TypeError: Error encountered during evaluation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;由于没有实践经历，这里仅对 ShadowRealm 提案及相关概念进行了简要介绍，但可以看出，这个提案的落地可能对于一个更完美的 JavaScript 沙箱设计有所帮助，当然，这个提案的应用场景远不止此，比如：&lt;/p&gt;

 &lt;ul&gt;
    &lt;li&gt;   &lt;strong&gt;Web 应用诸如     &lt;code&gt;IDE&lt;/code&gt; 或绘图等程序可以运行第三方代码，允许其以插件或者配置的方式引入；&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;利用     &lt;code&gt;ShadowRealms&lt;/code&gt; 建立一个可编程环境，来运行用户的代码；&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;服务器可以在     &lt;code&gt;ShadowRealms&lt;/code&gt; 中运行第三方代码；&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;在 ShadowRealms 中可以运行测试运行器（Test Runner），这样外部的 JS 执行环境不会受到影响，并且每个套件都可以在新环境中启动（这有助于提高可复用性），这种场景类似于微前端的 JavaScript 沙箱；&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;strong&gt;网页抓取和网页应用测试等；&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;  &lt;img alt="" src="https://hijiangtao.github.io/assets/in-post/2022-06-11-JavaScript-Sandbox-Mechanism-and-Its-History-5.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h2&gt;总结&lt;/h2&gt;

 &lt;p&gt;如果按照沙箱机制在实现时所用到的主要 Web 技术不同，当下已经论证、开源或者存在实现可能性的 JavaScript 沙箱机制可以分为以下几类：&lt;/p&gt;

 &lt;ol&gt;
    &lt;li&gt;基于 ES6 API Proxy 实现&lt;/li&gt;
    &lt;li&gt;基于属性 diff 实现&lt;/li&gt;
    &lt;li&gt;基于 iframe 实现&lt;/li&gt;
    &lt;li&gt;基于 ES 提案 ShadowRealm 实现&lt;/li&gt;
&lt;/ol&gt;

 &lt;p&gt;本文基于个人项目实践、阅读代码梳理等方式对每类沙箱机制均进行了介绍，部分引用了 qiankun 的代码实现，部分写了伪代码解释，部分引用了最新 ECMAScript 提案示例，但仍未能详尽每一处细节，比如没有针对微前端框架深入介绍，也不会就某一个沙箱机制的具体细节实现（比如如何构建闭包环境、属性读取的边界处理等）进行剖析，但这些对于从更大的层面了解微前端机制都不可或缺。&lt;/p&gt;

 &lt;p&gt;如果你想了解关于 CSS 样式隔离的内容可以搜索 Shadow DOM 相关内容进一步查阅；如果你想了解微前端的主子应用加载、运行机制，可以参考 single-spa 文档、qiankun 文档、ShadowRealm 提案等内容；如果你想了解文中涉及的一些概念与 API 用法可以在 MDN 进行搜索查阅，大部分均有对应介绍。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="https://hijiangtao.github.io/assets/in-post/2022-06-11-JavaScript-Sandbox-Mechanism-and-Its-History-2.jpeg"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h2&gt;参考&lt;/h2&gt;

 &lt;ol&gt;
    &lt;li&gt;   &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy"&gt;https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://single-spa.js.org/"&gt;https://single-spa.js.org/&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://github.com/umijs/qiankun"&gt;https://github.com/umijs/qiankun&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://tsejx.github.io/javascript-guidebook/standard-built-in-objects/fundamental-objects/symbol/unscopables/"&gt;https://tsejx.github.io/javascript-guidebook/standard-built-in-objects/fundamental-objects/symbol/unscopables/&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables"&gt;https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://juejin.cn/post/6981374562877308936"&gt;https://juejin.cn/post/6981374562877308936&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://micro-frontends.org/"&gt;https://micro-frontends.org/&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://2ality.com/2022/04/shadow-realms.html"&gt;https://2ality.com/2022/04/shadow-realms.html&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://github.com/tc39/proposal-shadowrealm"&gt;https://github.com/tc39/proposal-shadowrealm&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;   &lt;a href="https://qiankun.umijs.org/guide"&gt;https://qiankun.umijs.org/guide&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Document 微前端 隔离 沙箱机制 JavaScript 运行环境</category>
      <guid isPermaLink="true">https://itindex.net/detail/62300-%E5%89%8D%E7%AB%AF-javascript-%E9%9A%94%E7%A6%BB</guid>
      <pubDate>Sat, 11 Jun 2022 08:00:00 CST</pubDate>
    </item>
    <item>
      <title>GitHub 上周 JavaScript 趋势榜项目</title>
      <link>https://itindex.net/detail/60607-github-javascript-%E8%B6%8B%E5%8A%BF</link>
      <description>&lt;p&gt;  &lt;img alt="header.png" src="https://segmentfault.com/img/bVbHjsw" title="header.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;1. yemount/pose-animator&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/yemount/pose-animator" rel="nofollow noreferrer"&gt;https://github.com/yemount/pose-animator&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:4237 | forks:354 | 2117 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;Pose Animator拍摄2D矢量图，并基于PoseNet和FaceMesh的识别结果实时对其包含的曲线进行动画处理。 它从计算机图形学中借鉴了基于骨骼的动画的思想，并将其应用于矢量字符。&lt;/p&gt;
 &lt;h2&gt;2. renrenio/renren-fast-vue&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/renrenio/renren-fast-vue" rel="nofollow noreferrer"&gt;https://github.com/renrenio/renren-fast-vue&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:2023 | forks:1062 | 132 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;renren-fast-vue基于vue、element-ui构建开发，实现renren-fast后台管理前端功能，提供一套更优的前端解决方案。&lt;/p&gt;
 &lt;h2&gt;3. zeit/next.js&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/zeit/next.js" rel="nofollow noreferrer"&gt;https://github.com/zeit/next.js&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:48399 | forks:7232 | 483 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;React SSR 框架&lt;/p&gt;
 &lt;h2&gt;4. discordjs/discord.js&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/discordjs/discord.js" rel="nofollow noreferrer"&gt;https://github.com/discordjs/discord.js&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:5791 | forks:1447 | 84 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;强大的 JavaScript 库，可与 Discord API 进行交互&lt;/p&gt;
 &lt;h2&gt;5. openspug/spug&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/openspug/spug" rel="nofollow noreferrer"&gt;https://github.com/openspug/spug&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:2383 | forks:534 | 476 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;开源运维平台：面向中小型企业设计的轻量级无Agent的自动化运维平台，整合了主机管理、主机批量执行、主机在线终端、应用发布部署、在线任务计划、配置中心、监控、报警等一系列功能。&lt;/p&gt;
 &lt;h2&gt;6. ccxt/ccxt&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/ccxt/ccxt" rel="nofollow noreferrer"&gt;https://github.com/ccxt/ccxt&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:13828 | forks:3889 | 88 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;JavaScript / Python / PHP加密货币交易API，支持超过120个 比特币 / 数字货币 交换&lt;/p&gt;
 &lt;h2&gt;7. Advanced-Frontend/Daily-Interview-Question&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/Advanced-Frontend/Daily-Interview-Question" rel="nofollow noreferrer"&gt;https://github.com/Advanced-Frontend/Daily-Interview-Question&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:18641 | forks:2284 | 207 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;我是木易杨，公众号「高级前端进阶」作者，每天搞定一道前端大厂面试题，祝大家天天进步，一年后会看到不一样的自己。&lt;/p&gt;
 &lt;h2&gt;8. quasarframework/quasar&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/quasarframework/quasar" rel="nofollow noreferrer"&gt;https://github.com/quasarframework/quasar&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:14653 | forks:1667 | 77 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;Quasar Framework-在极短时间内构建高性能 VueJS 用户界面&lt;/p&gt;
 &lt;h2&gt;9. denysdovhan/wtfjs&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/denysdovhan/wtfjs" rel="nofollow noreferrer"&gt;https://github.com/denysdovhan/wtfjs&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:17262 | forks:1140 | 193 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;有趣又棘手的 JavaScript 示例列表&lt;/p&gt;
 &lt;h2&gt;10. YMFE/yapi&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/YMFE/yapi" rel="nofollow noreferrer"&gt;https://github.com/YMFE/yapi&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:15486 | forks:2619 | 310 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;YApi 是一个可本地部署的、打通前后端及QA的、可视化的接口管理平台&lt;/p&gt;
 &lt;h2&gt;11. agalwood/Motrix&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/agalwood/Motrix" rel="nofollow noreferrer"&gt;https://github.com/agalwood/Motrix&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:21052 | forks:2625 | 272 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;一个全功能的下载管理器。&lt;/p&gt;
 &lt;h2&gt;12. jgraph/drawio-desktop&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/jgraph/drawio-desktop" rel="nofollow noreferrer"&gt;https://github.com/jgraph/drawio-desktop&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:7603 | forks:811 | 322 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;drawio 桌面版&lt;/p&gt;
 &lt;h2&gt;13. facebook/react&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/facebook/react" rel="nofollow noreferrer"&gt;https://github.com/facebook/react&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:148832 | forks:28875 | 514 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;大名鼎鼎的 React 框架， 用于构建用户界面的声明性，高效且灵活的JavaScript库。&lt;/p&gt;
 &lt;h2&gt;14. iamadamdev/bypass-paywalls-chrome&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/iamadamdev/bypass-paywalls-chrome" rel="nofollow noreferrer"&gt;https://github.com/iamadamdev/bypass-paywalls-chrome&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:4799 | forks:359 | 198 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;绕过付费墙 web 浏览器扩展&lt;/p&gt;
 &lt;h2&gt;15. cypress-io/cypress&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/cypress-io/cypress" rel="nofollow noreferrer"&gt;https://github.com/cypress-io/cypress&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:20222 | forks:1213 | 227 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;对浏览器中运行的所有内容进行快速，轻松和可靠的测试。&lt;/p&gt;
 &lt;h2&gt;16. jgraph/drawio&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/jgraph/drawio" rel="nofollow noreferrer"&gt;https://github.com/jgraph/drawio&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:16966 | forks:3550 | 296 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;diagrams.net（以前为draw.io）是一个在线图表绘制网站，在此项目中提供了源代码。&lt;/p&gt;
 &lt;h2&gt;17. twbs/bootstrap&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/twbs/bootstrap" rel="nofollow noreferrer"&gt;https://github.com/twbs/bootstrap&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:140844 | forks:68838 | 281 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;最受欢迎的HTML，CSS和JavaScript框架，用于在网络上开发响应式，移动优先项目。&lt;/p&gt;
 &lt;h2&gt;18. mui-org/material-ui&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/mui-org/material-ui" rel="nofollow noreferrer"&gt;https://github.com/mui-org/material-ui&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:57433 | forks:16064 | 276 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;React UI 库，组件可以更快，更轻松地进行Web开发。 建立自己的设计系统，或从材料设计开始。&lt;/p&gt;
 &lt;h2&gt;19. gatsbyjs/gatsby&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/gatsbyjs/gatsby" rel="nofollow noreferrer"&gt;https://github.com/gatsbyjs/gatsby&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:44405 | forks:8044 | 228 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;使用React构建快速，现代化的应用程序和网站&lt;/p&gt;
 &lt;h2&gt;20. microsoft/playwright&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/microsoft/playwright" rel="nofollow noreferrer"&gt;https://github.com/microsoft/playwright&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:12773 | forks:370 | 408 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;节点库可通过单个API自动化Chromium，Firefox和WebKit&lt;/p&gt;
 &lt;h2&gt;21. MarkerHub/eblog&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/MarkerHub/eblog" rel="nofollow noreferrer"&gt;https://github.com/MarkerHub/eblog&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:448 | forks:149 | 100 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;eblog是一个基于Springboot2.1.2开发的博客学习项目，为了让项目融合更多的知识点，达到学习目的，编写了详细的从0到1开发文档。主要学习包括：自定义Freemarker标签，使用shiro+redis完成了会话共享，redis的zset结构完成本周热议排行榜，t-io+websocket完成即时消息通知和群聊，rabbitmq+elasticsearch完成博客内容搜索引擎等。值得学习的地方很多！&lt;/p&gt;
 &lt;h2&gt;22. goldbergyoni/nodebestpractices&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/goldbergyoni/nodebestpractices" rel="nofollow noreferrer"&gt;https://github.com/goldbergyoni/nodebestpractices&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:43891 | forks:3974 | 293 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;Node.js最佳实践列表（2020年5月）&lt;/p&gt;
 &lt;h2&gt;23. grommet/grommet&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/grommet/grommet" rel="nofollow noreferrer"&gt;https://github.com/grommet/grommet&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:6300 | forks:738 | 40 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;基于react的框架，可在整齐的程序包中提供可访问性，模块化，响应性和主题化&lt;/p&gt;
 &lt;h2&gt;24. egonSchiele/grokking_algorithms&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/egonSchiele/grokking_algorithms" rel="nofollow noreferrer"&gt;https://github.com/egonSchiele/grokking_algorithms&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:4004 | forks:1558 | 49 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;《 Grokking算法》源码&lt;/p&gt;
 &lt;h2&gt;25. jonasschmedtmann/complete-javascript-course&lt;/h2&gt;
 &lt;p&gt;项目地址：  &lt;a href="https://github.com/jonasschmedtmann/complete-javascript-course" rel="nofollow noreferrer"&gt;https://github.com/jonasschmedtmann/complete-javascript-course&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;⭐stars:3629 | forks:5226 | 54 ⭐stars this week&lt;/p&gt;
 &lt;p&gt;完整 JavaScript 课程的入门文件，最终项目和常见问题解答&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>github 前端 javascript</category>
      <guid isPermaLink="true">https://itindex.net/detail/60607-github-javascript-%E8%B6%8B%E5%8A%BF</guid>
      <pubDate>Mon, 18 May 2020 17:55:40 CST</pubDate>
    </item>
    <item>
      <title>Javascript 面试中经常被问到的三个问题！</title>
      <link>https://itindex.net/detail/59320-javascript-%E9%9D%A2%E8%AF%95-%E9%97%AE%E9%A2%98</link>
      <description>&lt;p&gt;  &lt;img alt="&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/bVboH5x?w=1000&amp;h=750" title="&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;本文不是讨论最新的 JavaScript 库、常见的开发实践或任何新的 ES6 函数。相反，在讨论 JavaScript 时，面试中通常会提到三件事。我自己也被问到这些问题，我的朋友们告诉我他们也被问到这些问题。&lt;/p&gt;
 &lt;p&gt;然，这些并不是你在面试之前应该学习的唯一三件事 - 你可以通过  &lt;a href="http://jstherightway.org/#getting-started" rel="nofollow noreferrer"&gt;多种&lt;/a&gt;  &lt;a href="https://medium.com/javascript-scene/10-interview-questions-every-javascript-developer-should-know-6fa6bdf5ad95#.7fty5p61c" rel="nofollow noreferrer"&gt;方式&lt;/a&gt;更好地为即将到来的面试做准备 - 但面试官可能会问到下面是三个问题，来判断你对   &lt;code&gt;JavaScript&lt;/code&gt; 语言的理解和   &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction" rel="nofollow noreferrer"&gt;DOM&lt;/a&gt; 的掌握程度。&lt;/p&gt;
 &lt;p&gt;让我们开始吧！注意，我们将在下面的示例中使用原生的 JavaScript，因为面试官通常希望了解你在没有 jQuery 等库的帮助下对JavaScript 和 DOM 的理解程度。&lt;/p&gt;
 &lt;h3&gt;问题 1: 事件委托代理&lt;/h3&gt;
 &lt;p&gt;在构建应用程序时，有时需要将事件绑定到页面上的按钮、文本或图像，以便在用户与元素交互时执行某些操作。&lt;/p&gt;
 &lt;p&gt;如果我们以一个简单的待办事项列表为例，面试官可能会告诉你，当用户点击列表中的一个列表项时执行某些操作。他们希望你用 JavaScript 实现这个功能，假设有如下 HTML 代码:&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&amp;lt;ul id=&amp;quot;todo-app&amp;quot;&amp;gt;
  &amp;lt;li class=&amp;quot;item&amp;quot;&amp;gt;Walk the dog&amp;lt;/li&amp;gt;
  &amp;lt;li class=&amp;quot;item&amp;quot;&amp;gt;Pay bills&amp;lt;/li&amp;gt;
  &amp;lt;li class=&amp;quot;item&amp;quot;&amp;gt;Make dinner&amp;lt;/li&amp;gt;
  &amp;lt;li class=&amp;quot;item&amp;quot;&amp;gt;Code for one hour&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;你可能想要做如下操作来将事件绑定到元素:&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;document.addEventListener(&amp;apos;DOMContentLoaded&amp;apos;, function() {
  let app = document.getElementById(&amp;apos;todo-app&amp;apos;);
  let times = app.getElementsByClassName(&amp;apos;item&amp;apos;);

  for (let item of items) {
    item.addEventListener(&amp;apos;click&amp;apos;, function(){
      alert(&amp;apos;you clicked on item: &amp;apos; + item.innerHTML);
    })
  }
})
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;虽然这在技术上是可行的，但问题是要将事件分别绑定到每个项。这对于目前   &lt;code&gt;4&lt;/code&gt; 个元素来说，没什么大问题，但是如果在待办事项列表中添加了   &lt;code&gt;10,000&lt;/code&gt; 项(他们可能有很多事情要做)怎么办?然后，函数将创建 10,000 个独立的事件侦听器，并将每个事件监听器绑定到 DOM ，这样代码执行的效率非常低下。&lt;/p&gt;
 &lt;p&gt;在面试中，最好先问面试官用户可以输入的最大元素数量是多少。例如，如果它不超过   &lt;code&gt;10&lt;/code&gt;，那么上面的代码就可以很好地工作。但是如果用户可以输入的条目数量没有限制，那么你应该使用一个更高效的解决方案。&lt;/p&gt;
 &lt;p&gt;如果你的应用程序最终可能有数百个事件侦听器，那么更有效的解决方案是将一个事件侦听器实际绑定到整个容器，然后在单击它时能够访问每个列表项， 这称为   &lt;strong&gt;   &lt;a href="https://davidwalsh.name/event-delegate" rel="nofollow noreferrer"&gt;事件委托&lt;/a&gt;&lt;/strong&gt;，它比附加单独的事件处理程序更有效。&lt;/p&gt;
 &lt;p&gt;下面是事件委托的代码:&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;document.addEventListener(&amp;apos;DOMContentLoaded&amp;apos;, function() {
  let app = document.getElementById(&amp;apos;todo-app&amp;apos;);

  app.addEventListener(&amp;apos;click&amp;apos;, function(e) {
    if (e.target &amp;amp;&amp;amp; e.target.nodeName === &amp;apos;LI&amp;apos;) {
      let item = e.target;
      alert(&amp;apos;you clicked on item: &amp;apos; + item.innerHTML)
    }
  })
})

&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;问题 2： 在循环中使用闭包&lt;/h2&gt;
 &lt;p&gt;闭包常常出现在面试中，以便面试官衡量你对 JS 的熟悉程度，以及你是否知道何时使用闭包。&lt;/p&gt;
 &lt;p&gt;闭包基本上是内部函数可以访问其范围之外的变量。 闭包可用于实现隐私和创建函数工厂， 闭包常见的面试题如下：&lt;/p&gt;
 &lt;blockquote&gt;编写一个函数，该函数将遍历整数列表，并在延迟3秒后打印每个元素的索引。&lt;/blockquote&gt;
 &lt;p&gt;经常不正确的写法是这样的：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;const arr = [10, 12, 15, 21];
for (var i = 0; i &amp;lt; arr.length; i++) {
  setTimeout(function() {
    console.log(&amp;apos;The index of this number is: &amp;apos; + i);
  }, 3000);
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;如果运行上面代码，  &lt;code&gt;3&lt;/code&gt; 秒延迟后你会看到，实际上每次打印输出是   &lt;code&gt;4&lt;/code&gt;，而不是期望的   &lt;code&gt;0，1，2，3&lt;/code&gt; 。&lt;/p&gt;
 &lt;p&gt;为了正确理解为什么会发生这种情况，了解为什么会在 JavaScript 中发生这种情况将非常有用，这正是面试官试图测试的内容。&lt;/p&gt;
 &lt;p&gt;原因是因为   &lt;code&gt;setTimeout&lt;/code&gt; 函数创建了一个可以访问其外部作用域的函数（闭包），该作用域是包含索引   &lt;code&gt;i&lt;/code&gt; 的循环。 经过   &lt;code&gt;3&lt;/code&gt; 秒后，执行该函数并打印出   &lt;code&gt;i&lt;/code&gt; 的值，该值在循环结束时为   &lt;code&gt;4&lt;/code&gt;，因为它循环经过  &lt;code&gt;0,1,2,3,4&lt;/code&gt;并且循环最终停止在   &lt;code&gt;4&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;实际上有  &lt;a href="https://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops" rel="nofollow noreferrer"&gt;多处&lt;/a&gt;方法来正确的解这道题：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;const arr = [10, 12, 15, 21];

for (var i = 0; i &amp;lt; arr.length; i++) {
  setTimeout(function(i_local){
    return function () {
      console.log(&amp;apos;The index of this number is: &amp;apos; + i_local);
    }
  }(i), 3000)
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;const arr = [10, 12, 15, 21];
for (let i = 0; i &amp;lt; arr.length; i++) {
  setTimeout(function() {
    console.log(&amp;apos;The index of this number is: &amp;apos; + i);
  }, 3000);
}


&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;问题 3：事件的节流（throttle）与防抖（debounce）&lt;/h2&gt;
 &lt;p&gt;有些浏览器事件可以在短时间内快速触发多次，比如调整窗口大小或向下滚动页面。例如，监听页面窗口滚动事件，并且用户持续快速地向下滚动页面，那么滚动事件可能在 3 秒内触发数千次，这可能会导致一些严重的性能问题。&lt;/p&gt;
 &lt;p&gt;如果在面试中讨论构建应用程序，出现滚动、窗口大小调整或按下键等事件请务必提及   &lt;strong&gt;防抖(Debouncing)&lt;/strong&gt; 和   &lt;strong&gt;函数节流（Throttling）&lt;/strong&gt;来提升页面速度和性能。这两兄弟的本质都是以  &lt;strong&gt;闭包&lt;/strong&gt;的形式存在。通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息，最后用 setTimeout 来控制事件的触发频率。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;Throttle： 第一个人说了算&lt;/strong&gt;&lt;/h4&gt;
 &lt;p&gt;throttle 的主要思想在于：在某段时间内，不管你触发了多少次回调，都只认第一次，并在计时结束时给予响应。&lt;/p&gt;
 &lt;p&gt;这个故事里，‘裁判’ 就是我们的节流阀， 他控制参赛者吃东西的时机， “参赛者吃东西”就是我们频繁操作事件而不断涌入的回调任务，它受 “裁判” 的控制,而计时器，就是上文提到的以自由变量形式存在的时间信息，它是 “裁判” 决定是否停止比赛的依据，最后，等待比赛结果就对应到回调函数的执行。&lt;/p&gt;
 &lt;p&gt;总结下来，所谓的“节流”，是通过在一段时间内无视后来产生的回调请求来实现的。只要 裁判宣布比赛开始，裁判就会开启计时器，在这段时间内，参赛者就尽管不断的吃，谁也无法知道最终结果。&lt;/p&gt;
 &lt;p&gt;对应到实际的交互上是一样一样的：每当用户触发了一次 scroll 事件，我们就为这个触发操作开启计时器。一段时间内，后续所有的 scroll 事件都会被当作“参赛者吃东西——它们无法触发新的 scroll 回调。直到“一段时间”到了，第一次触发的 scroll 事件对应的回调才会执行，而“一段时间内”触发的后续的 scroll 回调都会被节流阀无视掉。&lt;/p&gt;
 &lt;p&gt;现在一起实现一个 throttle：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;// fn 是我们需要包装的事件回调, interval 是时间间隔的阈值
function throttle(fn, interval) {
  // last 为上一次触发回调时间
  let last = 0

  // 将 throttle 处理结果当然函数返回
  return function () {
    // 保留调用时的 this 上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 记录本次触发回调的时间
    let now = +new Date()

    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if (now - last &amp;gt;= interval) {
      // 如果时间间隔大于我们设定的时间间隔阈值，则执行回调
      last = fn.apply(context, args)
    }
  }
}

// 用 throttle来包装 scroll 的回调
const better_scroll = throttle(() =&amp;gt; { console.log(&amp;apos;触发了滚动事件&amp;apos;)}, 1000)
document.addEventListener(&amp;apos;scroll&amp;apos;, better_scroll)
&lt;/code&gt;&lt;/pre&gt;
 &lt;h4&gt;  &lt;strong&gt;Debounce： 最后一个参赛者说了算&lt;/strong&gt;&lt;/h4&gt;
 &lt;p&gt;防抖的主要思想在于：我会等你到底。在某段时间内，不管你触发了多少次回调，我都只认最后一次。&lt;/p&gt;
 &lt;p&gt;继续大胃王比赛故事，这次换了一种比赛方式，时间不限，参赛者吃到不能吃为止，当每个参赛都吃不下的时候，后面10分钟如果没有人在吃，比赛结束，如果有人在10分钟内还能吃，则比赛继续，直到下一次10分钟内无人在吃时为止。&lt;/p&gt;
 &lt;p&gt;对比 throttle 来理解 debounce： 在 throttle 的逻辑里， ‘裁判’ 说了算，当比赛时间到时，就执行回调函数。而 debounce 认为最后一个参赛者说了算，只要还能吃的，就重新设定新的定时器。&lt;/p&gt;
 &lt;p&gt;现在一起实现一个 debounce：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;// fn 是我们需要包装事件回调，delay 是每次推迟执行的等待时间
function debounce(fn, delay) {
  // 定时器
  let timer = null

  // 将 debounce 处理结果当作函数返回
  return function () {
    // 保留调用时的 this 上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments

    // 每次事件被触发时，都去清除之前的旧定时器
    if (timer) {
      clearTimeout(timer)
    }
    // 设立新定时器
    timer = setTimeout(function() {
      fn.apply(context, args)
    }, delay)
  }
}

// 用 debounce 来包装 scroll 的回调
const better_scroll = debounce(() =&amp;gt; { console.log(&amp;apos;发了滚动事件&amp;apos;)}, 1000)
document.addEventListener(&amp;apos;scroll&amp;apos;, better_scroll)
&lt;/code&gt;&lt;/pre&gt;
 &lt;h4&gt;  &lt;strong&gt;用 Throttle 来优化 Debounce&lt;/strong&gt;&lt;/h4&gt;
 &lt;p&gt;debounce 的问题在于它“太有耐心了”。试想，如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作，于是每次 debounce 都为该用户重新生成定时器，回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应，用户同样会产生“这个页面卡死了”的观感。&lt;/p&gt;
 &lt;p&gt;为了避免弄巧成拙，我们需要借力 throttle 的思想，打造一个“有底线”的 debounce——等你可以，但我有我的原则：delay 时间内，我可以为你重新生成定时器；但只要delay的时间到了，我必须要给用户一个响应。这个 throttle 与 debounce “合体”思路，已经被很多成熟的前端库应用到了它们的加强版 throttle 函数的实现中：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;// fn是我们需要包装的事件回调, delay是时间间隔的阈值
function throttle(fn, delay) {
  // last为上一次触发回调的时间, timer是定时器
  let last = 0, timer = null
  // 将throttle处理结果当作函数返回
  
  return function () { 
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 记录本次触发回调的时间
    let now = +new Date()
    
    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if (now - last &amp;lt; delay) {
    // 如果时间间隔小于我们设定的时间间隔阈值，则为本次触发操作设立一个新的定时器
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        // 如果时间间隔超出了我们设定的时间间隔阈值，那就不等了，无论如何要反馈给用户一次响应
        last = now
        fn.apply(context, args)
    }
  }
}

// 用新的throttle包装scroll的回调
const better_scroll = throttle(() =&amp;gt; console.log(&amp;apos;触发了滚动事件&amp;apos;), 1000)

document.addEventListener(&amp;apos;scroll&amp;apos;, better_scroll)






&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;参考:&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://codeburst.io/throttling-and-debouncing-in-javascript-b01cad5c8edf#.ly8uqz8v4" rel="nofollow noreferrer"&gt;Throttling and Debouncing in JavaScript&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://css-tricks.com/the-difference-between-throttling-and-debouncing/" rel="nofollow noreferrer"&gt;The Difference Between Throttling and Debouncing&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://css-tricks.com/debouncing-throttling-explained-examples/" rel="nofollow noreferrer"&gt;Examples of Throttling and Debouncing&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://remysharp.com/2010/07/21/throttling-function-calls" rel="nofollow noreferrer"&gt;Remy Sharp’s blog post on Throttling function calls&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://juejin.im/book/5b936540f265da0a9624b04b/section/5bb6212be51d451a3f4c3570#heading-4" rel="nofollow noreferrer"&gt;前端性能优化原理与实践&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;原文：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://medium.freecodecamp.org/3-questions-to-watch-out-for-in-a-javascript-interview-725012834ccb" rel="nofollow noreferrer"&gt;https://medium.freecodecamp.o...&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;你的点赞是我持续分享好东西的动力，欢迎点赞！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;一个笨笨的码农，我的世界只能终身学习！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;更多内容请关注公众号   &lt;a href="http://shiyuaniot.com:8888/daqian/daqian.jpg" rel="nofollow noreferrer"&gt;《大迁世界》&lt;/a&gt;！&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>前端 程序员 javascript 面试</category>
      <guid isPermaLink="true">https://itindex.net/detail/59320-javascript-%E9%9D%A2%E8%AF%95-%E9%97%AE%E9%A2%98</guid>
      <pubDate>Thu, 28 Feb 2019 18:30:00 CST</pubDate>
    </item>
    <item>
      <title>我从来不理解 JavaScript 闭包，直到有人这样向我解释它... - Java架构—月亮 - 博客园</title>
      <link>https://itindex.net/detail/59303-%E7%90%86%E8%A7%A3-javascript-%E9%97%AD%E5%8C%85</link>
      <description>&lt;div&gt;    &lt;p&gt;正如标题所述，JavaScript 闭包对我来说一直有点神秘,看过很多闭包的文章，在工作使用过闭包，有时甚至在项目中使用闭包，但我确实是这是在使用闭包的知识。&lt;/p&gt;    &lt;p&gt;最近看国外的一些文章，终于，有人用于一种让我明白方式对闭包进行了解释，我将在本文中尝试使用这种方法来解释闭包。&lt;/p&gt;    &lt;h3&gt;准备&lt;/h3&gt;    &lt;p&gt;在理解闭包之前，有个重要的概念需要先了解一下，就是 js 执行上下文。&lt;/p&gt;    &lt;p&gt;这篇      &lt;a href="http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/" rel="noopener" target="_blank"&gt;文章&lt;/a&gt;是执行上下文 很不错的入门教程，文章中提到：&lt;/p&gt;    &lt;blockquote&gt;      &lt;p&gt;当代码在 JavaScript 中运行时，执行代码的环境非常重要，并将概括为以下几点：&lt;/p&gt;      &lt;p&gt;全局代码——第一次执行代码的默认环境。&lt;/p&gt;      &lt;p&gt;函数代码——当执行流进入函数体时。&lt;/p&gt;      &lt;p&gt;(…) —— 我们当作 执行上下文 是当前代码执行的一个环境与范围。&lt;/p&gt;&lt;/blockquote&gt;    &lt;p&gt;换句话说，当我们启动程序时，我们从全局执行上下文中开始。一些变量是在全局执行上下文中声明的。我们称之为全局变量。当程序调用一个函数时，会发生什么?&lt;/p&gt;    &lt;p&gt;以下几个步骤：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;JavaScript 创建一个新的执行上下文，我们叫作本地执行上下文。&lt;/li&gt;      &lt;li&gt;这个本地执行上下文将有它自己的一组变量，这些变量将是这个执行上下文的本地变量。&lt;/li&gt;      &lt;li&gt;新的执行上下文被推到到执行堆栈中。可以将执行堆栈看作是一种保存程序在其执行中的位置的容器。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;函数什么时候结束?当它遇到一个 return 语句或一个结束括号}。&lt;/p&gt;    &lt;p&gt;当一个函数结束时，会发生以下情况:&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;这个本地执行上下文从执行堆栈中弹出。&lt;/li&gt;      &lt;li&gt;函数将返回值返回调用上下文。调用上下文是调用这个本地的执行上下文，它可以是全局执行上下文，也可以是另外一个本地的执行上下文。这取决于调用执行上下文来处理此时的返回值，返回的值可以是一个对象、一个数组、一个函数、一个布尔值等等，如果函数没有 return 语句，则返回 undefined。&lt;/li&gt;      &lt;li&gt;这个本地执行上下文被销毁，销毁是很重要，这个本地执行上下文中声明的所有变量都将被删除，不在有变量，这个就是为什么 称为本地执行上下文中自有的变量。&lt;/li&gt;&lt;/ul&gt;    &lt;h4&gt;基础的例子&lt;/h4&gt;    &lt;p&gt;在讨论闭包之前，让我们看一下下面的代码：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1:let a =3            &lt;br /&gt;2:functionaddTwo(x) {            &lt;br /&gt;3:let ret = x +2            &lt;br /&gt;4:return ret            &lt;br /&gt;5: }            &lt;br /&gt;6:let b = addTwo(a)            &lt;br /&gt;7:console.log(b)            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;为了理解 JavaScript 引擎是如何工作的，让我们详细分析一下：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;在第 1 行，我们在全局执行上下文中声明了一个新变量 a，并将赋值为 3。&lt;/li&gt;      &lt;li&gt;接下来就变得棘手了，第 2 行到第 5 行实际上是在一起的。这里发生了什么? 我们在全局执行上下文中声明了一个名为        &lt;code&gt;addTwo&lt;/code&gt;的新变量,我们给它分配了什么?一个函数定义。两个括号{}之间的任何内容都被分配给        &lt;code&gt;addTwo&lt;/code&gt;，函数内部的代码没有被求值，没有被执行，只是存储在一个变量中以备将来使用。&lt;/li&gt;      &lt;li&gt;现在我们在第 6 行。它看起来很简单，但是这里有很多东西需要拆开分析。首先，我们在全局执行上下文中声明一个新变量，并将其标记为        &lt;code&gt;b&lt;/code&gt;，变量一经声明，其值即为 undefined。&lt;/li&gt;      &lt;li&gt;接下来，仍然在第 6 行，我们看到一个赋值操作符。我们准备给变量        &lt;code&gt;b&lt;/code&gt;赋一个新值，接下来我们看到一个函数被调用。当您看到一个变量后面跟着一个圆括号(…)时，这就是调用函数的信号，接着，每个函数都返回一些东西(值、对象或 undefined)，无论从函数返回什么，都将赋值给变量        &lt;code&gt;b&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;但是首先我们需要调用标记为        &lt;code&gt;addTwo&lt;/code&gt;的函数。JavaScript 将在其全局执行上下文内存中查找名为        &lt;code&gt;addTwo&lt;/code&gt;的变量。噢，它找到了一个，它是在步骤 2(或第 2 - 5 行)中定义的。变量        &lt;code&gt;add2&lt;/code&gt;包含一个函数定义。注意，变量        &lt;code&gt;a&lt;/code&gt;作为参数传递给函数。JavaScript 在全局执行上下文内存中搜索变量        &lt;code&gt;a&lt;/code&gt;，找到它，发现它的值是 3，并将数字 3 作为参数传递给函数，准备好执行函数。&lt;/li&gt;      &lt;li&gt;现在执行上下文将切换,创建了一个新的本地执行上下文，我们将其命名为“addTwo 执行上下文”，执行上下文被推送到调用堆栈上。在 addTwo 执行上下文中，我们要做的第一件事是什么?&lt;/li&gt;      &lt;li&gt;你可能会说，“在 addTwo 执行上下文中声明了一个新的变量 ret”，这是不对的。正确的答案是，我们需要先看函数的参数。在 addTwo 执行上下文中声明一个新的变量        &lt;code&gt;x`&lt;/code&gt;，因为值 3 是作为参数传递的，所以变量 x 被赋值为 3。&lt;/li&gt;      &lt;li&gt;下一步是:在 addTwo 执行上下文中声明一个新的变量        &lt;code&gt;ret&lt;/code&gt;。它的值被设置为 undefined(第三行)。&lt;/li&gt;      &lt;li&gt;仍然是第 3 行，需要执行一个相加操作。首先我们需要        &lt;code&gt;x&lt;/code&gt;的值，JavaScript 会寻找一个变量        &lt;code&gt;x&lt;/code&gt;，它会首先在        &lt;code&gt;addTwo&lt;/code&gt;执行上下文中寻找，找到了一个值为 3。第二个操作数是数字 2。两个相加结果为 5 就被分配给变量        &lt;code&gt;ret&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;第 4 行，我们返回变量        &lt;code&gt;ret&lt;/code&gt;的内容，在 addTwo 执行上下文中查找，找到值为 5，返回，函数结束。&lt;/li&gt;      &lt;li&gt;第 4 - 5 行，函数结束。addTwo 执行上下文被销毁，变量        &lt;code&gt;x&lt;/code&gt;和        &lt;code&gt;ret&lt;/code&gt;被消去了，它们已经不存在了。addTwo 执行上下文从调用堆栈中弹出，返回值返回给调用上下文，在这种情况下，调用上下文是全局执行上下文，因为函数        &lt;code&gt;addTw&lt;/code&gt;o 是从全局执行上下文调用的。&lt;/li&gt;      &lt;li&gt;现在我们继续第 4 步的内容，返回值 5 被分配给变量        &lt;code&gt;b&lt;/code&gt;，程序仍然在第 6 行。&lt;/li&gt;      &lt;li&gt;在第 7 行，        &lt;code&gt;b&lt;/code&gt;的值 5 被打印到控制台了。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;对于一个非常简单的程序，这是一个非常冗长的解释，我们甚至还没有涉及闭包。但肯定会涉及的，不过首先我们得绕一两个弯。&lt;/p&gt;    &lt;h3&gt;词法作用域（Lexical scope）&lt;/h3&gt;    &lt;p&gt;我们需要理解词法作用域的一些知识。请看下面的例子：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1:let val1 =2            &lt;br /&gt;2:functionmultiplyThis(n) {            &lt;br /&gt;3:let ret = n * val1            &lt;br /&gt;4:return ret            &lt;br /&gt;5: }            &lt;br /&gt;6:let multiplied = multiplyThis(6)            &lt;br /&gt;7:console.log(&amp;apos;example of scope:&amp;apos;, multiplied)            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;这里想说明，我们在函数执行上下文中有变量，在全局执行上下文中有变量。JavaScript 的一个复杂之处在于它如何查找变量，如果在函数执行上下文中找不到变量，它将在调用上下文中寻找它，如果在它的调用上下文中没有找到，就一直往上一级，直到它在全局执行上下文中查找为止。(如果最后找不到，它就是 undefined)。&lt;/p&gt;    &lt;p&gt;下面列出向个步骤来解释一下（如果你已经熟悉了，请跳过）：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;在全局执行上下文中声明一个新的变量        &lt;code&gt;val1&lt;/code&gt;，并将其赋值为 2。&lt;/li&gt;      &lt;li&gt;行 2 - 5，声明一个新的变量         &lt;code&gt;multiplyThis&lt;/code&gt;，并给它分配一个函数定义。&lt;/li&gt;      &lt;li&gt;第六行，声明一个在全局执行上下文         &lt;code&gt;multiplied&lt;/code&gt; 新变量。&lt;/li&gt;      &lt;li&gt;从全局执行上下文内存中查找变量        &lt;code&gt;multiplyThis&lt;/code&gt;，并将其作为函数执行，传递数字 6 作为参数。&lt;/li&gt;      &lt;li&gt;新函数调用(创建新执行上下文），创建一个新的         &lt;code&gt;multiplyThis&lt;/code&gt; 函数执行上下文。&lt;/li&gt;      &lt;li&gt;在         &lt;code&gt;multiplyThis&lt;/code&gt; 执行上下文中，声明一个变量 n 并将其赋值为 6&lt;/li&gt;      &lt;li&gt;第 3 行。在        &lt;code&gt;multiplyThis&lt;/code&gt;执行上下文中，声明一个变量        &lt;code&gt;ret&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;继续第 3 行。对两个操作数 n 和 val1 进行乘法运算.在        &lt;code&gt;multiplyThis&lt;/code&gt;执行上下文中查找变量         &lt;code&gt;n&lt;/code&gt;。我们在步骤 6 中声明了它,它的内容是数字 6。在        &lt;code&gt;multiplyThis&lt;/code&gt;执行上下文中查找变量        &lt;code&gt;val1&lt;/code&gt;。        &lt;code&gt;multiplyThis&lt;/code&gt;执行上下文没有一个标记为 val1 的变量。我们向调用上下文查找，调用上下文是全局执行上下文，在全局执行上下文中寻找         &lt;code&gt;val1&lt;/code&gt;。哦，是的、在那儿，它在步骤 1 中定义，数值是 2。&lt;/li&gt;      &lt;li&gt;继续第 3 行。将两个操作数相乘并将其赋值给        &lt;code&gt;ret&lt;/code&gt;变量，6 * 2 = 12，ret 现在值为 12。&lt;/li&gt;      &lt;li&gt;返回        &lt;code&gt;ret&lt;/code&gt;变量，销毁        &lt;code&gt;multiplyThis&lt;/code&gt;执行上下文及其变量         &lt;code&gt;ret&lt;/code&gt; 和         &lt;code&gt;n&lt;/code&gt; 。变量         &lt;code&gt;val1&lt;/code&gt; 没有被销毁，因为它是全局执行上下文的一部分。&lt;/li&gt;      &lt;li&gt;回到第 6 行。在调用上下文中，数字 12 赋值给         &lt;code&gt;multiplied&lt;/code&gt; 的变量。&lt;/li&gt;      &lt;li&gt;最后在第 7 行，我们在控制台中打印         &lt;code&gt;multiplied&lt;/code&gt; 变量的值&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;在这个例子中，我们需要记住一个函数可以访问在它的调用上下文中定义的变量，这个就是词法作用域（Lexical scope）。&lt;/p&gt;    &lt;h3&gt;返回函数的函数&lt;/h3&gt;    &lt;p&gt;在第一个例子中，函数      &lt;code&gt;addTwo&lt;/code&gt;返回一个数字。请记住，函数可以返回任何东西。让我们看一个返回函数的函数示例，因为这对于理解闭包非常重要。看粟子：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1:let val =7            &lt;br /&gt;2:functioncreateAdder() {            &lt;br /&gt;3:functionaddNumbers(a, b) {            &lt;br /&gt;4:let ret = a + b            &lt;br /&gt;5:return ret            &lt;br /&gt;6:   }            &lt;br /&gt;7:return addNumbers            &lt;br /&gt;8: }            &lt;br /&gt;9:let adder = createAdder()            &lt;br /&gt;10:let sum = adder(val,8)            &lt;br /&gt;11:console.log(&amp;apos;example of function returning a function: &amp;apos;, sum)            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;让我们回到分步分解：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;第一行。我们在全局执行上下文中声明一个变量        &lt;code&gt;val&lt;/code&gt;并赋值为 7。&lt;/li&gt;      &lt;li&gt;行 2 - 8。我们在全局执行上下文中声明了一个名为         &lt;code&gt;createAdder&lt;/code&gt; 的变量，并为其分配了一个函数定义。第 3 至 7 行描述了上述函数定义，和以前一样，在这一点上，我们没有直接讨论这个函数。我们只是将函数定义存储到那个变量(        &lt;code&gt;createAdder&lt;/code&gt;)中。&lt;/li&gt;      &lt;li&gt;第 9 行。我们在全局执行上下文中声明了一个名为         &lt;code&gt;adder&lt;/code&gt; 的新变量，暂时，值为 undefined。&lt;/li&gt;      &lt;li&gt;第 9 行。我们看到括号()，我们需要执行或调用一个函数，查找全局执行上下文的内存并查找名为        &lt;code&gt;createAdder&lt;/code&gt; 的变量，它是在步骤 2 中创建的。好吧，我们调用它。&lt;/li&gt;      &lt;li&gt;调用函数时，执行到第 2 行。创建一个新的        &lt;code&gt;createAdder&lt;/code&gt;执行上下文。我们可以在        &lt;code&gt;createAdder&lt;/code&gt;的执行上下文中创建自有变量。js 引擎将        &lt;code&gt;createAdder&lt;/code&gt;的上下文添加到调用堆栈。这个函数没有参数，让我们直接跳到它的主体部分.&lt;/li&gt;      &lt;li&gt;第 3 - 6 行。我们有一个新的函数声明，我们在        &lt;code&gt;createAdder&lt;/code&gt;执行上下文中创建一个变量 addNumbers。这很重要，        &lt;code&gt;addnumber&lt;/code&gt;只存在于        &lt;code&gt;createAdder&lt;/code&gt;执行上下文中。我们将函数定义存储在名为         &lt;code&gt;addNumbers`&lt;/code&gt; 的自有变量中。&lt;/li&gt;      &lt;li&gt;在第 7 行，我们返回变量        &lt;code&gt;addNumbers&lt;/code&gt;的内容。js 引擎查找一个名为        &lt;code&gt;addNumbers&lt;/code&gt;的变量并找到它，这是一个函数定义。好的，函数可以返回任何东西，包括函数定义。我们返        &lt;code&gt;addNumbers&lt;/code&gt;的定义。第 4 行和第 5 行括号之间的内容构成该函数定义。&lt;/li&gt;      &lt;li&gt;返回时，        &lt;code&gt;createAdder&lt;/code&gt;执行上下文将被销毁。        &lt;code&gt;addNumbers&lt;/code&gt; 变量不再存在。但        &lt;code&gt;addNumbers&lt;/code&gt;函数定义仍然存在，因为它返回并赋值给了 adder 变量。&lt;/li&gt;      &lt;li&gt;第 10 行。我们在全局执行上下文中定义了一个新的变量         &lt;code&gt;sum&lt;/code&gt;，先负值为 undefined;&lt;/li&gt;      &lt;li&gt;接下来我们需要执行一个函数。哪个函数?是名为        &lt;code&gt;adder&lt;/code&gt;变量中定义的函数。我们在全局执行上下文中查找它，果然找到了它，这个函数有两个参数。&lt;/li&gt;      &lt;li&gt;让我们查找这两个参数，第一个是我们在步骤 1 中定义的变量        &lt;code&gt;val&lt;/code&gt;，它表示数字 7，第二个是数字 8。&lt;/li&gt;      &lt;li&gt;现在我们要执行这个函数，函数定义概述在第 3-5 行，因为这个函数是匿名，为了方便理解，我们暂且叫它        &lt;code&gt;adder&lt;/code&gt;吧。这时创建一个        &lt;code&gt;adder&lt;/code&gt;函数执行上下文，在        &lt;code&gt;adder&lt;/code&gt;执行上下文中创建了两个新变量         &lt;code&gt;a&lt;/code&gt; 和         &lt;code&gt;b&lt;/code&gt;。它们分别被赋值为 7 和 8，因为这些是我们在上一步传递给函数的参数。&lt;/li&gt;      &lt;li&gt;第 4 行。在        &lt;code&gt;adder&lt;/code&gt;执行上下文中声明了一个名为        &lt;code&gt;ret&lt;/code&gt;的新变量,&lt;/li&gt;      &lt;li&gt;第 4 行。将变量        &lt;code&gt;a&lt;/code&gt;的内容和变量        &lt;code&gt;b&lt;/code&gt;的内容相加得 15 并赋给 ret 变量。&lt;/li&gt;      &lt;li&gt;        &lt;code&gt;ret&lt;/code&gt;变量从该函数返回。这个匿名函数执行上下文被销毁，从调用堆栈中删除，变量        &lt;code&gt;a&lt;/code&gt;、        &lt;code&gt;b&lt;/code&gt;和        &lt;code&gt;ret&lt;/code&gt;不再存在。&lt;/li&gt;      &lt;li&gt;返回值被分配给我们在步骤 9 中定义的        &lt;code&gt;sum&lt;/code&gt;变量。&lt;/li&gt;      &lt;li&gt;我们将        &lt;code&gt;sum&lt;/code&gt;的值打印到控制台。&lt;/li&gt;      &lt;li&gt;如预期，控制台将打印 15。我们在这里确实经历了很多困难，我想在这里说明几点。首先，函数定义可以存储在变量中，函数定义在程序调用之前是不可见的。其次，每次调用函数时，都会(临时)创建一个本地执行上下文。当函数完成时，执行上下文将消失。函数在遇到 return 或右括号}时执行完成。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;码部署后可能存在的 BUG 没法实时知道，事后为了解决这些 BUG，花了大量的时间进行 log 调试，这边顺便给大家推荐一个好用的 BUG 监控工具       &lt;a href="https://www.fundebug.com/?utm_source=xiaozhi" rel="noopener" target="_blank"&gt;Fundebug&lt;/a&gt;。&lt;/p&gt;    &lt;h3&gt;最后,一个闭包&lt;/h3&gt;    &lt;p&gt;看看下面的代码，并试着弄清楚会发生什么。&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1:functioncreateCounter() {            &lt;br /&gt;2:let counter =0            &lt;br /&gt;3:const myFunction =function() {            &lt;br /&gt;4:     counter = counter +1            &lt;br /&gt;5:return counter            &lt;br /&gt;6:   }            &lt;br /&gt;7:return myFunction            &lt;br /&gt;8: }            &lt;br /&gt;9:const increment = createCounter()            &lt;br /&gt;10:const c1 = increment()            &lt;br /&gt;11:const c2 = increment()            &lt;br /&gt;12:const c3 = increment()            &lt;br /&gt;13:console.log(&amp;apos;example increment&amp;apos;, c1, c2, c3)            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;现在，我们已经从前两个示例中掌握了它的诀窍，让我们按照预期的方式快速执行它：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;行 1 - 8。我们在全局执行上下文中创建了一个新的变量        &lt;code&gt;createCounter&lt;/code&gt;，并赋值了一个的函数定义。&lt;/li&gt;      &lt;li&gt;第 9 行。我们在全局执行上下文中声明了一个名为        &lt;code&gt;increment&lt;/code&gt;的新变量。&lt;/li&gt;      &lt;li&gt;第 9 行。我们需要调用        &lt;code&gt;createCounter&lt;/code&gt;函数并将其返回值赋给        &lt;code&gt;increment&lt;/code&gt;变量。&lt;/li&gt;      &lt;li&gt;行 1 - 8。调用函数，创建新的本地执行上下文。&lt;/li&gt;      &lt;li&gt;第 2 行。在本地执行上下文中，声明一个名为        &lt;code&gt;counter&lt;/code&gt;的新变量并赋值为 0;&lt;/li&gt;      &lt;li&gt;行 3 - 6。声明一个名为        &lt;code&gt;myFunction&lt;/code&gt;的新变量，变量在本地执行上下文中声明,变量的内容是为第 4 行和第 5 行所定义。&lt;/li&gt;      &lt;li&gt;第 7 行。返回        &lt;code&gt;myFunction&lt;/code&gt;变量的内容，删除本地执行上下文。变量        &lt;code&gt;myFunction&lt;/code&gt;和        &lt;code&gt;counter&lt;/code&gt;不再存在。此时控制权回到了调用上下文。&lt;/li&gt;      &lt;li&gt;第 9 行。在调用上下文(全局执行上下文)中，        &lt;code&gt;createCounter&lt;/code&gt;返回的值赋给了        &lt;code&gt;increment&lt;/code&gt;，变量        &lt;code&gt;increment&lt;/code&gt;现在包含一个函数定义内容为        &lt;code&gt;createCounter&lt;/code&gt;返回的函数。它不再标记为        &lt;code&gt;myFunction`&lt;/code&gt;，但它的定义是相同的。在全局上下文中，它是的标记为         &lt;code&gt;labeledincrement&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;第 10 行。声明一个新变量(c1)。&lt;/li&gt;      &lt;li&gt;继续第 10 行。查找        &lt;code&gt;increment&lt;/code&gt;变量，它是一个函数并调用它。它包含前面返回的函数定义，如第 4-5 行所定义的。&lt;/li&gt;      &lt;li&gt;创建一个新的执行上下文。没有参数。开始执行函数。&lt;/li&gt;      &lt;li&gt;第 4 行。counter=counter + 1。在本地执行上下文中查找        &lt;code&gt;counter&lt;/code&gt;变量。我们只是创建了那个上下文，从来没有声明任何局部变量。让我们看看全局执行上下文。这里也没有        &lt;code&gt;counter&lt;/code&gt;变量。Javascript 会将其计算为 counter = undefined + 1，声明一个标记为        &lt;code&gt;counter&lt;/code&gt;的新局部变量，并将其赋值为 number 1，因为 undefined 被当作值为 0。&lt;/li&gt;      &lt;li&gt;第 5 行。我们变量        &lt;code&gt;counter&lt;/code&gt;的值(1)，我们销毁本地执行上下文和        &lt;code&gt;counter&lt;/code&gt;变量。&lt;/li&gt;      &lt;li&gt;回到第 10 行。返回值(1)被赋给 c1。&lt;/li&gt;      &lt;li&gt;第 11 行。重复步骤 10-14，c2 也被赋值为 1。&lt;/li&gt;      &lt;li&gt;第 12 行。重复步骤 10-14，c3 也被赋值为 1。&lt;/li&gt;      &lt;li&gt;第 13 行。我们打印变量 c1 c2 和 c3 的内容。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;你自己试试，看看会发生什么。你会将注意到，它并不像从我上面的解释中所期望的那样记录 1,1,1。而是记录 1,2,3。这个是为什么?&lt;/p&gt;    &lt;p&gt;不知怎么滴，      &lt;code&gt;increment&lt;/code&gt;函数记住了那个      &lt;code&gt;cunter&lt;/code&gt;的值。这是怎么回事?&lt;/p&gt;    &lt;p&gt;      &lt;code&gt;counter&lt;/code&gt;是全局执行上下文的一部分吗?尝试 console.log(counter)，得到 undefined 的结果,显然不是这样的。&lt;/p&gt;    &lt;p&gt;也许，当你调用      &lt;code&gt;increment&lt;/code&gt;时，它会以某种方式返回它创建的函数(createCounter)?这怎么可能呢?变量      &lt;code&gt;increment&lt;/code&gt;包含函数定义，而不是函数的来源，显然也不是这样的。&lt;/p&gt;    &lt;p&gt;所以一定有另一种机制。闭包，我们终于找到了，丢失的那块。&lt;/p&gt;    &lt;p&gt;它是这样工作的，无论何时声明新函数并将其赋值给变量，都要存储函数定义和闭包。闭包包含在函数创建时作用域中的所有变量，它类似于背包。函数定义附带一个小背包，它的包中存储了函数定义创建时作用域中的所有变量。&lt;/p&gt;    &lt;p&gt;所以我们上面的解释都是错的，让我们再试一次，但是这次是正确的。&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1:functioncreateCounter() {            &lt;br /&gt;2:let counter =0            &lt;br /&gt;3:const myFunction =function() {            &lt;br /&gt;4:     counter = counter +1            &lt;br /&gt;5:return counter            &lt;br /&gt;6:   }            &lt;br /&gt;7:return myFunction            &lt;br /&gt;8: }            &lt;br /&gt;9:const increment = createCounter()            &lt;br /&gt;10:const c1 = increment()            &lt;br /&gt;11:const c2 = increment()            &lt;br /&gt;12:const c3 = increment()            &lt;br /&gt;13:console.log(&amp;apos;example increment&amp;apos;, c1, c2, c3)            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;ul&gt;      &lt;li&gt;同上， 行 1 - 8。我们在全局执行上下文中创建了一个新的变量 createCounter，它得到了指定的函数定义。&lt;/li&gt;      &lt;li&gt;同上，第 9 行。我们在全局执行上下文中声明了一个名为        &lt;code&gt;increment&lt;/code&gt;的新变量。&lt;/li&gt;      &lt;li&gt;同上，第 9 行。我们需要调用        &lt;code&gt;createCounter&lt;/code&gt;函数并将其返回值赋给        &lt;code&gt;increment&lt;/code&gt;变量。&lt;/li&gt;      &lt;li&gt;同上，行 1 - 8。调用函数，创建新的本地执行上下文。&lt;/li&gt;      &lt;li&gt;同上，第 2 行。在本地执行上下文中，声明一个名为        &lt;code&gt;counter&lt;/code&gt;的新变量并赋值为 0 。&lt;/li&gt;      &lt;li&gt;行 3 - 6。声明一个名为        &lt;code&gt;myFunction&lt;/code&gt;的新变量，变量在本地执行上下文中声明,变量的内容是另一个函数定义。如第 4 行和第 5 行所定义，现在我们还创建了一个闭包，并将其作为函数定义的一部分。闭包包含作用域中的变量，在本例中是变量        &lt;code&gt;counter&lt;/code&gt;(值为 0)。&lt;/li&gt;      &lt;li&gt;第 7 行。返回        &lt;code&gt;myFunction&lt;/code&gt;变量的内容,删除本地执行上下文。        &lt;code&gt;myFunction&lt;/code&gt;和        &lt;code&gt;counter&lt;/code&gt;不再存在。控制权交给了调用上下文，我们返回函数定义和它的闭包，闭包中包含了创建它时在作用域内的变量。&lt;/li&gt;      &lt;li&gt;第 9 行。在调用上下文(全局执行上下文)中，        &lt;code&gt;createCounter&lt;/code&gt;返回的值被指定为        &lt;code&gt;increment&lt;/code&gt;，变量        &lt;code&gt;increment&lt;/code&gt;现在包含一个函数定义(和闭包),由 createCounter 返回的函数定义,它不再标记为        &lt;code&gt;myFunction&lt;/code&gt;，但它的定义是相同的,在全局上下文中，称为        &lt;code&gt;increment&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;第 10 行。声明一个新变量(c1)。&lt;/li&gt;      &lt;li&gt;继续第 10 行。查找变量        &lt;code&gt;increment&lt;/code&gt;，它是一个函数，调用它。它包含前面返回的函数定义,如第 4-5 行所定义的。(它还有一个带有变量的闭包)。&lt;/li&gt;      &lt;li&gt;创建一个新的执行上下文，没有参数，开始执行函数。&lt;/li&gt;      &lt;li&gt;第 4 行。counter = counter + 1，寻找变量         &lt;code&gt;counter&lt;/code&gt;，在查找本地或全局执行上下文之前，让我们检查一下闭包，瞧，闭包包含一个名为        &lt;code&gt;counter&lt;/code&gt;的变量，其值为 0。在第 4 行表达式之后，它的值被设置为 1。它再次被储存在闭包里，闭包现在包含值为 1 的变量         &lt;code&gt;counter&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;第 5 行。我们返回        &lt;code&gt;counter的值&lt;/code&gt;，销毁本地执行上下文。&lt;/li&gt;      &lt;li&gt;回到第 10 行。返回值(1)被赋给变量        &lt;code&gt;c1&lt;/code&gt;。&lt;/li&gt;      &lt;li&gt;第 11 行。我们重复步骤 10-14。这一次，在闭包中此时变量        &lt;code&gt;counter&lt;/code&gt;的值是 1。它在第 12 步设置的，它的值被递增并以 2 的形式存储在递增函数的闭包中,c2 被赋值为 2。&lt;/li&gt;      &lt;li&gt;第 12 行。重复步骤 10-14,        &lt;code&gt;c3&lt;/code&gt;被赋值为 3。&lt;/li&gt;      &lt;li&gt;第 13 行。我们打印变量 c1 c2 和 c3 的值。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;您可能会问，是否有任何函数具有闭包，甚至是在全局范围内创建的函数?答案是肯定的。在全局作用域中创建的函数创建闭包，但是由于这些函数是在全局作用域中创建的，所以它们可以访问全局作用域中的所有变量，闭包的概念并不重要。&lt;/p&gt;    &lt;p&gt;当函数返回函数时，闭包的概念就变得更加重要了。返回的函数可以访问不属于全局作用域的变量，但它们仅存在于其闭包中。&lt;/p&gt;    &lt;h3&gt;闭包不是那么简单&lt;/h3&gt;    &lt;p&gt;有时候闭包在你甚至没有注意到它的时候就会出现，你可能已经看到了我们称为部分应用程序的示例，如下面的代码所示：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;let c =4;            &lt;br /&gt;const addX =x =&amp;gt; n =&amp;gt; n + x;            &lt;br /&gt;const addThree = addX(3);            &lt;br /&gt;let d = addThree(c);            &lt;br /&gt;console.log(&amp;quot;example partial application&amp;quot;, d);            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;如果箭头函数让您感到困惑，下面是同样效果：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;let c =4;            &lt;br /&gt;functionaddX(x) {            &lt;br /&gt;returnfunction(n) {            &lt;br /&gt;return n + x;            &lt;br /&gt;};            &lt;br /&gt;}            &lt;br /&gt;const addThree = addX(3);            &lt;br /&gt;let d = addThree(c);            &lt;br /&gt;console.log(&amp;quot;example partial application&amp;quot;, d);            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;我们声明一个能用加法函数      &lt;code&gt;addX&lt;/code&gt;，它接受一个参数(x)并返回另一个函数。返回的函数还接受一个参数并将其添加到变量      &lt;code&gt;x&lt;/code&gt;中。&lt;/p&gt;    &lt;p&gt;变量      &lt;code&gt;x&lt;/code&gt;是闭包的一部分，当变量      &lt;code&gt;addThree&lt;/code&gt;在本地上下文中声明时，它被分配一个函数定义和一个闭包，闭包包含变量 x。&lt;/p&gt;    &lt;p&gt;所以当      &lt;code&gt;addThree&lt;/code&gt;被调用并执行时，它可以从闭包中访问变量      &lt;code&gt;x&lt;/code&gt;以及为参数传递变量      &lt;code&gt;n&lt;/code&gt;并返回两者的和 7。&lt;/p&gt;    &lt;h3&gt;总结&lt;/h3&gt;    &lt;p&gt;我将永远记住闭包的方法是通过背包的类比。当一个函数被创建并传递或从另一个函数返回时，它会携带一个背包。背包中是函数声明时作用域内的所有变量。&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/59303-%E7%90%86%E8%A7%A3-javascript-%E9%97%AD%E5%8C%85</guid>
      <pubDate>Fri, 22 Feb 2019 07:14:40 CST</pubDate>
    </item>
    <item>
      <title>【JavaScript】获取页面停留时间并提交 - 枫芸志</title>
      <link>https://itindex.net/detail/57948-javascript-%E9%A1%B5%E9%9D%A2-%E6%97%B6%E9%97%B4</link>
      <description>&lt;ul&gt;    &lt;p&gt;花了点时间研究了下JavaScript获取页面停留时间并提交的方法，小结一下。&lt;/p&gt;    &lt;p&gt;《      &lt;a href="http://www.doyj.com/1970/01/01/%e9%9b%aa%e5%b1%b1/"&gt;页面停留时间为什么为0&lt;/a&gt;》中介绍Google Analytic的算法是：当发现一个访问者进入一个网站访问了第一个页面时，会记录下他的访问时间。等到他访问第二个页面时，记录下第二个时间，用第二个时间和第一个时间的差值作为这个访问者在第一个页面的停留时间。 以此类推，每个页面的停留时间就是用户访问下个页面时的时间减去他访问这个页面时的时间，并加入到这个页面总的停留时间中。 这个方法在诸多统计方法里，是最简单的，也比较合理，并且计算量最小。 但有个问题，就是用户访问的最后一个页面， 它没有下一个页面，那么怎么计算它的停留时间呢？ 用户访问的最后一个页面的停留时间被认为是0。&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;div&gt;      &lt;br /&gt;      &lt;br /&gt;&lt;/div&gt;    &lt;p&gt;下面是自己google一番后找到的两种实现方法。原理相同，皆是在页面加载后JS记录一个开始时间，在离开页面时（跳转到别的页面或是页面刷新，不包括页面关闭）记录结束时间，减一下就是页面停留时间，利用最简单的异步提交方式提交数据。&lt;/p&gt;    &lt;p&gt;方法一代码如下：1.html&lt;/p&gt;    &lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;JavaScript获取页面停留时间并提交-1&amp;lt;/title&amp;gt;
&amp;lt;script language=JavaScript&amp;gt;
var start = new Date();;
var end;
window.onbeforeunload = function(){
   end = new Date();
   var len = (end.getTime() - start.getTime()) / 1000;
   var img = new Image();
   img.src = &amp;quot;log.php?visitlength=&amp;quot; + len + &amp;quot;&amp;amp;visitpage=1.html&amp;quot;;
}
&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h2&amp;gt;JavaScript获取页面停留时间并提交-1&amp;lt;/h2&amp;gt;
  &amp;lt;a href=&amp;quot;1.html&amp;quot;&amp;gt;JavaScript获取页面停留时间并提交-1&amp;lt;/a&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;a href=&amp;quot;2.html&amp;quot;&amp;gt;JavaScript获取页面停留时间并提交-2&amp;lt;/a&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;a href=&amp;quot;http://witmax.cn&amp;quot;&amp;gt;晴枫&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;    &lt;p&gt;方法二代码如下：2.html&lt;/p&gt;    &lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;JavaScript获取页面停留时间并提交-2&amp;lt;/title&amp;gt;
&amp;lt;script language=JavaScript&amp;gt;
var start;
var end;
function load() {
   start=new Date();
}
function unload() {
   end=new Date();
   var len = (end.getTime() - start.getTime()) / 1000;
   var img = new Image();
   img.src = &amp;quot;log.php?visitlength=&amp;quot; + len + &amp;quot;&amp;amp;visitpage=2.html&amp;quot;;
}
&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body onload=&amp;quot;load()&amp;quot; onunload=&amp;quot;unload()&amp;quot;&amp;gt;
&amp;lt;h2&amp;gt;JavaScript获取页面停留时间并提交-2&amp;lt;/h2&amp;gt;
  &amp;lt;a href=&amp;quot;1.html&amp;quot;&amp;gt;JavaScript获取页面停留时间并提交-1&amp;lt;/a&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;a href=&amp;quot;2.html&amp;quot;&amp;gt;JavaScript获取页面停留时间并提交-2&amp;lt;/a&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;a href=&amp;quot;http://witmax.cn&amp;quot;&amp;gt;晴枫&amp;lt;/a&amp;gt;制作
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;    &lt;p&gt;服务器端采用PHP记录提交的数据，代码如下：log.php&lt;/p&gt;    &lt;pre&gt;&amp;lt;?php

$visittime = date(&amp;quot;Y-m-d H:i:s&amp;quot;);
$visitlength = $_GET[&amp;apos;visitlength&amp;apos;];
$visitpage = $_GET[&amp;apos;visitpage&amp;apos;];
$log = file_get_contents(&amp;quot;log.txt&amp;quot;);
file_put_contents(&amp;quot;log.txt&amp;quot;, $log.&amp;quot;\n&amp;quot;.$visittime .&amp;quot; &amp;quot; .$visitlength .&amp;quot; &amp;quot; .$visitpage );

?&amp;gt;&lt;/pre&gt;    &lt;p&gt;以上两种方法效果相同，缺陷也是一样，就是通过关闭标签页的方式关闭页面不会提交数据，暂未找到更好的解决方案。&lt;/p&gt;    &lt;p&gt;打包了下源码，需要的点击      &lt;a href="http://witmax.cn/downloads/27" target="_blank" title=""&gt;JS获取页面停留时间并提交的源码&lt;/a&gt;下载&lt;/p&gt;    &lt;div&gt;      &lt;input name="wurl" type="hidden" value="http://witmax.cn/js-submit-page-visit-time.html"&gt;&lt;/input&gt;      &lt;br /&gt;      &lt;input name="wtitle" type="hidden" value="&amp;#12304;JavaScript&amp;#12305;&amp;#33719;&amp;#21462;&amp;#39029;&amp;#38754;&amp;#20572;&amp;#30041;&amp;#26102;&amp;#38388;&amp;#24182;&amp;#25552;&amp;#20132;"&gt;&lt;/input&gt;      &lt;br /&gt;      &lt;input name="wpic" type="hidden" value=""&gt;&lt;/input&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;    &lt;div&gt;      &lt;div&gt;本博客所有文章如无特别注明均为原创。        &lt;br /&gt;复制或转载请以超链接形式注明转自        &lt;a href="http://witmax.cn"&gt;枫芸志&lt;/a&gt;，原文地址《        &lt;a href="http://witmax.cn/js-submit-page-visit-time.html" rel="bookmark" title=""&gt;【JavaScript】获取页面停留时间并提交&lt;/a&gt;》&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;标签：      &lt;a href="http://witmax.cn/tag/javascript" rel="tag"&gt;Javascript&lt;/a&gt;&lt;/div&gt;    &lt;div&gt;分享：      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;      &lt;a&gt;&lt;/a&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/ul&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/57948-javascript-%E9%A1%B5%E9%9D%A2-%E6%97%B6%E9%97%B4</guid>
      <pubDate>Sat, 20 Jan 2018 09:47:36 CST</pubDate>
    </item>
    <item>
      <title>巧用 Img / JavaScript 采集页面数据-云栖社区</title>
      <link>https://itindex.net/detail/57943-img-javascript-%E9%A1%B5%E9%9D%A2</link>
      <description>&lt;div&gt;    &lt;p&gt;我们发送重要邮件时为了确认对方已读，都会在邮件中设置一个“读取回执&amp;quot;标签以确定对方时候读信。&lt;/p&gt;    &lt;p&gt;这种模式用途很广，例如：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;发送传单时，确保对方已读&lt;/li&gt;      &lt;li&gt;推广网页时，多少用户做了点击&lt;/li&gt;      &lt;li&gt;移动App运营活动页面，分析用户访问情况&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;对这类个性化的采集与统计，针对站长CNZZ、百度统计，移动的Talking Data、友盟等都无法胜任。主要难点在于：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;个性化需求难满足：用户产生行为并非移动端场景，其中会包括一些运营个性化需求字段，例如：来源、渠道、环境、行为等参数&lt;/li&gt;      &lt;li&gt;开发难度大/成本高：为完成一次数据采集、分析需求，首先需要购买云主机，公网IP，开发数据接收服务器，消息中间件等，并且通过互备保障服务高可用；接下来需要开发服务端并进行测试&lt;/li&gt;      &lt;li&gt;使用不容易：数据达到服务端后，还需要工程师先清洗结果并导入数据库，生成运营需要的数据&lt;/li&gt;      &lt;li&gt;无法弹性：无法预估用户的使用量，因此需要预留很大的资源池&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;从以上几点看，当一个面向内容投放的运营需求来了后，如何能以很快捷的手段满足这类用户行为采集、分析需求是一个很大的挑战。&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://m.aliyun.com/yunqi/articles/www.aliyun.com/product/sls"&gt;日志服务&lt;/a&gt;提供      &lt;a href="https://help.aliyun.com/document_detail/31752.html"&gt;Web Tracking/JS/Tracking Pixel SDK&lt;/a&gt;就是为解决以上轻量级埋点采集场景而生，我们可以在1分钟时间内完成埋点和数据上报工作。此外日志服务每账号*每月提供 500MB      &lt;a href="https://www.aliyun.com/price/product?spm=5176.55536.857803.practice.12e37c02LUJddO#/sls/detail"&gt;免费额度&lt;/a&gt;，让你不花钱也能办事。&lt;/p&gt;    &lt;h3&gt;方案简介&lt;/h3&gt;    &lt;p&gt;这里引入采集 + 分析方案基于阿里云日志服务，该服务是针对日志类数据的一站式服务，无需开发就能快捷完成海量日志数据的采集、消费、投递以及查询分析等功能，提升运维、运营效率。      &lt;a href="https://help.aliyun.com/document_detail/48869.html"&gt;服务功能&lt;/a&gt;包括：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;LogHub：实时采集与消费。与Blink、Flink、Spark Streaming、Storm、Kepler打通。&lt;/li&gt;      &lt;li&gt;数据投递：LogShipper。与MaxCompute、E-MapReduce、OSS、FunctionCompute打通&lt;/li&gt;      &lt;li&gt;查询与实时分析：LogSearch/Analytics。与DataV，Grafana，Zipkin，Tableua等打通。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;img alt="Snip20180116_56.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/48bb95b6678b0286eb322208c068e053.png" title="Snip20180116_56.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;h3&gt;采集端介绍&lt;/h3&gt;    &lt;p&gt;日志服务提供      &lt;a href="https://help.aliyun.com/document_detail/28981.html"&gt;30+数据采集方式&lt;/a&gt;，针对服务器、移动端、嵌入式设备及各种开发语言都提供完整的解决方案，比较典型有：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;Logtail：针对X86服务器设计Agent&lt;/li&gt;      &lt;li&gt;Android/iOS：针对移动端SDK&lt;/li&gt;      &lt;li&gt;Producer Library：面向受限CPU/内存 智能设备&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;img alt="Snip20180116_58.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/d288f0fa291e139dab2c2deecf8a3262.png" title="Snip20180116_58.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;这里介绍的轻量级采集方案（Web Tracking），该方案只需一个http get请求即可将数据传输至日志服务Logstore端，适应各种无需任何验证的静态网页，广告投放，宣传资料，移动端数据采集。相比其他日志采集方案，特点如下：&lt;/p&gt;    &lt;p&gt;      &lt;img alt="Snip20180116_60.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/038e3fac9101842533665dcee7b83f06.png" title="Snip20180116_60.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;h3&gt;WebTracking接入步骤&lt;/h3&gt;    &lt;p&gt;Web Tracking（也叫Tracking Pixel）术语来自于HTML语法中的图片标签：我们可以在页面上嵌入一个0 Pixel图片，该图片默认对用户不可见，当访问该页面显示加载图片时，会顺带发起一个Get请求到服务端，这个时候就会把参数传给服务端。&lt;/p&gt;    &lt;p&gt;Web Tracking使用步骤如下：&lt;/p&gt;    &lt;ol&gt;      &lt;li&gt;为Logstore打开Web Tracking标签（Logstore默认不允许匿名写，在使用前需要先开通Logstore的Web Tracking开关）&lt;/li&gt;      &lt;li&gt;        &lt;p&gt;通过埋点方式向Logstore写入数据，有三种选择：&lt;/p&gt;        &lt;ol&gt;          &lt;li&gt;            &lt;p&gt;直接通过HTTP Get方式上报数据&lt;/p&gt;            &lt;pre&gt;              &lt;code&gt;curl --request GET &amp;apos;http://${project}.${sls-host}/logstores/${logstore}/track?APIVersion=0.6.0&amp;amp;key1=val1&amp;amp;key2=val2&amp;apos;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;          &lt;li&gt;            &lt;p&gt;通过嵌入HTML 下Image标签，当页面方式时自动上报数据&lt;/p&gt;            &lt;pre&gt;              &lt;code&gt;&amp;lt;img src=&amp;apos;http://${project}.${sls-host}/logstores/${logstore}/track.gif?APIVersion=0.6.0&amp;amp;key1=val1&amp;amp;key2=val2&amp;apos;/&amp;gt;
or
&amp;lt;img src=‘http://${project}.${sls-host}/logstores/${logstore}/track_ua.gif?APIVersion=0.6.0&amp;amp;key1=val1&amp;amp;key2=val2’/&amp;gt; 
track_ua.gif除了将自定义的参数上传外，在服务端还会将http头中的UserAgent、referer也作为日志中的字段。&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;          &lt;li&gt;            &lt;p&gt;通过Java Script SDK 上报数据&lt;/p&gt;            &lt;pre&gt;              &lt;code&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;loghub-tracking.js&amp;quot; async&amp;gt;&amp;lt;/script&amp;gt;

var logger = new window.Tracker(&amp;apos;${sls-host}&amp;apos;,&amp;apos;${project}&amp;apos;,&amp;apos;${logstore}&amp;apos;);
logger.push(&amp;apos;customer&amp;apos;, &amp;apos;zhangsan&amp;apos;);
logger.push(&amp;apos;product&amp;apos;, &amp;apos;iphone 6s&amp;apos;);
logger.push(&amp;apos;price&amp;apos;, 5500);
logger.logger();&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;    &lt;blockquote&gt;      &lt;p&gt;详细步骤        &lt;a href="https://help.aliyun.com/document_detail/31752.html"&gt;参见WebTracking接入文档&lt;/a&gt;。&lt;/p&gt;&lt;/blockquote&gt;    &lt;h3&gt;案例：内容多渠道推广&lt;/h3&gt;    &lt;p&gt;当我们有一个新内容时（例如新功能、新活动、新游戏、新文章），作为运营人员总是迫不及待地希望能尽快传达到用户，因为这是获取用户的第一步、也是最重要的一步。&lt;/p&gt;    &lt;p&gt;以游戏发行作为例子：&lt;/p&gt;    &lt;ol&gt;      &lt;li&gt;市场很大一笔费用进行游戏推广，例如投放了1W次广告&lt;/li&gt;      &lt;li&gt;广告成功加载的有2000人次，约占20%&lt;/li&gt;      &lt;li&gt;其中点击的有800人次&lt;/li&gt;      &lt;li&gt;最终下载并注册账号试玩的往往少之又少&lt;/li&gt;&lt;/ol&gt;    &lt;p&gt;      &lt;img alt="Snip20180116_62.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/b0743ac1a9661de31f31b7e0adc0d547.png" title="Snip20180116_62.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;从以上可见，能够准确、实时地获得内容推广有效性对于业务非常重要。为了打到整体推广目标，运营人员往往会会挑选各个渠道来进行推广，例如：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;用户站内信（Mail），官网博客（Blog），首页文案（Banner等）&lt;/li&gt;      &lt;li&gt;短信，用户Email，传单等&lt;/li&gt;      &lt;li&gt;新浪微博，钉钉用户群，微信公众账号，知乎论坛，今日头条等新媒体&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;img alt="Snip20180116_61.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/261ff6301635fc96654fb68ddbb0f81b.png" title="Snip20180116_61.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;h3&gt;方案设计&lt;/h3&gt;    &lt;ol&gt;      &lt;li&gt;我们日志服务中创建一个Logstore（例如叫：myclick），并开启WebTracking功能&lt;/li&gt;      &lt;li&gt;        &lt;p&gt;为需要宣传的文档（article=1001) 面对每个宣传渠道增加一个标示，并生成Web Tracking标签（以Img标签为例），如下：&lt;/p&gt;        &lt;ol&gt;          &lt;li&gt;            &lt;p&gt;站内信渠道(mailDec)：&lt;/p&gt;            &lt;pre&gt;              &lt;code&gt;&amp;lt;img src=&amp;quot;http://example.cn-hangzhou.log.aliyuncs.com/logstores/myclick/track_ua.gif?APIVersion=0.6.0&amp;amp;from=mailDec&amp;amp;article=1001&amp;quot; alt=&amp;quot;&amp;quot; title=&amp;quot;&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;          &lt;li&gt;            &lt;p&gt;官网渠道(aliyunDoc)：&lt;/p&gt;            &lt;pre&gt;              &lt;code&gt;&amp;lt;img src=&amp;quot;http://example.cn-hangzhou.log.aliyuncs.com/logstores/myclick/track_ua.gif?APIVersion=0.6.0&amp;amp;from=aliyundoc&amp;amp;article=1001&amp;quot; alt=&amp;quot;&amp;quot; title=&amp;quot;&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;          &lt;li&gt;            &lt;p&gt;用户邮箱渠道(email)：&lt;/p&gt;            &lt;pre&gt;              &lt;code&gt;&amp;lt;img src=&amp;quot;http://example.cn-hangzhou.log.aliyuncs.com/logstores/myclick/track_ua.gif?APIVersion=0.6.0&amp;amp;from=email&amp;amp;article=1001&amp;quot; alt=&amp;quot;&amp;quot; title=&amp;quot;&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;          &lt;li&gt;            &lt;pre&gt;              &lt;code&gt;其他更多渠道可以在from参数后加上，也可以在URL中加入更多需要采集的参数&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;      &lt;li&gt;将img标签放置在宣传内容中，就可以散布出去了，我们也可以去散步喝咖啡了&lt;/li&gt;&lt;/ol&gt;    &lt;h3&gt;采集日志分析&lt;/h3&gt;    &lt;p&gt;在完成埋点采集后，我们使用日志服务      &lt;a href="https://help.aliyun.com/document_detail/43772.html"&gt;LogSearch/Analytics&lt;/a&gt;功能可以对海量日志数据进行实时查询与分析。在结果分析可视化上，除      &lt;a href="https://help.aliyun.com/document_detail/59324.html"&gt;自带Dashboard&lt;/a&gt;外，还支持      &lt;a href="https://help.aliyun.com/document_detail/62961.html"&gt;DataV&lt;/a&gt;、      &lt;a href="https://help.aliyun.com/document_detail/60952.html"&gt;Grafana&lt;/a&gt;、Tableua等对接方式，我们这里做一些基本的演示：&lt;/p&gt;    &lt;p&gt;以下是截止目前采集日志数据，我们可以在搜索框中输入关键词进行查询：&lt;/p&gt;    &lt;p&gt;      &lt;img alt="Snip20180117_64.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/cb8bf01cf9c3efc3c4ccaeba5571673b.png" title="Snip20180117_64.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;也可以在查询后输入SQL进行秒级的实时分析并可视化：&lt;/p&gt;    &lt;p&gt;      &lt;img alt="Snip20180117_69.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/9d5b760a1baca542fcc48ae44091f604.png" title="Snip20180117_69.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;blockquote&gt;      &lt;p&gt;除了在日志服务中分析外，&lt;/p&gt;&lt;/blockquote&gt;    &lt;h4&gt;以下是我们对用户点击/阅读日志的实时分析：&lt;/h4&gt;    &lt;ul&gt;      &lt;li&gt;当前投放总流量与阅读数&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;* | select count(1) as c&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;每个小时阅读量的曲线&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;* | select count(1) as c, date_trunc(&amp;apos;hour&amp;apos;,from_unixtime(__time__)) as time group by time order by time desc limit 100000&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;每种渠道阅读量的比例&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;* | select count(1) as c, f group by f desc&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;阅读量来自哪些设备&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;* | select count_if(ua like &amp;apos;%Mac%&amp;apos;)  as mac, count_if(ua like &amp;apos;%Windows%&amp;apos;)  as win, count_if(ua like &amp;apos;%iPhone%&amp;apos;)  as ios, count_if(ua like &amp;apos;%Android%&amp;apos;)  as android&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;阅读量来自哪些省市&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;* | select ip_to_province(__source__) as province, count(1) as c group by province order by c desc limit 100&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;更多字段和分析场景可以参见        &lt;a href="https://help.aliyun.com/document_detail/43772.html"&gt;分析语法&lt;/a&gt;与        &lt;a href="https://help.aliyun.com/document_detail/29090.html"&gt;最佳实践&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;最终可以将这些实时数据配置到一个实时刷新Dashboard中，效果如下:&lt;/p&gt;    &lt;p&gt;      &lt;img alt="Snip20180117_71.png" src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/71293631f5c044dbefcd8227297f0d78.png" title="Snip20180117_71.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;h4&gt;写在最后&lt;/h4&gt;    &lt;blockquote&gt;      &lt;p&gt;当你看完本文时，可以留意这行文字下有个不可见Img标签将这次访问记录下来了，找找看吧：）&lt;/p&gt;&lt;/blockquote&gt;    &lt;p&gt;      &lt;img alt="" src="http://yunlei-statics.cn-hangzhou.log.aliyuncs.com/logstores/click/track_ua.gif?APIVersion=0.6.0&amp;from=yq&amp;article=1001" title=""&gt;&lt;/img&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/57943-img-javascript-%E9%A1%B5%E9%9D%A2</guid>
      <pubDate>Fri, 19 Jan 2018 08:29:09 CST</pubDate>
    </item>
    <item>
      <title>使用JavaScript和MQTT开发物联网应用</title>
      <link>https://itindex.net/detail/57126-javascript-mqtt-%E5%BC%80%E5%8F%91</link>
      <description>&lt;p&gt;如果说Java和C#哪个是最好的开发语言，无疑会挑起程序员之间的相互怒怼，那如果说JavaScript是动态性最好的语言，相信大家都不会有太大的争议。随着越来越多的硬件平台和开发板开始支持JavaScript，JavaScript在硬件端以及物联网领域有了新的机会。&lt;/p&gt;



 &lt;h3&gt;IoT应用开发的数据链路&lt;/h3&gt;

 &lt;p&gt;图1是一个智能家居物联平台的数据链路。&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;  &lt;img alt="&amp;#36825;&amp;#37324;&amp;#20889;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="http://img.blog.csdn.net/20170626174053644" title=""&gt;&lt;/img&gt;&lt;/center&gt; &lt;p&gt;&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;图1  智能家居物联平台的数据链路&lt;/center&gt; &lt;p&gt;&lt;/p&gt;

 &lt;p&gt;一般来说，可以把IoT应用分为如图所示的四层。&lt;/p&gt;

 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;client层：指的是IoT设备，可以是冰箱、空调，也可以是一些温湿度传感器。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;gateway层：大多数场景中gateway是家里的WiFi路由器，也有小部分是基于Zigbee或蓝牙的网关设备。智能生活场景中的gateway数量相对于工业领域要少很多，在工业领域存在大量的边缘计算放在gateway层进行处理（雾计算）。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;cloud云层：这里是集中处理业务的地方。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;应用层：这一层是直接与用户打交道的地方，可以是通过电脑的Web浏览器、手机App，也可以是有屏幕的智能设备的显示器。随着语音技术的发展，无屏设备也可以通过语音交互，作为一个应用存在于物联网的交互层。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;物联设备（下文统称为client），可以是单个设备或多个设备组成的应用场景。比如冰箱把运行的功耗数据、库存数据、温度数据采集，通过gateway发送到cloud层，cloud层收集数据后进行异常判断，做智能模式推荐等业务处理后到application层进行展现和交互。用户可以通过冰箱的设备数据进行模式选择，还可以做一些与设备无关的增值服务，比如听音乐、买菜等，这就是一个智能冰箱的数据链路。还有些client是成组智能场景的，比如温湿度传感器将数据上传到cloud，经过处理和加工，动态控制家中空调的温度，调节空气净化器的运行模式等。这么描述好像没有体现出cloud层的作用，那如果运行模式是用户预先配置好的呢？如“当温度超过25度，请帮我打开空调”，这些业务都可以通过cloud层进行处理。&lt;/p&gt;

 &lt;p&gt;client层的连接方式有WiFi、Bluetooth、Zigbee，而MQTT是为了让物联网设备更加互联互通而出现的应用层数据协议。&lt;/p&gt;



 &lt;h3&gt;MQTT+JavaScript&lt;/h3&gt;

 &lt;p&gt;MQTT是一个长连接的通讯应用层协议，最大的特点是数据精简、消息可靠、Publish-Subscribe模式灵活易用。MQTT已经成为IoT传输的标准协议，应用非常广泛。&lt;/p&gt;

 &lt;p&gt;图2中Client指的是物联网设备。Client通过对Topic的订阅和发布数据管理应用中的数据流动，而Broker是MQTT应用中用于管理Topic的角色。Server是物联网应用中的服务端，用于处理业务逻辑。&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;  &lt;img alt="&amp;#36825;&amp;#37324;&amp;#20889;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="http://img.blog.csdn.net/20170626174329369" title=""&gt;&lt;/img&gt;   &lt;br /&gt;
  &lt;center&gt;图2 MQTT的数据链路图&lt;/center&gt;&lt;/center&gt; &lt;p&gt;&lt;/p&gt;

 &lt;p&gt;MQTT被广泛使用的一个重要的原因是MQTT的生态非常完善，同时也支持JavaScript。因此图2所示的所有链路和模块，都可以通过JavaScript实现。&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;  &lt;img alt="&amp;#36825;&amp;#37324;&amp;#20889;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="http://img.blog.csdn.net/20170626174452818" title=""&gt;&lt;/img&gt;   &lt;br /&gt;
  &lt;center&gt;图3 JavaScript在MQTT架构中常用的架构&lt;/center&gt;&lt;/center&gt; &lt;p&gt;&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;JavaScript在MQTT架构中常用的框架&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;mosca（   &lt;a href="https://github.com/mcollina/mosca"&gt;https://github.com/mcollina/mosca&lt;/a&gt;）&lt;/strong&gt;   &lt;br /&gt;
mosca是一个用JavaScript实现的MQTT Broker。不仅如此，mosca还增加了对数据库，如Redis、MongoDB的支持，用来实现消息数据的存储。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;MQTT.js（   &lt;a href="https://github.com/mqttjs/MQTT.js"&gt;https://github.com/mqttjs/MQTT.js&lt;/a&gt;）&lt;/strong&gt;   &lt;br /&gt;
MQTT.js是官网推荐的JavaScript实现的Client端。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;KOA和Express&lt;/strong&gt;   &lt;br /&gt;
这两者都是非常主流的Node版本的Server，简单易用。&lt;/p&gt;

 &lt;h3&gt;实战物联网应用&lt;/h3&gt;

 &lt;p&gt;这节我们运用之前介绍的框架，自己动手完成一个简单的物联网应用。应用场景如图4所示，温度传感器用于接收温度，并把文档通过MQTT发送到Server端，在Server端进行业务处理，根据温度计算出穿衣提示，通过MQTT把数据发送到特定的Topic，App订阅Topic获取数据后进行展现。&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;  &lt;img alt="&amp;#36825;&amp;#37324;&amp;#20889;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="http://img.blog.csdn.net/20170626174822490" title=""&gt;&lt;/img&gt;   &lt;br /&gt;
  &lt;center&gt;图4  “穿衣提示”业务场景框架&lt;/center&gt;&lt;/center&gt; &lt;p&gt;&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;Broker端的实现&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;Broker端使用mosca，参考网页  &lt;a href="https://github.com/mcollina/mosca"&gt;https://github.com/mcollina/mosca&lt;/a&gt;。&lt;/p&gt;

 &lt;ul&gt;
  &lt;li&gt;安装mosca。&lt;/li&gt;
&lt;/ul&gt;

 &lt;pre&gt;  &lt;code&gt;nmp install mosca --save&lt;/code&gt;&lt;/pre&gt;

 &lt;ul&gt;
  &lt;li&gt;启动mosca。这里需要注意，如果本地没有配置MongoDB，则需要把ascoltatore中的内容全部注释掉。&lt;/li&gt;
&lt;/ul&gt;



 &lt;pre&gt;  &lt;code&gt;var mosca = require(&amp;apos;mosca&amp;apos;);

var ascoltatore = {
  //using ascoltatore
  // type: &amp;apos;mongo&amp;apos;,
  // url: &amp;apos;mongodb://localhost:27017/mqtt&amp;apos;,
  // pubsubCollection: &amp;apos;ascoltatori&amp;apos;,
  // mongo: {}
};

var settings = {
  port: 1883,
  backend: ascoltatore
};

var server = new mosca.Server(settings);

server.on(&amp;apos;clientConnected&amp;apos;, function(client) {
    console.log(&amp;apos;client connected&amp;apos;, client.id);
});

// fired when a message is received
server.on(&amp;apos;published&amp;apos;, function(packet, client) {
  console.log(&amp;apos;Published&amp;apos;, packet.payload); //{&amp;quot;clientId&amp;quot;:&amp;quot;mqttjs_02fea7b4&amp;quot;,&amp;quot;topic&amp;quot;:&amp;quot;/tips&amp;quot;}
  // console.log(&amp;apos;&amp;gt;&amp;gt;&amp;gt;packet&amp;apos;, packet); //{&amp;quot;clientId&amp;quot;:&amp;quot;mqttjs_02fea7b4&amp;quot;,&amp;quot;topic&amp;quot;:&amp;quot;/tips&amp;quot;}
});

server.on(&amp;apos;ready&amp;apos;, setup);

// fired when the mqtt server is ready
function setup() {
  console.log(&amp;apos;Mosca server is up and running&amp;apos;);
}&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;代码完成后，启动文件，本地的一个Broker就跑在localhost的1883端口上了。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;Client端的温度传感器实现&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;Client使用MQTT.js实现，参考网页  &lt;a href="https://github.com/mqttjs/MQTT.js"&gt;https://github.com/mqttjs/MQTT.js&lt;/a&gt;&lt;/p&gt;

 &lt;ul&gt;
  &lt;li&gt;安装&lt;/li&gt;
&lt;/ul&gt;



 &lt;pre&gt;  &lt;code&gt;npm install mqtt --save&lt;/code&gt;&lt;/pre&gt;

 &lt;ul&gt;
  &lt;li&gt;启动&lt;/li&gt;
&lt;/ul&gt;



 &lt;pre&gt;  &lt;code&gt;var mqtt = require(&amp;apos;mqtt&amp;apos;);
var client  = mqtt.connect(&amp;apos;mqtt://localhost:1883&amp;apos;);

client.on(&amp;apos;connect&amp;apos;, function () {
   console.log(&amp;apos;&amp;gt;&amp;gt;&amp;gt; connected&amp;apos;)
   // client.subscribe(&amp;apos;/tips&amp;apos;)
   setInterval(
        ()=&amp;gt;{client.publish(&amp;apos;/temperature&amp;apos;, &amp;apos;30&amp;apos;);},
        3000
    );

})

client.on(&amp;apos;message&amp;apos;, function (topic, message) {
  // message is Buffer
  console.log(message.toString())
})

// client.end();&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;执行Node index后Client就启动了，可以看到在MQTT.connect方法中连接了上一节中启动的Broker地址，连接成功后，Client会输出日志，“&amp;gt;&amp;gt;&amp;gt; connected”，Broker的控制台也会输出Client的连接信息。&lt;/p&gt;

 &lt;p&gt;这里模拟了温度传感器，定时3秒向/temperature的Topic中发送温度数据。&lt;/p&gt;

 &lt;p&gt;本节的温度器可以在电脑中使用Node方式运行，也可以运行在支持JavaScript的开发板中，如RUFF、NodeMCU、Raspberry Pi，并且可以使用真实的传感器。&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;Server的实现&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;Server使用MQTT.js订阅Client发送到/temperature Topic的数据进行处理，把处理后的数据转译成JSON发送到另一业务主题/tips中。&lt;/p&gt;

 &lt;p&gt;实现代码如下：&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;&amp;apos;use strict&amp;apos;

const mqtt = require(&amp;apos;mqtt&amp;apos;);
var client  = mqtt.connect(&amp;apos;mqtt://localhost:1883&amp;apos;);

client.on(&amp;apos;connect&amp;apos;, function () {
   console.log(&amp;apos;&amp;gt;&amp;gt;&amp;gt; connected&amp;apos;);
   client.subscribe(&amp;apos;/temperature&amp;apos;);
})

client.on(&amp;apos;message&amp;apos;, function (topic, message) {
  var temperature = parseInt(message.toString());
  var data = {temperature};

  if (temperature &amp;gt;= 60) {
        data.tips = &amp;quot;热... 500服务器故障&amp;quot;;
  }
  else if (temperature &amp;gt;= 50) {
        data.tips = &amp;quot;今天天气非常热，建议不要穿衣服了&amp;quot;;
  }
  else if (temperature &amp;gt;= 40) {
        data.tips = &amp;quot;今天天气十分的热，建议穿短袖T恤+短裤&amp;quot;;
  }
  else if (temperature &amp;gt;= 30) {
        data.tips = &amp;quot;今天天气有点的热，建议穿短袖T恤&amp;quot;;
  }
  else if (temperature &amp;gt;= 0) {
        data.tips = &amp;quot;今天天气正好，可以穿上一件薄衣服&amp;quot;;
  }
  else if (temperature &amp;gt;= -10) {
        data.tips = &amp;quot;今天天气十分寒冷，棉袄可以穿上一件&amp;quot;;    
  }
  else {
        data.tips = &amp;quot;今天天气十分十分寒冷，棉袄可以穿上二件&amp;quot;;  
  }
  client.publish(&amp;apos;/tips&amp;apos;, JSON.stringify(data));
  // if (temperature+1) {}
  // message is Buffer
  console.log(JSON.stringify(data));
})&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;  &lt;strong&gt;App的实现&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;Demo的App使用KOA启动一个Web，在Web中展现当前温度对应的穿衣提示，通过订阅tips获取数据。&lt;/p&gt;

 &lt;ul&gt;
  &lt;li&gt;安装koa&lt;/li&gt;
&lt;/ul&gt;



 &lt;pre&gt;  &lt;code&gt;$ npm install koa&lt;/code&gt;&lt;/pre&gt;

 &lt;ul&gt;
  &lt;li&gt;实现代码&lt;/li&gt;
&lt;/ul&gt;



 &lt;pre&gt;  &lt;code&gt;&amp;apos;use strict&amp;apos;

const Koa = require(&amp;apos;koa&amp;apos;);
const mqtt = require(&amp;apos;mqtt&amp;apos;);
const app = new Koa();


var msg = {temperature:&amp;quot;-&amp;quot;,tips:&amp;quot;&amp;quot;};
// response
app.use(ctx =&amp;gt; {
  ctx.body = &amp;quot;当前温度:&amp;quot; + msg.temperature + &amp;quot;度&amp;quot; + &amp;quot;\n&amp;quot; + &amp;apos;穿衣提示:&amp;apos;+msg.tips + &amp;quot;\n&amp;quot;  ;
});

app.listen(3000);

//mqtt
var client  = mqtt.connect(&amp;apos;mqtt://localhost:1883&amp;apos;);

client.on(&amp;apos;connect&amp;apos;, function () {
   console.log(&amp;apos;&amp;gt;&amp;gt;&amp;gt; connected&amp;apos;);
   client.subscribe(&amp;apos;/tips&amp;apos;);
})

client.on(&amp;apos;message&amp;apos;, function (topic, message) {
  var data = JSON.parse(message.toString());
  console.log(message.toString()); 
  console.log(data.tips); 
  msg = data;

  // if (temperature+1) {}
  // message is Buffer
  // let str = message.toString();
  // let data = JSON.parse(message);
  // console.log(data.tips);
  // msg =  message.toString();
})&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;  &lt;strong&gt;Demo小节&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;本章给出了一个简单的物联网业务的业务场景和实现逻辑，其中Client也可以运行在电脑上进行Demo查看，或是跑在真实物联设备或开发版上。如图5，笔者使用RUFF开发板实现了一次。&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;  &lt;img alt="&amp;#36825;&amp;#37324;&amp;#20889;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="http://img.blog.csdn.net/20170626175357079" title=""&gt;&lt;/img&gt;   &lt;br /&gt;
  &lt;center&gt;图5  Demo硬件演示&lt;/center&gt;&lt;/center&gt; &lt;p&gt;&lt;/p&gt;

 &lt;p&gt;完整Demo代码已经分享在github中，大家可以输入URL下载。   &lt;br /&gt;
  &lt;a href="https://github.com/coolnameismy/javascript-mqtt-demo-wearingTip"&gt;https://github.com/coolnameismy/javascript-mqtt-demo-wearingTip&lt;/a&gt;&lt;/p&gt;

 &lt;h3&gt;总结&lt;/h3&gt;

 &lt;p&gt;本文和大家交流了物联网应用的一般数据链路、MQTT协议的架构，并基于MQTT实现了一个简单的物联网应用。&lt;/p&gt;

 &lt;p&gt;现在正是前端工程师的大好机会，越来越多的嵌入式设备都开始支持JavaScript，原因是现在有很多JavaScript引擎可以把JavaScript转换成各种平台的底层代码，比较有名的有Jerryscript、Duktape等。随着越来越多的JavaScript工程师进入嵌入式开发的领域，嵌入式应用开发也会出现前后端分离的情况（应用开发或是驱动开发），类似于Web开发的前后端分离。前端关注在应用、创意、数据链路、用户体现上，而后端则关心GPIO、I2C的底层数据接口和驱动，平台兼容性等方向。&lt;/p&gt;

 &lt;p&gt;&lt;/p&gt; &lt;center&gt;  &lt;img alt="&amp;#36825;&amp;#37324;&amp;#20889;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="http://img.blog.csdn.net/20170626173602575" title=""&gt;&lt;/img&gt;&lt;/center&gt; &lt;p&gt;&lt;/p&gt;
 &lt;div&gt;
    作者：tangxiaoyin 发表于2017/6/26 18:00:02   &lt;a href="http://blog.csdn.net/tangxiaoyin/article/details/73743166"&gt;原文链接&lt;/a&gt;
&lt;/div&gt;
 &lt;div&gt;
    阅读：1954 评论：2   &lt;a href="http://blog.csdn.net/tangxiaoyin/article/details/73743166#comments" target="_blank"&gt;查看评论&lt;/a&gt;
&lt;/div&gt;

&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/57126-javascript-mqtt-%E5%BC%80%E5%8F%91</guid>
      <pubDate>Tue, 27 Jun 2017 02:00:02 CST</pubDate>
    </item>
    <item>
      <title>JavaScript 启动性能瓶颈分析与解决方案</title>
      <link>https://itindex.net/detail/56678-javascript-%E6%80%A7%E8%83%BD-%E7%93%B6%E9%A2%88</link>
      <description>&lt;blockquote&gt;  &lt;p&gt;   &lt;a href="https://zhuanlan.zhihu.com/p/25221314"&gt;JavaScript 启动性能瓶颈分析与解决方案&lt;/a&gt; 翻译自 Addy Osmani 的    &lt;a href="https://medium.com/@addyosmani/javascript-start-up-performance-69200f43b201#.58r6ky779"&gt;JavaScript Start-up Performance&lt;/a&gt;，从属于笔者的   &lt;a href="https://github.com/wxyyxc1992/Web-Frontend-Introduction-And-Engineering-Practices"&gt;Web 前端入门与工程实践&lt;/a&gt;。本文已获得原作者授权，为InfoQ中文站特供稿件，首发地址为   &lt;a href="https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&amp;mid=2247484987&amp;idx=1&amp;sn=7f20da20bc6baed62ca8ff115209942b"&gt;这里&lt;/a&gt;；如需转载，请与InfoQ中文站联系。随着现代 Web 技术的发展与用户交互复杂度的增加，我们的网站变得日益臃肿，也要求着我们不断地优化网站性能以保证友好的用户体验。本文作者则着眼于 JavaScript 启动阶段优化，首先以大量的数据分析阐述了语法分析、编译等步骤耗时占比过多是很多网站的性能瓶颈之一。然后作者提供了一系列用于在现代浏览器中进行性能评测的工具，还分别从开发者工程实践与 JavaScript 引擎内部实现的角度阐述了应当如何提高解析与编译速度。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/ZpwZLFDNYZodJDerr7-37A.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在 Web 开发中，随着需求的增加与代码库的扩张，我们最终发布的 Web 页面也逐渐膨胀。不过这种膨胀远不止意味着占据更多的传输带宽，其还意味着用户浏览网页时可能更差劲的性能体验。浏览器在下载完某个页面依赖的脚本之后，其还需要经过语法分析、解释与运行这些步骤。而本文则会深入分析浏览器对于 JavaScript 的这些处理流程，挖掘出那些影响你应用启动时间的罪魁祸首，并且根据我个人的经验提出相对应的解决方案。回顾过去，我们还没有专门地考虑过如何去优化 JavaScript 解析/编译这些步骤；我们预想中的是解析器在发现  &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签后会瞬时完成解析操作，不过这很明显是痴人说梦。下图是对于 V8 引擎工作原理的概述：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/GuWInZljjvtDpdeT6O0emA.png" title=""&gt;&lt;/img&gt;  &lt;br /&gt;下面我们深入其中的关键步骤进行分析。&lt;/p&gt;
 &lt;h1&gt;到底是什么拖慢了我们应用的启动时间？&lt;/h1&gt;
 &lt;p&gt;在启动阶段，语法分析，编译与脚本执行占据了 JavaScript 引擎运行的绝大部分时间。换言之，这些过程造成的延迟会真实地反应到用户可交互时延上；譬如用户已经看到了某个按钮，但是要好几秒之后才能真正地去点击操作，这一点会大大影响用户体验。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/M94-AavlZjGoudZG.png" title=""&gt;&lt;/img&gt;  &lt;br /&gt;上图是我们使用 Chrome Canary 内置的 V8 RunTime Call Stats 对于某个网站的分析结果；需要注意的是桌面浏览器中语法解析与编译占用的时间还是蛮长的，而在移动端中占用的时间则更长。实际上，对于 Facebook, Wikipedia, Reddit 这些大型网站中语法解析与编译所占的时间也不容忽视：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/XjHkzz0B7KlcDbLFD1JS8Q.png" title=""&gt;&lt;/img&gt;  &lt;br /&gt;上图中的粉色区域表示花费在 V8 与 Blink&amp;apos;s C++ 中的时间，而橙色和黄色分别表示语法解析与编译的时间占比。Facebook 的 Sebastian Markbage 与 Google 的 Rob Wormald 也都在 Twitter 发文表示过 JavaScript 的语法解析时间过长已经成为了不可忽视的问题，后者还表示这也是 Angular 启动时主要的消耗之一。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/nkJwMuE5PpgF_pE0e6RM6g.jpeg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;随着移动端浪潮的涌来，我们不得不面对一个残酷的事实：移动端对于相同包体的解析与编译过程要花费相当于桌面浏览器2~5倍的时间。当然，对于高配的 iPhone 或者 Pixel 这样的手机相较于 Moto G4 这样的中配手机表现会好很多；这一点提醒我们在测试的时候不能仅用身边那些高配的手机，而应该中高低配兼顾：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/dnhO1M_zlmAhvtQY_7tZmA.jpeg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;上图是部分桌面浏览器与移动端浏览器对于 1MB 的 JavaScript 包体进行解析的时间对比，显而易见的可以发现不同配置的移动端手机之间的巨大差异。当我们应用包体已经非常巨大的时候，使用一些现代的打包技巧，譬如代码分割，TreeShaking，Service Workder 缓存等等会对启动时间有很大的影响。另一个角度来看，即使是小模块，你代码写的很糟或者使用了很糟的依赖库都会导致你的主线程花费大量的时间在编译或者冗余的函数调用中。我们必须要清醒地认识到全面评测以挖掘出真正性能瓶颈的重要性。&lt;/p&gt;
 &lt;h1&gt;JavaScript 语法解析与编译是否成为了大部分网站的瓶颈？&lt;/h1&gt;
 &lt;p&gt;我曾不止一次听到有人说，我又不是 Facebook，你说的 JavaScript 语法解析与编译到  &lt;br /&gt;底会对其他网站造成什么样的影响呢？对于这个问题我也很好奇，于是我花费了两个月的时间对于超过 6000 个网站进行分析；这些网站囊括了 React，Angular，Ember，Vue 这些流行的框架或者库。大部分的测试是基于 WebPageTest 进行的，因此你可以很方便地重现这些测试结果。  &lt;strong&gt;光纤接入的桌面浏览器大概需要 8 秒的时间才能允许用户交互，而 3G 环境下的 Moto G4 大概需要 16 秒 才能允许用户交互。&lt;/strong&gt;  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/WC4zanI0DKAoSiJVU3VUeA.png" title=""&gt;&lt;/img&gt;  &lt;br /&gt;  &lt;strong&gt;大部分应用在桌面浏览器中会耗费约 4 秒的时间进行 JavaScript 启动阶段（语法解析、编译、执行）&lt;/strong&gt;：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/NacL9cZJ1osZowPS6hbCsQ.jpeg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;而在移动端浏览器中，大概要花费额外 36% 的时间来进行语法解析：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/uTRfB5pne06h8lp5jGtiIQ.jpeg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;另外，统计显示并不是所有的网站都甩给用户一个庞大的 JS 包体，用户下载的经过 Gzip 压缩的平均包体大小是 410KB，这一点与 HTTPArchive 之前发布的 420KB 的数据基本一致。不过最差劲的网站则是直接甩了 10MB 的脚本给用户，简直可怕。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/GvwfE2GjKQyLBKPmmfRwuA.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;通过上面的统计我们可以发现，包体体积固然重要，但是其并非唯一因素，语法解析与编译的耗时也不一定随着包体体积的增长而线性增长。总体而言小的 JavaScript 包体是会加载地更快（忽略浏览器、设备与网络连接的差异），但是同样 200KB 的大小，不同开发者的包体在语法解析、编译上的时间却是天差地别，不可同日而语。&lt;/p&gt;
 &lt;h1&gt;现代 JavaScript 语法解析 &amp;amp; 编译性能评测&lt;/h1&gt;
 &lt;h2&gt;Chrome DevTools&lt;/h2&gt;
 &lt;p&gt;打开 Timeline( Performance panel ) &amp;gt; Bottom-Up/Call Tree/Event Log 就会显示出当前网站在语法解析/编译上的时间占比。如果你希望得到更完整的信息，那么可以打开 V8 的 Runtime Call Stats。在 Canary 中，其位于 Timeline 的 Experims &amp;gt; V8 Runtime Call Stats 下。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/rWkYJzc6Cp0r3Xkr.jpeg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Chrome Tracing&lt;/h2&gt;
 &lt;p&gt;打开 about:tracing 页面，Chrome 提供的底层的追踪工具允许我们使用  &lt;code&gt;disabled-by-default-v8.runtime_stats&lt;/code&gt;来深度了解 V8 的时间消耗情况。V8 也提供了  &lt;a href="https://docs.google.com/presentation/d/1Lq2DD28CGa7bxawVH_2OcmyiTiBn74dvC6vn2essroY/edit#slide=id.g1a504e63c9_2_84"&gt;详细的指南&lt;/a&gt;来介绍如何使用这个功能。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/P-_pLIITtYJRikRN.jpeg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;WebPageTest&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/y6x_vr7aOxK4jHG9blgseg.png" title=""&gt;&lt;/img&gt;  &lt;br /&gt;WebPageTest 中 Processing Breakdown 页面在我们启用 Chrome &amp;gt; Capture Dev Tools Timeline 时会自动记录 V8 编译、EvaluateScript 以及 FunctionCall 的时间。我们同样可以通过指明  &lt;code&gt;disabled-by-default-v8.runtime_stats&lt;/code&gt;的方式来启用 Runtime Call Stats。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/tV48evC-XzYkoHonyKGkOw.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;更多使用说明参考我的  &lt;a href="https://gist.github.com/addyosmani/45b135900a7e3296e22673148ae5165b"&gt;gist&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;User Timing&lt;/h2&gt;
 &lt;p&gt;我们还可以使用 Nolan Lawson 推荐的  &lt;a href="https://w3c.github.io/user-timing/#dom-performance-mark"&gt;User Timing API&lt;/a&gt;来评估语法解析的时间。不过这种方式可能会受 V8 预解析过程的影响，我们可以借鉴 Nolan 在 optimize-js 评测中的方式，在脚本的尾部添加随机字符串来解决这个问题。我基于 Google Analytics 使用相似的方式来评估真实用户与设备访问网站时候的解析时间：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/ziA8f9KhB1gOt-Mq07cRFw.jpg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;DeviceTiming&lt;/h2&gt;
 &lt;p&gt;Etsy 的   &lt;a href="https://github.com/danielmendel/DeviceTiming"&gt;DeviceTiming&lt;/a&gt; 工具能够模拟某些受限环境来评估页面的语法解析与执行时间。其将本地脚本包裹在了某个仪表工具代码内从而使我们的页面能够模拟从不同的设备中访问。可以阅读 Daniel Espeset 的  &lt;a href="http://talks.desp.in/unpacking-the-black-box"&gt;Benchmarking JS Parsing and Execution on Mobile Devices&lt;/a&gt; 一文来了解更详细的使用方式。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/FFzrH2QUiQZFX2rlF5e2-g.jpg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h1&gt;我们可以做些什么以降低 JavaScript 的解析时间？&lt;/h1&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;减少 JavaScript 包体体积。我们在上文中也提及，更小的包体往往意味着更少的解析工作量，也就能降低浏览器在解析与编译阶段的时间消耗。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;使用代码分割工具来按需传递代码与懒加载剩余模块。这可能是最佳的方式了，类似于    &lt;a href="https://developers.google.com/web/fundamentals/performance/prpl-pattern/"&gt;PRPL&lt;/a&gt;这样的模式鼓励基于路由的分组，目前被 Flipkart, Housing.com 与 Twitter 广泛使用。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;Script streaming: 过去 V8 鼓励开发者使用    &lt;code&gt;async/defer&lt;/code&gt;来基于    &lt;a href="https://blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html"&gt;script streaming&lt;/a&gt;实现 10-20% 的性能提升。这个技术会允许 HTML 解析器将相应的脚本加载任务分配给专门的 script streaming 线程，从而避免阻塞文档解析。V8 推荐尽早加载较大的模块，毕竟我们只有一个 streamer 线程。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;评估我们依赖的解析消耗。我们应该尽可能地选择具有相同功能但是加载地更快的依赖，譬如使用 Preact 或者 Inferno 来代替 React，二者相较于 React 体积更小具有更少的语法解析与编译时间。Paul Lewis 在最近的    &lt;a href="https://aerotwist.com/blog/when-everything-is-important-nothing-is/"&gt;一篇文章&lt;/a&gt;中也讨论了框架启动的代价，与 Sebastian Markbage 的    &lt;a href="https://twitter.com/sebmarkbage/status/829733454119989248"&gt;说法&lt;/a&gt;不谋而合：最好地评测某个框架启动消耗的方式就是先渲染一个界面，然后删除，最后进行重新渲染。第一次渲染的过程会包含了分析与编译，通过对比就能发现该框架的启动消耗。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;如果你的 JavaScript 框架支持 AOT（ahead-of-time）编译模式，那么能够有效地减少解析与编译的时间。Angular 应用就受益于这种模式：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/sr4eb-cx3lq7hVrJGfDNaw.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h1&gt;现代浏览器是如何提高解析与编译速度的？&lt;/h1&gt;
 &lt;p&gt;不用灰心，你并不是唯一纠结于如何提升启动时间的人，我们 V8 团队也一直在努力。我们发现之前的某个评测工具 Octane 是个不错的对于真实场景的模拟，它在微型框架与冷启动方面很符合真实的用户习惯。而基于这些工具，V8 团队在过去的工作中也实现了大约 25% 的启动性能提升：  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/1/cE8uvuvb0-iZslygh2NCTQ.jpg" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;本部分我们就会对过去几年中我们使用的提升语法解析与编译时间的技巧进行阐述。&lt;/p&gt;
 &lt;h2&gt;代码缓存&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/2/xChjWSbT1rCqgLMacOMotQ.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Chrome 42 开始引入了所谓的  &lt;a href="http://v8project.blogspot.com/2015/07/code-caching.html"&gt;代码缓存&lt;/a&gt;的概念，为我们提供了一种存放编译后的代码副本的机制，从而当用户二次访问该页面时可以避免脚本抓取、解析与编译这些步骤。除以之外，我们还发现在重复访问的时候这种机制还能避免 40% 左右的编译时间，这里我会深入介绍一些内容：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;代码缓存会对于那些在 72 小时之内重复执行的脚本起作用。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;对于 Service Worker 中的脚本，代码缓存同样对 72 小时之内的脚本起作用。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;对于利用 Service Worker 缓存在 Cache Storage 中的脚本，代码缓存能在脚本首次执行的时候起作用。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;总而言之，对于主动缓存的 JavaScript 代码，最多在第三次调用的时候其能够跳过语法分析与编译的步骤。我们可以通过  &lt;code&gt;chrome://flags/#v8-cache-strategies-for-cache-storage&lt;/code&gt;来查看其中的差异，也可以设置  &lt;code&gt; js-flags=profile-deserialization&lt;/code&gt;运行 Chrome 来查看代码是否加载自代码缓存。不过需要注意的是，代码缓存机制仅会缓存那些经过编译的代码，主要是指那些顶层的往往用于设置全局变量的代码。而对于类似于函数定义这样懒编译的代码并不会被缓存，不过 IIFE 同样被包含在了 V8 中，因此这些函数也是可以被缓存的。&lt;/p&gt;
 &lt;h2&gt;Script Streaming&lt;/h2&gt;
 &lt;p&gt;  &lt;a href="https://blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html"&gt;Script Streaming&lt;/a&gt;允许在后台线程中对异步脚本执行解析操作，可以对于页面加载时间有大概 10% 的提升。上文也提到过，这个机制同样会对同步脚本起作用。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/2/ooXJ0NES-gXEzteaGPL2nQ.png" title=""&gt;&lt;/img&gt;  &lt;br /&gt;这个特性倒是第一次提及，因此 V8 会允许所有的脚本，即使阻塞型的  &lt;code&gt;&amp;lt;script src=&amp;apos;&amp;apos;&amp;gt;&lt;/code&gt;脚本也可以由后台线程进行解析。不过缺陷就是目前仅有一个 streaming 后台线程存在，因此我们建议首先解析大的、关键性的脚本。在实践中，我们建议将  &lt;code&gt;&amp;lt;script defer&amp;gt;&lt;/code&gt;添加到  &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;块内，这样浏览器引擎就能够尽早地发现需要解析的脚本，然后将其分配给后台线程进行处理。我们也可以查看 DevTools Timeline 来确定脚本是否被后台解析，特别是当你存在某个关键性脚本需要解析的时候，更需要确定该脚本是由 streaming 线程解析的。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/2/FAvUG7DrVJUXCK3oweMSLQ.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;语法解析 &amp;amp; 编译优化&lt;/h2&gt;
 &lt;p&gt;我们同样致力于打造更轻量级、更快的解析器，目前 V8 主线程中最大的瓶颈在于所谓的非线性解析消耗。譬如我们有如下的代码片：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function (global, module) { … })(this, function module() { my functions })&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;V8 并不知道我们编译主脚本的时候是否需要  &lt;code&gt;module&lt;/code&gt;这个模块，因此我们会暂时放弃编译它。而当我们打算编译  &lt;code&gt;module&lt;/code&gt;时，我们需要重分析所有的内部函数。这也就是所谓的 V8 解析时间非线性的原因，任何一个处于 N 层深度的函数都有可能被重新分析 N 次。V8 已经能够在首次编译的时候搜集所有内部函数的信息，因此在未来的编译过程中 V8 会忽略所有的内部函数。对于上面这种  &lt;code&gt;module&lt;/code&gt;形式的函数会是很大的性能提升，建议阅读  &lt;a href="https://docs.google.com/presentation/d/1214p4CFjsF-NY4z9in0GEcJtjbyVQgU0A-UqEvovzCs/edit#slide=id.p"&gt;The V8 Parser(s) — Design, Challenges, and Parsing JavaScript Better&lt;/a&gt;来获取更多内容。V8 同样在寻找合适的分流机制以保证启动时能在后台线程中执行 JavaScript 编译过程。&lt;/p&gt;
 &lt;h2&gt;预编译 JavaScript？&lt;/h2&gt;
 &lt;p&gt;每隔几年就有人提出引擎应该提供一些处理预编译脚本的机制，换言之，开发者可以使用构建工具或者其他服务端工具将脚本转化为字节码，然后浏览器直接运行这些字节码即可。从我个人观点来看，直接传送字节码意味着更大的包体，势必会增加加载时间；并且我们需要去对代码进行签名以保证能够安全运行。目前我们对于 V8 的定位是尽可能地避免上文所说的内部重分析以提高启动时间，而预编译则会带来额外的风险。不过我们欢迎大家一起来讨论这个问题，虽然 V8 目前专注于提升编译效率以及推广利用 Service Worker 缓存脚本代码来提升启动效率。我们在 BlinkOn7 上与 Facebook 以及 Akamai 也讨论过  &lt;a href="https://gist.github.com/addyosmani/4009ee1238c4b1ff6f2a2d8a5057c181"&gt;预编译相关内容&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;Optimize JS 优化&lt;/h2&gt;
 &lt;p&gt;类似于 V8 这样的 JavaScript 引擎在进行完整的解析之前会对脚本中的大部分函数进行预解析，这主要是考虑到大部分页面中包含的 JavaScript 函数并不会立刻被执行。  &lt;br /&gt;  &lt;img alt="" src="https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/2/2/LMRg_jHJeP53vdy8aiTEJQ.png" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;预编译能够通过只处理那些浏览器运行所需要的最小函数集合来提升启动时间，不过这种机制在 IIFE 面前却反而降低了效率。尽管引擎希望避免对这些函数进行预处理，但是远不如  &lt;a href="https://github.com/nolanlawson/optimize-js"&gt;optimize-js&lt;/a&gt;这样的库有作用。optimize-js 会在引擎之前对于脚本进行处理，对于那些立即执行的函数插入圆括号从而保证更快速地执行。这种预处理对于 Browserify, Webpack 生成包体这样包含了大量即刻执行的小模块起到了非常不错的优化效果。尽管这种小技巧并非 V8 所希望使用的，但是在当前阶段不得不引入相应的优化机制。&lt;/p&gt;
 &lt;h1&gt;总结&lt;/h1&gt;
 &lt;p&gt;启动阶段的性能至关重要，缓慢的解析、编译与执行时间可能成为你网页性能的瓶颈所在。我们应该评估页面在这个阶段的时间占比并且选择合适的方式来优化。我们也会继续致力于提升 V8 的启动性能，尽我所能！&lt;/p&gt;
 &lt;h1&gt;延伸阅读&lt;/h1&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="https://www.youtube.com/watch?v=RWLzUnESylc"&gt;Planning for Performance&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="https://twitter.com/MSEdgeDev/status/819985530775404544"&gt;Solving the Web Performance Crisis by Nolan Lawson&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="https://timkadlec.com/2014/09/js-parse-and-execution-time/"&gt;JS Parse and Execution Time&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="http://carlos.bueno.org/2010/02/measuring-javascript-parse-and-load.html"&gt;Measuring Javascript Parse and Load&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="https://www.safaribooksonline.com/library/view/velocity-conference-new/9781491900406/part78.html"&gt;Unpacking the Black Box: Benchmarking JS Parsing and Execution on Mobile Devices&lt;/a&gt; (    &lt;a href="https://speakerdeck.com/desp/unpacking-the-black-box-benchmarking-js-parsing-and-execution-on-mobile-devices"&gt;slides&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="https://aerotwist.com/blog/when-everything-is-important-nothing-is/"&gt;When everything’s important, nothing is!&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="http://benediktmeurer.de/2016/12/16/the-truth-about-traditional-javascript-benchmarks/"&gt;The truth about traditional JavaScript benchmarks&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="http://stackoverflow.com/questions/1096907/do-browsers-parse-javascript-on-every-page-load/"&gt;Do Browsers Parse JavaScript On Every Page Load&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>javascript v8 性能优化</category>
      <guid isPermaLink="true">https://itindex.net/detail/56678-javascript-%E6%80%A7%E8%83%BD-%E7%93%B6%E9%A2%88</guid>
      <pubDate>Mon, 13 Feb 2017 22:28:59 CST</pubDate>
    </item>
    <item>
      <title>加快JavaScript加载和执行效率</title>
      <link>https://itindex.net/detail/56622-javascript-%E5%8A%A0%E8%BD%BD</link>
      <description>&lt;div&gt;  &lt;h4&gt;概览&lt;/h4&gt;  &lt;p&gt;无论当前 JavaScript 代码是内嵌还是在外链文件中，页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript 执行过程耗时越久，浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时出现阻塞的原因在于，脚本可能会改变页面或 JavaScript 的命名空间，它们对后面页面内容造成影响。一个典型的例子就是在页面中使用document.write()。&lt;/p&gt;  &lt;p&gt;JavaScript 代码内嵌示例&lt;/p&gt;  &lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Source Example&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
        document.write(&amp;quot;Today is &amp;quot; + (new Date()).toDateString());
    &amp;lt;/script&amp;gt;
    &amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;  &lt;p&gt;当浏览器遇到&amp;lt;script&amp;gt;标签时，当前 html 页面无从获知 JavaScript 是否会向&amp;lt;p&amp;gt; 标签添加内容，或引入其他元素，或甚至移除该标签。因此，这时浏览器会停止处理页面，先执行 JavaScript代码，然后再继续解析和渲染页面。同样的情况也发生在使用 src 属性加载 JavaScript的过程中，浏览器必须先花时间下载外链文件中的代码，然后解析并执行它。在这个过程中，页面渲染和用户交互完全被阻塞了。&lt;/p&gt;  &lt;h4&gt;脚本位置&lt;/h4&gt;  &lt;p&gt;HTML 4 规范指出 &amp;lt;script&amp;gt; 标签可以放在 HTML 文档的&amp;lt;head&amp;gt;或&amp;lt;body&amp;gt;中，并允许出现多次。web 开发人员一般习惯在 &amp;lt;head&amp;gt; 中加载外链的 JavaScript，接着用 &amp;lt;link&amp;gt; 标签用来加载外链的 CSS 文件或者其他页面信息。&lt;/p&gt;  &lt;p&gt;低效率脚本位置示例&lt;/p&gt;  &lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Source Example&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script1.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script2.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script3.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;styles.css&amp;quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;  &lt;p&gt;然而这种常规的做法却隐藏着严重的性能问题。在清单 2 的示例中，当浏览器解析到 &amp;lt;script&amp;gt; 标签（第 4 行）时，浏览器会停止解析其后的内容，而优先下载脚本文件，并执行其中的代码，这意味着，其后的 styles.css 样式文件和&amp;lt;body&amp;gt;标签都无法被加载，由于&amp;lt;body&amp;gt;标签无法被加载，那么页面自然就无法渲染了。因此在该 JavaScript 代码完全执行完之前，页面都是一片空白。&lt;/p&gt;  &lt;p&gt;由于脚本会阻塞页面其他资源的下载，因此推荐将所有&amp;lt;script&amp;gt;标签尽可能放到&amp;lt;body&amp;gt;标签的底部，以尽量减少对整个页面下载的影响。&lt;/p&gt;  &lt;p&gt;推荐的代码放置位置示例&lt;/p&gt;  &lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Source Example&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;styles.css&amp;quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;gt;

    &amp;lt;!-- Example of efficient script positioning --&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script1.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script2.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script3.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;  &lt;p&gt;这段代码展示了在 HTML 文档中放置&amp;lt;script&amp;gt;标签的推荐位置。尽管脚本下载会阻塞另一个脚本，但是页面的大部分内容都已经下载完成并显示给了用户，因此页面下载不会显得太慢。这是优化 JavaScript 的首要规则：将脚本放在底部。&lt;/p&gt;  &lt;h4&gt;组织脚本&lt;/h4&gt;  &lt;p&gt;由于每个   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签初始下载时都会阻塞页面渲染，所以减少页面包含的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签数量有助于改善这一情况。这不仅针对外链脚本，内嵌脚本的数量同样也要限制。浏览器在解析 HTML 页面的过程中每遇到一个   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签，都会因执行脚本而导致一定的延时，因此最小化延迟时间将会明显改善页面的总体性能。&lt;/p&gt;  &lt;p&gt;这个问题在处理外链 JavaScript 文件时略有不同。考虑到 HTTP 请求会带来额外的性能开销，因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也就是说，减少页面中外链脚本的数量将会改善性能。&lt;/p&gt;  &lt;p&gt;通常一个大型网站或应用需要依赖数个 JavaScript 文件。您可以把多个文件合并成一个，这样只需要引用一个   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签，就可以减少性能消耗。文件合并的工作可通过离线的打包工具或者一些实时的在线服务来实现。&lt;/p&gt;  &lt;p&gt;需要特别提醒的是，把一段内嵌脚本放在引用外链样式表的   &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;之后会导致页面阻塞去等待样式表的下载。这样做是为了确保内嵌脚本在执行时能获得最精确的样式信息。因此，建议不要把内嵌脚本紧跟在   &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;标签后面。&lt;/p&gt;  &lt;h4&gt;无阻塞的脚本&lt;/h4&gt;  &lt;p&gt;减少 JavaScript 文件大小并限制 HTTP 请求数在功能丰富的 Web 应用或大型网站上并不总是可行。Web 应用的功能越丰富，所需要的 JavaScript 代码就越多，尽管下载单个较大的 JavaScript 文件只产生一次 HTTP 请求，却会锁死浏览器的一大段时间。为避免这种情况，需要通过一些特定的技术向页面中逐步加载 JavaScript 文件，这样做在某种程度上来说不会阻塞浏览器。&lt;/p&gt;  &lt;p&gt;无阻塞脚本的秘诀在于，在页面加载完成后才加载 JavaScript 代码。这就意味着在   &lt;code&gt;window&lt;/code&gt;对象的   &lt;code&gt;onload&lt;/code&gt;事件触发后再下载脚本。有多种方式可以实现这一效果。&lt;/p&gt;  &lt;h4&gt;延迟加载脚本&lt;/h4&gt;  &lt;p&gt;HTML 4 为   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签定义了一个扩展属性：   &lt;code&gt;defer&lt;/code&gt;。   &lt;code&gt;Defer&lt;/code&gt;属性指明本元素所含的脚本不会修改 DOM，因此代码能安全地延迟执行。   &lt;code&gt;defer&lt;/code&gt;属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持，所以它不是一个理想的跨浏览器解决方案。在其他浏览器中，   &lt;code&gt;defer&lt;/code&gt;属性会被直接忽略，因此   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签会以默认的方式处理，也就是说会造成阻塞。然而，如果您的目标浏览器支持的话，这仍然是个有用的解决方案。&lt;/p&gt;  &lt;p&gt;defer 属性使用方法示例&lt;/p&gt;  &lt;pre&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;script1.js&amp;quot; defer&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;  &lt;p&gt;带有   &lt;code&gt;defer&lt;/code&gt;属性的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签时开始下载，但不会执行，直到 DOM 加载完成，即   &lt;code&gt;onload&lt;/code&gt;事件触发前才会被执行。当一个带有   &lt;code&gt;defer&lt;/code&gt;属性的 JavaScript 文件下载时，它不会阻塞浏览器的其他进程，因此这类文件可以与其他资源文件一起并行下载。&lt;/p&gt;  &lt;p&gt;任何带有   &lt;code&gt;defer&lt;/code&gt;属性的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素在 DOM 完成加载之前都不会被执行，无论内嵌或者是外链脚本都是如此。清单 5 的例子展示了   &lt;code&gt;defer&lt;/code&gt;属性如何影响脚本行为：&lt;/p&gt;  &lt;p&gt;defer 属性对脚本行为的影响&lt;/p&gt;  &lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Script Defer Example&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; defer&amp;gt;
        alert(&amp;quot;defer&amp;quot;);
    &amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
        alert(&amp;quot;script&amp;quot;);
    &amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
        window.onload = function(){
            alert(&amp;quot;load&amp;quot;);
        };
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;  &lt;p&gt;这段代码在页面处理过程中弹出三次对话框。不支持   &lt;code&gt;defer&lt;/code&gt;属性的浏览器的弹出顺序是：“defer”、“script”、“load”。而在支持   &lt;code&gt;defer&lt;/code&gt;属性的浏览器上，弹出的顺序则是：“script”、“defer”、“load”。请注意，带有   &lt;code&gt;defer&lt;/code&gt;属性的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素不是跟在第二个后面执行，而是在   &lt;code&gt;onload&lt;/code&gt;事件被触发前被调用。&lt;/p&gt;  &lt;p&gt;如果您的目标浏览器只包括 Internet Explorer 和 Firefox 3.5，那么   &lt;code&gt;defer&lt;/code&gt;脚本确实有用。如果您需要支持跨领域的多种浏览器，那么还有更一致的实现方式。&lt;/p&gt;  &lt;p&gt;HTML 5 为   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签定义了一个新的扩展属性：   &lt;code&gt;async&lt;/code&gt;。它的作用和   &lt;code&gt;defer&lt;/code&gt;一样，能够异步地加载和执行脚本，不因为加载脚本而阻塞页面的加载。但是有一点需要注意，在有   &lt;code&gt;async&lt;/code&gt;的情况下，JavaScript 脚本一旦下载好了就会执行，所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性，使用   &lt;code&gt;async&lt;/code&gt;就很有可能出现错误。&lt;/p&gt;  &lt;h4&gt;动态脚本元素&lt;/h4&gt;  &lt;p&gt;文档对象模型（DOM）允许您使用 JavaScript 动态创建 HTML 的几乎全部文档内容。   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素与页面其他元素一样，可以非常容易地通过标准 DOM 函数创建：&lt;/p&gt;  &lt;p&gt;通过标准 DOM 函数创建&amp;lt;script&amp;gt;元素&lt;/p&gt;  &lt;pre&gt;var script = document.createElement (&amp;quot;script&amp;quot;);
   script.type = &amp;quot;text/javascript&amp;quot;;
   script.src = &amp;quot;script1.js&amp;quot;;
   document.getElementsByTagName(&amp;quot;head&amp;quot;)[0].appendChild(script);&lt;/pre&gt;  &lt;p&gt;新的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素加载 script1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于：无论在何处启动下载，文件的下载和运行都不会阻塞其他页面处理过程。您甚至可以将这些代码放在   &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;部分而不会对其余部分的页面代码造成影响（除了用于下载文件的 HTTP 连接）。&lt;/p&gt;  &lt;p&gt;当文件使用动态脚本节点下载时，返回的代码通常立即执行（除了 Firefox 和 Opera，他们将等待此前的所有动态脚本节点执行完毕）。当脚本是“自运行”类型时，这一机制运行正常，但是如果脚本只包含供页面其他脚本调用调用的接口，则会带来问题。这种情况下，您需要跟踪脚本下载完成并是否准备妥善。可以使用动态   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;节点发出事件得到相关信息。&lt;/p&gt;  &lt;p&gt;Firefox、Opera, Chorme 和 Safari 3+会在   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;节点接收完成之后发出一个   &lt;code&gt;onload&lt;/code&gt;事件。您可以监听这一事件，以得到脚本准备好的通知：&lt;/p&gt;  &lt;p&gt;通过监听 onload 事件加载 JavaScript 脚本&lt;/p&gt;  &lt;pre&gt;var script = document.createElement (&amp;quot;script&amp;quot;)
script.type = &amp;quot;text/javascript&amp;quot;;

//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
    alert(&amp;quot;Script loaded!&amp;quot;);
};

script.src = &amp;quot;script1.js&amp;quot;;
document.getElementsByTagName(&amp;quot;head&amp;quot;)[0].appendChild(script);&lt;/pre&gt;  &lt;p&gt;Internet Explorer 支持另一种实现方式，它发出一个   &lt;code&gt;readystatechange&lt;/code&gt;事件。   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素有一个   &lt;code&gt;readyState&lt;/code&gt;属性，它的值随着下载外部文件的过程而改变。   &lt;code&gt;readyState&lt;/code&gt;有五种取值：&lt;/p&gt;  &lt;p&gt;微软文档上说，在   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素的生命周期中，   &lt;code&gt;readyState&lt;/code&gt;的这些取值不一定全部出现，但并没有指出哪些取值总会被用到。实践中，我们最感兴趣的是“loaded”和“complete”状态。Internet Explorer 对这两个   &lt;code&gt;readyState&lt;/code&gt;值所表示的最终状态并不一致，有时   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素会得到“loader”却从不出现“complete”，但另外一些情况下出现“complete”而用不到“loaded”。最安全的办法就是在   &lt;code&gt;readystatechange&lt;/code&gt;事件中检查这两种状态，并且当其中一种状态出现时，删除   &lt;code&gt;readystatechange&lt;/code&gt;事件句柄（保证事件不会被处理两次）：&lt;/p&gt;  &lt;p&gt;通过检查 readyState 状态加载 JavaScript 脚本&lt;/p&gt;  &lt;pre&gt;var script = document.createElement(&amp;quot;script&amp;quot;)
script.type = &amp;quot;text/javascript&amp;quot;;

//Internet Explorer
script.onreadystatechange = function(){
     if (script.readyState == &amp;quot;loaded&amp;quot; || script.readyState == &amp;quot;complete&amp;quot;){
           script.onreadystatechange = null;
           alert(&amp;quot;Script loaded.&amp;quot;);
     }
};

script.src = &amp;quot;script1.js&amp;quot;;
document.getElementsByTagName(&amp;quot;head&amp;quot;)[0].appendChild(script);&lt;/pre&gt;  &lt;p&gt;大多数情况下，您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能：&lt;/p&gt;  &lt;p&gt;通过函数进行封装&lt;/p&gt;  &lt;pre&gt;function loadScript(url, callback){
    var script = document.createElement (&amp;quot;script&amp;quot;)
    script.type = &amp;quot;text/javascript&amp;quot;;
    if (script.readyState){ //IE
        script.onreadystatechange = function(){
            if (script.readyState == &amp;quot;loaded&amp;quot; || script.readyState == &amp;quot;complete&amp;quot;){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //Others
        script.onload = function(){
            callback();
        };
    }
    script.src = url;
    document.getElementsByTagName(&amp;quot;head&amp;quot;)[0].appendChild(script);
}&lt;/pre&gt;  &lt;p&gt;此函数接收两个参数：JavaScript 文件的 URL，和一个当 JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步，设置   &lt;code&gt;src&lt;/code&gt;属性，并将   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素添加至页面。此   &lt;code&gt;loadScript()&lt;/code&gt;函数使用方法如下：&lt;/p&gt;  &lt;p&gt;loadScript()函数使用方法&lt;/p&gt;  &lt;pre&gt;loadScript(&amp;quot;script1.js&amp;quot;, function(){
    alert(&amp;quot;File is loaded!&amp;quot;);
});&lt;/pre&gt;  &lt;p&gt;您可以在页面中动态加载很多 JavaScript 文件，但要注意，浏览器不保证文件加载的顺序。所有主流浏览器之中，只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以保证他们的次序，如下：&lt;/p&gt;  &lt;p&gt;通过 loadScript()函数加载多个 JavaScript 脚本&lt;/p&gt;  &lt;pre&gt;loadScript(&amp;quot;script1.js&amp;quot;, function(){
    loadScript(&amp;quot;script2.js&amp;quot;, function(){
        loadScript(&amp;quot;script3.js&amp;quot;, function(){
            alert(&amp;quot;All files are loaded!&amp;quot;);
        });
    });
});&lt;/pre&gt;  &lt;p&gt;此代码等待 script1.js 可用之后才开始加载 script2.js，等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行，但如果要下载和执行的文件很多，还是有些麻烦。如果多个文件的次序十分重要，更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码（由于这是异步进行的，使用一个大文件并没有什么损失）。&lt;/p&gt;  &lt;p&gt;动态脚本加载是非阻塞 JavaScript 下载中最常用的模式，因为它可以跨浏览器，而且简单易用。&lt;/p&gt;  &lt;h4&gt;使用 XMLHttpRequest(XHR)对象&lt;/h4&gt;  &lt;p&gt;此技术首先创建一个 XHR 对象，然后下载 JavaScript 文件，接着用一个动态   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素将 JavaScript 代码注入页面。清单 12 是一个简单的例子：&lt;/p&gt;  &lt;p&gt;通过 XHR 对象加载 JavaScript 脚本&lt;/p&gt;  &lt;pre&gt;var xhr = new XMLHttpRequest();
xhr.open(&amp;quot;get&amp;quot;, &amp;quot;script1.js&amp;quot;, true);
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if (xhr.status &amp;gt;= 200 &amp;amp;&amp;amp; xhr.status &amp;lt; 300 || xhr.status == 304){
            var script = document.createElement (&amp;quot;script&amp;quot;);
            script.type = &amp;quot;text/javascript&amp;quot;;
            script.text = xhr.responseText;
            document.body.appendChild(script);
        }
    }
};
xhr.send(null);&lt;/pre&gt;  &lt;p&gt;此代码向服务器发送一个获取 script1.js 文件的 GET 请求。   &lt;code&gt;onreadystatechange&lt;/code&gt;事件处理函数检查   &lt;code&gt;readyState&lt;/code&gt;是不是 4，然后检查 HTTP 状态码是不是有效（2XX 表示有效的回应，304 表示一个缓存响应）。如果收到了一个有效的响应，那么就创建一个新的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素，将它的文本属性设置为从服务器接收到的   &lt;code&gt;responseText&lt;/code&gt;字符串。这样做实际上会创建一个带有内联代码的   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素。一旦新   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;元素被添加到文档，代码将被执行，并准备使用。&lt;/p&gt;  &lt;p&gt;这种方法的主要优点是，您可以下载不立即执行的 JavaScript 代码。由于代码返回在   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签之外（换句话说不受   &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签约束），它下载后不会自动执行，这使得您可以推迟执行，直到一切都准备好了。另一个优点是，同样的代码在所有现代浏览器中都不会引发异常。&lt;/p&gt;  &lt;p&gt;此方法最主要的限制是：JavaScript 文件必须与页面放置在同一个域内，不能从 CDN 下载（CDN 指&amp;quot;内容投递网络（Content Delivery Network）&amp;quot;，所以大型网页通常不采用 XHR 脚本注入技术。&lt;/p&gt;  &lt;h4&gt;总结&lt;/h4&gt;  &lt;p&gt;减少 JavaScript 对性能的影响有以下几种方法：&lt;/p&gt;  &lt;p&gt;通过以上策略，可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 网站和应用的实际性能。&lt;/p&gt;  &lt;p&gt;原文来自：https://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/&lt;/p&gt;  &lt;p&gt;补充js加载函数：&lt;/p&gt;  &lt;pre&gt;function loadJs(url, callback, charset) {
    var head = document.getElementsByTagName(&amp;quot;head&amp;quot;)[0];
    var script = document.createElement(&amp;quot;script&amp;quot;);
    if ( !!charset) script.charset = &amp;quot;utf-8&amp;quot;;
    script.src = url;
    script.onload = script.onreadystatechange = function() {
        var f = script.readyState;
        if (f &amp;amp;&amp;amp; f != &amp;quot;loaded&amp;quot; &amp;amp;&amp;amp; f != &amp;quot;complete&amp;quot;) return;
        script.onload = script.onreadystatechange = null;
        head.removeChild(script) if (callback) {
            callback() || callback
        };
    };
    head.appendChild(script);
}&lt;/pre&gt;  &lt;pre&gt;// js同步加载
function getScripts(i, linkArray, fn) {
    env || getEnv();
    var script = document.createElement(&amp;apos;script&amp;apos;);
    script.type = &amp;apos;text/javascript&amp;apos;;
    script.src = linkArray[i];
    var head = document.head || document.getElementsByTagName(&amp;apos;head&amp;apos;)[0];
    head.appendChild(script);

    if (env.ie &amp;amp;&amp;amp; &amp;apos;onreadystatechange&amp;apos; in script &amp;amp;&amp;amp; !(&amp;apos;draggable&amp;apos; in script)){ //ie浏览器使用以下方式加载
        script.onreadystatechange = function () {
          if (/loaded|complete/.test(script.readyState)) {
            script.onreadystatechange = null;
            if(i === linkArray.length-1) {
                if (fn) {
                    fn();
                }
            } else {
                getScripts(++i, linkArray, fn);
            }
          }
        };
    }else{
        script.onload = function() {
            if(i === linkArray.length-1) {
                if (fn) {
                    fn();
                }
            } else {
                getScripts(++i, linkArray, fn);
            }
        };
    }
}&lt;/pre&gt;  &lt;pre&gt;// js存在依赖关系 依次加载
getScripts(0, [
    &amp;apos;http://caibaojian.com/demo/base.js&amp;apos;,
    &amp;apos;http://caibaojian.com/demo/reset.js&amp;apos;], function() {
     alert(&amp;apos;callback&amp;apos;);
});&lt;/pre&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>tuicool</category>
      <guid isPermaLink="true">https://itindex.net/detail/56622-javascript-%E5%8A%A0%E8%BD%BD</guid>
      <pubDate>Tue, 31 Jan 2017 08:00:00 CST</pubDate>
    </item>
    <item>
      <title>Rhino -- 基于java的javascript实现</title>
      <link>https://itindex.net/detail/55684-rhino-java-javascript</link>
      <description>&lt;div&gt;
  &lt;p&gt;这几天突然对js引擎非常感兴趣，大概了解了一下，记下来备忘。javascript引擎大家比较熟悉的应该是当前比较火的Chrome V8引擎。以快速闻名。v8基于c++开发。这里就不多阐述了。另外一个是 基于java的Rhino引擎，这个想来大家不是很了解。下面来简单介绍一下。&lt;/p&gt;
  &lt;h2&gt;Rhino简介&lt;/h2&gt;
  &lt;p&gt;(简介这部分是抄的)&lt;/p&gt;
  &lt;p&gt;Rhino 是一种使用 Java 语言编写的 JavaScript 的开源实现，原先由Mozilla开发，现在被集成进入JDK 6.0。与其他很多语言一样，Rhino 是一种动态类型的、基于对象的脚本语言，它可以简单地访问各种 Java 类库。Rhino 从 JavaScript 中借用了很多语法，让程序员可以快速编写功能强大的程序。最为明显的区别是，Rhino 不再使用语句结束符(    &lt;code&gt;;&lt;/code&gt; )，放宽了变量声明规则，并且极大地简化了修改和检索对象属性的语法（没有求助于调用存取方法）。&lt;/p&gt;
  &lt;p&gt;由于 Rhino 是 JavaScript 的一种基于 Java 的实现，所以对于 Java 开发人员来说，它应该特别易于使用。JavaScript 的（以及 Rhino 的）语法非常类似于 Java 编程语言。这两种语言都采用了与 Java 编程语言相似的循环和条件结构，并且遵循类似的语法模式来表示这些结构。&lt;/p&gt;
  &lt;p&gt;虽然 Rhino 与本系列文章中介绍的其他编程语言具有很多共同点，但对于 Java 平台上的编程而言，它也有独特之处（可能有时有些不同的地方）。Rhino 是一种    &lt;em&gt;基于原型的（prototype-based）&lt;/em&gt;语言，而不是一种    &lt;em&gt;基于类的（class-based）&lt;/em&gt;语言。在 Rhino 中，您可以构建对象，而不是类。除了类的对象实例之外，这样做还可以让您避免构建和操作类的开发、部署和执行成本。正如您将在文中发现的那样，诸如 Rhino 这类基于原型的语言在开发和运行基于 GUI 的应用程序时尤其有效。&lt;/p&gt;
  &lt;h2&gt;如何使用Rhino&lt;/h2&gt;
  &lt;p&gt;你可以去Rhino的   &lt;a href="http://www.mozilla.org/rhino/download.html" target="_blank"&gt;主页&lt;/a&gt;下载最新的源代码和文档，解压开之后有一个js.jar 这就是Rhino的库文件。Rhino应用方式我分为console，js调java,java调js三种。下面来一一阐述。&lt;/p&gt;
  &lt;h3&gt;
一 console　　shell命令   &lt;br /&gt;相信很多技术猿们习惯使用酷酷的console控制台来敲打shell命令。可这些一般都是服务端语言。现在你也可以在控制台中敲到js代码了。用法很简单。首先要将js.jar文件放入windows的java\jdk1.6.0\jre\lib\ext目录，mac的 /System/Library/Java/Extensions/目录下。&lt;/h3&gt;
  &lt;p&gt;然后你就可以在命令行中敲打&lt;/p&gt;
  &lt;div&gt;
   &lt;pre&gt;java  org.mozilla.javascript.tools.shell.Main&lt;/pre&gt;
&lt;/div&gt;
  &lt;p&gt;然后你就会看到像这样的输出：&lt;/p&gt;
  &lt;div&gt;
   &lt;pre&gt;Rhino 1.7 release 4 2012 06 18
js&amp;gt; &lt;/pre&gt;
&lt;/div&gt;
  &lt;p&gt;这样你就可以调试js代码了，这里你既可以加载js文件来运行，也可以直接运行js语法语句,并且Rhino借用了java中很多的基础语法,使我们写起代码来更easy。下面我们来试试看。&lt;/p&gt;
  &lt;p&gt;直接写代码，效果如下：&lt;/p&gt;
  &lt;div&gt;
   &lt;pre&gt;js&amp;gt; var a={&amp;apos;name&amp;apos;:&amp;apos;zhangwen&amp;apos;,&amp;apos;age&amp;apos;:30}
js&amp;gt; a.name
zhangwen
js&amp;gt; a.age
30&lt;/pre&gt;
&lt;/div&gt;
  &lt;p&gt;新建一个test.js文件,内容如下：&lt;/p&gt;
  &lt;div&gt;
   &lt;pre&gt;function test(str){
   var name=str;
   print(name);
}&lt;/pre&gt;
&lt;/div&gt;
  &lt;p&gt; &lt;/p&gt;
  &lt;p&gt;然后在console中输入:&lt;/p&gt;
  &lt;div&gt;
   &lt;pre&gt;js&amp;gt; load(&amp;apos;test.js&amp;apos;);
js&amp;gt; test(&amp;apos;awen&amp;apos;);  
awen
js&amp;gt; &lt;/pre&gt;
&lt;/div&gt;
  &lt;p&gt;体验了这两种方式后感觉是不是很酷?&lt;/p&gt;
  &lt;p&gt; &lt;/p&gt;
  &lt;h3&gt;二 Rhino中调用JAVA&lt;/h3&gt;
  &lt;p&gt;不说废话，直接上代码：&lt;/p&gt;
  &lt;div&gt;
   &lt;pre&gt;js&amp;gt; var date=new java.util.Date();
js&amp;gt; date
Mon Jul 16 17:04:07 CST 2012
js&amp;gt; java.lang.Math.PI
3.141592653589793&lt;/pre&gt;
&lt;/div&gt;
  &lt;p&gt;当然，你也先导入包，importPackage(java.util)（相当于java中的import package）那么代码中就不用写类名的包路径了，var date=new Date();不知道细心地同学发现没有。在javascript中也有原生的Date，那这样我们就应该避免这样的写法，防止代码中混乱。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt; Rhino实现Java接口：&lt;/strong&gt;&lt;/p&gt;
  &lt;div&gt;
   &lt;div&gt;    &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;     &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="http://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
   &lt;pre&gt;js&amp;gt; var obj={run:function(){print(&amp;quot;\nrunning...&amp;quot;);}}
js&amp;gt; var r=new java.lang.Runnable(obj);
js&amp;gt; var t=new java.lang.Thread(r);
js&amp;gt; t.start();
js&amp;gt; 
running...&lt;/pre&gt;
   &lt;div&gt;    &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;     &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="http://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
  &lt;p&gt;   &lt;strong&gt;Rhino创建Java 数组&lt;/strong&gt;&lt;/p&gt;
  &lt;div&gt;
   &lt;div&gt;    &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;     &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="http://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
   &lt;pre&gt;js&amp;gt; var array=java.lang.reflect.Array.newInstance(java.lang.String, 2);  
js&amp;gt; array[0]=&amp;quot;恩&amp;quot;;     
恩
js&amp;gt; array[1]=&amp;quot;哦&amp;quot;;
哦
js&amp;gt; java.util.Arrays.toString(array);
[恩, 哦]&lt;/pre&gt;
   &lt;div&gt;    &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;     &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="http://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
  &lt;h3&gt;三 Java对象操纵JavaScript&lt;/h3&gt;
  &lt;div&gt;
   &lt;div&gt;    &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;     &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="http://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
   &lt;pre&gt;import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;

public static void main(String[] args) {
   Context ctx=Context.enter();
   Scriptable scope=ctx.initStandardObjects();
   
   String jsStr=&amp;quot;100*20/10&amp;quot;;
   Object result=ctx.evaluateString(scope, jsStr, null, 0,null); 
   System.out.println(&amp;quot;result=&amp;quot;+result);
}&lt;/pre&gt;
   &lt;div&gt;    &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;     &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="http://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
  &lt;p&gt; http://www.cnblogs.com/cczw/archive/2012/07/16/2593957.html&lt;/p&gt;
&lt;/div&gt;
          
           &lt;br /&gt; &lt;br /&gt;
          
             &lt;a href="http://m635674608.iteye.com/blog/2304513#comments"&gt;已有   &lt;strong&gt;0&lt;/strong&gt; 人发表留言，猛击-&amp;gt;&amp;gt;  &lt;strong&gt;这里&lt;/strong&gt;&amp;lt;&amp;lt;-参与讨论&lt;/a&gt;
          
           &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
ITeye推荐
 &lt;br /&gt;
 &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.iteye.com/clicks/433" target="_blank"&gt;—软件人才免语言低担保 赴美带薪读研！— &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
          
        &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/55684-rhino-java-javascript</guid>
      <pubDate>Mon, 13 Jun 2016 00:38:37 CST</pubDate>
    </item>
    <item>
      <title>JavaScript 方法的4种调用模式</title>
      <link>https://itindex.net/detail/55172-javascript-%E6%96%B9%E6%B3%95-%E6%A8%A1%E5%BC%8F</link>
      <description>&lt;p&gt;  &lt;strong&gt;函数&lt;/strong&gt;（Function）是JavaScript的基本模块单元，JavaScript的代码重用，
信息隐藏，对象组合等都可以借助函数来实现。
JavaScript中的函数有4种调用模式：&lt;/p&gt;

 &lt;ul&gt;
  &lt;li&gt;方法调用（Method Invocation Pattern）&lt;/li&gt;
  &lt;li&gt;函数调用（Function Invocation Pattern）&lt;/li&gt;
  &lt;li&gt;构造函数调用（Constructor Invocation Pattern）&lt;/li&gt;
  &lt;li&gt;apply调用（Apply Invocation Pattern）&lt;/li&gt;
&lt;/ul&gt;

 &lt;p&gt;与其他语言不通，JavaScript的函数是一种特殊的对象
（事实上，JavaScript函数是一级对象）。
这意味着函数可以有属性，可以有方法，也可以传参、返回、赋值给变量、加入数组。
与对象不同的是函数可以被调用。&lt;/p&gt;

 &lt;p&gt;既然是对象便会有原型。我们知道通过花括号语法创建的对象的原型是  &lt;code&gt;Object.prototype&lt;/code&gt;。
通过函数语法（  &lt;code&gt;function&lt;/code&gt;关键字或  &lt;code&gt;new Function()&lt;/code&gt;）创建的函数，其原型自然是  &lt;code&gt;Function.prototype&lt;/code&gt;。
而  &lt;code&gt;Function.prototype&lt;/code&gt;的原型是  &lt;code&gt;Object.prototype&lt;/code&gt;。&lt;/p&gt;

 &lt;blockquote&gt;
  &lt;p&gt;注意上述『原型』指的是原型链上的隐式原型，区别于   &lt;code&gt;prototype&lt;/code&gt;原型属性。&lt;/p&gt;
&lt;/blockquote&gt;



 &lt;h1&gt;调用模式&lt;/h1&gt;

 &lt;p&gt;我们知道在函数里可见的名称包括：函数体内声明的变量、函数参数、来自外部的闭包变量。
此外还有两个：  &lt;code&gt;this&lt;/code&gt;和  &lt;code&gt;arguments&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;  &lt;code&gt;this&lt;/code&gt;在面向对象程序设计中非常重要，而它的值在JavaScript中取决于  &lt;strong&gt;调用模式&lt;/strong&gt;。
JavaScript中的函数有4种调用模式：方法调用、函数调用、构造函数调用、apply调用。&lt;/p&gt;

 &lt;p&gt;  &lt;code&gt;arguments&lt;/code&gt;是一个类数组变量（array like），拥有  &lt;code&gt;length&lt;/code&gt;属性并可以取下标，
它存着所有参数构成的有序数组。
在JavaScript中，函数调用与函数签名不一致（个数不正确、类型不正确定）
时不会产生运行时错。少了的参数会被置为  &lt;code&gt;undefined&lt;/code&gt;，多了的参数会被忽略。&lt;/p&gt;

 &lt;h1&gt;方法调用模式&lt;/h1&gt;

 &lt;p&gt;在面向对象程序设计中，当函数（Function）作为对象属性时被称为方法（Method）。
方法被调用时  &lt;code&gt;this&lt;/code&gt;会被绑定到对应的对象。在JavaScript中有两种语法可以完成方法调用：
  &lt;code&gt;a.func()&lt;/code&gt;和  &lt;code&gt;a[&amp;apos;func&amp;apos;]()&lt;/code&gt;。&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;var obj = {
    val: 0,
    count: function(i){
        this.val += i;
        console.log(this.val);
    }
};

obj.count();    // 1
obj.count();    // 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;值得注意的是，  &lt;code&gt;this&lt;/code&gt;到  &lt;code&gt;obj&lt;/code&gt;的绑定属于极晚绑定（very late binding），
绑定发生在调用的那一刻。这使得JavaScript函数在被重用时有极大的灵活性。&lt;/p&gt;

 &lt;h1&gt;函数调用模式&lt;/h1&gt;

 &lt;p&gt;当函数不是对象属性时，它就会被当做函数来调用，比如  &lt;code&gt;add(2,3)&lt;/code&gt;。
此时  &lt;code&gt;this&lt;/code&gt;绑定到了全局对象  &lt;code&gt;global&lt;/code&gt;。&lt;/p&gt;

 &lt;blockquote&gt;
  &lt;p&gt;在[那些 JavaScript 的优秀特性][elegant-js]一文中曾提到，
JavaScript的编译（预处理）需要   &lt;code&gt;global&lt;/code&gt;对象来链接所有全局名称。&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;其实  &lt;code&gt;this&lt;/code&gt;绑定到  &lt;code&gt;global&lt;/code&gt;是JavaScript的一个设计错误（可以说是最严重的错误），
它导致了对象方法不能直接调用内部函数来做一些辅助工作，
因为内不函数里的  &lt;code&gt;this&lt;/code&gt;的绑定到了  &lt;code&gt;global&lt;/code&gt;。
所以如果要重新设计语言，方法调用的  &lt;code&gt;this&lt;/code&gt;应该绑定到上一级函数的  &lt;code&gt;this&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;然而共有方法总是需要调用内部辅助函数，于是产生了这样一个非常普遍的解决方案：&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;man.love = function(){
    var self = this;
    function fuck(){
        self.happy++;
    }
    function marry(){
        self.happy--;
    }
    fuck() &amp;amp;&amp;amp; marry();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;有些场景下用  &lt;code&gt;Function.prototype.bind&lt;/code&gt;会更加方便：&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;man.love = function(){
    function fuck(girl1, girl2, ...){
        this.happy++;
    }
    fuck.bind(this)();
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;h1&gt;构造函数调用模式&lt;/h1&gt;

 &lt;blockquote&gt;
  &lt;p&gt;Classically inspired syntax obscures the language’s true prototypal natur.&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;JavaScript中，那些用来  &lt;code&gt;new&lt;/code&gt;对象的函数成为构造函数。&lt;/p&gt;

 &lt;p&gt;JavaScript采用原型继承方式。这意味着一个对象可以从另一个对象直接继承属性，
JavaScript是class free的~ 但JavaScript为了迎合主流的基于类声明的继承方式，
同时也给出了构造函数机制：使用  &lt;code&gt;new&lt;/code&gt;关键字，便会创建一个对象，
根据  &lt;code&gt;prototype&lt;/code&gt;属性创建原型链，并以该对象为  &lt;code&gt;this&lt;/code&gt;执行指定的（构造）函数。&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;function Man(name, age){
    this.sex = &amp;apos;man&amp;apos;;
    this.name = name;
    this.age = age;
}
Man.prototype.fuck = function(girl1, girl2, ...){}
var man = new Man();
man.fuck();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;当构造函数有很多参数时，它们的顺序很难记住，所以通常使用对象进行传参：&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;var man = new Man({
    name: &amp;apos;bob&amp;apos;,
    age: 18
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;给参数起名字以达到顺序无关的做法在Python中也存在，但JavaScript的对象传参还将带来另一个好处：
JSON兼容。因为JavaScript常常需要数据库（例如MongoDB）或网络（  &lt;code&gt;application/json&lt;/code&gt;）传来的JSON数据，这一点使得对象构造非常方便。&lt;/p&gt;

 &lt;h1&gt;Apply 调用模式&lt;/h1&gt;

 &lt;p&gt;JavaScript函数是一种特殊的对象，而对象可以有属性和方法。
其中的  &lt;code&gt;apply&lt;/code&gt;方法提供了一个更加特殊的调用方式。
它接受两个参数：第一个是该函数要绑定的  &lt;code&gt;this&lt;/code&gt;，第二个是参数数组。&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;var args = [girl1, girl2];
var animal = new Animal();
Man.prototype.fuck.apply(animal, args);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;Apply使得一个方法可以用不同的对象对象来调用，比如  &lt;code&gt;animal&lt;/code&gt;也可以用  &lt;code&gt;Man&lt;/code&gt;的方式来  &lt;code&gt;fuck&lt;/code&gt;。&lt;/p&gt;

 &lt;p&gt;[elegant-js]: {% post_url 2016-01-18-elegant-javascript %}&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>JavaScript 构造函数 调用模式</category>
      <guid isPermaLink="true">https://itindex.net/detail/55172-javascript-%E6%96%B9%E6%B3%95-%E6%A8%A1%E5%BC%8F</guid>
      <pubDate>Wed, 03 Feb 2016 08:00:00 CST</pubDate>
    </item>
    <item>
      <title>javaScript跨浏览器事件处理程序</title>
      <link>https://itindex.net/detail/54235-javascript-%E6%B5%8F%E8%A7%88%E5%99%A8-%E4%BA%8B%E4%BB%B6</link>
      <description>&lt;p&gt;最近在阅读  &lt;code&gt;javascript高级程序设计&lt;/code&gt;，事件这一块还是有很多东西要学的，就把一些思考和总结记录下。  &lt;br /&gt;在事件处理，事件对象，阻止事件的传播等方法或对象存在着浏览器兼容性问题，开发过程中最好编写成一个通用的事件处理工具。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function(){
    var EU = {};
    //...
    //在这里添加一些通用的事件处理方法
    //...
    window.EventUtil = EU;
})();&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;事件处理程序&lt;/h2&gt;
 &lt;p&gt;事件的绑定主要为IE8以下浏览器做兼容处理：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;IE8以下只支持事件冒泡   &lt;br /&gt; IE事件处理使用   &lt;code&gt;attachEvent&lt;/code&gt;    &lt;code&gt;detachEvent&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;
 &lt;h3&gt;绑定事件&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;EU.addHandler = function(element,type,handler){
    //DOM2级事件处理，IE9也支持
    if(element.addEventListener){
        element.addEventListener(type,handler,false);
    }
    else if(element.attachEvent){
        //type加&amp;apos;on&amp;apos;
        //IE9也可以这样绑定
        element.attachEvent(&amp;apos;on&amp;apos; + type,handler);
    }
    //DOM0级事件处理步，事件流也是冒泡
    else{
        element[&amp;apos;on&amp;apos; + type] = handler;
    }
};&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;取消绑定事件&lt;/h3&gt;
 &lt;p&gt;和绑定事件的处理基本一致，有一个注意点：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;传入的   &lt;code&gt;handler&lt;/code&gt;必须与绑定事件时传入的相同（指向同一个函数）&lt;/p&gt;&lt;/blockquote&gt;
 &lt;pre&gt;  &lt;code&gt;EU.removeHandler = function(element,type,handler){
    if(element.removeEventListener){
        element.removeEventListener(type,handler);
    }
    else if(element.attachEvent){
       element.detachEvent(&amp;apos;on&amp;apos; + type,handler);
    }
    else{
        //属性置空就可以
        element[&amp;apos;on&amp;apos; + type] = null;
    }
};&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;事件对象&lt;/h2&gt;
 &lt;p&gt;注意点：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;IE下   &lt;code&gt;event&lt;/code&gt;是全局对象，通过   &lt;code&gt;window.event&lt;/code&gt;取得&lt;/p&gt;&lt;/blockquote&gt;
 &lt;pre&gt;  &lt;code&gt;EU.getEvent = function(event){
    return event || window.event;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;事件的目标&lt;/h2&gt;
 &lt;p&gt;注意点:&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;IE下通过   &lt;code&gt;attachEvent&lt;/code&gt;绑定事件，内部   &lt;code&gt;this&lt;/code&gt;并非触发事件的   &lt;code&gt;DOM&lt;/code&gt;,而是window;   &lt;br /&gt; 通过目标对象来获取   &lt;code&gt;DOM&lt;/code&gt;节点，IE下是   &lt;code&gt;srcElement&lt;/code&gt;属性，等同于其他浏览器的   &lt;code&gt;target&lt;/code&gt;属性&lt;/p&gt;&lt;/blockquote&gt;
 &lt;pre&gt;  &lt;code&gt;EU.addTarget = function(event){
    return event.target || event.srcElement;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;阻止默认事件&lt;/h2&gt;
 &lt;pre&gt;  &lt;code&gt;EU.preventDefault = function(event){
    if(event.preventDefault){
        event.preventDefault();
    }
    //IE下处理
    else{
        event.returnValue = false; //默认为true
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;关于  &lt;a href="http://segmentfault.com/q/1010000003486347"&gt;事件默认行为&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;阻止事件传播&lt;/h2&gt;
 &lt;pre&gt;  &lt;code&gt;EU.stopPropagation = function(event){
    if(event.stopPropagation){
        event.stopPropagation();
    }
    //IE下处理
    else{
        event.cancelBubble = true;//默认为false，注意区分于returnValue
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;注意点：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;阻止的是   &lt;code&gt;DOM层级&lt;/code&gt;间的事件传播&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;比如：对于一个DOM元素，同时绑定捕获事件与冒泡事件，如果在捕获阶段使用  &lt;code&gt;stopPropagation&lt;/code&gt;,不会阻断冒泡事件的执行；&lt;/p&gt;
 &lt;p&gt;Demo地址：  &lt;a href="http://runjs.cn/detail/hyrdjfyj"&gt;http://runjs.cn/detail/hyrdjfyj&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;如果对子元素和父元素以冒泡形式都绑定&amp;apos;click&amp;apos;事件，在子元素的事件处理中使用  &lt;code&gt;stopPropagation&lt;/code&gt;阻止事件传播,父元素绑定的  &lt;code&gt;click&lt;/code&gt;事件不会执行。  &lt;br /&gt;Demo地址：  &lt;a href="http://runjs.cn/detail/sf0t1bso"&gt;http://runjs.cn/detail/sf0t1bso&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>javascript web前端开发 javascript高级程序设计</category>
      <guid isPermaLink="true">https://itindex.net/detail/54235-javascript-%E6%B5%8F%E8%A7%88%E5%99%A8-%E4%BA%8B%E4%BB%B6</guid>
      <pubDate>Tue, 25 Aug 2015 21:45:38 CST</pubDate>
    </item>
    <item>
      <title>BrowserBackdoor：一款基于JavaScript WebSocket的后门套装</title>
      <link>https://itindex.net/detail/55749-browserbackdoor-javascript-websocket</link>
      <description>&lt;p&gt;*免责声明：本站提供的安全工具仅供安全测试、安全研究用途，禁止非法使用&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;BrowserBackdoor是一款基于Electron框架的后门安全测试工具，使用JavaScript WebSocket来连接监听器。配套的BrowserBackdoorServer是一个WebSocket服务器，用于监听WebSocket连接并创建用于向远程服务器发送命令的命令行接口。&lt;/strong&gt;    &lt;/p&gt;
 &lt;h3&gt;  &lt;strong&gt;FreeBuf百科：&lt;/strong&gt;&lt;/h3&gt;
 &lt;blockquote&gt;  &lt;p&gt;Electron 框架的前身是 Atom Shell，可以让你写使用 JavaScript，HTML 和 CSS 构建跨平台的桌面应用程序。Electron 是开源的，由 GitHub 维护，有一个活跃的社区。最重要的是，Electron 应用服务构建和运行在 Mac，Windows 和 Linux。   &lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;BrowserBackdoor中的JavaScript后门支持所有WebSockets的浏览器，可通过Electron API执行下列操作：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;1、打开浏览器窗口，详见   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/server/modules/openURL.js" target="_blank"&gt;server/modules/openURL.js&lt;/a&gt;文件。    &lt;/p&gt;
  &lt;p&gt;2、更改并读取剪贴板，详见   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/server/modules/readClipboard.js" target="_blank"&gt;server/modules/readClipboard.js&lt;/a&gt;和   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/server/modules/writeClipboard.js" target="_blank"&gt;server/modules/writeClipboard.js&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;3、访问跨平台操作系统的通知和OS X或Windows平台上的   &lt;a href="http://electron.atom.io/docs/api/tray/#tray" target="_blank"&gt;Tray&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;4、截屏，详见   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/server/modules/screenshot.js" target="_blank"&gt;server/modules/screenshot.js&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;5、执行任意系统命令，详见   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/server/modules/execCommand.js" target="_blank"&gt;server/modules/execCommand.js&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;6、在启动时运行，详见   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/client/main.js" target="_blank"&gt;client/main.js&lt;/a&gt;（）和   &lt;a href="https://github.com/IMcPwn/browser-backdoor/blob/master/server/modules/manageStartup.js" target="_blank"&gt;server/modules/manageStartup.js&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;用例&lt;/h2&gt;
 &lt;p&gt;客户端应用程序会在后台运行，如果需要检查它的运行，可以按“CommandOrControl+Alt+\”（OS X平台上用Command，Windows/Linux平台上用Control，也可以在main.js文件中更改快捷键）来选择启用或禁用启动。&lt;/p&gt;
 &lt;p&gt;具体用例说明可在命令行中输入“help”。&lt;/p&gt;
 &lt;h2&gt;安装&lt;/h2&gt;
 &lt;p&gt;BrowserBackdoor要求在NodeJS平台上并使用NPM工具进行安装。BrowserBackdoorServer则要求在Ruby 2.1+环境中并且具备Gem包。    &lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;BrowserBackdoor可以在所有支持Electron的设备中运行，当前支持的平台有Windows 32/64，OS X 64，Linux 32/64。BrowserBackdoorServer在Ubuntu 14.04、Debian 8和Kali Linux平台上测试通过。&lt;/p&gt;
 &lt;p&gt;首先需要克隆Git资源库：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&lt;/code&gt;  &lt;p&gt;   &lt;code&gt;git clone https://github.com/IMcPwn/browser-backdoor&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;cd browser-backdoor&lt;/p&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;安装并运行BrowserBackdoor Electron：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&lt;/code&gt;  &lt;p&gt;   &lt;code&gt;cd client&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;npm install&lt;/p&gt;  &lt;p&gt;# Configure index.html and main.js before the next command&lt;/p&gt;  &lt;p&gt;npm start&lt;/p&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;创建全平台的可执行文件&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&lt;/code&gt;  &lt;p&gt;   &lt;code&gt;cd client&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;npm install electron-packager -g&lt;/p&gt;  &lt;p&gt;electron-packager . –all&lt;/p&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;安装并运行BrowserBackdoorServer：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&lt;/code&gt;  &lt;p&gt;   &lt;code&gt;cd server&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;gem install bundler&lt;/p&gt;  &lt;p&gt;bundle install&lt;/p&gt;  &lt;p&gt;# Configure config.yml before the next command&lt;/p&gt;  &lt;p&gt;ruby bbsconsole.rb&lt;/p&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;命令行截图&lt;/h2&gt;
 &lt;p&gt;默认配置的命令行界面：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="01_BbsConsole.png" height="227" src="http://image.3001.net/images/20160624/14667598346707.png!small" width="640"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;p&gt;帮助界面（文本内容会随着时间改变）：    &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="02_BbsHelp.png" height="291" src="http://image.3001.net/images/20160624/1466759847249.png!small" width="640"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;p&gt;会话打开的状态（本例中包含3个会话）：      &lt;br /&gt;    &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="03_BbsSessionOpen.png" height="255" src="http://image.3001.net/images/20160624/146675987345.png!small" width="640"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;p&gt;向所有会话发送命令:      &lt;br /&gt;        &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="04_BbsCommandAllSessions.png" height="283" src="http://image.3001.net/images/20160624/14667598894908.png!small" width="640"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;p&gt;向指定的会话发送命令：      &lt;br /&gt;        &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="05_BbsScreenshot.png" height="97" src="http://image.3001.net/images/20160624/14667599015823.png!small" width="640"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;h2&gt;下载地址&lt;/h2&gt;
 &lt;p&gt;BrowserBackdoor下载地址：  &lt;a href="https://github.com/IMcPwn/browser-backdoor" target="_blank"&gt;github&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;*原文地址：   &lt;a href="http://www.kitploit.com/2016/06/browserbackdoor-secure-javascript.htmlJavaScript" target="_blank"&gt;kitploit&lt;/a&gt;，安小白编译，转载请注明来自FreeBuf黑客与极客（FreeBuf.COM）&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 BrowserBackdoor 后门 监听器</category>
      <guid isPermaLink="true">https://itindex.net/detail/55749-browserbackdoor-javascript-websocket</guid>
      <pubDate>Mon, 04 Jul 2016 14:00:54 CST</pubDate>
    </item>
    <item>
      <title>[总结贴] 十个 JavaScript 中易犯的小错误</title>
      <link>https://itindex.net/detail/53502-javascript-%E9%94%99%E8%AF%AF</link>
      <description>&lt;h3&gt;  &lt;strong&gt;序言&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;在今天，JavaScript已经成为了网页编辑的核心。尤其是过去的几年，互联网见证了在SPA开发、图形处理、交互等方面大量JS库的出现。&lt;/p&gt;

 &lt;p&gt;如果初次打交道，很多人会觉得js很简单。确实，对于很多有经验的工程师，或者甚至是初学者而言，实现基本的js功能几乎毫无障碍。但是JS的真实功能却比很多人想象的要更加多样、复杂。JavaScript的许多细节规定会让你的网页出现很多意想不到的bug，搞懂这些bug，对于成为一位有经验的JS开发者很重要。&lt;/p&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误一：对于this关键词的不正确引用&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;我曾经听一位喜剧演员说过：&lt;/p&gt;

 &lt;p&gt;  &lt;strong&gt;“我从未在这里，因为我不清楚这里是哪里，是除了那里之外的地方吗？”&lt;/strong&gt;&lt;/p&gt;

 &lt;p&gt;这句话或多或少地暗喻了在js开发中开发者对于this关键字的使用误区。This指代的是什么？它和日常英语口语中的this是一个意思吗？&lt;/p&gt;

 &lt;p&gt;随着近些年js编程不断地复杂化，功能多样化，对于一个程序结构的内部指引、引用也逐渐变多起来&lt;/p&gt;

 &lt;p&gt;下面让我们一起来看这一段代码：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;Game.prototype.restart = function () {   this.clearLocalStorage(); 

    this.timer = setTimeout(function(){     this.clearBoard();        }, 0);

 };
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;运行上面的代码将会出现如下错误：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;Uncaught TypeError: undefined is not a function
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;这是为什么？this的调用和它所在的环境密切相关。之所以会出现上面的错误，是因为当你在调用 setTimeout()函数的时候, 你实际调用的是window.setTimeout(). 因此，在 setTimeout() 定义的函数其实是在window背景下定义的，而window中并没有 clearBoard() 这个函数方法。&lt;/p&gt;

 &lt;p&gt;下面提供两种解决方案。第一种比较简单直接的方法便是，把this存储到一个变量当中，这样他就可以在不同的环境背景中被继承下来：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;Game.prototype.restart = function () {   this.clearLocalStorage();  

 var self = this;

this.timer = setTimeout(function(){     self.clearBoard();}, 0); };
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;第二种方法便是用bind()的方法，不过这个相比上一种要复杂一些，对于不熟悉bind()的同学可以在微软官方查看它的使用方法：  &lt;a href="https://msdn.microsoft.com/zh-cn/library/ff841995" rel="nofollow"&gt;https://msdn.microsoft.com/zh-cn/library/ff841995&lt;/a&gt;&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;Game.prototype.restart = function () {   this.clearLocalStorage(); 

this.timer = setTimeout(this.reset.bind(this), 0); };      

Game.prototype.reset = function(){     this.clearBoard();};
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;上面的例子中，两个this均指代的是Game.prototype。&lt;/p&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误二：传统编程语言的生命周期误区&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;另一种易犯的错误，便是带着其他编程语言的思维，认为在JS中，也存在生命周期这么一说。请看下面的代码：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;for (var i = 0; i &amp;lt; 10; i++) {   /* ... */ } console.log(i);
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;如果你认为在运行console.log() 时肯定会报出 undefined 错误，那么你就大错特错了。我会告诉你其实它会返回 10吗。&lt;/p&gt;

 &lt;p&gt;当然，在许多其他语言当中，遇到这样的代码，肯定会报错。因为i明显已经超越了它的生命周期。在for中定义的变量在循环结束后，它的生命也就结束了。但是在js中，i的生命还会继续。这种现象叫做 variable hoisting。&lt;/p&gt;

 &lt;p&gt;而如果我们想要实现和其他语言一样的在特定逻辑模块中具有生命周期的变量，可以用let关键字。&lt;/p&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误三：内存泄露&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;内存泄露在js变成中几乎是一个无法避免的问题。如果不是特别细心的话，在最后的检查过程中，肯定会出现各种内存泄露问题。下面我们就来举例说明一下：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var theThing = null; 

var replaceThing = function () { 



    var priorThing = theThing; 

    var unused = function () { 

              if (priorThing) {       console.log(&amp;quot;hi&amp;quot;);     }   

   }; 

   theThing = {     longStr: new Array(1000000).join(&amp;apos;*&amp;apos;),  // 

              someMethod: function () {       console.log(someMessage);     }   
   }; 
};   
setInterval(replaceThing, 1000); 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;如果运行上面的代码，你会发现你已经造成了大量的内存泄露，每秒泄露1M的内存，显然光靠GC（垃圾回收器）是无法帮助你的了。由上面的代码来看，似乎是longstr在每次replaceThing调用的时候都没有得到回收。这是为什么呢？&lt;/p&gt;

 &lt;p&gt;每一个theThing结构都含有一个longstr结构列表。每一秒当我们调用 replaceThing, 它就会把当前的指向传递给 priorThing. 但是到这里我们也会看到并没有什么问题，因为 priorThing 每回也是先解开上次函数的指向才会接受新的赋值。并且所有的这一切都是发生在 replaceThing 函数体当中，按常理来说当函数体结束之后，函数中的本地变量也将会被GC回收，也就不会出现内存泄露的问题了，但是为什么会出现上面的错误呢？&lt;/p&gt;

 &lt;p&gt;这是因为longstr的定义是在一个闭包中进行的，而它又被其他的闭包所引用，js规定，在闭包中引入闭包外部的变量时，当闭包结束时此对象无法被垃圾回收（GC）。关于在JS中的内存泄露问题可以查看  &lt;a href="http://javascript.info/tutorial/memory-leaks#memory-management-in-javascript" rel="nofollow"&gt;http://javascript.info/tutorial/memory-leaks#memory-management-in-java...&lt;/a&gt;&lt;/p&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误四:比较运算符&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;JavaScript中一个比较便捷的地方，便是它可以给每一个在比较运算的结果变量强行转化成布尔类型。但是从另一方面来考虑，有时候它也会为我们带来很多不便，下面的这些例子便是一些一直困扰很多程序员的代码实例：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;console.log(false == &amp;apos;0&amp;apos;); 

console.log(null == undefined); 

console.log(&amp;quot; \t\r\n&amp;quot; == 0); 

console.log(&amp;apos;&amp;apos; == 0);  // And these do too! 

if ({}) // ... 

if ([]) // ...
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;最后两行的代码虽然条件判断为空（经常会被人误认为转化为false），但是其实不管是{ }还是[ ]都是一个实体类，而任何的类其实都会转化为true。就像这些例子所展示的那样，其实有些类型强制转化非常模糊。因此很多时候我们更愿意用 === 和 !== 来替代== 和 !=， 以此来避免发生强制类型转化。. ===和!== 的用法和之前的== 和 != 一样，只不过他们不会发生类型强制转换。另外需要注意的一点是，当任何值与 NaN 比较的时候，甚至包括他自己，结果都是false。因此我们不能用简单的比较字符来决定一个值是否为 NaN 。我们可以用内置的 isNaN() 函数来辨别:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;console.log(NaN == NaN);    // false 

console.log(NaN === NaN);   // false 

console.log(isNaN(NaN));    // true 
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误五：低效的DOM操作&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;js中的DOM基本操作非常简单，但是如何能有效地进行这些操作一直是一个难题。这其中最典型的问题便是批量增加DOM元素。增加一个DOM元素是一步花费很大的操作。而批量增加对系统的花销更是不菲。一个比较好的批量增加的办法便是使用 document fragments ：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var div = document.getElementsByTagName(&amp;quot;my_div&amp;quot;);  

var fragment = document.createDocumentFragment(); 

 for (var e = 0; e &amp;lt; elems.length; e++) { fragment.appendChild(elems[e]); } div.appendChild(fragment.cloneNode(true)); 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;直接添加DOM元素是一个非常昂贵的操作。但是如果是先把要添加的元素全部创建出来，再把它们全部添加上去就会高效很多。&lt;/p&gt;

 &lt;h3&gt;常见错误六：在for循环中的不正确函数调用&lt;/h3&gt;

 &lt;p&gt;请大家看以下代码：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var elements = document.getElementsByTagName(&amp;apos;input&amp;apos;);

var n = elements.length; 

for (var i = 0; i &amp;lt; n; i++) {     

elements[i].onclick = function() {         

console.log(&amp;quot;This is element #&amp;quot; + i);     }; } 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;运行以上代码，如果页面上有10个按钮的话，点击每一个按钮都会弹出 “This is element #10”! 。这和我们原先预期的并不一样。这是因为当点击事件被触发的时候，for循环早已执行完毕，i的值也已经从0变成了。&lt;/p&gt;

 &lt;p&gt;我们可以通过下面这段代码来实现真正正确的效果：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var elements = document.getElementsByTagName(&amp;apos;input&amp;apos;); 

var n = elements.length; 

var makeHandler = function(num) {  // outer function

      return function() { 

console.log(&amp;quot;This is element #&amp;quot; + num);      }; }; 

for (var i = 0; i &amp;lt; n; i++) 

{     elements[i].onclick = makeHandler(i+1); }
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;在这个版本的代码中， makeHandler 在每回循环的时候都会被立即执行，把i＋1传递给变量num。外面的函数返回里面的函数，而点击事件函数便被设置为里面的函数。这样每个触发函数就都能够是用正确的i值了。&lt;/p&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误七：原型继承问题&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;很大一部分的js开发者都不能完全掌握原型的继承问题。下面具一个例子来说明：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;BaseObject = function(name) {     

if(typeof name !== &amp;quot;undefined&amp;quot;) 

{         this.name = name;     } 

else 

{         this.name = &amp;apos;default&amp;apos;     } }; 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;这段代码看起来很简单。如果你有name值，则使用它。如果没有，则使用 ‘default’:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var firstObj = new BaseObject(); 

var secondObj = new BaseObject(&amp;apos;unique&amp;apos;);  

console.log(firstObj.name);  // -&amp;gt; 结果是&amp;apos;default&amp;apos; 

console.log(secondObj.name); // -&amp;gt; 结果是 &amp;apos;unique&amp;apos;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;但是如果我们执行delete语句呢:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;delete secondObj.name; 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;我们会得到:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;console.log(secondObj.name); // -&amp;gt; 结果是 &amp;apos;undefined&amp;apos; 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;但是如果能够重新回到 ‘default’状态不是更好么? 其实要想达到这样的效果很简单，如果我们能够使用原型继承的话:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;BaseObject = function (name) 

{     if(typeof name !== &amp;quot;undefined&amp;quot;) 

{         this.name = name;     } };  

BaseObject.prototype.name = &amp;apos;default&amp;apos;; 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;在这个版本中, BaseObject 继承了原型中的name 属性, 被设置为了 &amp;apos;default&amp;apos;.。这时，如果构造函数被调用时没有参数，则会自动设置为 default。相同地，如果name 属性被从BaseObject移出，系统将会自动寻找原型链，并且获得 &amp;apos;default&amp;apos;值：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt; var thirdObj = new BaseObject(&amp;apos;unique&amp;apos;); 

 console.log(thirdObj.name);  

 delete thirdObj.name;

 console.log(thirdObj.name);  // -&amp;gt; 结果是 &amp;apos;default&amp;apos; 
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误八：为实例方法创建错误的指引&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;我们来看下面一段代码:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var MyObject = function() {} 

 MyObject.prototype.whoAmI = function() {     

console.log(this === window ? &amp;quot;window&amp;quot; : &amp;quot;MyObj&amp;quot;); }; 

 var obj = new MyObject(); 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;现在为了方便起见，我们新建一个变量来指引 whoAmI 方法, 因此我们可以直接用 whoAmI() 而不是更长的obj.whoAmI():&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var whoAmI = obj.whoAmI;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;接下来为了确保一切都如我们所预测的进行，我们可以将 whoAmI 打印出来:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;console.log(whoAmI); 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;结果是：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;function () {     console.log(this === window ? &amp;quot;window&amp;quot; : &amp;quot;MyObj&amp;quot;); } 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;没有错误！&lt;/p&gt;

 &lt;p&gt;但是现在我们来查看一下两种引用的方法：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;obj.whoAmI();  // 输出 &amp;quot;MyObj&amp;quot; (as expected) 

whoAmI();      // 输出 &amp;quot;window&amp;quot; (uh-oh!) 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;哪里出错了呢？&lt;/p&gt;

 &lt;p&gt;原理其实和上面的第二个常见错误一样，当我们执行 var whoAmI = obj.whoAmI;的时候，新的变量 whoAmI 是在全局环境下定义的。因此它的this 是指window, 而不是obj！&lt;/p&gt;

 &lt;p&gt;正确的编码方式应该是：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;var MyObject = function() {}  

MyObject.prototype.whoAmI = function() {     

      console.log(this === window ? &amp;quot;window&amp;quot; : &amp;quot;MyObj&amp;quot;); }; 

var obj = new MyObject(); 

obj.w = obj.whoAmI;   // still in the obj namespace  obj.whoAmI();  // 输出 &amp;quot;MyObj&amp;quot; (as expected) 

obj.w();       // 输出 &amp;quot;MyObj&amp;quot; (as expected) 
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误九：用字符串作为setTimeout 或者 setInterval的第一个参数&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;首先我们要声明，用字符串作为这两个函数的第一个参数并没有什么语法上的错误。但是其实这是一个非常低效的做法。因为从系统的角度来说，当你用字符串的时候，它会被传进构造函数，并且重新调用另一个函数。这样会拖慢程序的进度。&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;setInterval(&amp;quot;logTime()&amp;quot;, 1000); 

setTimeout(&amp;quot;logMessage(&amp;apos;&amp;quot; + msgValue + &amp;quot;&amp;apos;)&amp;quot;, 1000);
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;另一种方法是直接将函数作为参数传递进去：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;setInterval(logTime, 1000);   

setTimeout(function() { 

logMessage(msgValue); }, 1000); 
&lt;/code&gt;&lt;/pre&gt;

 &lt;h3&gt;  &lt;strong&gt;常见错误十：忽略 “strict mode”的作用&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;“strict mode” 是一种更加严格的代码检查机制，并且会让你的代码更加安全。当然，不选择这个模式并不意味着是一个错误，但是使用这个模式可以确保你的代码更加准确无误。&lt;/p&gt;

 &lt;p&gt;下面我们总结几条“strict mode”的优势：&lt;/p&gt;

 &lt;ol&gt;
  &lt;li&gt;   &lt;p&gt;让Debug更加容易：在正常模式下很多错误都会被忽视掉，“strict mode”模式会让Debug极致更加严谨。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;防止默认的全局变量：在正常模式下，给一个为经过声明的变量命名将会将这个变量自动设置为全局变量。在strict模式下，我们取消了这个默认机制。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;取消this的默认转换：在正常模式下，给this关键字指引到null或者undefined会让它自动转换为全局。在strict模式下，我们取消了这个默认机制。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;防止重复的变量声明和参数声明：在strict模式下进行重复的变量声明会被抱错，如 (e.g., var object = {foo: &amp;quot;bar&amp;quot;, foo: &amp;quot;baz&amp;quot;};) 同时，在函数声明中重复使用同一个参数名称也会报错，如 (e.g., function foo(val1, val2, val1){}),&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;让eval()函数更加安全。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;当遇到无效的delete指令的事后报错：delete指令不能对类中未有的属性执行，在正常情况下这种情况只是默默地忽视掉，而在strict模式是会报错的。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
 &lt;h3&gt;  &lt;strong&gt;结语&lt;/strong&gt;&lt;/h3&gt;

 &lt;p&gt;正如和其他的技术语言一样，你对JavaScript了解的的越深，知道它是如何运作，为什么这样运作，你才会熟练地掌握并且运用这门语言。相反地，如果你缺少对JS模式的认知的话，你就会碰上很多的问题。了解JS的一些细节上的语法或者功能将会有助于你提高编程的效率，减少变成中遇到的问题。&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;原文地址：  &lt;a href="http://www.toptal.com/javascript/10-most-common-javascript-mistakes" rel="nofollow"&gt;http://www.toptal.com/javascript/10-most-common-javascript-mistakes&lt;/a&gt;  &lt;br /&gt;
译文地址：  &lt;a href="http://1ke.co/course/136?utm_source=segment&amp;utm_medium=1&amp;utm_campaign=will" rel="nofollow"&gt;http://1ke.co/course/136?utm_source=segment&amp;amp;utm_medium=1&amp;amp;utm_c...&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>javascript</category>
      <guid isPermaLink="true">https://itindex.net/detail/53502-javascript-%E9%94%99%E8%AF%AF</guid>
      <pubDate>Mon, 25 May 2015 15:46:39 CST</pubDate>
    </item>
    <item>
      <title>统治Web的JavaScript准备要接管桌面了</title>
      <link>https://itindex.net/detail/55578-%E7%BB%9F%E6%B2%BB-web-javascript</link>
      <description>&lt;p&gt;  &lt;em&gt;   &lt;strong&gt;编者按：&lt;/strong&gt;曾几何时，通信网络曾经活跃过许多协议，那些协议在不同的场景下都有各自的优势。但是后来 TCP/IP 随着互联网的崛起而异军突起，最终形成一统天下的格局。而原本被视为不入流的 Web 前端玩具 JavaScript 现在似乎也在复制着类似的路径，先是渗透到 web 后端开发，然后甚至开始   &lt;a href="http://www.wired.com/2016/05/javascript-conquered-web-now-taking-desktop/" target="_blank"&gt;入侵到桌面了&lt;/a&gt;。最流行语言 JavaScript 会变成编程界的世界语吗？&lt;/em&gt;&lt;/p&gt; &lt;p&gt;创建于 1995年 的 JavaScript 原本是想给苍白的网页在&amp;lt;blink&amp;gt;标记以外再增加一点生气的。但现在它的用处已经远远超出了当时的意图。像 Google、Facebook 这样的公司都用 JavaScript 来开发复杂的、类桌面的 web 应用；自从 2009年 推出了 Node.js 之后，它还成为了开发服务器端最受欢迎的语言之一（连续很多年雄踞  &lt;a href="http://36kr.com/p/5044701.html" target="_blank"&gt;最受欢迎编程语言&lt;/a&gt;）。时至今日，甚至连 web 也容纳不下 JavaScript 的雄才大略了：现在，它正在开始向桌面应用进军。&lt;/p&gt; &lt;p&gt;Electron 是 GitHub 开发的一个软件开发平台，它可以让开发者用 JavaScript 加上 HTML、CSS 等 web 技术来创建跨平台（可运行于 Windows、Mac OS X 以及 Linux）运行的桌面应用。近日该公司刚刚发布了第一款完整版的 Eletron。但是业界的一些大拿早已用上这一工具来让 JavaScript 冲出浏览器了。&lt;/p&gt; &lt;p&gt;微软去年发布的一款代码编辑器 Visual Studio Code 就是用 Eletron 开发的。协作聊天工具的独角兽 Slack 的桌面客户端也是用 Eletron 开发的。初创企业 Nylas（以前的 Inbox）的整个邮件客户端都是用 Eletron 开发的。甚至 Brave 这家由 JavaScript 创造者 Brendan Eich 创建的公司也用 Electron 开发了一整个 web 浏览器。&lt;/p&gt; &lt;p&gt;为什么在开发桌面应用有无数选择的情况下那么多公司会选择 JavaScript 呢？不妨想想 JavaScript 在服务器端的崛起。Node.js 出来以前开发者也已经有了很多开发服务器端应用的利器了。但是由于开发者可以使用同一门语言来开发前端（浏览器）和后端（服务器），使得 JavaScript 变得非常流行。事实表明，在 web 上采用通用语是很有用的。而 JavaScript 的无所不在使得将它移植到桌面成为了自然之举。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;面向每一个人开放&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;许多使用 Electron 的公司都在开发开源软件，也就是说，谁都可以查看和修改里面的代码。而理解你开放的代码的人越多，可以为其贡献 bug 修补和新功能的人就越多。通过利用 JavaScript，这些公司向了解 JavaScript 但不熟悉 C++ 等传统桌面编程语言的 web 开发者打开了一道大门。&lt;/p&gt; &lt;p&gt;比方说初创企业 Jibo 就用 Electron 来开发自己的机器人个人助理，因为这支团队希望第三方替自己的机器人写软件时可用更容易些。“我们希望把门槛降到尽可能的低，” Jobo 开发者 Jonathan Ross 说：“所以我们马上想到应该向游戏开发者和 web 开发者推销。”&lt;/p&gt; &lt;p&gt;这也是起初做 Electron 的一大原因。Electron 原先是做来给开发 Atom 当平台用的，后者是 GitHub 创建的一款文本编辑器。GitHub 联合创始人 Chris Wanstrath 说，他之所以用 JavaScript 来做文本编辑器是希望 web 开发者合适的时候能够对其进行定制。“我自己可以 hack 我的编辑器是一回事，” Wanstrath 说：“但是能够使用别人写的插件却要强大得多。”&lt;/p&gt; &lt;p&gt;当然，跑相当于 web 浏览器的应用也有不好的地方。“跑 Electron 应用的负载相当的大，” Nylas 的首席前端开发者 Ben Gotow 说。但是权衡之下还是值得的，因为 Electron 能做的不仅仅是提供使用一门流行语言的能力。用 JavaScript 创建桌面应用使得开发者可以选择海量的免费代码库和框架，免去了大量乏味枯燥的编码工作。比方说登录系统就不用再写了，网上有大量开源的身份验证库可供选择。&lt;/p&gt; &lt;p&gt;“我们仅用 1年 的时间就搞定了邮件客户端，那是相当惊人的了，” Gotow 说。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;极客产物&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;用 web 技术开发桌面应用的想法并不新鲜。Adobe 的 AIR 系统就可以让开发者利用其 Flash 平台开发桌面应用，另一家小公司 Appcelerator（现在专注于移动 app 的开发）也曾经提供过利用 HTML 和 JavaScript 开发桌面应用的工具。最近又有一支开发者团队做出了 NW.js，也就是之前 Node Webkit。&lt;/p&gt; &lt;p&gt;但是到了 2014年 的时候，GitHub 团队发现所有的可用选择都没了，于是他们做了极客都会做的事情：自己写一个然后发布给全世界。“我们认为做出的好应用应该给大家一起用，” GitHub 产品 VP Kakul Srivastava 这样说。&lt;/p&gt; &lt;p&gt;GitHub Electron 团队的 JessicaLord 则说 Electron 要比之前的那些工具都要高效。Electron 捆绑了 Chromium（Google 浏览器 Chrome 的开源版），但是对其进行了瘦身。这使得该团队集成 Google 的新功能变得更加容易，因为这样每次 Chromium 发布新版时他们就不需要对每个部分都要进行测试来看看是否会导致 Electron 功能出问题了。&lt;/p&gt; &lt;p&gt;GitHub 的努力似乎收到了回报。从 2年 前推出项目至今，Electron 贡献者的数量已经超过了 300。其下载量也已经突破百万大关。但是竞争也日趋激烈。NW.js 还在不断改进，而 Facebook 也发布了自己的 JavaScript 框架 React Native，开发者可以使用后者来开发 Windows、Xbox 以及 Android 等各种平台的应用。&lt;/p&gt; &lt;p&gt;无论开发者最终决定使用哪种框架或者平台，显然 JavaScript 都已经在桌面找到了安身之所。对于用户来说，在线和离线之间的界限早就已经模糊了。现在对于代码来说也是一样的。&lt;/p&gt;
      &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>深度</category>
      <guid isPermaLink="true">https://itindex.net/detail/55578-%E7%BB%9F%E6%B2%BB-web-javascript</guid>
      <pubDate>Sat, 14 May 2016 22:09:30 CST</pubDate>
    </item>
    <item>
      <title>如何抵御基于JavaScript的DDoS攻击</title>
      <link>https://itindex.net/detail/53344-javascript-ddos-%E6%94%BB%E5%87%BB</link>
      <description>DDoS攻击技术正在快速演化，最近出现的基于JavaScript的DDoS攻击具有一个与众不同的特点：任何有浏览器的设备都可能参与攻击，其潜在攻击规模接近无限。现代网站的绝大多数互动采用的都是JavaScript。JavaScript脚本可直接植入HTML中，或者是从远程服务器载入。JavaScript攻击主要发生在共享JavaScript脚本在第三方服务器上被替换，或者是在传输过程中被中间人替换。对于屏蔽服务器上被替换的脚本，目前HTTP还没有相关机制，但W3C已经提出了名叫子资源完整性的新功能，使用哈希值验证脚本，如果哈希值不匹配浏览器可以阻止其运行。支持子资源完整性的浏览器暂时还只有Chrome和Firefox。对于传输过程中脚本被中间人替换成恶意代码，全面启用HTTPS是唯一有用的方法。 &lt;img border="0" height="1" src="http://solidot.org.feedsportal.com/c/33236/f/556826/s/45e86bb4/sc/4/mf.gif" width="1"&gt;&lt;/img&gt; &lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/rc/1/rc.htm" rel="nofollow"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/rc/1/rc.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/rc/2/rc.htm" rel="nofollow"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/rc/2/rc.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/rc/3/rc.htm" rel="nofollow"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/rc/3/rc.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;br /&gt; &lt;br /&gt; &lt;a href="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/a2.htm"&gt;  &lt;img border="0" src="http://da.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/a2.img"&gt;&lt;/img&gt;&lt;/a&gt; &lt;img border="0" height="1" src="http://pi.feedsportal.com/r/224464236021/u/49/f/556826/c/33236/s/45e86bb4/sc/4/a2t.img" width="1"&gt;&lt;/img&gt; &lt;div&gt;
  &lt;a href="http://feeds.feedburner.com/~ff/solidot?a=dvJHaXvXx5Y:st6sAe_8Tk8:yIl2AUoC8zA"&gt;   &lt;img border="0" src="http://feeds.feedburner.com/~ff/solidot?d=yIl2AUoC8zA"&gt;&lt;/img&gt;&lt;/a&gt;   &lt;a href="http://feeds.feedburner.com/~ff/solidot?a=dvJHaXvXx5Y:st6sAe_8Tk8:7Q72WNTAKBA"&gt;   &lt;img border="0" src="http://feeds.feedburner.com/~ff/solidot?d=7Q72WNTAKBA"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/53344-javascript-ddos-%E6%94%BB%E5%87%BB</guid>
      <pubDate>Fri, 01 May 2015 13:58:53 CST</pubDate>
    </item>
    <item>
      <title>javaScript高级技巧</title>
      <link>https://itindex.net/detail/54177-javascript-%E6%8A%80%E5%B7%A7</link>
      <description>一、变量作用域和闭包
 &lt;br /&gt;注意： uncaught refereceError：is not defined 和 undefined 不是一样
 &lt;br /&gt;
 &lt;br /&gt;js是函数级作用域,在内部的变量，函数内部都能访问，函数外部不能访问函数内部的变量，函数内部能访问外部的变量
 &lt;br /&gt;
 &lt;br /&gt;注意：在函数里声明变量，都会提到函数的最前面
 &lt;br /&gt;
 &lt;br /&gt;如何可以在函数外部拿到函数内部的变量，就需要使用闭包(注意使用，闭包会造成内存泄露)
 &lt;br /&gt;
 &lt;br /&gt;代码例子
 &lt;br /&gt;
 &lt;br /&gt;function test(){
 &lt;br /&gt; 
 &lt;br /&gt;   var k = 1000;
 &lt;br /&gt;   return function(){
 &lt;br /&gt;       return k ;
 &lt;br /&gt;   }
 &lt;br /&gt;}
 &lt;br /&gt;
 &lt;br /&gt;var t = test()();//t的值是1000;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;二、this的使用（谁调用this就指向谁）
 &lt;br /&gt;
 &lt;br /&gt;代码例子
 &lt;br /&gt;闭包的使用 
 &lt;br /&gt;this.m=1000;
 &lt;br /&gt;var obj = {
 &lt;br /&gt;   m:100,
 &lt;br /&gt;   test : function(){
 &lt;br /&gt;          alert(this.m);//值是100
 &lt;br /&gt;          return function(){
 &lt;br /&gt;              alert(this.m);//值是1000
 &lt;br /&gt;          }   
 &lt;br /&gt;   }
 &lt;br /&gt;}
 &lt;br /&gt;执行 (obj.test())();
 &lt;br /&gt;
 &lt;br /&gt;面向切面的使用
 &lt;br /&gt;this.a=1000;
 &lt;br /&gt;function test(){
 &lt;br /&gt;  this.a = 1;
 &lt;br /&gt;}
 &lt;br /&gt;test.prototype.geta = function(){ //将方法挂在原型上
 &lt;br /&gt;  return this.a;
 &lt;br /&gt;}
 &lt;br /&gt;执行
 &lt;br /&gt;var p = new test();
 &lt;br /&gt;console.log(p.geta());//值是1
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;三、按值传递 
 &lt;br /&gt;
 &lt;br /&gt;1、如果将Stirng、number、boolean作为变量传递给函数，函数内部的修改不会影响外面的变量值。
 &lt;br /&gt;2、如果将object、或者array传递给函数，函数内部的修改会影响外面的变量值。
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;四、通过原型链继承 （面向对象）
 &lt;br /&gt;
 &lt;br /&gt;function People(){
 &lt;br /&gt;
 &lt;br /&gt;}
 &lt;br /&gt;People.prototype.say = function(){
 &lt;br /&gt;  alert(&amp;quot;hello world&amp;quot;)
 &lt;br /&gt;}
 &lt;br /&gt;
 &lt;br /&gt;function Student(){
 &lt;br /&gt;
 &lt;br /&gt;}
 &lt;br /&gt;
 &lt;br /&gt;var super = Student.prototype.say;
 &lt;br /&gt;
 &lt;br /&gt;Student.prototype.say = function(){
 &lt;br /&gt;    super.call(this)//子类调用父类的方法
 &lt;br /&gt;    alert(&amp;quot;stu-hello&amp;quot;); 
 &lt;br /&gt;}
 &lt;br /&gt;
 &lt;br /&gt;Student.prototype = new People();//通过原型链来继承
 &lt;br /&gt;var s = new Student();
 &lt;br /&gt;s.say();
 &lt;br /&gt;
 &lt;br /&gt;五、面向对象封装
 &lt;br /&gt;(function(){
 &lt;br /&gt;
 &lt;br /&gt;  var n = &amp;quot;ime&amp;quot; (只能在内部使用变量)
 &lt;br /&gt;  function People(){
 &lt;br /&gt;
 &lt;br /&gt;  }
 &lt;br /&gt;  People.prototype.say = function(){
 &lt;br /&gt;   alert(&amp;quot;hello world&amp;quot;)
 &lt;br /&gt;  }
 &lt;br /&gt;   
 &lt;br /&gt;  window.People = People ;(对外提供一个接口)
 &lt;br /&gt;})()
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;六、DOM事件处理
 &lt;br /&gt;
 &lt;br /&gt;1、DOM0级事件处理，如果多次绑定一个事件，那么事件会被覆盖。
 &lt;br /&gt;2、DOM2级事件处理，一个事件多次绑定，并不会覆盖，是依次执行。
 &lt;br /&gt;3、阻止事件冒泡：event.stopPropagation();
 &lt;br /&gt;4、阻止默认行为：如超链接标签的默认行为是连接到其他界面,event.preventDefault()可以阻止。
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
          
           &lt;br /&gt; &lt;br /&gt;
          
             &lt;a href="http://kayak2015.iteye.com/blog/2235753#comments"&gt;已有   &lt;strong&gt;0&lt;/strong&gt; 人发表留言，猛击-&amp;gt;&amp;gt;  &lt;strong&gt;这里&lt;/strong&gt;&amp;lt;&amp;lt;-参与讨论&lt;/a&gt;
          
           &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
ITeye推荐
 &lt;br /&gt;
 &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.iteye.com/clicks/433" target="_blank"&gt;—软件人才免语言低担保 赴美带薪读研！— &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
          
        &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/54177-javascript-%E6%8A%80%E5%B7%A7</guid>
      <pubDate>Sun, 16 Aug 2015 10:05:40 CST</pubDate>
    </item>
    <item>
      <title>JavaScript性能优化小知识总结</title>
      <link>https://itindex.net/detail/52861-javascript-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E7%9F%A5%E8%AF%86</link>
      <description>&lt;p&gt;JavaScript的性能问题不容小觑，这就需要我们开发人员在编写JavaScript程序时多注意一些细节，本文非常详细的介绍了一下JavaScript性能优化方面的知识点，绝对是干货。&lt;/p&gt;
 &lt;h2&gt;前言&lt;/h2&gt;
 &lt;p&gt;一直在学习javascript，也有看过《犀利开发Jquery内核详解与实践》，对这本书的评价只有两个字犀利，可能是对javascript理解的还不够透彻异或是自己太笨，更多的是自己不擅于思考懒得思考以至于里面说的一些精髓都没有太深入的理解。&lt;/p&gt;
 &lt;p&gt;鉴于想让自己有一个提升，进不了一个更加广阔的天地，总得找一个属于自己的居所好好生存，所以平时会有意无意的去积累一些使用jQuerry的常用知识，特别是对于性能要求这一块，总是会想是不是有更好的方式来实现。&lt;/p&gt;
 &lt;p&gt;下面是我总结的一些小技巧，仅供参考。(我先会说一个总标题，然后用一小段话来说明这个意思 再最后用一个demo来简单言明)&lt;/p&gt;
 &lt;h2&gt;避免全局查找&lt;/h2&gt;
 &lt;p&gt;在一个函数中会用到全局对象存储为局部变量来减少全局查找，因为访问局部变量的速度要比访问全局变量的速度更快些&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function search() {
            //当我要使用当前页面地址和主机域名
            alert(window.location.href + window.location.host);
        }
        //最好的方式是如下这样  先用一个简单变量保存起来
        function search() {
            var location = window.location;
            alert(location.href + location.host);
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;定时器&lt;/h2&gt;
 &lt;p&gt;如果针对的是不断运行的代码，  &lt;strong&gt;不应该使用setTimeout，而应该是用setInterval&lt;/strong&gt;，因为setTimeout每一次都会初始化一个定时器，而setInterval只会在开始的时候初始化一个定时器&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var timeoutTimes = 0;
        function timeout() {
            timeoutTimes++;
            if (timeoutTimes &amp;lt; 10) {
                setTimeout(timeout, 10);
            }
        }
        timeout();
        //可以替换为：
        var intervalTimes = 0;
        function interval() {
            intervalTimes++;
            if (intervalTimes &amp;gt;= 10) {
                clearInterval(interv);
            }
        }
        var interv = setInterval(interval, 10);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;字符串连接&lt;/h2&gt;
 &lt;p&gt;如果要连接多个字符串，应该少使用+=，如&lt;/p&gt;
 &lt;p&gt;s+=a;&lt;/p&gt;
 &lt;p&gt;s+=b;&lt;/p&gt;
 &lt;p&gt;s+=c;&lt;/p&gt;
 &lt;p&gt;应该写成s+=a + b + c；&lt;/p&gt;
 &lt;p&gt;而如果是收集字符串，比如多次对同一个字符串进行+=操作的话，最好使用一个缓存，使用JavaScript数组来收集，最后  &lt;strong&gt;使用join方法连接起来&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var buf = [];
        for (var i = 0; i &amp;lt; 100; i++) {
            buf.push(i.toString());
        }
        var all = buf.join(&amp;quot;&amp;quot;);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;避免with语句&lt;/h2&gt;
 &lt;p&gt;和函数类似 ，with语句会创建自己的作用域，因此会增加其中执行的代码的作用域链的长度，由于额外的作用域链的查找，在with语句中执行的代码肯定会比外面执行的代码要慢，在  &lt;strong&gt;能不使用with语句的时候尽量不要使用with语句&lt;/strong&gt;。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt; with (a.b.c.d) {
            property1 = 1;
            property2 = 2;
        }
        //可以替换为：
        var obj = a.b.c.d;
        obj.property1 = 1;
        obj.property2 = 2;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;数字转换成字符串&lt;/h2&gt;
 &lt;p&gt;般最好用”&amp;quot; + 1来将数字转换成字符串，虽然看起来比较丑一点，但事实上这个效率是最高的，性能上来说：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;(“” +) &amp;gt; String() &amp;gt; .toString() &amp;gt; new String()&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;浮点数转换成整型&lt;/h2&gt;
 &lt;p&gt;很多人喜欢使用parseInt()，其实parseInt()是用于将字符串转换成数字，而不是浮点数和整型之间的转换，我们应该使用Math.floor()或者Math.round()&lt;/p&gt;
 &lt;h2&gt;各种类型转换&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;var myVar = &amp;quot;3.14159&amp;quot;,
        str = &amp;quot;&amp;quot; + myVar, //  to string  
        i_int = ~ ~myVar,  //  to integer  
        f_float = 1 * myVar,  //  to float  
        b_bool = !!myVar,  /*  to boolean - any string with length 
                                and any number except 0 are true */
        array = [myVar];  //  to array&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;如果定义了toString()方法来进行类型转换的话，推荐  &lt;strong&gt;显式调用toString()&lt;/strong&gt;，因为内部的操作在尝试所有可能性之后，会尝试对象的toString()方法尝试能否转化为String，所以直接调用这个方法效率会更高&lt;/p&gt;
 &lt;h2&gt;多个类型声明&lt;/h2&gt;
 &lt;p&gt;在JavaScript中所有变量都可以使用单个var语句来声明，这样就是组合在一起的语句，以减少整个脚本的执行时间，就如上面代码一样，上面代码格式也挺规范，让人一看就明了。&lt;/p&gt;
 &lt;h2&gt;插入迭代器&lt;/h2&gt;
 &lt;p&gt;如var name=values[i]; i++;前面两条语句可以写成var name=values[i++]&lt;/p&gt;
 &lt;h2&gt;使用直接量&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;var aTest = new Array(); //替换为
        var aTest = [];
        var aTest = new Object; //替换为
        var aTest = {};
        var reg = new RegExp(); //替换为
        var reg = /../;
        //如果要创建具有一些特性的一般对象，也可以使用字面量，如下：
        var oFruit = new O;
        oFruit.color = &amp;quot;red&amp;quot;;
        oFruit.name = &amp;quot;apple&amp;quot;;
        //前面的代码可用对象字面量来改写成这样：
        var oFruit = { color: &amp;quot;red&amp;quot;, name: &amp;quot;apple&amp;quot; };&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用DocumentFragment优化多次append&lt;/h2&gt;
 &lt;p&gt;一旦需要更新DOM,请考虑使用文档碎片来构建DOM结构，然后再将其添加到现存的文档中。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            document.body.appendChild(el);
        }
        //可以替换为：
        var frag = document.createDocumentFragment();
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用一次innerHTML赋值代替构建dom元素&lt;/h2&gt;
 &lt;p&gt;对于大的DOM更改，使用innerHTML要比使用标准的DOM方法创建同样的DOM结构快得多。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var frag = document.createDocumentFragment();
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);
        //可以替换为：
        var html = [];
        for (var i = 0; i &amp;lt; 1000; i++) {
            html.push(&amp;apos;&amp;lt;p&amp;gt;&amp;apos; + i + &amp;apos;&amp;lt;/p&amp;gt;&amp;apos;);
        }
        document.body.innerHTML = html.join(&amp;apos;&amp;apos;);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;通过模板元素clone，替代createElement&lt;/h2&gt;
 &lt;p&gt;很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低，如果需要直接插入HTML，可以找一个容器元素，比如指定一个div或者span，并设置他们的innerHTML来将自己的HTML代码插入到页面中。通常我们可能会使用字符串直接写HTML来创建节点，其实这样做，1无法保证代码的有效性2字符串操作效率低，所以应该是用document.createElement()方法，而如果文档中存在现成的样板节点，应该是用cloneNode()方法，因为使用createElement()方法之后，你需要设置多次元素的属性，使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素，应该先准备一个样板节点&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var frag = document.createDocumentFragment();
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);
        //替换为：
        var frag = document.createDocumentFragment();
        var pEl = document.getElementsByTagName(&amp;apos;p&amp;apos;)[0];
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = pEl.cloneNode(false);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用firstChild和nextSibling代替childNodes遍历dom元素&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;img alt="" src="http://static.codeceo.com/images/2015/03/8d08ae397597d1b7ed9b79fc83e1db24.png"&gt;&lt;/img&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var nodes = element.childNodes;
        for (var i = 0, l = nodes.length; i &amp;lt; l; i++) {
            var node = nodes[i];
            //……
        }
        //可以替换为：
        var node = element.firstChild;
        while (node) {
            //……
            node = node.nextSibling;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;删除DOM节点&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;删除dom节点之前,一定要删除注册在该节点上的事件&lt;/strong&gt;,不管是用observe方式还是用attachEvent方式注册的事件,否则将会产生无法回收的内存。另外，在removeChild和innerHTML=’’二者之间,尽量选择后者. 因为在sIEve(内存泄露监测工具)中监测的结果是用removeChild无法有效地释放dom节点&lt;/p&gt;
 &lt;h2&gt;使用事件代理&lt;/h2&gt;
 &lt;p&gt;任何可以冒泡的事件都不仅仅可以在事件目标上进行处理，目标的任何祖先节点上也能处理，使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理，同样，  &lt;strong&gt;对于内容动态增加并且子节点都需要相同的事件处理函数的情况，可以把事件注册提到父节点上，这样就不需要为每个子节点注册事件监听了&lt;/strong&gt;。另外，现有的js库都采用observe方式来创建事件监听,其实现上隔离了dom对象和事件处理函数之间的循环引用,所以应该尽量采用这种方式来创建事件监听&lt;/p&gt;
 &lt;h2&gt;重复使用的调用结果，事先保存到局部变量&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;        //避免多次取值的调用开销
        var h1 = element1.clientHeight + num1;
        var h2 = element1.clientHeight + num2;
        //可以替换为：
        var eleHeight = element1.clientHeight;
        var h1 = eleHeight + num1;
        var h2 = eleHeight + num2;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;注意NodeList&lt;/h2&gt;
 &lt;p&gt;最小化访问NodeList的次数可以极大的改进脚本的性能&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var images = document.getElementsByTagName(&amp;apos;img&amp;apos;);
        for (var i = 0, len = images.length; i &amp;lt; len; i++) {

        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;编写JavaScript的时候一定要知道何时返回NodeList对象，这样可以最小化对它们的访问&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;进行了对getElementsByTagName()的调用&lt;/li&gt;
  &lt;li&gt;获取了元素的childNodes属性&lt;/li&gt;
  &lt;li&gt;获取了元素的attributes属性&lt;/li&gt;
  &lt;li&gt;访问了特殊的集合，如document.forms、document.images等等&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;要了解了当使用NodeList对象时，合理使用会极大的提升代码执行速度&lt;/p&gt;
 &lt;h2&gt;优化循环&lt;/h2&gt;
 &lt;p&gt;可以使用下面几种方式来优化循环&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;减值迭代&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;大多数循环使用一个从0开始、增加到某个特定值的迭代器，在很多情况下，从最大值开始，在循环中不断减值的迭代器更加高效&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;简化终止条件&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于每次循环过程都会计算终止条件，所以必须保证它尽可能快，也就是说避免属性查找或者其它的操作，最好是将循环控制量保存到局部变量中，也就是说对数组或列表对象的遍历时，提前将length保存到局部变量中，避免在循环的每一步重复取值。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var list = document.getElementsByTagName(&amp;apos;p&amp;apos;);
        for (var i = 0; i &amp;lt; list.length; i++) {
            //……
        }

        //替换为：
        var list = document.getElementsByTagName(&amp;apos;p&amp;apos;);
        for (var i = 0, l = list.length; i &amp;lt; l; i++) {
            //……
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;简化循环体&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;循环体是执行最多的，所以要确保其被最大限度的优化&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;使用后测试循环&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在JavaScript中，我们可以使用for(;;),while(),for(in)三种循环，事实上，这三种循环中for(in)的效率极差，因为他需要查询散列键，只要可以，就应该尽量少用。for(;;)和while循环，while循环的效率要优于for(;;)，可能是因为for(;;)结构的问题，需要经常跳转回去。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var arr = [1, 2, 3, 4, 5, 6, 7];
        var sum = 0;
        for (var i = 0, l = arr.length; i &amp;lt; l; i++) {
            sum += arr[i];
        }

        //可以考虑替换为：

        var arr = [1, 2, 3, 4, 5, 6, 7];
        var sum = 0, l = arr.length;
        while (l--) {
            sum += arr[l];
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;最常用的for循环和while循环都是前测试循环，而如do-while这种后测试循环，可以避免最初终止条件的计算，因此运行更快。&lt;/p&gt;
 &lt;h2&gt;展开循环&lt;/h2&gt;
 &lt;p&gt;当循环次数是确定的，消除循环并使用多次函数调用往往会更快。&lt;/p&gt;
 &lt;h2&gt;避免双重解释&lt;/h2&gt;
 &lt;p&gt;如果要提高代码性能，尽可能避免出现需要按照JavaScript解释的字符串，也就是&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;尽量少使用&lt;/strong&gt;   &lt;strong&gt;eval&lt;/strong&gt;   &lt;strong&gt;函数&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;使用eval相当于在运行时再次调用解释引擎对内容进行运行，需要消耗大量时间，而且使用Eval带来的安全性问题也是不容忽视的。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;不要使用&lt;/strong&gt;   &lt;strong&gt;Function&lt;/strong&gt;   &lt;strong&gt;构造器&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;不要给setTimeout或者setInterval传递字符串参数&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var num = 0;
        setTimeout(&amp;apos;num++&amp;apos;, 10);
        //可以替换为：
        var num = 0;
        function addNum() {
            num++;
        }
        setTimeout(addNum, 10);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;缩短否定检测&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;       if (oTest != &amp;apos;#ff0000&amp;apos;) {
            //do something
        }
        if (oTest != null) {
            //do something
        }
        if (oTest != false) {
            //do something
        }
        //虽然这些都正确，但用逻辑非操作符来操作也有同样的效果：
        if (!oTest) {
            //do something
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;条件分支&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;将条件分支，按可能性顺序从高到低排列：可以减少解释器对条件的探测次数&lt;/li&gt;
  &lt;li&gt;在同一条件子的多（&amp;gt;2）条件分支时，使用switch优于if：switch分支选择的效率高于if，在IE下尤为明显。4分支的测试，IE下switch的执行时间约为if的一半。&lt;/li&gt;
  &lt;li&gt;使用三目运算符替代条件分支&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;pre&gt;        if (a &amp;gt; b) {
            num = a;
        } else {
            num = b;
        }
        //可以替换为：
        num = a &amp;gt; b ? a : b;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用常量&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;重复值&lt;/strong&gt;:任何在多处用到的值都应该抽取为一个常量&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;用户界面字符串&lt;/strong&gt;:任何用于显示给用户的字符串，都应该抽取出来以方便国际化&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;URLs&lt;/strong&gt;:在Web应用中，资源位置很容易变更，所以推荐用一个公共地方存放所有的URL&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;任意可能会更改的值:&lt;/strong&gt;每当你用到字面量值的时候，你都要问一下自己这个值在未来是不是会变化，如果答案是“是”，那么这个值就应该被提取出来作为一个常量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;避免与null进行比较&lt;/h2&gt;
 &lt;p&gt;由于JavaScript是弱类型的，所以它不会做任何的自动类型检查，所以如果看到与null进行比较的代码，尝试使用以下技术替换&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果值应为一个引用类型，使用instanceof操作符检查其构造函数&lt;/li&gt;
  &lt;li&gt;如果值应为一个基本类型，作用typeof检查其类型&lt;/li&gt;
  &lt;li&gt;如果是希望对象包含某个特定的方法名，则使用typeof操作符确保指定名字的方法存在于对象上&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;避免全局量&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;全局变量应该全部字母大写，各单词之间用_下划线来连接&lt;/strong&gt;。尽可能避免全局变量和函数, 尽量减少全局变量的使用，因为在一个页面中包含的所有JavaScript都在同一个域中运行。所以如果你的代码中声明了全局变量或者全局函数的话，后面的代码中载入的脚本文件中的同名变量和函数会覆盖掉（overwrite）你的。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;//糟糕的全局变量和全局函数
var current = null;
function init(){
//...
}
function change() {
    //...
}
function verify() {
    //...
}
//解决办法有很多，Christian Heilmann建议的方法是：
//如果变量和函数不需要在“外面”引用，那么就可以使用一个没有名字的方法将他们全都包起来。
(function(){
var current = null;
function init() {
    //...
}
function change() {
    //...
}
function verify() {
    //...
}
})();
//如果变量和函数需要在“外面”引用，需要把你的变量和函数放在一个“命名空间”中
//我们这里用一个function做命名空间而不是一个var，因为在前者中声明function更简单，而且能保护隐私数据
myNameSpace = function() {
    var current = null;

    function init() {
        //...
    }

    function change() {
        //...
    }

    function verify() {
        //...
    }

//所有需要在命名空间外调用的函数和属性都要写在return里面
    return {
        init: init,
        //甚至你可以为函数和属性命名一个别名
        set: change
    };
};&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;尊重对象的所有权&lt;/h2&gt;
 &lt;p&gt;因为JavaScript可以在任何时候修改任意对象，这样就可以以不可预计的方式覆写默认的行为，所以如果你不负责维护某个对象，它的对象或者它的方法，那么你就不要对它进行修改，具体一点就是说：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;不要为实例或原型添加属性&lt;/li&gt;
  &lt;li&gt;不要为实例或者原型添加方法&lt;/li&gt;
  &lt;li&gt;不要重定义已经存在的方法&lt;/li&gt;
  &lt;li&gt;不要重复定义其它团队成员已经实现的方法，永远不要修改不是由你所有的对象，你可以通过以下方式为对象创建新的功能:&lt;/li&gt;
  &lt;li&gt;创建包含所需功能的新对象，并用它与相关对象进行交互&lt;/li&gt;
  &lt;li&gt;创建自定义类型，继承需要进行修改的类型，然后可以为自定义类型添加额外功能&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;循环引用&lt;/h2&gt;
 &lt;p&gt;如果循环引用中包含DOM对象或者ActiveX对象，那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前，即使是刷新页面，这部分内存不会被浏览器释放。&lt;/p&gt;
 &lt;p&gt;简单的循环引用：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
        var func = function () {
            //…
        }
        el.func = func;
        func.element = el;&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;init在执行的时候，当前上下文我们叫做context。这个时候，context引用了el，el引用了function，function引用了context。这时候形成了一个循环引用。&lt;/p&gt;
 &lt;p&gt;下面2种方法可以解决循环引用：&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;1)  &lt;/strong&gt;  &lt;strong&gt;置空dom对象&lt;/strong&gt;&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;       function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
        }
        init();
        //可以替换为：
        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
            el = null;
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;将el置空，context中不包含对dom对象的引用，从而打断循环应用。&lt;/p&gt;
 &lt;p&gt;如果我们需要将dom对象返回，可以用如下方法：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
            return el;
        }
        init();
        //可以替换为：
        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
            try {
                return el;
            } finally {
                el = null;
            }
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;strong&gt;2)  &lt;/strong&gt;  &lt;strong&gt;构造新的context&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
        }
        init();
        //可以替换为：
        function elClickHandler() {
            //……
        }
        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = elClickHandler;
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;把function抽到新的context中，这样，function的context就不包含对el的引用，从而打断循环引用。&lt;/p&gt;
 &lt;h3&gt;通过javascript创建的dom对象，必须append到页面中&lt;/h3&gt;
 &lt;p&gt;IE下，脚本创建的dom对象，如果没有append到页面中，刷新页面，这部分内存是不会回收的！&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function create() {
            var gc = document.getElementById(&amp;apos;GC&amp;apos;);
            for (var i = 0; i &amp;lt; 5000; i++) {
                var el = document.createElement(&amp;apos;div&amp;apos;);
                el.innerHTML = &amp;quot;test&amp;quot;;
                //下面这句可以注释掉，看看浏览器在任务管理器中，点击按钮然后刷新后的内存变化
                gc.appendChild(el);
            }
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;释放dom元素占用的内存&lt;/h2&gt;
 &lt;p&gt;将dom元素的innerHTML设置为空字符串，可以释放其子元素占用的内存。&lt;/p&gt;
 &lt;p&gt;在rich应用中，用户也许会在一个页面上停留很长时间，可以使用该方法释放积累得越来越多的dom元素使用的内存。&lt;/p&gt;
 &lt;h2&gt;释放javascript对象&lt;/h2&gt;
 &lt;p&gt;在rich应用中，随着实例化对象数量的增加，内存消耗会越来越大。所以应当及时释放对对象的引用，让GC能够回收这些内存控件。&lt;/p&gt;
 &lt;p&gt;对象：obj = null&lt;/p&gt;
 &lt;p&gt;对象属性：delete obj.myproperty&lt;/p&gt;
 &lt;p&gt;数组item：使用数组的splice方法释放数组中不用的item&lt;/p&gt;
 &lt;h2&gt;避免string的隐式装箱&lt;/h2&gt;
 &lt;p&gt;对string的方法调用，比如’xxx’.length，浏览器会进行一个隐式的装箱操作，将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时，采用如下写法：&lt;/p&gt;
 &lt;p&gt;var myString = new String(‘Hello World’);&lt;/p&gt;
 &lt;h2&gt;松散耦合&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1、解耦HTML/JavaScript&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;JavaScript和HTML的紧密耦合：直接写在HTML中的JavaScript、使用包含内联代码的&amp;lt;script&amp;gt;元素、使用HTML属性来分配事件处理程序等&lt;/p&gt;
 &lt;p&gt;HTML和JavaScript的紧密耦合：JavaScript中包含HTML，然后使用innerHTML来插入一段html文本到页面&lt;/p&gt;
 &lt;p&gt;其实应该是保持层次的分离，这样可以很容易的确定错误的来源，所以我们应确保HTML呈现应该尽可能与JavaScript保持分离&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、解耦CSS/JavaScript&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;显示问题的唯一来源应该是CSS，行为问题的唯一来源应该是JavaScript，层次之间保持松散耦合才可以让你的应用程序更加易于维护，所以像以下的代码element.style.color=”red”尽量改为element.className=”edit”，而且不要在css中通过表达式嵌入JavaScript&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、解耦应用程序/事件处理程序&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;将应用逻辑和事件处理程序相分离：一个事件处理程序应该从事件对象中提取，并将这些信息传送给处理应用逻辑的某个方法中。这样做的好处首先可以让你更容易更改触发特定过程的事件，其次可以在不附加事件的情况下测试代码，使其更易创建单元测试&lt;/p&gt;
 &lt;h2&gt;性能方面的注意事项&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1、尽量使用原生方法&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、switch语句相对if较快&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;通过将case语句按照最可能到最不可能的顺序进行组织&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、位运算较快&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;当进行数字运算时，位运算操作要比任何布尔运算或者算数运算快&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;strong&gt;4、    &lt;strong&gt;巧用&lt;/strong&gt;    &lt;strong&gt;||&lt;/strong&gt;    &lt;strong&gt;和&lt;/strong&gt;    &lt;strong&gt;&amp;amp;&amp;amp;&lt;/strong&gt;    &lt;strong&gt;布尔运算符&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function eventHandler(e) {
            if (!e) e = window.event;
        }
        //可以替换为：
        function eventHandler(e) {
            e = e || window.event;
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;div&gt;
  &lt;pre&gt;        if (myobj) {
            doSomething(myobj);
        }
        //可以替换为：
        myobj &amp;amp;&amp;amp; doSomething(myobj);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;避免错误应注意的地方&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1、每条语句末尾须加分号&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在if语句中，即使条件表达式只有一条语句也要用{}把它括起来，以免后续如果添加了语句之后造成逻辑错误&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、使用+号时需谨慎&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;JavaScript 和其他编程语言不同的是，在 JavaScript 中，’+&amp;apos;除了表示数字值相加，字符串相连接以外，还可以作一元运算符用，把字符串转换为数字。因而如果使用不当，则可能与自增符’++’混淆而引起计算错误&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var valueA = 20;
        var valueB = &amp;quot;10&amp;quot;;
        alert(valueA + valueB);     //ouput: 2010 
        alert(valueA + (+valueB));  //output: 30 
        alert(valueA + +valueB);    //output:30 
        alert(valueA ++ valueB);     //Compile error&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;strong&gt;3、使用return语句需要注意&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;一条有返回值的return语句不要用()括号来括住返回值，如果返回表达式，则表达式应与return关键字在同一行，以避免压缩时，压缩工具自动加分号而造成返回与开发人员不一致的结果&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function F1() {
            var valueA = 1;
            var valueB = 2;
            return valueA + valueB;
        }
        function F2() {
            var valueA = 1;
            var valueB = 2;
            return
            valueA + valueB;
        }
        alert(F1());  //output: 3 
        alert(F2());  //ouput: undefined&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;==和===的区别&lt;/h2&gt;
 &lt;p&gt;避免在if和while语句的条件部分进行赋值，如if (a = b)，应该写成if (a == b)，但是在比较是否相等的情况下，最好使用全等运行符，也就是使用===和!==操作符会相对于==和!=会好点。==和!=操作符会进行类型强制转换&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var valueA = &amp;quot;1&amp;quot;;
        var valueB = 1;
        if (valueA == valueB) {
            alert(&amp;quot;Equal&amp;quot;);
        }
        else {
            alert(&amp;quot;Not equal&amp;quot;);
        }
        //output: &amp;quot;Equal&amp;quot;
        if (valueA === valueB) {
            alert(&amp;quot;Equal&amp;quot;);
        }
        else {
            alert(&amp;quot;Not equal&amp;quot;);
        }
        //output: &amp;quot;Not equal&amp;quot;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;不要使用生偏语法&lt;/h2&gt;
 &lt;p&gt;不要使用生偏语法，写让人迷惑的代码，虽然计算机能够正确识别并运行，但是晦涩难懂的代码不方便以后维护&lt;/p&gt;
 &lt;h2&gt;函数返回统一类型&lt;/h2&gt;
 &lt;p&gt;虽然JavaScript是弱类型的，对于函数来说，前面返回整数型数据，后面返回布尔值在编译和运行都可以正常通过，但为了规范和以后维护时容易理解，应保证函数应返回统一的数据类型&lt;/p&gt;
 &lt;h2&gt;总是检查数据类型&lt;/h2&gt;
 &lt;p&gt;要检查你的方法输入的所有数据，一方面是为了安全性，另一方面也是为了可用性。用户随时随地都会输入错误的数据。这不是因为他们蠢，而是因为他们很忙，并且思考的方式跟你不同。用typeof方法来检测你的function接受的输入是否合法&lt;/p&gt;
 &lt;h2&gt;何时用单引号，何时用双引号&lt;/h2&gt;
 &lt;p&gt;虽然在JavaScript当中，双引号和单引号都可以表示字符串, 为了避免混乱，我们建议在HTML中使用双引号，在JavaScript中使用单引号，但为了兼容各个浏览器，也为了解析时不会出错，定义JSON对象时，最好使用双引号&lt;/p&gt;
 &lt;h2&gt;部署&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;用JSLint运行JavaScript验证器来确保没有语法错误或者是代码没有潜在的问&lt;/li&gt;
  &lt;li&gt;部署之前推荐使用压缩工具将JS文件压缩&lt;/li&gt;
  &lt;li&gt;文件编码统一用UTF-8&lt;/li&gt;
  &lt;li&gt;JavaScript 程序应该尽量放在 .js 的文件中，需要调用的时候在 HTML 中以 &amp;lt;script src=”filename.js”&amp;gt; 的形式包含进来。JavaScript 代码若不是该 HTML 文件所专用的，则应尽量避免在 HTML 文件中直接编写 JavaScript 代码。因为这样会大大增加 HTML 文件的大小，无益于代码的压缩和缓存的使用。另外，&amp;lt;script src=”filename.js”&amp;gt; 标签应尽量放在文件的后面,最好是放在&amp;lt;/body&amp;gt;标签前。这样会降低因加载 JavaScript 代码而影响页面中其它组件的加载时间。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;永远不要忽略代码优化工作，重构是一项从项目开始到结束需要持续的工作，只有不断的优化代码才能让代码的执行效率越来越好&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>WEB开发 编程开发 JavaScript 性能优化</category>
      <guid isPermaLink="true">https://itindex.net/detail/52861-javascript-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E7%9F%A5%E8%AF%86</guid>
      <pubDate>Wed, 04 Mar 2015 09:56:23 CST</pubDate>
    </item>
    <item>
      <title>高性能JavaScript模板引擎原理解析</title>
      <link>https://itindex.net/detail/52843-%E6%80%A7%E8%83%BD-javascript-%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E</link>
      <description>&lt;div&gt;
  &lt;div&gt;
&lt;/div&gt;
  &lt;p&gt;随着 web 发展，前端应用变得越来越复杂，基于后端的 javascript(Node.js) 也开始崭露头角，此时 javascript 被寄予了更大的期望，与此同时 javascript MVC 思想也开始流行起来。javascript 模板引擎作为数据与界面分离工作中最重要一环，越来越受开发者关注，近一年来在开源社区中更是百花齐放，在 Twitter、淘宝网、新浪微博、腾讯QQ空间、腾讯微博等大型网站中均能看到它们的身影。&lt;/p&gt;
  &lt;p&gt;本文将用最简单的示例代码描述现有的 javascript 模板引擎的原理，包括新一代 javascript 模板引擎 artTemplate 的特性实现原理，欢迎共同探讨。&lt;/p&gt;
  &lt;h2&gt;artTemplate 介绍&lt;/h2&gt;
  &lt;p&gt;artTemplate 是新一代 javascript 模板引擎，它采用预编译方式让性能有了质的飞跃，并且充分利用 javascript 引擎特性，使得其性能无论在前端还是后端都有极其出色的表现。在 chrome 下渲染效率测试中分别是知名引擎 Mustache 与 micro tmpl 的 25 、 32 倍。&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="&amp;#36895;&amp;#24230;&amp;#23545;&amp;#27604;" height="234" src="http://static.codeceo.com/images/2015/03/c4ca4238a0b923820dcc509a6f75849b.png" width="643"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;除了性能优势外，调试功能也值得一提。模板调试器可以精确定位到引发渲染错误的模板语句，解决了编写模板过程中无法调试的痛苦，让开发变得高效，也避免了因为单个模板出错导致整个应用崩溃的情况发生。&lt;/p&gt;
  &lt;p&gt;artTemplate 这一切都在 1.7kb(gzip) 中实现！&lt;/p&gt;
  &lt;h2&gt;javascript 模板引擎基本原理&lt;/h2&gt;
  &lt;p&gt;虽然每个引擎从模板语法、语法解析、变量赋值、字符串拼接的实现方式各有所不同，但关键的渲染原理仍然是动态执行 javascript 字符串。&lt;/p&gt;
  &lt;p&gt;关于动态执行 javascript 字符串，本文以一段模板代码举例：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" height="72" src="http://static.codeceo.com/images/2015/03/1e2c4066d2f0c0ab31882518788444e6.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;这是一段非常朴素的模板写法，其中，”” 为 closeTag (逻辑语句闭合标签)，若 openTag 后面紧跟 “=” 则会输出变量的内容。&lt;/p&gt;
  &lt;p&gt;HTML语句与变量输出语句被直接输出，解析后的字符串类似：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" height="72" src="http://static.codeceo.com/images/2015/03/602d1b91f8ddfc25930d4b4eac851f8e.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;语法分析完毕一般还会返回渲染方法：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" height="216" src="http://static.codeceo.com/images/2015/03/e7680b9959574fa938882179097488b4.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;渲染测试：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" height="24" src="http://static.codeceo.com/images/2015/03/fd190b3b77b0cdaff3c32add4ccb88b2.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;在上面 render 方法中，模板变量赋值采用了 with 语句，字符串拼接采用数组的 push 方法以提升在 IE6、7 下的性能，jQuery 作者 john 开发的微型模板引擎 tmpl 是这种方式的典型代表，参见： http://ejohn.org/blog/javascript-micro-templating/&lt;/p&gt;
  &lt;p&gt;由原理实现可见，传统 javascript 模板引擎中留下两个待解决的问题：&lt;/p&gt;
  &lt;p&gt;1、性能：模板引擎渲染的时候依赖 Function 构造器实现，Function 与 eval、setTimeout、setInterval 一样，提供了使用文本访问 javascript 解析引擎的方法，但这样执行 javascript 的性能非常低下。&lt;/p&gt;
  &lt;p&gt;2、调试：由于是动态执行字符串，若遇到错误调试器无法捕获错误源，导致模板 BUG 调试变得异常痛苦。在没有进行容错的引擎中，局部模板若因为数据异常甚至可以导致整个应用崩溃，随着模板的数目增加，维护成本将剧增。&lt;/p&gt;
  &lt;h2&gt;artTemplate 高效的秘密&lt;/h2&gt;
  &lt;p&gt;   &lt;strong&gt;1、预编译&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;在上述模板引擎实现原理中，因为要对模板变量进行赋值，所以每次渲染都需要动态编译 javascript 字符串完成变量赋值。而 artTemplate 的编译赋值过程却是在渲染之前完成的，这种方式称之为“预编译”。artTemplate 模板编译器会根据一些简单的规则提取好所有模板变量，声明在渲染函数头部，这个函数类似：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" height="156" src="http://static.codeceo.com/images/2015/03/5b796f5f7a641d57f5fc4857ec3f9239.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;这个自动生成的函数就如同一个手工编写的 javascript 函数一样，同等的执行次数下无论 CPU 还是内存占用都有显著减少，性能近乎极限。&lt;/p&gt;
  &lt;p&gt;值得一提的是：artTemplate 很多特性都基于预编译实现，如沙箱规范与自定义语法等。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;2、更快的字符串相加方式&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;很多人误以为数组 push 方法拼接字符串会比 += 快，要知道这仅仅是 IE6-8 的浏览器下。实测表明现代浏览器使用 += 会比数组 push 方法快，而在 v8 引擎中，使用 += 方式比数组拼接快 4.7 倍。所以 artTemplate 根据 javascript 引擎特性采用了两种不同的字符串拼接方式。&lt;/p&gt;
  &lt;h2&gt;artTemplate 调试模式原理&lt;/h2&gt;
  &lt;p&gt;前端模板引擎不像后端模板引擎，它是动态解析，所以调试器无法定位到错误行号，而 artTemplate 通过巧妙的方式让模板调试器可以精确定位到引发渲染错误的模板语句，例如：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="debug" height="265" src="http://static.codeceo.com/images/2015/03/c81e728d9d4c2f636f067f89cc14862c.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;artTemplate 支持两种类型的错误捕获，一是渲染错误(Render Error)与编译错误(Syntax Error)。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;1、渲染错误&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;渲染错误一般是因为模板数据错误或者变量错误产生的，渲染的时候只有遇到错误才会进入调试模式重新编译模板，而不会影响正常的模板执行效率。模板编译器根据模板换行符记录行号，编译后的函数类似：&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" height="300" src="http://static.codeceo.com/images/2015/03/c5c37923c5e6697e9f9b6c0534d674ae.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;当执行过程遇到错误，立马抛出异常模板对应的行号，模板调试器再根据行号反查模板对应的语句并打印到控制台。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;2、编译错误&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;编译错误一般是模板语法错误，如不合格的套嵌、未知语法等。由于 artTemplate 没有进行完整的词法分析，故无法确定错误源所在的位置，只能对错误信息与源码进行原文输出，供开发者判断。&lt;/p&gt;
  &lt;h2&gt;开源节流&lt;/h2&gt;
  &lt;p&gt;artTemplate 基于开源协议发布，无论是商业公司还是个人都可以免费在项目中使用，欢迎共同完善。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;下载地址：&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;a href="https://github.com/aui/artTemplate" target="_blank"&gt;https://github.com/aui/artTemplate&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;在线预览：&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;a href="http://aui.github.com/artTemplate/" target="_blank"&gt;http://aui.github.com/artTemplate/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>General</category>
      <guid isPermaLink="true">https://itindex.net/detail/52843-%E6%80%A7%E8%83%BD-javascript-%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E</guid>
      <pubDate>Sun, 01 Mar 2015 22:00:04 CST</pubDate>
    </item>
    <item>
      <title>网易前端云课堂，JavaScript程序设计：JS调试</title>
      <link>https://itindex.net/detail/54788-%E7%BD%91%E6%98%93-%E5%89%8D%E7%AB%AF-%E8%AF%BE%E5%A0%82</link>
      <description>&lt;p&gt;本节主要通过一个加法器，介绍JS如何调试。先上代码：&lt;/p&gt; &lt;p&gt;test.html：  &lt;br /&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;计算器&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;div&amp;gt;
    &amp;lt;h2&amp;gt;计算器&amp;lt;/h2&amp;gt;

    &amp;lt;div&amp;gt;
        &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;num1&amp;quot;&amp;gt;
        &amp;lt;span&amp;gt;+&amp;lt;/span&amp;gt;
        &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;num2&amp;quot;&amp;gt;
        &amp;lt;span&amp;gt;=&amp;lt;/span&amp;gt;
        &amp;lt;span id=&amp;quot;result&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;button id=&amp;quot;add&amp;quot;&amp;gt;计算&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;script src=&amp;quot;./demo.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt; &lt;br /&gt;demo.js &lt;br /&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;/**
 * Created by yanzi on 15/12/8.
 */
var num1 = document.getElementById(&amp;quot;num1&amp;quot;),
    num2 = document.getElementById(&amp;quot;num2&amp;quot;),
    result = document.getElementById(&amp;quot;result&amp;quot;),
    btn_add = document.getElementById(&amp;quot;add&amp;quot;);

btn_add.addEventListener(&amp;quot;click&amp;quot;, onAddClick, false);
function onAddClick(){
    var a = parseInt(num1.value),
    b = parseInt(num2.value);
    var sum = add(a, b);
    result.innerHTML = sum;
}

/**
 *
 * @param a
 * @param b
 * @returns {*}
 */
function add(a, b){
    return a+b;
}&lt;/pre&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;效果：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="http://img.blog.csdn.net/20151208003910022?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt; &lt;p&gt;课堂摘要：&lt;/p&gt; &lt;p&gt;1，一般调试JS，打印信息有如下三种：&lt;/p&gt; &lt;p&gt;a，用alert，缺点是每次都弹框&lt;/p&gt; &lt;p&gt;b,用console.log，这个数据量小还可以&lt;/p&gt; &lt;p&gt;c,加断点调试&lt;/p&gt; &lt;p&gt;2,在JS里，如果变量前面加var，表示局部变量，function里如果不带var表示全局变量。因此一般情况下变量前面带var.&lt;/p&gt; &lt;p&gt;3,一般在chrome调试器里，elements看代码，在source目录下进行调试。在该模式下，点击js里每一行即可以加断点。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="http://img.blog.csdn.net/20151208003926218?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt; &lt;p&gt;4，调试模式下最右边的四个按钮分别是：下一个断点处，单步执行，进到下一函数，跳出函数。基本所有的调试工具都有这四种。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="http://img.blog.csdn.net/20151208003941460?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"&gt;&lt;/img&gt;  &lt;br /&gt;&lt;/p&gt; &lt;p&gt;5，断点模式下，在console里输入变量就可以看到当前值，同时可以随意对值进行修改。&lt;/p&gt; &lt;div&gt;  &lt;img alt="" src="http://img.blog.csdn.net/20151208003957717?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"&gt;&lt;/img&gt;&lt;/div&gt;
 &lt;div&gt;
    作者：yanzi1225627 发表于2015/12/8 0:41:20   &lt;a href="http://blog.csdn.net/yanzi1225627/article/details/50212773"&gt;原文链接&lt;/a&gt;
&lt;/div&gt;
 &lt;div&gt;
    阅读：74 评论：0   &lt;a href="http://blog.csdn.net/yanzi1225627/article/details/50212773#comments" target="_blank"&gt;查看评论&lt;/a&gt;
&lt;/div&gt;

&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/54788-%E7%BD%91%E6%98%93-%E5%89%8D%E7%AB%AF-%E8%AF%BE%E5%A0%82</guid>
      <pubDate>Tue, 08 Dec 2015 08:41:20 CST</pubDate>
    </item>
    <item>
      <title>iOS中UIWebView与其中网页的javascript的交互</title>
      <link>https://itindex.net/detail/54814-ios-uiwebview-%E4%B8%AD%E7%BD%91</link>
      <description>&lt;div&gt;
  &lt;div&gt;
   &lt;p&gt;首发：    &lt;a href="http://427studio.net/blog/1/258"&gt;个人博客，更新&amp;amp;纠错&amp;amp;回复&lt;/a&gt;&lt;/p&gt;
   &lt;div&gt;1.本地语言调js的方式与android中的方式类似，也是向WebView控件发送要调用的js语句&lt;/div&gt;
   &lt;div&gt;2. 但js调本地语言，则不是像android那样直接调一个全局变量的方法，而是通过location.href=xx://yy这样的方式触发UIWebViewDelegate接口实现者的webView shouldStartLoadWithRequest navigationType方法，该方法应该判断目标路径（即xx://yy）的schema（即xx）是否实际是要调用swift，如果是，则按约定执行之，并返回false阻止网页路径变化，如果不是要调用swift，则返回true，让改网页继续正常加载目标url。&lt;/div&gt;
   &lt;div&gt;android和iOS对比，它们都用了伪url的技术，但android是在本地语言调js时使用了伪url（该url的schema为javascript），而iOS是js调本地语言时使用了伪url（该url是自定义的标识），这个错落很有意思。&lt;/div&gt;
   &lt;div&gt; &lt;/div&gt;
   &lt;div&gt; &lt;/div&gt;
   &lt;p&gt;    &lt;strong&gt;swift代码：&lt;/strong&gt;&lt;/p&gt;
   &lt;div&gt;
    &lt;blockquote&gt;
     &lt;p&gt;import UIKit&lt;/p&gt;
     &lt;p&gt; &lt;/p&gt;
     &lt;p&gt;class ViewController: UIViewController, UIWebViewDelegate {&lt;/p&gt;
     &lt;p&gt;    &lt;/p&gt;
     &lt;p&gt;    @IBOutlet weak var theWebView: UIWebView!&lt;/p&gt;
     &lt;p&gt;    &lt;/p&gt;
     &lt;p&gt;    override func viewDidLoad() {&lt;/p&gt;
     &lt;p&gt;              &lt;strong&gt;//加载本地html&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;        let res = NSBundle.mainBundle().pathForResource(&amp;quot;index&amp;quot;,ofType:&amp;quot;html&amp;quot;)&lt;/p&gt;
     &lt;p&gt;        let url = NSURL(fileURLWithPath: res!);&lt;/p&gt;
     &lt;p&gt;        let request = NSURLRequest(URL: url);&lt;/p&gt;
     &lt;p&gt;        self.theWebView.loadRequest(request);&lt;/p&gt;
     &lt;p&gt;        self.theWebView.delegate = self;&lt;/p&gt;
     &lt;p&gt;    }&lt;/p&gt;
     &lt;p&gt;    &lt;/p&gt;
     &lt;p&gt;          &lt;strong&gt;//让js可以调用swift&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -&amp;gt; Bool {&lt;/p&gt;
     &lt;p&gt;        //判断是不是js在调用swift，如果是，则处理并返回false&lt;/p&gt;
     &lt;p&gt;        if(request.URL!.scheme == &amp;quot;myschema&amp;quot;){&lt;/p&gt;
     &lt;p&gt;            let host = request.URL!.host;&lt;/p&gt;
     &lt;p&gt;            if(host == &amp;quot;go&amp;quot;){&lt;/p&gt;
     &lt;p&gt;                let query = request.URL!.query!;&lt;/p&gt;
     &lt;p&gt;                print(query);&lt;/p&gt;
     &lt;p&gt;                let split = query.componentsSeparatedByString(&amp;quot;=&amp;quot;);&lt;/p&gt;
     &lt;p&gt;                let text = split[1];&lt;/p&gt;
     &lt;p&gt;                self.theWebView.stringByEvaluatingJavaScriptFromString(&amp;quot;changeContent(\&amp;quot;&amp;quot; + text + &amp;quot;\&amp;quot;)&amp;quot;);&lt;/p&gt;
     &lt;p&gt;            }&lt;/p&gt;
     &lt;p&gt;            return false;&lt;/p&gt;
     &lt;p&gt;        }else{&lt;/p&gt;
     &lt;p&gt;            return true;&lt;/p&gt;
     &lt;p&gt;        }&lt;/p&gt;
     &lt;p&gt;    }&lt;/p&gt;
     &lt;p&gt;    &lt;/p&gt;
     &lt;p&gt;          &lt;strong&gt;//swift调用js&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;    @IBAction func btnClicked(sender: AnyObject) { &lt;/p&gt;
     &lt;p&gt;        self.theWebView.stringByEvaluatingJavaScriptFromString(&amp;quot;changeContent(\&amp;quot;123\&amp;quot;)&amp;quot;);&lt;/p&gt;
     &lt;p&gt;    }&lt;/p&gt;
     &lt;p&gt;}&lt;/p&gt;
&lt;/blockquote&gt;
    &lt;p&gt; &lt;/p&gt;
    &lt;p&gt;     &lt;strong&gt;网页代码：&lt;/strong&gt;&lt;/p&gt;
    &lt;blockquote&gt;
     &lt;p&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/p&gt;
     &lt;p&gt;&amp;lt;html&amp;gt;&lt;/p&gt;
     &lt;p&gt;&amp;lt;head&amp;gt;&lt;/p&gt;
     &lt;p&gt;    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;/p&gt;
     &lt;p&gt;&amp;lt;/head&amp;gt;&lt;/p&gt;
     &lt;p&gt;&amp;lt;body&amp;gt;&lt;/p&gt;
     &lt;p&gt;    &amp;lt;span id=&amp;quot;theSpan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/p&gt;
     &lt;p&gt;    &amp;lt;button onclick=&amp;quot;doIt()&amp;quot;&amp;gt;js调用swift&amp;lt;/button&amp;gt;&lt;/p&gt;
     &lt;p&gt;    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;t&amp;quot;&amp;gt;&lt;/p&gt;
     &lt;p&gt;    &amp;lt;script&amp;gt;&lt;/p&gt;
     &lt;p&gt;              &lt;strong&gt;//让swift可以调用js&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;        function changeContent(str){&lt;/p&gt;
     &lt;p&gt;            document.getElementById(&amp;apos;theSpan&amp;apos;).innerHTML = str;&lt;/p&gt;
     &lt;p&gt;        }&lt;/p&gt;
     &lt;p&gt;              &lt;strong&gt;//js调用swift&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;        function doIt(){&lt;/p&gt;
     &lt;p&gt;            document.location.href = &amp;quot;myschema://go?a=&amp;quot; + document.getElementById(&amp;quot;t&amp;quot;).value;&lt;/p&gt;
     &lt;p&gt;        }&lt;/p&gt;
     &lt;p&gt;    &amp;lt;/script&amp;gt;&lt;/p&gt;
     &lt;p&gt;&amp;lt;/body&amp;gt;&lt;/p&gt;
     &lt;p&gt; &lt;/p&gt;
     &lt;p&gt;&amp;lt;/html&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
  &lt;div&gt;长期欢迎项目合作机会介绍，项目收入10%用于酬谢介绍人。新浪微博：   &lt;a href="http://weibo.com/zidafone"&gt;@冷镜&lt;/a&gt;，QQ：   &lt;a&gt;908789432&lt;/a&gt;。&lt;/div&gt;
&lt;/div&gt;
          
           &lt;br /&gt; &lt;br /&gt;
          
             &lt;a href="http://zidafone.iteye.com/blog/2263042#comments"&gt;已有   &lt;strong&gt;0&lt;/strong&gt; 人发表留言，猛击-&amp;gt;&amp;gt;  &lt;strong&gt;这里&lt;/strong&gt;&amp;lt;&amp;lt;-参与讨论&lt;/a&gt;
          
           &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
ITeye推荐
 &lt;br /&gt;
 &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.iteye.com/clicks/433" target="_blank"&gt;—软件人才免语言低担保 赴美带薪读研！— &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
          
        &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/54814-ios-uiwebview-%E4%B8%AD%E7%BD%91</guid>
      <pubDate>Thu, 10 Dec 2015 01:38:55 CST</pubDate>
    </item>
    <item>
      <title>不可错过的javascript迷你库</title>
      <link>https://itindex.net/detail/54973-%E9%94%99%E8%BF%87-javascript-%E8%BF%B7%E4%BD%A0</link>
      <description>&lt;p&gt;最近看着下自己的  &lt;a href="https://github.com/yanhaijing"&gt;github star&lt;/a&gt;，把我吓坏了，手贱党，收藏癖的我都收藏了300+个仓库了，是时候整理一下了。&lt;/p&gt;

 &lt;p&gt;Unix主张kiss，小而美被实践是最好用的，本文将介绍笔者收集的一些非常赞的开源库。&lt;/p&gt;

 &lt;p&gt;这些库的共性是非常小，而且功能单一。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/js-coder/cookie.js"&gt;cookie.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;如果你操作过cookie的接口，那么你一定会感觉这东西的规范真的是太复杂了，根本记不住啊，其实你是对的，因为cookie的接口设计的是有问题的，也就是说设计的太底层了，根本不友好，那么来试试这个js库吧。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/marcuswestin/store.js"&gt;store.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;再来说说浏览器的localStore吧，这东西太赞了，可惜尼玛每个浏览器都实现的各不相同，我也是醉了，如果你也有同样的烦恼，不如来试试这个迷你库，它有更简单的api，最重要的是他解决了跨浏览器问题，甚至解决了低版本浏览器(ie6)不支持localStore的问题。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/yanhaijing/data.js"&gt;data.js&lt;/a&gt;&lt;/h2&gt;
 &lt;blockquote&gt;
    &lt;p&gt;data.js 是带有消息通知的数据中心，我称其为会说话的数据。旨在让编程变得简单，世界变得美好&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;如果你使用模块化编程，或者在node环境下的话，你一定纠结过不同模块间到底如何共享数据的问题（虽然这是反模式），全局变量。。。那么试试这个迷你库吧，简单可以来，会让你消除上面的烦恼问题，同时他还支持消息，当数据更新时，会发出消息。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/yanhaijing/template.js"&gt;template.js&lt;/a&gt;&lt;/h2&gt;
 &lt;blockquote&gt;
    &lt;p&gt;template.js 一款javascript模板引擎，简单，好用。&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/yanhaijing/lodjs"&gt;lodJS&lt;/a&gt;&lt;/h2&gt;
 &lt;blockquote&gt;
    &lt;p&gt;JavaScript模块加载器，基于AMD。迄今为止，对AMD理解最好的实现&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;h2&gt;  &lt;a href="http://lab.ejci.net/favico.js/"&gt;favico.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;在favico上添加数字书不是很nice，点击下面的官网查看效果，这肯定要逼死强迫症了。&lt;/p&gt;

 &lt;p&gt;  &lt;a href="http://lab.ejci.net/favico.js/"&gt;官网&lt;/a&gt;。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="http://modernizr.com/"&gt;Modernizr&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;这个就不过多解释了，各种html css js检测器，功能检测哦。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="http://visionmedia.github.io/move.js/"&gt;Move.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;如果你操作过css3的属性，一定会觉得非常痛苦的，那不如来试试合格，css3动画瞬间变得简单了。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="http://dmauro.github.io/Keypress/"&gt;Keypress&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;一定记不住键盘上每个键的键位码吧，来试试这个，直观的展示，再也不需要记忆了。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="http://matthewhudson.me/projects/device.js/"&gt;device.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;你想检测用户的设备，试试这个吧，比jq.browser全面多了。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="http://arasatasaygin.github.io/is.js/"&gt;is.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;迷你检查库，这个几乎涵盖了全部的各种检测。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/es-shims/es5-shim"&gt;es5-shim&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;还没使用es5，只能鄙视你了，担心兼容性，用这个吧，主要是为了es6打基础啊。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/jakearchibald/es6-promise"&gt;es6-promise&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;promise太好用了，兼容性问题靠这个全解决了。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/wagerfield/parallax"&gt;parallax&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;先来看个视差效果的  &lt;a href="http://zhidao.baidu.com/s/10year/index.html"&gt;demo&lt;/a&gt;，是不是很赞，如果你也想实现这个效果，那么来试试这个吧。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/jaredreich/notie.js"&gt;notie.js&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;还在使用弹窗通知用户，太low了，快来试试这款非阻塞式，小清新的通知插件吧，对移动端有好，界面好到爆炸啊。&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/overtrue/share.js"&gt;share.js&lt;/a&gt;&lt;/h2&gt;
 &lt;blockquote&gt;
    &lt;p&gt;一键分享到微博、QQ空间、QQ好友、微信、腾讯微博、豆瓣、Facebook、Twitter、Linkedin、Google+、点点等社交网站。&lt;/p&gt;
&lt;/blockquote&gt;

 &lt;p&gt;如果你收购了分享组件的烦恼，那么来试试这个对移动端有好的分享组件吧，界面优美，看起来很赞。&lt;/p&gt;

 &lt;p&gt;  &lt;a href="http://overtrue.me/share.js/"&gt;demo&lt;/a&gt;&lt;/p&gt;

 &lt;h2&gt;  &lt;a href="https://github.com/josdejong/mathjs"&gt;mathjs&lt;/a&gt;&lt;/h2&gt;
 &lt;p&gt;js自带的数学运算不能满足你的需求了，那试试这个，扩展了很多数学运算。&lt;/p&gt;

 &lt;p&gt;这里是  &lt;a href="http://mathjs.org/"&gt;官网&lt;/a&gt;。&lt;/p&gt;

 &lt;p&gt;  &lt;img alt="" src="https://raw.github.com/josdejong/mathjs/master/img/mathjs.png"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;本文介绍的只是作者收集的一小部分而已，作者将会保持时时更新的，如果你有什么推荐的欢迎反馈给我。&lt;/p&gt;

 &lt;p&gt;最后向大家推荐依稀  &lt;a href="http://microjs.com/"&gt;microjs&lt;/a&gt;，这里收集了太多小而美的库，自己来淘宝吧。&lt;/p&gt;

&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/54973-%E9%94%99%E8%BF%87-javascript-%E8%BF%B7%E4%BD%A0</guid>
      <pubDate>Tue, 29 Dec 2015 08:00:00 CST</pubDate>
    </item>
    <item>
      <title>你有必要知道的 25 个 JavaScript 面试题</title>
      <link>https://itindex.net/detail/54927-%E7%9F%A5%E9%81%93-javascript-%E9%9D%A2%E8%AF%95</link>
      <description>&lt;h1&gt;你有必要知道的 25 个 JavaScript 面试题&lt;/h1&gt;
 &lt;p&gt;  &lt;strong&gt;1、使用    &lt;code&gt;typeof bar === &amp;quot;object&amp;quot;&lt;/code&gt; 判断    &lt;code&gt;bar&lt;/code&gt; 是不是一个对象有神马潜在的弊端？如何避免这种弊端？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;使用   &lt;code&gt;typeof&lt;/code&gt; 的弊端是显而易见的(这种弊端同使用   &lt;code&gt;instanceof&lt;/code&gt;)：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;let obj = {};
let arr = [];

console.log(typeof obj === &amp;apos;object&amp;apos;);  //true
console.log(typeof arr === &amp;apos;object&amp;apos;);  //true
console.log(typeof null === &amp;apos;object&amp;apos;);  //true
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;从上面的输出结果可知，  &lt;code&gt;typeof bar === &amp;quot;object&amp;quot;&lt;/code&gt; 并不能准确判断   &lt;code&gt;bar&lt;/code&gt; 就是一个 Object。可以通过   &lt;code&gt;Object.prototype.toString.call(bar) === &amp;quot;[object Object]&amp;quot;&lt;/code&gt; 来避免这种弊端：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;let obj = {};
let arr = [];

console.log(Object.prototype.toString.call(obj));  //[object Object]
console.log(Object.prototype.toString.call(arr));  //[object Array]
console.log(Object.prototype.toString.call(null));  //[object Null]
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;另外，为了珍爱生命，请远离   &lt;code&gt;==&lt;/code&gt;：  &lt;br /&gt;  &lt;br /&gt;
  &lt;img alt="&amp;#29645;&amp;#29233;&amp;#29983;&amp;#21629;" src="http://i12.tietuku.com/5d6a01e81c248f3b.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;而   &lt;code&gt;[] === false&lt;/code&gt; 是返回   &lt;code&gt;false&lt;/code&gt; 的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、下面的代码会在 console 输出神马？为什么？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function(){
  var a = b = 3;
})();

console.log(&amp;quot;a defined? &amp;quot; + (typeof a !== &amp;apos;undefined&amp;apos;));   
console.log(&amp;quot;b defined? &amp;quot; + (typeof b !== &amp;apos;undefined&amp;apos;));
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这跟变量作用域有关，输出换成下面的：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(b); //3
console,log(typeof a); //undefined
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;拆解一下自执行函数中的变量赋值：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;b = 3;
var a = b;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;所以   &lt;code&gt;b&lt;/code&gt; 成了全局变量，而   &lt;code&gt;a&lt;/code&gt; 是自执行函数的一个局部变量。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、下面的代码会在 console 输出神马？为什么？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var myObject = {
    foo: &amp;quot;bar&amp;quot;,
    func: function() {
        var self = this;
        console.log(&amp;quot;outer func:  this.foo = &amp;quot; + this.foo);
        console.log(&amp;quot;outer func:  self.foo = &amp;quot; + self.foo);
        (function() {
            console.log(&amp;quot;inner func:  this.foo = &amp;quot; + this.foo);
            console.log(&amp;quot;inner func:  self.foo = &amp;quot; + self.foo);
        }());
    }
};
myObject.func();
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;第一个和第二个的输出不难判断，在 ES6 之前，JavaScript 只有函数作用域，所以   &lt;code&gt;func&lt;/code&gt; 中的 IIFE 有自己的独立作用域，并且它能访问到外部作用域中的   &lt;code&gt;self&lt;/code&gt;，所以第三个输出会报错，因为   &lt;code&gt;this&lt;/code&gt; 在可访问到的作用域内是   &lt;code&gt;undefined&lt;/code&gt;，第四个输出是   &lt;code&gt;bar&lt;/code&gt;。如果你知道闭包，也很容易解决的：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function(test) {
            console.log(&amp;quot;inner func:  this.foo = &amp;quot; + test.foo);  //&amp;apos;bar&amp;apos;
            console.log(&amp;quot;inner func:  self.foo = &amp;quot; + self.foo);
}(self));
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;如果对闭包不熟悉，可以戳此：  &lt;a href="http://www.ido321.com/1641.html"&gt;从作用域链谈闭包&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4、将 JavaScript 代码包含在一个函数块中有神马意思呢？为什么要这么做？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;换句话说，为什么要用立即执行函数表达式（Immediately-Invoked Function Expression）。&lt;/p&gt;
 &lt;p&gt;IIFE 有两个比较经典的使用场景，一是类似于在循环中定时输出数据项，二是类似于 JQuery/Node 的插件和模块开发。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;for(var i = 0; i &amp;lt; 5; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;上面的输出并不是你以为的0，1，2，3，4，而输出的全部是5，这时 IIFE 就能有用了：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;for(var i = 0; i &amp;lt; 5; i++) {
    (function(i) {
      setTimeout(function() {
        console.log(i);  
      }, 1000);
    })(i)
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;而在 JQuery/Node 的插件和模块开发中，为避免变量污染，也是一个大大的 IIFE：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function($) { 
        //代码
 } )(jQuery);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;5、在严格模式(&amp;apos;use strict&amp;apos;)下进行 JavaScript 开发有神马好处？&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;消除Javascript语法的一些不合理、不严谨之处，减少一些怪异行为;&lt;/li&gt;
  &lt;li&gt;消除代码运行的一些不安全之处，保证代码运行的安全；&lt;/li&gt;
  &lt;li&gt;提高编译器效率，增加运行速度；&lt;/li&gt;
  &lt;li&gt;为未来新版本的Javascript做好铺垫。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;6、下面两个函数的返回值是一样的吗？为什么？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function foo1()
{
  return {
      bar: &amp;quot;hello&amp;quot;
  };
}

function foo2()
{
  return
  {
      bar: &amp;quot;hello&amp;quot;
  };
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;在编程语言中，基本都是使用分号（;）将语句分隔开，这可以增加代码的可读性和整洁性。而在JS中，如若语句各占独立一行，通常可以省略语句间的分号（;），JS 解析器会根据能否正常编译来决定是否自动填充分号：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var test = 1 + 
2
console.log(test);  //3
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;在上述情况下，为了正确解析代码，就不会自动填充分号了，但是对于   &lt;code&gt;return&lt;/code&gt; 、  &lt;code&gt;break&lt;/code&gt;、  &lt;code&gt;continue&lt;/code&gt; 等语句，如果后面紧跟换行，解析器一定会自动在后面填充分号(;)，所以上面的第二个函数就变成了这样：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function foo2()
{
  return;
  {
      bar: &amp;quot;hello&amp;quot;
  };
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;所以第二个函数是返回   &lt;code&gt;undefined&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7、神马是 NaN，它的类型是神马？怎么测试一个值是否等于 NaN?&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;code&gt;NaN&lt;/code&gt; 是 Not a Number 的缩写，JavaScript 的一种特殊数值，其类型是 Number，可以通过   &lt;code&gt;isNaN(param)&lt;/code&gt; 来判断一个值是否是   &lt;code&gt;NaN&lt;/code&gt;：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(isNaN(NaN)); //true
console.log(isNaN(23)); //false
console.log(isNaN(&amp;apos;ds&amp;apos;)); //true
console.log(isNaN(&amp;apos;32131sdasd&amp;apos;)); //true
console.log(NaN === NaN); //false
console.log(NaN === undefined); //false
console.log(undefined === undefined); //false
console.log(typeof NaN); //number
console.log(Object.prototype.toString.call(NaN)); //[object Number]
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;ES6 中，  &lt;code&gt;isNaN()&lt;/code&gt; 成为了 Number 的静态方法：  &lt;code&gt;Number.isNaN()&lt;/code&gt;.&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;8、解释一下下面代码的输出&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(0.1 + 0.2);   //0.30000000000000004
console.log(0.1 + 0.2 == 0.3);  //false
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;JavaScript 中的 number 类型就是浮点型，JavaScript 中的浮点数采用IEEE-754 格式的规定，这是一种二进制表示法，可以精确地表示分数，比如1/2，1/8，1/1024，每个浮点数占64位。但是，二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字，会有舍入误差。&lt;/p&gt;
 &lt;p&gt;由于采用二进制，JavaScript 也不能有限表示 1/10、1/2 等这样的分数。在二进制中，1/10(0.1)被表示为   &lt;code&gt;0.00110011001100110011……&lt;/code&gt; 注意   &lt;code&gt;0011&lt;/code&gt; 是无限重复的，这是舍入误差造成的，所以对于 0.1 + 0.2 这样的运算，操作数会先被转成二进制，然后再计算：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;0.1 =&amp;gt; 0.0001 1001 1001 1001…（无限循环）
0.2 =&amp;gt; 0.0011 0011 0011 0011…（无限循环）
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;双精度浮点数的小数部分最多支持 52 位，所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100…因浮点数小数位的限制而截断的二进制数字，这时候，再把它转换为十进制，就成了 0.30000000000000004。&lt;/p&gt;
 &lt;p&gt;对于保证浮点数计算的正确性，有两种常见方式。&lt;/p&gt;
 &lt;p&gt;一是先升幂再降幂：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function add(num1, num2){
  let r1, r2, m;
  r1 = (&amp;apos;&amp;apos;+num1).split(&amp;apos;.&amp;apos;)[1].length;
  r2 = (&amp;apos;&amp;apos;+num2).split(&amp;apos;.&amp;apos;)[1].length;

  m = Math.pow(10,Math.max(r1,r2));
  return (num1 * m + num2 * m) / m;
}
console.log(add(0.1,0.2));   //0.3
console.log(add(0.15,0.2256)); //0.3756
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;二是是使用内置的   &lt;code&gt;toPrecision()&lt;/code&gt; 和   &lt;code&gt;toFixed()&lt;/code&gt; 方法，  &lt;strong&gt;注意，方法的返回值字符串。&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function add(x, y) {
    return x.toPrecision() + y.toPrecision()
}
console.log(add(0.1,0.2));  //&amp;quot;0.10.2&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;9、实现函数    &lt;code&gt;isInteger(x)&lt;/code&gt; 来判断 x 是否是整数&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;可以将   &lt;code&gt;x&lt;/code&gt; 转换成10进制，判断和本身是不是相等即可：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function isInteger(x) { 
    return parseInt(x, 10) === x; 
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;ES6 对数值进行了扩展，提供了静态方法   &lt;code&gt;isInteger()&lt;/code&gt; 来判断参数是否是整数：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger(&amp;quot;15&amp;quot;) // false
Number.isInteger(true) // false
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;JavaScript能够准确表示的整数范围在   &lt;code&gt;-2^53&lt;/code&gt; 到   &lt;code&gt;2^53&lt;/code&gt; 之间（不含两个端点），超过这个范围，无法精确表示这个值。ES6 引入了  &lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt; 和   &lt;code&gt;Number.MIN_SAFE_INTEGER&lt;/code&gt;这两个常量，用来表示这个范围的上下限，并提供了   &lt;code&gt;Number.isSafeInteger()&lt;/code&gt; 来判断整数是否是安全型整数。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;10、在下面的代码中，数字 1-4 会以什么顺序输出？为什么会这样输出？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function() {
    console.log(1); 
    setTimeout(function(){console.log(2)}, 1000); 
    setTimeout(function(){console.log(3)}, 0); 
    console.log(4);
})();
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这个就不多解释了，主要是 JavaScript 的定时机制和时间循环，不要忘了，JavaScript 是单线程的。详解可以参考   &lt;a href="http://www.cnblogs.com/zichi/p/4604053.html"&gt;从setTimeout谈JavaScript运行机制&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;11、写一个少于 80 字符的函数，判断一个字符串是不是回文字符串&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function isPalindrome(str) {
    str = str.replace(/\W/g, &amp;apos;&amp;apos;).toLowerCase();
    return (str == str.split(&amp;apos;&amp;apos;).reverse().join(&amp;apos;&amp;apos;));
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这个题我在   &lt;code&gt;codewars&lt;/code&gt; 上碰到过，并收录了一些不错的解决方式，可以戳这里：  &lt;a href="https://github.com/dwqs/awesome-codewars/blob/master/codewars/palindrome-for-your-dome.md"&gt;Palindrome For Your Dome&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;12、写一个按照下面方式调用都能正常工作的 sum 方法&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(sum(2,3));   // Outputs 5
console.log(sum(2)(3));  // Outputs 5
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;针对这个题，可以判断参数个数来实现：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function sum() {
  var fir = arguments[0];
  if(arguments.length === 2) {
    return arguments[0] + arguments[1]
  } else {
    return function(sec) {
       return fir + sec;
    }
  }
   
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;13、根据下面的代码片段回答后面的问题&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;for (var i = 0; i &amp;lt; 5; i++) {
  var btn = document.createElement(&amp;apos;button&amp;apos;);
  btn.appendChild(document.createTextNode(&amp;apos;Button &amp;apos; + i));
  btn.addEventListener(&amp;apos;click&amp;apos;, function(){ console.log(i); });
  document.body.appendChild(btn);
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;1、点击   &lt;code&gt;Button 4&lt;/code&gt;，会在控制台输出什么？  &lt;br /&gt;  &lt;br /&gt;
2、给出一种符合预期的实现方式&lt;/p&gt;
 &lt;p&gt;1、点击5个按钮中的任意一个，都是输出5  &lt;br /&gt;  &lt;br /&gt;
2、参考 IIFE。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;14、下面的代码会输出什么？为什么？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var arr1 = &amp;quot;john&amp;quot;.split(&amp;apos;&amp;apos;); j o h n
var arr2 = arr1.reverse(); n h o j
var arr3 = &amp;quot;jones&amp;quot;.split(&amp;apos;&amp;apos;); j o n e s
arr2.push(arr3);
console.log(&amp;quot;array 1: length=&amp;quot; + arr1.length + &amp;quot; last=&amp;quot; + arr1.slice(-1));
console.log(&amp;quot;array 2: length=&amp;quot; + arr2.length + &amp;quot; last=&amp;quot; + arr2.slice(-1));
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;会输出什么呢？你运行下就知道了，可能会在你的意料之外。&lt;/p&gt;
 &lt;p&gt;MDN 上对于   &lt;code&gt;reverse()&lt;/code&gt; 的描述是酱紫的：&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;Description   &lt;br /&gt;   &lt;br /&gt;
The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;code&gt;reverse()&lt;/code&gt; 会改变数组本身，并返回原数组的引用。&lt;/p&gt;
 &lt;p&gt;  &lt;code&gt;slice&lt;/code&gt; 的用法请参考：  &lt;a href="http://javascript.ruanyifeng.com/stdlib/string.html#toc6"&gt;slice&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;15、下面的代码会输出什么？为什么？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(1 +  &amp;quot;2&amp;quot; + &amp;quot;2&amp;quot;);
console.log(1 +  +&amp;quot;2&amp;quot; + &amp;quot;2&amp;quot;);
console.log(1 +  -&amp;quot;1&amp;quot; + &amp;quot;2&amp;quot;);
console.log(+&amp;quot;1&amp;quot; +  &amp;quot;1&amp;quot; + &amp;quot;2&amp;quot;);
console.log( &amp;quot;A&amp;quot; - &amp;quot;B&amp;quot; + &amp;quot;2&amp;quot;);
console.log( &amp;quot;A&amp;quot; - &amp;quot;B&amp;quot; + 2);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;输出什么，自己去运行吧，需要注意三个点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;多个数字和数字字符串混合运算时，跟操作数的位置有关&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(2 + 1 + &amp;apos;3&amp;apos;); / /‘33’
console.log(&amp;apos;3&amp;apos; + 2 + 1); //&amp;apos;321&amp;apos;
&lt;/code&gt;&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;数字字符串之前存在数字中的正负号(+/-)时，会被转换成数字&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(typeof &amp;apos;3&amp;apos;);   // string
console.log(typeof +&amp;apos;3&amp;apos;);  //number
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;同样，可以在数字前添加   &lt;code&gt;&amp;apos;&amp;apos;&lt;/code&gt;，将数字转为字符串&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(typeof 3);   // number
console.log(typeof (&amp;apos;&amp;apos;+3));  //string
&lt;/code&gt;&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;对于运算结果不能转换成数字的，将返回    &lt;code&gt;NaN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(&amp;apos;a&amp;apos; * &amp;apos;sd&amp;apos;);   //NaN
console.log(&amp;apos;A&amp;apos; - &amp;apos;B&amp;apos;);  // NaN
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这张图是运算转换的规则&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#36816;&amp;#31639;&amp;#31526;&amp;#36716;&amp;#25442;" src="http://i4.tietuku.com/44a7458f5a824275.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;16、如果 list 很大，下面的这段递归代码会造成堆栈溢出。如果在不改变递归模式的前提下修善这段代码？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        nextListItem();
    }
};
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;原文上的解决方式是加个定时器：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;解决方式的原理请参考第10题。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;17、什么是闭包？举例说明&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;可以参考此篇：  &lt;a href="http://www.ido321.com/1641.html"&gt;从作用域链谈闭包&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;18、下面的代码会输出什么？为啥？&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;for (var i = 0; i &amp;lt; 5; i++) {
  setTimeout(function() { console.log(i); }, i * 1000 );
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;请往前面翻，参考第4题，解决方式已经在上面了&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;19、解释下列代码的输出&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(&amp;quot;0 || 1 = &amp;quot;+(0 || 1));
console.log(&amp;quot;1 || 2 = &amp;quot;+(1 || 2));
console.log(&amp;quot;0 &amp;amp;&amp;amp; 1 = &amp;quot;+(0 &amp;amp;&amp;amp; 1));
console.log(&amp;quot;1 &amp;amp;&amp;amp; 2 = &amp;quot;+(1 &amp;amp;&amp;amp; 2));
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;逻辑与和逻辑或运算符会返回一个值，并且二者都是短路运算符：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;逻辑与返回第一个是    &lt;code&gt;false&lt;/code&gt; 的操作数 或者 最后一个是    &lt;code&gt;true&lt;/code&gt;的操作数&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(1 &amp;amp;&amp;amp; 2 &amp;amp;&amp;amp; 0);  //0
console.log(1 &amp;amp;&amp;amp; 0 &amp;amp;&amp;amp; 1);  //0
console.log(1 &amp;amp;&amp;amp; 2 &amp;amp;&amp;amp; 3);  //3
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;如果某个操作数为   &lt;code&gt;false&lt;/code&gt;，则该操作数之后的操作数都不会被计算&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;逻辑或返回第一个是    &lt;code&gt;true&lt;/code&gt; 的操作数 或者 最后一个是    &lt;code&gt;false&lt;/code&gt;的操作数&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(1 || 2 || 0); //1
console.log(0 || 2 || 1); //2
console.log(0 || 0 || false); //false
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;如果某个操作数为   &lt;code&gt;true&lt;/code&gt;，则该操作数之后的操作数都不会被计算&lt;/p&gt;
 &lt;p&gt;如果逻辑与和逻辑或作混合运算，则逻辑与的优先级高：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(1 &amp;amp;&amp;amp; 2 || 0); //2
console.log(0 || 2 &amp;amp;&amp;amp; 1); //1
console.log(0 &amp;amp;&amp;amp; 2 || 1); //1
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;在 JavaScript，常见的   &lt;code&gt;false&lt;/code&gt; 值：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;0, &amp;apos;0&amp;apos;, +0, -0, false, &amp;apos;&amp;apos;,null,undefined,null,NaN
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;要注意  &lt;strong&gt;空数组([])&lt;/strong&gt;和  &lt;strong&gt;空对象({}):&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log([] == false) //true
console.log({} == false) //false
console.log(Boolean([])) //true
console.log(Boolean({})) //true
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;所以在   &lt;code&gt;if&lt;/code&gt; 中，  &lt;code&gt;[]&lt;/code&gt; 和   &lt;code&gt;{}&lt;/code&gt; 都表现为   &lt;code&gt;true&lt;/code&gt;：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="if" src="http://i4.tietuku.com/5036556cc6f5a060.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;20、解释下面代码的输出&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log(false == &amp;apos;0&amp;apos;)
console.log(false === &amp;apos;0&amp;apos;)
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;请参考前面第14题运算符转换规则的图。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;21、解释下面代码的输出&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var a={},
    b={key:&amp;apos;b&amp;apos;},
    c={key:&amp;apos;c&amp;apos;};

a[b]=123;
a[c]=456;

console.log(a[b]);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;输出是 456，参考原文的解释：&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;The reason for this is as follows: When setting an object property, JavaScript will implicitly stringify the parameter value. In this case, since b and c are both objects, they will both be converted to [object Object]. As a result, a[b] anda[c] are both equivalent to a[[object Object]] and can be used interchangeably. Therefore, setting or referencing a[c] is precisely the same as setting or referencing a[b].&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;22、解释下面代码的输出&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;console.log((function f(n){return ((n &amp;gt; 1) ? n * f(n-1) : n)})(10));
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;结果是10的阶乘。这是一个递归调用，为了简化，我初始化 n=5，则调用链和返回链如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#36882;&amp;#24402;" src="http://i4.tietuku.com/84fe524326f5bf6c.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;23、解释下面代码的输出&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(function(x) {
    return (function(y) {
        console.log(x);
    })(2)
})(1);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;输出1，闭包能够访问外部作用域的变量或参数。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;24、解释下面代码的输出，并修复存在的问题&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var hero = {
    _name: &amp;apos;John Doe&amp;apos;,
    getSecretIdentity: function (){
        return this._name;
    }
};

var stoleSecretIdentity = hero.getSecretIdentity;

console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;将   &lt;code&gt;getSecretIdentity&lt;/code&gt; 赋给   &lt;code&gt;stoleSecretIdentity&lt;/code&gt;，等价于定义了   &lt;code&gt;stoleSecretIdentity&lt;/code&gt; 函数：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var stoleSecretIdentity =  function (){
        return this._name;
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;code&gt;stoleSecretIdentity&lt;/code&gt; 的上下文是全局环境，所以第一个输出   &lt;code&gt;undefined&lt;/code&gt;。若要输出   &lt;code&gt;John Doe&lt;/code&gt;，则要通过   &lt;code&gt;call&lt;/code&gt; 、  &lt;code&gt;apply&lt;/code&gt; 和   &lt;code&gt;bind&lt;/code&gt; 等方式改变   &lt;code&gt;stoleSecretIdentity&lt;/code&gt; 的  &lt;code&gt;this&lt;/code&gt; 指向(hero)。&lt;/p&gt;
 &lt;p&gt;第二个是调用对象的方法，输出   &lt;code&gt;John Doe&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;25、给你一个 DOM 元素，创建一个能访问该元素所有子元素的函数，并且要将每个子元素传递给指定的回调函数。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;函数接受两个参数：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;DOM&lt;/li&gt;
  &lt;li&gt;指定的回调函数&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;原文利用   &lt;a href="https://en.wikipedia.org/wiki/Depth-first_search"&gt;深度优先搜索&lt;/a&gt;(Depth-First-Search) 给了一个实现：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function Traverse(p_element,p_callback) {
   p_callback(p_element);
   var list = p_element.children;
   for (var i = 0; i &amp;lt; list.length; i++) {
       Traverse(list[i],p_callback);  // recursive call
   }
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;文章参考：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.toptal.com/javascript/interview-questions"&gt;25 Essential JavaScript Interview Questions&lt;/a&gt;  &lt;br /&gt;  &lt;br /&gt;
  &lt;a href="http://weizhifeng.net/immediately-invoked-function-expression.html"&gt;JavaScript中的立即执行函数表达式&lt;/a&gt;  &lt;br /&gt;  &lt;br /&gt;
  &lt;a href="http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html"&gt;Javascript 严格模式详解&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;声明：文中对问题的回答仅代表博主个人观点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;转载请注明：  &lt;a href="http://www.ido321.com"&gt;淡忘~浅思&lt;/a&gt; »   &lt;a href="http://www.ido321.com/1660.html"&gt;你有必要知道的 25 个 JavaScript 面试题&lt;/a&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>JavaScript javascript 面试</category>
      <guid isPermaLink="true">https://itindex.net/detail/54927-%E7%9F%A5%E9%81%93-javascript-%E9%9D%A2%E8%AF%95</guid>
      <pubDate>Sat, 19 Dec 2015 00:55:23 CST</pubDate>
    </item>
    <item>
      <title>JavaScript 异步机制及应用 入门教程</title>
      <link>https://itindex.net/detail/53791-javascript-%E5%BC%82%E6%AD%A5-%E5%BA%94%E7%94%A8</link>
      <description>&lt;h1&gt;1. 异步与同步 技术研究&lt;/h1&gt;

 &lt;h3&gt;(1). 概念介绍&lt;/h3&gt;

 &lt;p&gt;异步: asynchronous 简写async  &lt;br /&gt;
同步: synchronous  简写sync&lt;/p&gt;

 &lt;p&gt;用比方来比喻  &lt;br /&gt;
异步就是: N个人同时起跑, 起点和出发时间相同, 在起跑时不去关心其他人会啥时候跑完～尼玛这不废话吗？大家都才起跑怎么知道别人多就跑完.  &lt;br /&gt;
同步就是: N个人接力跑, 起点和出发时间不同, 且后一个人会等待前一个人跑完才能继续跑, 也就是要关心前一个人的结果（上一行代码的返回值）.&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;(2). JS里面的异步/同步&lt;/h3&gt;

 &lt;p&gt;JS运行场景多数是在用户浏览器上, 程序效率优劣会直接影响用户的体验交互. 比如一个网站, 在用户注册时, 会ajax校验输入再发提交表单, 如果用同步就可能会一直卡着等待ajax响应, 好几秒结束后再跳到注册结果页, 这个体验将是非常糟糕的.&lt;/p&gt;

 &lt;p&gt;说到JS的异步, 不得不提及一个非常有代表意义函数了.&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;var url = &amp;apos;/action/&amp;apos;;
var data = &amp;apos;i=1&amp;apos;;
xmlHTTP = new XMLHttpRequest();
xmlHTTP.nonce = nonce;
xmlHTTP.open(&amp;quot;POST&amp;quot;, url);
xmlHTTP.onreadystatechange = function(a) {
    if(a.target.readyState!=4)return false;
    try{
        console.log(a.target.responseText)
    }catch(e){
        return false;
    }
};
xmlHTTP.send(data);
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;或者在jQuery写作:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;$.ajax({
    url: &amp;apos;/action/&amp;apos;,
    type: &amp;apos;POST&amp;apos;,
    data: &amp;apos;i=1&amp;apos;,
    success: function(responseText){
        console.log(responseText);
    }
})
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;上面的无论是  &lt;code&gt;xmlHTTP.onreadystatechange&lt;/code&gt;, 还是  &lt;code&gt;success&lt;/code&gt;, 在JavaScript中均称为  &lt;code&gt;回调方法&lt;/code&gt;,  &lt;br /&gt;
以原生JS的  &lt;code&gt;XMLHttpRequest&lt;/code&gt;为例,   &lt;code&gt;xmlHTTP&lt;/code&gt;变量是个  &lt;code&gt;XMLHttpRequest&lt;/code&gt;对象, 他的  &lt;code&gt;onreadystatechange&lt;/code&gt;是在每次请求响应状态发生变化时会触发的一个函数/方法, 然后在发出请求  &lt;code&gt;xmlHTTP.send(data)&lt;/code&gt;的时候, JS并不会理会  &lt;code&gt;onreadystatechang&lt;/code&gt;e方法, 而当改送请求到达服务器, 开始响应或者响应状态改变时会调用  &lt;code&gt;onreadystatechange&lt;/code&gt;方法:&lt;/p&gt;

 &lt;p&gt;也就是  &lt;br /&gt;
 1) 请求发出  &lt;br /&gt;
 2) 服务器开始响应数据  &lt;br /&gt;
 3) 执行回调方法, 可能执行多次&lt;/p&gt;

 &lt;p&gt;以jQuery版为例, $.ajax本身是个函数, 唯一一个参数是{...} 这个对象, 然后回调方法  &lt;code&gt;success&lt;/code&gt;是作为这个对象的一个属性传入$.ajax的.  &lt;br /&gt;
$.ajax()先将数据post到&amp;apos;/action/&amp;apos;, 返回结果后再调用  &lt;code&gt;success&lt;/code&gt;(如果发生错误会调用  &lt;code&gt;error&lt;/code&gt;).  &lt;br /&gt;
也就是&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt; 1) 请求发出
 2) 服务器开始响应数据
 3) 响应结束执行回调方法
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;然后作为函数$.ajax, 是函数就应该有返回值(哪怕没有return也会返回undefined), 他本身的返回值是多少呢?  &lt;br /&gt;
分为  &lt;code&gt;async:true&lt;/code&gt;和  &lt;code&gt;async:false&lt;/code&gt;两个版本:&lt;/p&gt;

 &lt;p&gt;  &lt;code&gt;async:true&lt;/code&gt;版本:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;$.ajax({&amp;apos;url&amp;apos;:&amp;apos;a.html&amp;apos;, type:&amp;apos;GET&amp;apos;, async:true})
&amp;gt; Object {readyState: 1}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;  &lt;code&gt;async:false&lt;/code&gt;版本:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;$.ajax({&amp;apos;url&amp;apos;:&amp;apos;robots.txt&amp;apos;, type:&amp;apos;GET&amp;apos;, false})
&amp;gt; Object {readyState: 4, responseText: &amp;quot;&amp;lt;!DOCTYPE HTML PUBLIC ...&amp;quot;, status: 200, statusText: &amp;quot;OK&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;我们可以直接看到, async:true异步模式下, jquery/javascript未将结果返回... 而async:false就将结果返回了.&lt;/p&gt;

 &lt;p&gt;然后问题就来了, 为什么async:true未返回结果呢?  &lt;br /&gt;
答案很简单:  &lt;br /&gt;
因为在返回的时候, 程序不可能知道结果. 异步就是指不用等此操作执行出结果再往下执行, 也就是返回的值中未包含结果.&lt;/p&gt;

 &lt;p&gt;留下一个问题, 我们是不是为了程序流程的简单化而使用同步呢?&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;(3). 异步的困惑&lt;/h3&gt;

 &lt;p&gt;先帖一段代码:  &lt;br /&gt;
a.php&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;php&lt;/code&gt;  &lt;code&gt;&amp;lt;?php
sleep(1);      // 休息一秒钟
echo &amp;apos;{}&amp;apos;;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;page.js&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;for( i = 1; i &amp;lt;= 4; i++ ){
    $.ajax({
    url: &amp;apos;a.php&amp;apos;,
    type: &amp;apos;POST&amp;apos;,
    dataType: &amp;apos;json&amp;apos;,
    data: {data: i},
    async: true,         // 默认即为异步
    success: function(json) {
            console.log(i + &amp;apos;: &amp;apos; + json); // 打印
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;你们猜猜  &lt;code&gt;打印&lt;/code&gt;的那行会最终打印出什么内容?  &lt;br /&gt;
是&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;1: {}
2: {}
3: {}
4: {}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;吗？&lt;/p&gt;

 &lt;p&gt;错！&lt;/p&gt;

 &lt;p&gt;输出的将是:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;4: {}
4: {}
4: {}
4: {}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;你TM在逗我？  &lt;br /&gt;
没有, 这并不是JS的BUG, 也不是jQuery的BUG.  &lt;br /&gt;
这是因为, PHP休息了一秒, 而js异步地循环从1到4, 远远用不到1秒.  &lt;br /&gt;
然后在1秒钟后, 才开始返回数据, 触发  &lt;code&gt;success&lt;/code&gt;, 此时此刻  &lt;code&gt;i&lt;/code&gt;已经自增成了4.  &lt;br /&gt;
自然而然地, 第一次  &lt;code&gt;console.log(i...)&lt;/code&gt;就是4, 第二次也是, 第三次也是, 第四次也是.  &lt;br /&gt;
那么如果我们希望程序输出也1,2,3,4这样输出怎么办呢?&lt;/p&gt;

 &lt;p&gt;两种方案:&lt;/p&gt;

 &lt;p&gt;1) 让后端输出i&lt;/p&gt;

 &lt;p&gt;a.php&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;php&lt;/code&gt;  &lt;code&gt;&amp;lt;?php
sleep(1);
echo &amp;apos;{i: &amp;apos; . $_POST[&amp;apos;data&amp;apos;] . &amp;apos;}&amp;apos;; // 这一行改了
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;page.js&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;for( i = 1; i &amp;lt;= 4; i++ ){
    $.ajax({
    url: &amp;apos;a.php&amp;apos;,
    type: &amp;apos;POST&amp;apos;,
    dataType: &amp;apos;json&amp;apos;,
    data: {data: i},
    async: true,
    success: function(json) {
            console.log(json.i + &amp;apos;: &amp;apos; + json); // 这一行改了
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;2) 给回调的事件对象赋属性&lt;/p&gt;

 &lt;p&gt;a.php&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;php&lt;/code&gt;  &lt;code&gt;保持原代码不变
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;page.js&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;JavaScript&lt;/code&gt;  &lt;code&gt;for( i = 1; i &amp;lt;= 4; i++ ){
    ajaxObj = $.ajax({          // 将ajax赋给ajaxObj
        url: &amp;apos;a.php&amp;apos;,
        type: &amp;apos;POST&amp;apos;,
        dataType: &amp;apos;json&amp;apos;,
        data: {data: i},
        async: true,
        success: function(json, status, obj) {    // 增加回调参数, jQuery文档有说第三个参数就是ajax方法产生的对象.
                console.log(obj.i + &amp;apos;: &amp;apos; + json); // 从jQuery.ajax返回的对象中取i
        }
    });
    ajaxObj.i = i;            // 给ajaxObj赋属性i 值为循环的i 
}
&lt;/code&gt;&lt;/pre&gt;

 &lt;pre&gt;  &lt;code&gt;有可能你会感到困惑, 为何可以给ajaxObj设置一个i属性然后在回调时用第三个回调参数的i属性呢?
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;  &lt;a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings" rel="nofollow"&gt;jQuery.ajax文档&lt;/a&gt;中写到:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;jQuery.ajax( [settings ] )
settings
...
success: Function( Anything data, String textStatus, jqXHR jqXHR )
第1个参数就是响应的文本/HTML/XML/数据/json之类的, 跟你的dataType设置有关
第2个参数就是status状态, 如success
第3个参数就是jqXHR, 也就是jQuery的XMLHttpRequest对象, 当然, 在这里就是$.ajax()生成的对象, 也就是事件的触发者本身, 
给本身设置一个属性(ajaxObj.i = i), 然后再调用本身的回调时, 使用本身的那个属性(obj.i), 当然会保持一致了.
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;然后  &lt;br /&gt;
1)输出的结果将是&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;1: {i:1}
2: {i:2}
3: {i:3}
4: {i:4}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;2)输出的结果将是&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;1: {}
2: {}
3: {}
4: {}
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;虽然略有区别, 但两者均可达到要求. 若要论代码的逼格, 相信你一定会被第二个方案给震惊的.  &lt;br /&gt;
凭什么你给ajaxObj赋个属性就可以在  &lt;code&gt;success&lt;/code&gt;中用了呢?&lt;/p&gt;

 &lt;p&gt;请看  &lt;a href="http://segmentfault.com/a/1190000002941748#articleHeader4" rel="nofollow"&gt;   &lt;code&gt;(4). 异步的回调机制&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;(4). 异步的回调机制 ------ 事件&lt;/h3&gt;

 &lt;p&gt;一个有经验的JavaScript程序员一定会将js回调用得得心应手.  &lt;br /&gt;
因为JavaScript天生异步, 异步的好处是顾及了用户的体验, 但坏处就是导致流程化循环或者递归的逻辑明明在别的语言中无任何问题, 却在js中无法取得期待的值...  &lt;br /&gt;
而JavaScript异步在设计之初就将这一点考虑到了. 任何流行起来的JS插件方法, 如jQuery的插件, 一定考虑到了这一点了的.&lt;/p&gt;

 &lt;p&gt;举个例子.&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;ajaxfileupload插件, 实现原理是将选择的文件$.clone到一个form中, form的target设置成了一个页面中的iframe, 然后定时取iframe的contents().body, 即可获得响应的值.
如果要支持multiple文件上传(一些现代化的浏览器支持), 还是得要用`XMLHttpRequest`
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;如下面代码:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;$(&amp;apos;input#file&amp;apos;).on(&amp;apos;change&amp;apos;, function(e){
    for(i = 0; i &amp;lt; e.target.files.length; i++ ){
        var data = new FormData();
        data.append(&amp;quot;file&amp;quot;, e.target.files[i]);
        xmlHTTP = new XMLHttpRequest();
        xmlHTTP.open(&amp;quot;POST&amp;quot;, s.url);
        xmlHTTP.onreadystatechange = function(a) { // a 为 事件event对象
            if(a.target.readyState!=4)return false; // a.target为触发这个事件的对象 即xmlHTTP (XMLHttpRequest) 对象
            try{
                console.log(a.target.responseText);
            }catch(e){
                return false;
            }
        };
        xmlHTTP.send(data);
    }
})
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;你可以很明显地知道, 在  &lt;code&gt;onreadystatechange&lt;/code&gt;调用且走到  &lt;code&gt;console.log(a.target.responseText)&lt;/code&gt;时, 如果服务器不返回文件名, 我们根本并不知道返回的是哪个文件的URL. 如果根据i去取的话, 那么很容易地, 我们只会取到始终1个或几个, 并不能保证准确.  &lt;br /&gt;
那么我们应该怎么去保证在  &lt;code&gt;console.log(a.target.responseText)&lt;/code&gt;时能知道我信上传的文件的基本信息呢?&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;$(&amp;apos;input#file&amp;apos;).on(&amp;apos;change&amp;apos;, function(e){
    for(i = 0; i &amp;lt; e.target.files.length; i++ ){
        var data = new FormData();
        data.append(&amp;quot;file&amp;quot;, e.target.files[i]);
        xmlHTTP = new XMLHttpRequest();
        xmlHTTP.file = e.target.files[i];
        xmlHTTP.open(&amp;quot;POST&amp;quot;, s.url);
        xmlHTTP.onreadystatechange = function(a) {
            if(a.target.readyState!=4)return false;
            try{
                console.log(a.target.file);         //这儿是上面`xmlHTTP.file = e.target.files[i]` 赋进去的
                console.log(a.target.responseText);
            }catch(e){
                return false;
            }
        };
        xmlHTTP.send(data);
    }
})
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;是不是很简单?&lt;/p&gt;

 &lt;hr&gt;&lt;/hr&gt;
 &lt;h1&gt;2. 展望&lt;/h1&gt;

 &lt;h3&gt;(1). Google对同步JavaScript的态度&lt;/h3&gt;

 &lt;p&gt;在你尝试在chrome打开的页面中执行  &lt;code&gt;async: false&lt;/code&gt;的代码时, chrome将会警告你:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user&amp;apos;s experience. For more help, check http://xhr.spec.whatwg.org/.
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;  &lt;img alt="clipboard.png" src="http://segmentfault.com/img/bVmvrH"&gt;&lt;/img&gt;&lt;/p&gt;

 &lt;h3&gt;(2). 职场展望&lt;/h3&gt;

 &lt;p&gt;异步和事件将是JavaScript工程师必备技能&lt;/p&gt;

 &lt;p&gt;[完]  &lt;br /&gt;
Reference:&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;1.《Javascript异步编程的4种方法》     http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
2.《什么是 Event Loop？》            http://www.ruanyifeng.com/blog/2013/10/event_loop.html
3.《JavaScript 运行机制详解：再谈Event Loop》 http://www.ruanyifeng.com/blog/2014/10/event-loop.html
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>javascript</category>
      <guid isPermaLink="true">https://itindex.net/detail/53791-javascript-%E5%BC%82%E6%AD%A5-%E5%BA%94%E7%94%A8</guid>
      <pubDate>Mon, 29 Jun 2015 02:54:29 CST</pubDate>
    </item>
    <item>
      <title>【译文】Top 10：HTML5、JavaScript 3D游戏引擎和框架</title>
      <link>https://itindex.net/detail/53748-%E8%AF%91%E6%96%87-top-html5</link>
      <description>&lt;p&gt;  &lt;img alt="best-3d-javascript-game-engines-frameworks-webgl-html5" src="http://ido321.qiniudn.com/qiniu/1599/image/4056fbf291c8f7e871c3f62b1d87271b.png" title=""&gt;&lt;/img&gt;
&lt;/p&gt; &lt;p&gt;由于很多人都在用JavaScript、HTML5和WebGL技术创建基于浏览器的3D游戏，所有JavaScript 3D游戏引擎是一个人们主题。基于浏览器的游戏最棒的地方是平台独立，它们能在iOS、Android、Windows或其他任何平台上运行。&lt;/p&gt;
 &lt;p&gt;有很多的JavaScript能够用于创建基于浏览器、使用HTML5和WebGL的3D游戏。然后，选择一个合适的游戏引擎是一个不小的挑战，它有时能帮你完成项目或突破项目瓶颈。&lt;/p&gt;
 &lt;p&gt;为了让你的选择变的容易，我们已经通过分析大多数JavaScript 3D游戏引擎在今天的市场并列出了十大游戏引擎来帮助您用JavaScript创建非常棒的HTML5,WebGL游戏。&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://www.babylonjs.com/" target="_blank"&gt;1. Babylon.js&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;毫无疑问，Babylon.JS是最好的JavaScript3D游戏引擎，它能创建可以出售的专业级游戏。   &lt;br /&gt;Babylon.JS是David Catuhe对3D游戏引擎热爱的结果，他在用DirectX, OpenGL, 和Silverlight创建3D游戏引擎方面是经验丰富，并最终自己完成了一个游戏引擎。   &lt;br /&gt;Babylon.js的一些核心功能包含了场景图与灯光、摄像机、材料和网格、碰撞引擎、物理引擎、音频引擎和优化引擎。   &lt;br /&gt;这是一个  &lt;a href="http://www.babylonjs.com/playground" target="_blank"&gt;Babylon.js&lt;/a&gt;试验场，你可以拿你手边的游戏来试试这个游戏引擎。&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://threejs.org/" target="_blank"&gt;2.Three.js&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;Three.js是另一个广泛应用并且功能强大的JavaScript 3D库，从创建简单的3D动画到创建交互的3D游戏， 它都能实现。   &lt;br /&gt;Threejs带来的不仅是支持WebGL渲染，也支持SVG、Canvas和CSS3D渲染。然而，从游戏的角度来看，你可以只关注Threejs的WebGL渲染。   &lt;br /&gt;获取Three.js 3D引擎的源代码—  &lt;a href="https://github.com/mrdoob/three.js/" target="_blank"&gt;github&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://biz.turbulenz.com/developers" target="_blank"&gt;3. Turbulenz&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;Turbulenz是最好的游戏引擎之一，在2009年，当HTML5和WebGL还在酝酿时，它已经被推出。直到2013年，Turbulenz才基于MIT协议拥抱开源。   &lt;br /&gt;Turbulenz包含了很多功能，例如2d物理、3d物理、声音、视频和其他服务,如排行榜、multichat,支付和用户数据。   &lt;br /&gt;了解更多：  &lt;a href="http://biz.turbulenz.com/developers" target="_blank"&gt;biz.turbulenz.com&lt;/a&gt;   &lt;br /&gt;获取源代码：  &lt;a href="https://github.com/turbulenz/turbulenz_engine" target="_blank"&gt;turbulenz_engine&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://famo.us/" target="_blank"&gt;4. Famo.us&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;在HTML5 3D发展的市场中，Famo.us占据了非常重要的地位，并且它是最好的JavaScript 3D开源框架之一。对于famo.us，最好的事情就是包装了3D布局引擎，其完全继承了3D物理驱动的动画引擎。   &lt;br /&gt;了解更多—  &lt;a href="http://famo.us/docs/" target="_blank"&gt;famo.us/docs&lt;/a&gt;   &lt;br /&gt;获取源代码—  &lt;a href="https://github.com/Famous/famous" target="_blank"&gt;famous&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="https://playcanvas.com/" target="_blank"&gt;5. PlayCanvas.js&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;PlayCanvas是一个基于  &lt;strong&gt;WebGL游戏引擎&lt;/strong&gt;的企业级开源JavaScript框架，它有许多的开发工具能帮你快速创建3D游戏。PlayCanvas.js由一个专业社区创建，最初并不是开源的，但现在你可以在github上fork PlayCanvas.js，然后在你的下一个3D游戏项目中免费使用。   &lt;br /&gt;它还提供了能在浏览器中云心的云编辑器，开始使用PalyCanvas和导航到编辑器的URL一样容易。   &lt;br /&gt;了解更多—  &lt;a href="https://playcanvas.com/" target="_blank"&gt;playcanvas.com&lt;/a&gt;   &lt;br /&gt;获取源代码—  &lt;a href="https://github.com/playcanvas/engine" target="_blank"&gt;playcanvas/engine&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://www.goocreate.com/" target="_blank"&gt;6. Goo Engine&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;来自于GOO技术家族，Goo引擎有一组功能强大的JavaScript API，可通过使用HTML5和WebGL创建任何3D事物。有一个在线的编辑器goocreate，可以运行在Goo引擎上，并封装了一些功能，例如视觉三维编辑器,材质编辑器、脚本和容易发布选项等。   &lt;br /&gt;你可以通过支付一些相关的许可费用，在浏览器中使用在线编辑器。   &lt;br /&gt;尽管编辑器需要一些费用，但是Goo引擎是绝对免费的，你可以下载它并在你的3D项目中使用。   &lt;br /&gt;了解更多—-  &lt;a href="https://code.gooengine.com/" target="_blank"&gt;labs.gooengine.com&lt;/a&gt;   &lt;br /&gt;获取源代码—  &lt;a href="https://code.gooengine.com/" target="_blank"&gt;code.gooengine.com&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://www.ambiera.com/copperlicht/" target="_blank"&gt;7. CooperLicht&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;对于创建基于浏览器的游戏，CopperLIcht是最出色的3D引擎之一，也是CopperCube 3D游戏编辑器的后端引擎。   &lt;br /&gt;CopperCube是一个支持创建3D游戏和动画所需要的所有功能的编辑器，但是，它并不开源，需要一些相关的许可费用。   &lt;br /&gt;了解更多—  &lt;a href="http://www.ambiera.com/copperlicht/download.html" target="_blank"&gt;ambiera.com/copperlicht&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://voxeljs.com/" target="_blank"&gt;8. Voxel.JS&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;Voxel.Js是开源的，基于JavaScript的一个3D游戏引擎，自从它发布以来，社区成长非常快。如果你喜欢模块化方法，Voxel是一个不错的选择。   &lt;br /&gt;Voxel-engine是创建3D游戏的核心模块，其它模块可以根据需要插入。到目前为止，已经有超过200个扩展在npm上发布。   &lt;br /&gt;了解更多—  &lt;a href="https://github.com/maxogden/voxel-engine" target="_blank"&gt;maxogden/voxel-engine&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="https://www.blend4web.com/en/" target="_blank"&gt;9. Blend4Web&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;在2014年，Blend4Web作为开源的3D框架而发布，它高度集成了Triumph的3D内容创建工具“Blender”，并且本地支持Blender的节点材料、粒子系统、bullet物理引擎和其他功能。   &lt;br /&gt;获取源代码—  &lt;a href="https://www.blend4web.com/en/downloads/" target="_blank"&gt;blend4web.com/en/downloads&lt;/a&gt;&lt;/p&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h3&gt;  &lt;a href="http://enchantjs.com/" target="_blank"&gt;10. Enchant.js&lt;/a&gt;&lt;/h3&gt;
 &lt;p&gt;Enchant.js是一个模块化的、面向对象的JavaScript框架，可用HTML5创建简单的APP和游戏。它是基于MIT协议开源的，因此开源免费使用。3D动画和游戏开源使用额外的插件（基于WebGL）创建。   &lt;br /&gt;获取源代码–  &lt;a href="https://github.com/wise9/enchant.js" target="_blank"&gt;wise9/enchant.js&lt;/a&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;本文根据@Nicolas Bevacqua的   &lt;a href="http://noeticforce.com/best-3d-javascript-game-engines-frameworks-webgl-html5" target="_blank"&gt;《best-3d-javascript-game-engines-frameworks-webgl-html5》&lt;/a&gt;所译，整个译文带有我自己的理解与思想，如果译得不好或有不对之处还请同行朋友指点。如需转载此译文，需注明英文出处：   &lt;a href="http://noeticforce.com/best-3d-javascript-game-engines-frameworks-webgl-html5" target="_blank"&gt;http://noeticforce.com/best-3d-javascript-game-engines-frameworks-webgl-html5&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;table border="0" cellpadding="3" cellspacing="0"&gt;
    
      &lt;tr&gt;
           &lt;td colspan="5"&gt;    &lt;strong&gt;淡忘~浅思猜你喜欢&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    
          &lt;tr&gt;
                   &lt;td valign="top" width="102"&gt;
                        &lt;a href="http://app.wumii.com/ext/redirect?url=http%3A%2F%2Fwww.ido321.com%2F1116.html&amp;from=http%3A%2F%2Fwww.ido321.com%2F1599.html" target="_blank" title="&amp;#24590;&amp;#20040;&amp;#23545;HTML 5&amp;#30340;&amp;#29305;&amp;#24615;&amp;#20570;&amp;#26816;&amp;#27979;&amp;#65311;"&gt;
                             &lt;img height="96px" src="http://wumii-cc.wumii.cn/site_images/ti/7dT4Oij.jpg?i=15Yx6yXs7" width="96px"&gt;&lt;/img&gt;     &lt;br /&gt;
                        怎么对HTML 5的特性做检测？
                    &lt;/a&gt;
                &lt;/td&gt;
                   &lt;td valign="top" width="102"&gt;
                        &lt;a href="http://app.wumii.com/ext/redirect?url=http%3A%2F%2Fwww.ido321.com%2F1308.html&amp;from=http%3A%2F%2Fwww.ido321.com%2F1599.html" target="_blank" title="HTML 5&amp;#65306;&amp;#32472;&amp;#21046;&amp;#26059;&amp;#36716;&amp;#30340;&amp;#22826;&amp;#26497;&amp;#22270;"&gt;
                             &lt;img height="96px" src="http://wumii-cc.wumii.cn/site_images/ti/lFJYnCwM.jpg?i=9LXHq6jw" width="96px"&gt;&lt;/img&gt;     &lt;br /&gt;
                        HTML 5：绘制旋转的太极图
                    &lt;/a&gt;
                &lt;/td&gt;
                   &lt;td valign="top" width="102"&gt;
                        &lt;a href="http://app.wumii.com/ext/redirect?url=http%3A%2F%2Fwww.ido321.com%2F1293.html&amp;from=http%3A%2F%2Fwww.ido321.com%2F1599.html" target="_blank" title="&amp;#12304;&amp;#35793;&amp;#12305;&amp;#21033;&amp;#29992;HTML 5&amp;#20013;&amp;#30340;Menu&amp;#21644;Menuitem&amp;#20803;&amp;#32032;&amp;#24555;&amp;#36895;&amp;#21019;&amp;#24314;&amp;#33756;&amp;#21333;"&gt;
                             &lt;img height="96px" src="http://wumii-cc.wumii.cn/site_images/ti/5AlZA4D5.jpg?i=u78WlRr7" width="96px"&gt;&lt;/img&gt;     &lt;br /&gt;
                        【译】利用HTML 5中的Menu和Menuitem元素快速创建菜单
                    &lt;/a&gt;
                &lt;/td&gt;
                   &lt;td valign="top" width="102"&gt;
                        &lt;a href="http://app.wumii.com/ext/redirect?url=http%3A%2F%2Fwww.ido321.com%2F949.html&amp;from=http%3A%2F%2Fwww.ido321.com%2F1599.html" target="_blank" title="&amp;#21021;&amp;#35782;HTML 5&amp;#65306;&amp;#20851;&amp;#20110;&amp;#23427;&amp;#30340;&amp;#19977;&amp;#20010;&amp;#19977;"&gt;
                             &lt;img height="96px" src="http://wumii-cc.wumii.cn/site_images/ti/1dhvu1uY3.jpg?i=af7W8CRC" width="96px"&gt;&lt;/img&gt;     &lt;br /&gt;
                        初识HTML 5：关于它的三个三
                    &lt;/a&gt;
                &lt;/td&gt;
                   &lt;td valign="top" width="102"&gt;
                        &lt;a href="http://app.wumii.com/ext/redirect?url=http%3A%2F%2Fwww.ido321.com%2F1069.html&amp;from=http%3A%2F%2Fwww.ido321.com%2F1599.html" target="_blank" title="HTML 5 API&amp;#30340;&amp;#8221;&amp;#21069;&amp;#29983;&amp;#20170;&amp;#19990;&amp;#8221;"&gt;
                             &lt;img height="96px" src="http://wumii-cc.wumii.cn/site_images/ti/Zo7Qg2Fz.jpg?i=6ARMefIk" width="96px"&gt;&lt;/img&gt;     &lt;br /&gt;
                        HTML 5 API的”前生今世”
                    &lt;/a&gt;
                &lt;/td&gt;
        &lt;/tr&gt;
    
      &lt;tr&gt;
           &lt;td align="right" colspan="5"&gt;
                &lt;a href="http://www.wumii.com/widget/relatedItems" target="_blank" title="&amp;#26080;&amp;#35269;&amp;#20851;&amp;#32852;&amp;#25512;&amp;#33616;"&gt;
                无觅
            &lt;/a&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt; &lt;p&gt;转载请注明：  &lt;a href="http://www.ido321.com"&gt;淡忘~浅思&lt;/a&gt; »   &lt;a href="http://www.ido321.com/1599.html"&gt;【译文】Top 10：HTML5、JavaScript 3D游戏引擎和框架&lt;/a&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>HTML JavaScript HTML5 js 游戏</category>
      <guid isPermaLink="true">https://itindex.net/detail/53748-%E8%AF%91%E6%96%87-top-html5</guid>
      <pubDate>Wed, 24 Jun 2015 12:13:19 CST</pubDate>
    </item>
    <item>
      <title>JavaScript内存优化</title>
      <link>https://itindex.net/detail/52965-javascript-%E5%86%85%E5%AD%98-%E4%BC%98%E5%8C%96</link>
      <description>&lt;div&gt;
  &lt;p&gt;相对C/C++ 而言，我们所用的JavaScript 在内存这一方面的处理已经让我们在开发中更注重业务逻辑的编写。但是随着业务的不断复杂化，单页面应用、移动HTML5 应用和Node.js 程序等等的发展，JavaScript 中的内存问题所导致的卡顿、内存溢出等现象也变得不再陌生。&lt;/p&gt;
  &lt;h2&gt;1. 语言层面的内存管理&lt;/h2&gt;
  &lt;h3&gt;1.1 作用域&lt;/h3&gt;
  &lt;p&gt;作用域(scope)是JavaScript 编程中一个非常重要的运行机制，在同步JavaScript 编程中它并不能充分引起初学者的注意，但在异步编程中，良好的作用域控制技能成为了JavaScript 开发者的必备技能。另外，作用域在JavaScript 内存管理中起着至关重要的作用。&lt;/p&gt;
  &lt;p&gt;在JavaScript中，能形成作用域的有函数的调用、   &lt;code&gt;with&lt;/code&gt;语句和全局作用域。如以下代码为例：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;var foo = function() {
  var local = {};
};
foo();
console.log(local); //=&amp;gt; undefined

var bar = function() {
  local = {};
};
bar();
console.log(local); //=&amp;gt; {}
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;这里我们定义了   &lt;code&gt;foo()&lt;/code&gt;函数和   &lt;code&gt;bar()&lt;/code&gt;函数，他们的意图都是为了定义一个名为   &lt;code&gt;local&lt;/code&gt;的变量。但最终的结果却截然不同。&lt;/p&gt;
  &lt;p&gt;在   &lt;code&gt;foo()&lt;/code&gt;函数中，我们使用   &lt;code&gt;var&lt;/code&gt;语句来声明定义了一个   &lt;code&gt;local&lt;/code&gt;变量，而因为函数体内部会形成一个作用域，所以这个变量便被定义到该作用域中。而且   &lt;code&gt;foo()&lt;/code&gt;函数体内并没有做任何作用域延伸的处理，所以在该函数执行完毕后，这个   &lt;code&gt;local&lt;/code&gt;变量也随之被销毁。而在外层作用域中则无法访问到该变量。&lt;/p&gt;
  &lt;p&gt;而在   &lt;code&gt;bar()&lt;/code&gt;函数内，   &lt;code&gt;local&lt;/code&gt;变量并没有使用   &lt;code&gt;var&lt;/code&gt;语句进行声明，取而代之的是直接把   &lt;code&gt;local&lt;/code&gt;作为全局变量来定义。故外层作用域可以访问到这个变量。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;local = {};
// 这里的定义等效于
global.local = {};
&lt;/code&gt;&lt;/pre&gt;
  &lt;h3&gt;1.2 作用域链&lt;/h3&gt;
  &lt;p&gt;在JavaScript编程中，你一定会遇到多层函数嵌套的场景，这就是典型的作用域链的表示。 如以下代码所示：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;function foo() {
  var val = &amp;apos;hello&amp;apos;;

  function bar() {
    function baz() {
      global.val = &amp;apos;world;&amp;apos;
    }
    baz();
    console.log(val); //=&amp;gt; hello
  }
  bar();
}
foo();
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;根据前面关于作用域的阐述，你可能会认为这里的代码所显示的结果是   &lt;code&gt;world&lt;/code&gt;，但实际的结果却是   &lt;code&gt;hello&lt;/code&gt;。很多初学者在这里就会开始感到困惑了，那么我们再来看看这段代码是怎么工作的。&lt;/p&gt;
  &lt;p&gt;由于JavaScript 中，变量标识符的查找是从当前作用域开始向外查找，直到全局作用域为止。所以JavaScript 代码中对变量的访问只能向外进行，而不能逆而行之。&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" src="http://ww1.sinaimg.cn/large/7287333fgw1eegzhw6g3mj209805faa5.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;code&gt;baz()&lt;/code&gt;函数的执行在全局作用域中定义了一个全局变量   &lt;code&gt;val&lt;/code&gt;。而在   &lt;code&gt;bar()&lt;/code&gt;函数中，对   &lt;code&gt;val&lt;/code&gt;这一标识符进行访问时，按照从内到外厄德查找原则：在   &lt;code&gt;bar&lt;/code&gt;函数的作用域中没有找到，便到上一层，即   &lt;code&gt;foo()&lt;/code&gt;函数的作用域中查找。&lt;/p&gt;
  &lt;p&gt;然而，使大家产生疑惑的关键就在这里：本次标识符访问在   &lt;code&gt;foo()&lt;/code&gt;函数的作用域中找到了符合的变量，便不会继续向外查找，故在   &lt;code&gt;baz()&lt;/code&gt;函数中定义的全局变量   &lt;code&gt;val&lt;/code&gt;并没有在本次变量访问中产生影响。&lt;/p&gt;
  &lt;h3&gt;1.3 闭包&lt;/h3&gt;
  &lt;p&gt;我们知道JavaScript 中的标识符查找遵循从内到外的原则。但随着业务逻辑的复杂化，单一的传递顺序已经远远不能满足日益增多的新需求。&lt;/p&gt;
  &lt;p&gt;我们先来看看下面的代码：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;function foo() {
  var local = &amp;apos;Hello&amp;apos;;
  return function() {
    return local;
  };
}
var bar = foo();
console.log(bar()); //=&amp;gt; Hello
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;这里所展示的让外层作用域访问内层作用域的技术便是闭包(Closure)。得益于高阶函数的应用，使   &lt;code&gt;foo()&lt;/code&gt;函数的作用域得到『延伸』。&lt;/p&gt;
  &lt;p&gt;   &lt;code&gt;foo()&lt;/code&gt;函数返回了一个匿名函数，该函数存在于   &lt;code&gt;foo()&lt;/code&gt;函数的作用域内，所以可以访问到   &lt;code&gt;foo()&lt;/code&gt;函数作用域内的   &lt;code&gt;local&lt;/code&gt;变量，并保存其引用。而因这个函数直接返回了   &lt;code&gt;local&lt;/code&gt;变量，所以在外层作用域中便可直接执行   &lt;code&gt;bar()&lt;/code&gt;函数以获得   &lt;code&gt;local&lt;/code&gt;变量。&lt;/p&gt;
  &lt;p&gt;闭包是JavaScript 的高级特性，我们可以借助它来实现更多更复杂的效果来满足不同的需求。但是要注意的是因为把带有​​内部变量引用的函数带出了函数外部，所以该作用域内的变量在函数执行完毕后的并不一定会被销毁，直到内部变量的引用被全部解除。所以闭包的应用很容易造成内存无法释放的情况。&lt;/p&gt;
  &lt;h2&gt;2. JavaScript 的内存回收机制&lt;/h2&gt;
  &lt;p&gt;这里我将以Chrome 和Node.js 所使用的，由Google 推出的V8 引擎为例，简要介绍一下JavaScript 的内存回收机制，更详尽的内容可以购买我的好朋友朴灵的书《深入浅出Node.js 》进行学习，其中『内存控制』一章中有相当详细的介绍。&lt;/p&gt;
  &lt;p&gt;在V8 中，所有的JavaScript 对象都是通过『堆』来进行内存分配的。&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="" src="http://ww4.sinaimg.cn/large/7287333fgw1eeh08y6gqlj208x020mwz.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;当我们在代码中声明变量并赋值时，V8 就会在堆内存中分配一部分给这个变量。如果已申请的内存不足以存储这个变量时，V8 就会继续申请内存，直到堆的大小达到了V8 的内存上限为止。默认情况下，V8 的堆内存的大小上限在64位系统中为1464MB，在32位系统中则为732MB，即约1.4GB 和0.7GB。&lt;/p&gt;
  &lt;p&gt;另外，V8 对堆内存中的JavaScript 对象进行分代管理：新生代和老生代。新生代即存活周期较短的JavaScript 对象，如临时变量、字符串等；而老生代则为经过多次垃圾回收仍然存活，存活周期较长的对象，如主控制器、服务器对象等。&lt;/p&gt;
  &lt;p&gt;垃圾回收算法一直是编程语言的研发中是否重要的​​一环，而V8 中所使用的垃圾回收算法主要有以下几种：&lt;/p&gt;
  &lt;ol&gt;
   &lt;li&gt;Scavange 算法：通过复制的方式进行内存空间管理，主要用于新生代的内存空间；&lt;/li&gt;
   &lt;li&gt;Mark-Sweep 算法和Mark-Compact 算法：通过标记来对堆内存进行整理和回收，主要用于老生代对象的检查和回收。&lt;/li&gt;
&lt;/ol&gt;
  &lt;p&gt;   &lt;strong&gt;PS: 更详细的V8 垃圾回收实现可以通过阅读相关书籍、文档和源代码进行学习。&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;我们再来看看JavaScript 引擎在什么情况下会对哪些对象进行回收。&lt;/p&gt;
  &lt;h3&gt;2.1 作用域与引用&lt;/h3&gt;
  &lt;p&gt;初学者常常会误认为当函数执行完毕时，在函数内部所声明的对象就会被销毁。但实际上这样理解并不严谨和全面，很容易被其导致混淆。&lt;/p&gt;
  &lt;p&gt;引用(Reference)是JavaScript 编程中十分重要的一个机制，但奇怪的是一般的开发者都不会刻意注意它、甚至不了解它。引用是指『代码对对象的访问』这一抽象关系，它与C/C++ 的指针有点相似，但并非同物。引用同时也是JavaScript 引擎在进行垃圾回收中最关键的一个机制。&lt;/p&gt;
  &lt;p&gt;一下面代码为例：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;// ......
var val = &amp;apos;hello world&amp;apos;;
function foo() {
  return function() {
    return val;
  };
}
global.bar = foo();
// ......
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;阅读完这段代码，你能否说出这部分代码在执行过后，有哪些对象是依然存活的么？&lt;/p&gt;
  &lt;p&gt;根据相关原则，这段代码中没有被回收释放的对象有   &lt;code&gt;val&lt;/code&gt;和   &lt;code&gt;bar()&lt;/code&gt;，究竟是什么原因使他们无法被回收？&lt;/p&gt;
  &lt;p&gt;JavaScript 引擎是如何进行垃圾回收的？前面说到的垃圾回收算法只是用在回收时的，那么它是如何知道哪些对象可以被回收，哪些对象需要继续生存呢？答案就是JavaScript 对象的引用。&lt;/p&gt;
  &lt;p&gt;JavaScript 代码中，哪怕是简单的写下一个变量名称作为单独一行而不做任何操作，JavaScript 引擎都会认为这是对对象的访问行为，存在了对对象的引用。为了保证垃圾回收的行为不影响程序逻辑的运行，JavaScript 引擎就决不能把正在使用的对象进行回收，不然就乱套了。所以判断对象是否正在使用中的标准，就是是否仍然存在对该对象的引用。但事实上，这是一种妥协的做法，因为JavaScript 的引用是可以进行转移的，那么就有可能出现某些引用被带到了全局作用域，但事实上在业务逻辑里已经不需要对其进行访问了，应该被回收，但是JavaScript 引擎仍会死板地认为程序仍然需要它。&lt;/p&gt;
  &lt;p&gt;如何用正确的姿势使用变量、引用，正是从语言层面优化JavaScript 的关键所在。&lt;/p&gt;
  &lt;h2&gt;3. 优化你的JavaScript&lt;/h2&gt;
  &lt;p&gt;终于进入正题了，非常感谢你秉着耐心看到了这里，经过上面这么多介绍，相信你已经对JavaScript 的内存管理机制有了不错的理解，那么下面的技巧将会让你如虎添翼。&lt;/p&gt;
  &lt;h3&gt;3.1 善用函数&lt;/h3&gt;
  &lt;p&gt;如果你有阅读优秀JavaScript 项目的习惯的话，你会发现，很多大牛在开发前端JavaScript 代码的时候，常常会使用一个匿名函数在代码的最外层进行包裹。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;;(function() {
  // 主业务代码
})();
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;有的甚至更高级一点：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;;(function(win, doc, $, undefined) {
  // 主业务代码
})(window, document, jQuery);
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;甚至连如RequireJS, SeaJS, OzJS 等前端模块化加载解决方案，都是采用类似的形式：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;// RequireJS
define([&amp;apos;jquery&amp;apos;], function($) {
  // 主业务代码
});

// SeaJS
define(&amp;apos;m​​odule&amp;apos;, [&amp;apos;dep&amp;apos;, &amp;apos;underscore&amp;apos;], function($, _) {
  // 主业务代码
});
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;如果你说很多Node.js 开源项目的代码都没有这样处理的话，那你就错了。Node.js 在实际运行代码之前，会把每一个.js 文件进行包装，变成如下的形式：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;(function(exports, require, module, __dirname, __filename) {
  // 主业务代码
});
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;这样做有什么好处？我们都知道文章开始的时候就说了，JavaScript中能形成作用域的有函数的调用、   &lt;code&gt;with&lt;/code&gt;语句和全局作用域。而我们也知道，被定义在全局作用域的对象，很有可能是会一直存活到进程退出的，如果是一个很大的对象，那就麻烦了。比如有的人喜欢在JavaScript中做模版渲染：&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;&amp;lt;?php
  $db = mysqli_connect(server, user, password, &amp;apos;myapp&amp;apos;);
  $topics = mysqli_query($db, &amp;quot;SELECT * FROM topics;&amp;quot;);
?&amp;gt;
&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
  &amp;lt;title&amp;gt;你是猴子请来的逗比么？&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;ul id=&amp;quot;topics&amp;quot;&amp;gt;&amp;lt;/ul&amp;gt;
  &amp;lt;script type=&amp;quot;text/tmpl&amp;quot; id=&amp;quot;topic-tmpl&amp;quot;&amp;gt;
    &amp;lt;li class=&amp;quot;topic&amp;quot;&amp;gt;
      &amp;lt;h1&amp;gt;&amp;lt;%=title%&amp;gt;&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;&amp;lt;%=content%&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;/li&amp;gt;
  &amp;lt;/script&amp;gt;
  &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
    var data = &amp;lt;?php echo json_encode($topics); ?&amp;gt;;
    var topicTmpl = document.querySelector(&amp;apos;#topic-tmpl&amp;apos;).innerHTML;
    var render = function(tmlp, view) {
      var complied = tmlp
        .replace(/\n/g, &amp;apos;\\n&amp;apos;)
        .replace(/&amp;lt;%=([\s\S]+?)%&amp;gt;/g, function(match, code) {
          return &amp;apos;&amp;quot; + escape(&amp;apos; + code + &amp;apos;) + &amp;quot;&amp;apos;;
        });

      complied = [
        &amp;apos;var res = &amp;quot;&amp;quot;;&amp;apos;,
        &amp;apos;with (view || {}) {&amp;apos;,
          &amp;apos;res = &amp;quot;&amp;apos; + complied + &amp;apos;&amp;quot;;&amp;apos;,
        &amp;apos;}&amp;apos;,
        &amp;apos;return res;&amp;apos;
      ].join(&amp;apos;\n&amp;apos;);

      var fn = new Function(&amp;apos;view&amp;apos;, complied);
      return fn(view);
    };

    var topics = document.querySelector(&amp;apos;#topics&amp;apos;);
    function init()     
      data.forEach(function(topic) {
        topics.innerHTML += render(topicTmpl, topic);
      });
    }
    init();
  &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;这种代码在新手的作品中经常能看得到，这里存在什么问题呢？如果在从数据库中获取到的数据的量是非常大的话，前端完成模板渲染以后，   &lt;code&gt;data&lt;/code&gt;变量便被闲置在一边。可因为这个变量是被定义在全局作用域中的，所以JavaScript引擎不会将其回收销毁。如此该变量就会一直存在于老生代堆内存中，直到页面被关闭。&lt;/p&gt;
  &lt;p&gt;可是如果我们作出一些很简单的修改，在逻辑代码外包装一层函数，这样效果就大不同了。当UI渲染完成之后，代码对   &lt;code&gt;data&lt;/code&gt;的引用也就随之解除，而在最外层函数执行完毕时，JavaScript引擎就开始对其中的对象进行检查，   &lt;code&gt;data&lt;/code&gt;也就可以随之被回收。&lt;/p&gt;
  &lt;h3&gt;3.2 绝对不要定义全局变量&lt;/h3&gt;
  &lt;p&gt;我们刚才也谈到了，当一个变量被定义在全局作用域中，默认情况下JavaScript 引擎就不会将其回收销毁。如此该变量就会一直存在于老生代堆内存中，直到页面被关闭。&lt;/p&gt;
  &lt;p&gt;那么我们就一直遵循一个原则：绝对不要使用全局变量。虽然全局变量在开发中确实很省事，但是全局变量所导致的问题远比其所带来的方便更严重。&lt;/p&gt;
  &lt;ol&gt;
   &lt;li&gt;使变量不易被回收；&lt;/li&gt;
   &lt;li&gt;多人协作时容易产生混淆；&lt;/li&gt;
   &lt;li&gt;在作用域链中容易被干扰。&lt;/li&gt;
&lt;/ol&gt;
  &lt;p&gt;配合上面的包装函数，我们也可以通过包装函数来处理『全局变量』。&lt;/p&gt;
  &lt;h3&gt;3.3 手工解除变量引用&lt;/h3&gt;
  &lt;p&gt;如果在业务代码中，一个变量已经确切是不再需要了，那么就可以手工解除变量引用，以使其被回收。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;var data = { /* some big data */ };
// blah blah blah
data = null;
&lt;/code&gt;&lt;/pre&gt;
  &lt;h3&gt;3.4 善用回调&lt;/h3&gt;
  &lt;p&gt;除了使用闭包进行内部变量访问，我们还可以使用现在十分流行的回调函数来进行业务处理。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;function getData(callback) {
  var data = &amp;apos;some big data&amp;apos;;

  callback(null, data);
}

getData(function(err, data) {
  console.log(data);
});
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;回调函数是一种后续传递风格(Continuation Passing Style, CPS)的技术，这种风格的程序编写将函数的业务重点从返回值转移到回调函数中去。而且其相比闭包的好处也不少：&lt;/p&gt;
  &lt;ol&gt;
   &lt;li&gt;如果传入的参数是基础类型（如字符串、数值），回调函数中传入的形参就会是复制值，业务代码使用完毕以后，更容易被回收；&lt;/li&gt;
   &lt;li&gt;通过回调，我们除了可以完成同步的请求外，还可以用在异步编程中，这也就是现在非常流行的一种编写风格；&lt;/li&gt;
   &lt;li&gt;回调函数自身通常也是临时的匿名函数，一旦请求函数执行完毕，回调函数自身的引用就会被解除，自身也得到回收。&lt;/li&gt;
&lt;/ol&gt;
  &lt;h3&gt;3.5 良好的闭包管理&lt;/h3&gt;
  &lt;p&gt;当我们的业务需求(如循环事件绑定、私有属性、含参回调等)一定要使用闭包时，请谨慎对待其中的细节。&lt;/p&gt;
  &lt;p&gt;循环绑定事件可谓是JavaScript 闭包入门的必修课，我们假设一个场景：有六个按钮，分别对应六种事件，当用户点击按钮时，在指定的地方输出相应的事件。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;var btns = document.querySelectorAll(&amp;apos;.btn&amp;apos;); // 6 elements
var output = document.querySelector(&amp;apos;#output&amp;apos;);
var events = [1, 2, 3, 4, 5, 6];

// Case 1
for (var i = 0; i &amp;lt; btns.length; i++) {
  btns[i].onclick = function(evt) {
    output.innerText += &amp;apos;Clicked &amp;apos; + events[i];
  };
}

// Case 2
for (var i = 0; i &amp;lt; btns.length; i++) {
  btns[i].onclick = (function(index) {
    return function(evt) {
      output.innerText += &amp;apos;Clicked &amp;apos; + events[index];
    };
  })(i);
}

// Case 3
for (var i = 0; i &amp;lt; btns.length; i++) {
  btns[i].onclick = (function(event) {
    return function(evt) {
      output.innerText += &amp;apos;Clicked &amp;apos; + event;
    };
  })(events[i]);
}
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;这里第一个解决方案显然是典型的循环绑定事件错误，这里不细说，详细可以参照   &lt;a href="http://www.html-js.com/qa/308#answer-654"&gt;我给一个网友的回答&lt;/a&gt;；而第二和第三个方案的区别就在于闭包传入的参数。&lt;/p&gt;
  &lt;p&gt;第二个方案传入的参数是当前循环下标，而后者是直接传入相应的事件对象。事实上，后者更适合在大量数据应用的时候，因为在JavaScript的函数式编程中，函数调用时传入的参数是基本类型对象，那么在函数体内得到的形参会是一个复制值，这样这个值就被当作一个局部变量定义在函数体的作用域内，在完成事件绑定之后就可以对   &lt;code&gt;events&lt;/code&gt;变量进行手工解除引用，以减轻外层作用域中的内存占用了。而且当某个元素被删除时，相应的事件监听函数、事件对象、闭包函数也随之被销毁回收。&lt;/p&gt;
  &lt;h3&gt;3.6 内存不是缓存&lt;/h3&gt;
  &lt;p&gt;缓存在业务开发中的作用举足轻重，可以减轻时空资源的负担。但需要注意的是，不要轻易将内存当作缓存使用。内存对于任何程序开发来说都是寸土寸金的东西，如果不是很重要的资源，请不要直接放在内存中，或者制定过期机制，自动销毁过期缓存。&lt;/p&gt;
  &lt;h2&gt;4. 检查JavaScript 的内存使用情况&lt;/h2&gt;
  &lt;p&gt;在平时的开发中，我们也可以借助一些工具来对JavaScript 中内存使用情况进行分析和问题排查。&lt;/p&gt;
  &lt;h3&gt;4.1 Blink / Webkit 浏览器&lt;/h3&gt;
  &lt;p&gt;在Blink / Webkit 浏览器中（Chrome, Safari, Opera etc.），我们可以借助其中的Developer Tools 的Profiles 工具来对我们的程序进行内存检查。&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="Developer Tools - Profiles" src="http://ww3.sinaimg.cn/large/7287333fgw1eehhj2zplnj20sq0pvq83.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;h3&gt;4.2 Node.js 中的内存检查&lt;/h3&gt;
  &lt;p&gt;在Node.js 中，我们可以使用node-heapdump 和node-memwatch 模块进​​行内存检查。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;var heapdump = require(&amp;apos;heapdump&amp;apos;);
var fs = require(&amp;apos;fs&amp;apos;);
var path = require(&amp;apos;path&amp;apos;);
fs.writeFileSync(path.join(__dirname, &amp;apos;app.pid&amp;apos;), process.pid);
// ...
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;在业务代码中引入node-heapdump 之后，我们需要在某个运行时期，向Node.js 进程发送SIGUSR2 信号，让node-heapdump 抓拍一份堆内存的快照。&lt;/p&gt;
  &lt;pre&gt;   &lt;code&gt;$ kill -USR2 (cat app.pid)
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;这样在文件目录下会有一个以   &lt;code&gt;heapdump-&amp;lt;sec&amp;gt;.&amp;lt;usec&amp;gt;.heapsnapshot&lt;/code&gt;格式命名的快照文件，我们可以使用浏览器的Developer Tools中的Profiles工具将其打开，并进行检查。&lt;/p&gt;
  &lt;h2&gt;5. 小结&lt;/h2&gt;
  &lt;p&gt;很快又来到了文章的结束，这篇分享主要向大家展示了以下几点内容：&lt;/p&gt;
  &lt;ol&gt;
   &lt;li&gt;JavaScript 在语言层面上，与内存使用息息相关的东西；&lt;/li&gt;
   &lt;li&gt;JavaScript 中的内存管理、回收机制；&lt;/li&gt;
   &lt;li&gt;如何更高效地使用内存，以至于让出产的JavaScript 能更有拓展的活力；&lt;/li&gt;
   &lt;li&gt;如何在遇到内存问题的时候，进行内存检查。&lt;/li&gt;
&lt;/ol&gt;
  &lt;p&gt;原帖url：   &lt;a href="http://lifemap.in/javascript-memory-optimize/"&gt;http://lifemap.in/javascript-memory-optimize/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
          
           &lt;br /&gt; &lt;br /&gt;
          
             &lt;a href="http://jm1999.iteye.com/blog/2193300#comments"&gt;已有   &lt;strong&gt;0&lt;/strong&gt; 人发表留言，猛击-&amp;gt;&amp;gt;  &lt;strong&gt;这里&lt;/strong&gt;&amp;lt;&amp;lt;-参与讨论&lt;/a&gt;
          
           &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
ITeye推荐
 &lt;br /&gt;
 &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.iteye.com/clicks/433" target="_blank"&gt;—软件人才免语言低担保 赴美带薪读研！— &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;
          
        &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/52965-javascript-%E5%86%85%E5%AD%98-%E4%BC%98%E5%8C%96</guid>
      <pubDate>Tue, 17 Mar 2015 17:55:16 CST</pubDate>
    </item>
    <item>
      <title>Chrome开发者工具之JavaScript内存分析</title>
      <link>https://itindex.net/detail/52929-chrome-%E5%BC%80%E5%8F%91-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;
  &lt;div&gt;
&lt;/div&gt;
  &lt;p&gt;   &lt;strong&gt;内存泄漏&lt;/strong&gt;是指计算机可用内存的逐渐减少。当程序持续无法释放其使用的临时内存时就会发生。JavaScript的web应用也会经常遇到在原生应用程序中出现的内存相关的问题，如   &lt;strong&gt;泄漏&lt;/strong&gt;和溢出，web应用也需要应对   &lt;strong&gt;垃圾回收停顿&lt;/strong&gt;。&lt;/p&gt;
  &lt;div&gt;
   &lt;div&gt;
    &lt;div&gt;
尽管JavaScript使用垃圾回收进行自动内存管理，但有效的(effective)内存管理依然很重要。在这篇文章中我们将探讨分析JavaScript web应用中的内存问题。在学习有关特性时请确保尝试一下相关案例以提高你对这些工具在实践中如何工作的认识。请阅读     &lt;a href="https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101"&gt;内存 101(Memory 101)&lt;/a&gt;页面来帮助你熟悉这篇文章中用到的术语。     &lt;strong&gt;注意：&lt;/strong&gt;我们将要用到的某些特性目前仅对     &lt;a href="http://www.google.com/intl/en/chrome/browser/canary.html"&gt;Chrome Canary版&lt;/a&gt;浏览器可用。我们推荐使用这个版本来获得最佳的工具，以分析你的应用程序的内存问题。     &lt;p&gt;&lt;/p&gt;
     &lt;h2&gt;你需要思考的问题&lt;/h2&gt;

     &lt;p&gt;总体来说，当你觉得你遇到了内存泄漏问题时，你需要思考三个问题：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;我的页面是否占用了过多的内存?&lt;/strong&gt; -        &lt;a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools/blob/master/md/Performance-Profiling/javascript-memory-profiling.md#heading=h.3gfl4k8caz0k"&gt;Timeline内存查看工具(Timeline memory view)&lt;/a&gt; 和        &lt;a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools/blob/master/md/Performance-Profiling/javascript-memory-profiling.md#chrome-%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8"&gt;Chrome任务管理(Chrome task manager)&lt;/a&gt; 能帮助你确认你是否使用了过多的内存。Memory view 能跟踪页面渲染过程中DOM节点计数，documents文档计数和JS事件监听计数。作为一个经验法则：避免对不再需要用到的DOM元素的引用，移除不需要的事件监听并且在存储你可能不会用到的大块数据时要留意。&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;我的页面有没有内存泄漏?&lt;/strong&gt; -        &lt;a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools/blob/master/md/Performance-Profiling/javascript-memory-profiling.md#heading=h.8yjlf68i8qix"&gt;对象分配跟踪(Object allocation tracker)&lt;/a&gt;通过实时查看JS对象的分配来帮助你定位泄漏。你也可以使用       &lt;a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools/blob/master/md/Performance-Profiling/javascript-memory-profiling.md#heading=h.g0yxr1o33gky"&gt;堆分析仪(Heap Profiler)&lt;/a&gt;生成JS堆快照，通过分析内存图和比较快照之间的差异，来找出没有被垃圾回收清理掉的对象。&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;我的页面垃圾强制回收有多频繁?&lt;/strong&gt; - 如果你的页面垃圾回收很频繁，那说明你的页面可能内存使用分配太频繁了。       &lt;a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools/blob/master/md/Performance-Profiling/javascript-memory-profiling.md#heading=h.3gfl4k8caz0k"&gt;Timeline内存查看工具(Timeline memory view)&lt;/a&gt; 能够帮助你发现感兴趣的停顿。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/df747a20b00d975453ef8286478dcb3c.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;h2&gt;术语和基本概念&lt;/h2&gt;
     &lt;p&gt;本小节介绍在      &lt;strong&gt;内存分析&lt;/strong&gt;时使用的常用术语，这些术语在为其它语言做内存分析的工具中也适用。这里的术语和概念用在了堆分析仪(Heap Profiler)UI工具和相关的文档中。&lt;/p&gt;
     &lt;p&gt;这些能够帮助我们熟悉如何有效的使用内存分析工具。如果你曾用过像Java、.NET等语言的内存分析工具的话，那么这将是一个复习。&lt;/p&gt;
     &lt;h3&gt;对象大小(Object sizes)&lt;/h3&gt;
     &lt;p&gt;把内存想象成一个包含基本类型(像数字和字符串)和对象(关联数组)的图表。它可能看起来像下面这幅一系列相关联的点组成的图。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/fbd36a99961d75dd9236a768eaccb88f.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;一个对象有两种使用内存的方法：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;对象自身直接使用&lt;/li&gt;
      &lt;li&gt;隐含的保持对其它对象的引用，这种方式会阻止垃圾回收(简称GC)对那些对象的自动回收处理。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;当你使用DevTools中的堆分析仪(Heap Profiler，用来分析内存问题的工具，在DevTools的”Profile”标签下)时，你可能会惊喜的发现一些显示各种信息的栏目。其中有两项是：      &lt;strong&gt;直接占用内存(Shallow Size)&lt;/strong&gt;和      &lt;strong&gt;占用总内存(Retained Size)&lt;/strong&gt;，那它们是什么意思呢？&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/ba32b5fb78133dc1112b61d2265e6759.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;h4&gt;直接占用内存(Shallow Size，不包括引用的对象占用的内存)&lt;/h4&gt;
     &lt;p&gt;这个是对象本身占用的内存。&lt;/p&gt;
     &lt;p&gt;典型的JavaScript对象都会有保留内存用来描述这个对象和存储它的直接值。一般，只有数组和字符串会有明显的直接占用内存(Shallow Size)。但字符串和数组常常会在渲染器内存中存储主要数据部分，仅仅在JavaScript对象栈中暴露一个很小的包装对象。&lt;/p&gt;
     &lt;p&gt;渲染器内存指你分析的页面在渲染的过程中所用到的所有内存：页面本身的内存 + 页面中的JS堆用到的内存 + 页面触发的相关工作进程(workers)中的JS堆用到的内存。然而，通过阻止垃圾自动回收别的对象，一个小对象都有可能间接占用大量的内存。&lt;/p&gt;
     &lt;h4&gt;占用总内存(Retained Size，包括引用的对象所占用的内存)&lt;/h4&gt;
     &lt;p&gt;一个对象一但删除后它引用的依赖对象就不能被      &lt;strong&gt;GC根(GC root)&lt;/strong&gt;引用到，它们所占用的内存就会被释放，一个对象占用总内存包括这些依赖对象所占用的内存。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;GC根&lt;/strong&gt;是由      &lt;em&gt;控制器(handles)&lt;/em&gt;组成的，这些控制器(不论是局部还是全局)是在建立由build-in函数(native code)到V8引擎之外的JavaScript对象的引用时创建的。所有这些控制器都能够在堆快照的      &lt;strong&gt;GC roots(GC根)&lt;/strong&gt; &amp;gt;       &lt;strong&gt;Handle scope&lt;/strong&gt; 和       &lt;strong&gt;GC roots&lt;/strong&gt; &amp;gt;      &lt;strong&gt;Global handlers&lt;/strong&gt;中找到。如果不深入了解浏览器的实现原理，在这篇文章中介绍这些控制器可能会让人不能理解。GC根和控制器你都不需要过多关心。&lt;/p&gt;
     &lt;p&gt;有很多内部的GC根对用户来说都是不重要的。从应用的角度来说有下面几种情况：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;Window 全局对象 (所有iframe中的)。在堆快照中有一个distance字段，它是从window对象到达对应对象的最短路径长度。&lt;/li&gt;
      &lt;li&gt;由所有document能够遍历到的DOM节点组成的文档DOM树。不是所有节点都会被对应的JS引用，但有JS引用的节点在document存在的情况下都会被保留。&lt;/li&gt;
      &lt;li&gt;有很多对象可能是在调试代码时或者DevTools console中(比如：console中的一些代码执行结束后)创建出来的。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;strong&gt;注意：&lt;/strong&gt;我们推荐用户在创建堆快照时，不要在console中执行代码，也不要启用调试断点。&lt;/p&gt;
     &lt;p&gt;内存图由一个根部开始，可能是浏览器的      &lt;code&gt;window&lt;/code&gt;对象或Node.js模块      &lt;code&gt;Global&lt;/code&gt;对象。这些对象如何被内存回收不受用户的控制。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/99de0c819e755a4e3888d9c83746e22a.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;不能被GC根遍历到的对象都将被内存回收。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;注意：&lt;/strong&gt;直接占用内存和占用总内存字段中的数据是用字节表示的。&lt;/p&gt;
     &lt;h3&gt;对象的占用总内存树&lt;/h3&gt;
     &lt;p&gt;之前我们已经了解到，堆是由各种互相关联的对象组成的网状结构。在数字领域，这种结构被称为      &lt;em&gt;图&lt;/em&gt;或内存图。图是由      &lt;em&gt;边缘(edges)&lt;/em&gt;连接着的      &lt;em&gt;节点(nodes)&lt;/em&gt;组成的，他们都被贴了标签。&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;节点(Nodes)&lt;/strong&gt; (       &lt;em&gt;或对象&lt;/em&gt;) 节点的标签名是由创建他们的       &lt;em&gt;构造(constructor)&lt;/em&gt;函数的名称确定&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;边缘(Edges)&lt;/strong&gt; 标签名就是属性名&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;本文档的后面你将了解到如何使用堆分析仪生成快照。从下图的堆分析仪生成的快照中，我们能看到距离(distance)这个字段：是指对象到GC根的距离。如果同一个类型的所有对象的距离都一样，而有一小部分的距离却比较大，那么就可能出了些你需要进行调查的问题了。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/58bb8983bc563506bad8bb8e41b67b7f.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;h3&gt;支配对象(Dominators)&lt;/h3&gt;
     &lt;p&gt;支配对象就像一个树结构，因为每个对象都有一个支配者。一个对象的支配者可能不会直接引用它支配的对象，就是说，支配对象树结构不是图中的生成树。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/6b10aeb43c641941bda5e5b4074a71fb.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;在上图中：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;节点1支配节点2&lt;/li&gt;
      &lt;li&gt;节点2支配节点3，4和6&lt;/li&gt;
      &lt;li&gt;节点3支配节点5&lt;/li&gt;
      &lt;li&gt;节点5支配节点8&lt;/li&gt;
      &lt;li&gt;节点6支配节点7&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;在下图的例子中，节点      &lt;code&gt;#3&lt;/code&gt;是      &lt;code&gt;#10&lt;/code&gt;的支配者，但      &lt;code&gt;#7&lt;/code&gt;也在每个从GC到      &lt;code&gt;#10&lt;/code&gt;的路经中都出现了。像这样，如果B对象在每个从根节点到A对象的路经中都出现，那么B对象就是A对象的支配对象。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/5c8c42722fb9d78402c6baa520dfc81b.gif"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;h3&gt;V8介绍&lt;/h3&gt;
     &lt;p&gt;在本节，我们将描述一些内存相关的概念，这些概念是和      &lt;strong&gt;V8 JavaScript虚拟机&lt;/strong&gt;(V8 VM 或VM)有关的。当分析内存时，了解这些概念对理解堆快照是有帮助的。&lt;/p&gt;
     &lt;h4&gt;JavaScript对象描述&lt;/h4&gt;
     &lt;p&gt;有三个原始类型：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;数字(Numbers) (如 3.14159..)&lt;/li&gt;
      &lt;li&gt;布尔值(Booleans) (true或false)&lt;/li&gt;
      &lt;li&gt;字符型(Strings) (如 ‘Werner Heisenberg’)&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;它们不会引用别的值，它们只会是叶子节点或终止节点。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;数字(Numbers)&lt;/strong&gt;以下面两种方式之一被存储：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;31位整数直接值，称做：       &lt;strong&gt;小整数(small integers)&lt;/strong&gt;(       &lt;em&gt;SMIs&lt;/em&gt;)，或&lt;/li&gt;
      &lt;li&gt;堆对象，引用为       &lt;strong&gt;堆值&lt;/strong&gt;。堆值是用来存储不适合用SMI形式存储的数据，像       &lt;em&gt;双精度数(doubles)&lt;/em&gt;，或者当一个值需要被       &lt;em&gt;打包(boxed)&lt;/em&gt;时，如给这个值再设置属性值。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;strong&gt;字符型&lt;/strong&gt;数据会以下面两种方式存储：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;VM堆&lt;/strong&gt;，或&lt;/li&gt;
      &lt;li&gt;外部的       &lt;strong&gt;渲染器内存&lt;/strong&gt;中。这时会创建一个包装对象用来访问存储的位置，比如，Web页面包存的脚本资源和其它内容，而不是直接复制至VM堆中。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;新创建的JavaScript对象会被在JavaScript堆上(或      &lt;strong&gt;VM堆&lt;/strong&gt;)分配内存。这些对象由V8的垃圾回收器管理，只要还有一个强引用他们就会在内存中保留。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;本地对象&lt;/strong&gt;是所有不在JavaScript堆中的对象，与堆对象不同的是，在它们的生命周期中，不会被V8垃圾加收器处理，只能通过JavaScript包装对象引用。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;连接字符串&lt;/strong&gt;是由一对字符串合并成的对象，是合并后的结果。      &lt;strong&gt;连接字符串&lt;/strong&gt;只在有需要时合并。像一连接字符串的子字符串需要被构建时。&lt;/p&gt;
     &lt;p&gt;比如：如果你连接      &lt;strong&gt;a&lt;/strong&gt;和      &lt;strong&gt;b&lt;/strong&gt;，你得到字符串(a, b)这用来表示连接的结果。如果你之后要再把这个结果与      &lt;strong&gt;d&lt;/strong&gt;连接，你就得到了另一个连接字符串((a, b), d)。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;数组(Arrays)&lt;/strong&gt; - 数组是数字类型键的对象。它们在V8引擎中存储大数据量的数据时被广泛的使用。像字典这种有键-值对的对象就是用数组实现的。&lt;/p&gt;
     &lt;p&gt;一个典型的JavaScript对象可以通过两种数组类型之一的方式来存储：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;命名属性，和&lt;/li&gt;
      &lt;li&gt;数字化的元素&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;如果只有少量的属性，它们会被直接存储在JavaScript对象本身中。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;Map&lt;/strong&gt; - 一种用来描述对象类型和它的结构的对象。比如，maps会被用来描述对象的结构以实现对对象属性的      &lt;a href="https://developers.google.com/v8/design.html#prop_access"&gt;快速访问&lt;/a&gt;&lt;/p&gt;
     &lt;h4&gt;对象组&lt;/h4&gt;
     &lt;p&gt;每个本地对象组都是由一组之间相互关联的对象组成的。比如一个DOM子树，每个节点都能访问到它的父元素，下一个子元素和下一个兄弟元素，它们构成了一个关联图。需要注意的是本地元素没有在JavaScript堆中表现－这就是它们的大小是零的原因，而它的包装对象被创建了。&lt;/p&gt;
     &lt;p&gt;每个包装对象都会有一个到本地对象的引用，用来传递对这些本地对象的操作。这些本地对象也有到包装对象的引用。但这并不会创造无法收回的循环，GC是足够智能的，能够分辨出那些已经没有引用包装对象的本地对象并释放它们的。但如果有一个包装对象没有被释放那它将会保留所有对象组和相关的包装对象。&lt;/p&gt;
     &lt;h2&gt;先决条件和有用提示&lt;/h2&gt;
     &lt;h3&gt;Chrome 任务管理器&lt;/h3&gt;
     &lt;p&gt;      &lt;strong&gt;注意：&lt;/strong&gt; 当使用Chrome做内存分析时，最好设置一个      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/clean-testing-environment"&gt;洁净的测试环境&lt;/a&gt;&lt;/p&gt;
     &lt;p&gt;打开Chrome的内存管理器，观察内存字段，在一个页面上做相关的操作，你可以很快定位这个操作是否会导致页面占用很多内存。你可以从Chrome菜单 &amp;gt; 工具或按Shift + Esc，找到内存管理器。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/3f351c21553e99767bc3a4f726087c00.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;打开后，在标头右击选用 JavasScript使用的内存 这项。&lt;/p&gt;
     &lt;h3&gt;通过DevTools Timeline来定位内存问题&lt;/h3&gt;
     &lt;p&gt;解决问题的第一步就是要能够证明问题存在。这就需要创建一个可重现的测试来做为问题的基准度量。没有可再现的程序，就不能可靠的度量问题。换句话说如果没有基准来做为对比，就无法知道是哪些改变使问题出现的。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;时间轴面版(Timeline panel)&lt;/strong&gt;对于发现程序什么时候出了问题很用帮助。它展示了你的web应用或网站加载和交互的时刻。所有的事件：从加载资源到解JavaScript，样式计算，垃圾回收停顿和页面重绘。都在时间轴上表示出来了。&lt;/p&gt;
     &lt;p&gt;当分析内存问题时，时间轴面版上的      &lt;strong&gt;内存视图(Memory view)&lt;/strong&gt;能用来观察：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;使用的总内存 – 内存使用增长了么?&lt;/li&gt;
      &lt;li&gt;DOM节点数&lt;/li&gt;
      &lt;li&gt;文档(documents)数&lt;/li&gt;
      &lt;li&gt;注册的事件监听器(event listeners)数&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/64c9e34ff18d65d374b4d55e546cb779.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;更多的关于在内存分析时，定位内存泄漏的方法，请阅Zack Grossbart的      &lt;a href="http://coding.smashingmagazine.com/2012/06/12/javascript-profiling-chrome-developer-tools/"&gt;Memory profiling with the Chrome DevTools&lt;/a&gt;&lt;/p&gt;
     &lt;h4&gt;      &lt;strong&gt;证明一个问题的存在&lt;/strong&gt;&lt;/h4&gt;
     &lt;p&gt;首先要做的事情是找出你认为可能导致内存泄漏的一些动作。可以是发生在页面上的任何事件，鼠标移入，点击，或其它可能会导致页面性能下降的交互。&lt;/p&gt;
     &lt;p&gt;在时间轴面版上开始记录(Ctrl+E 或 Cmd+E)然后做你想要测试的动作。想要强制进行垃圾回收点面版上的垃圾筒图标(      &lt;a target="_blank"&gt;       &lt;img alt=""&gt;&lt;/img&gt;&lt;/a&gt;)。&lt;/p&gt;
     &lt;p&gt;下面是一个内存泄漏的例子，有些点没有被垃圾回收：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/7c1befcca92c60bffbab762cfc32c4ca.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;如果经过一些反复测试后，你看到的是      &lt;a href="http://en.wikipedia.org/wiki/Sawtooth_wave"&gt;锯齿&lt;/a&gt;状的图形(在内存面版的上方)，说明你的程序中有很多短时存在的对象。而如果一系列的动作没有让内存保持在一定的范围，并且DOM节点数没有返回到开始时的数目，你就可以怀疑有内存泄漏了。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/d01f67ede9d57e796abc1f4772dec744.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;一旦确定了存在内存上的问题，你就可以使用      &lt;strong&gt;分析面板(Profiles panel)&lt;/strong&gt;上的      &lt;strong&gt;堆分析仪(heap profiler)&lt;/strong&gt;来定位问题的来源。&lt;/p&gt;
     &lt;p&gt;例子: 尝试一下      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/demos/memory/example1.html"&gt;memory growth&lt;/a&gt;的例子，能帮助你有效的练习通过时间轴分析内存问题。&lt;/p&gt;
     &lt;h3&gt;内存回收&lt;/h3&gt;
     &lt;p&gt;      &lt;em&gt;内存回收器&lt;/em&gt;(像V8中的)需要能够定位哪些对象是      &lt;em&gt;活的(live)&lt;/em&gt;，而那些被认为是      &lt;em&gt;死的&lt;/em&gt;(垃圾      &lt;em&gt;)&lt;/em&gt;的对象是      &lt;em&gt;无法引用到的(unreachable)&lt;/em&gt;。&lt;/p&gt;
     &lt;p&gt;如果      &lt;strong&gt;垃圾回收&lt;/strong&gt; (GC)因为JavaScript执行时有逻辑错误而没有能够回收到垃圾对象，这些垃圾对象就无法再被重新回收了。像这样的情况最终会让你的应用越来越慢。&lt;/p&gt;
     &lt;p&gt;比如你在写代码时，有的变量和事件监听器已经用不到了，但是却仍然被有些代码引用。只要引用还存在，那被引用的对象就无法被GC正确的回收。&lt;/p&gt;
     &lt;p&gt;当你的应用程序在运行中，有些DOM对象可能已经更新/移除了，要记住检查引用了DOM对象的变量并将其设null。检查可能会引用到其它对象(或其它DOM元素)的对象属性。双眼要盯着可能会越来越增长的变量缓存。&lt;/p&gt;
     &lt;h2&gt;堆分析仪&lt;/h2&gt;
     &lt;h3&gt;拍一个快照&lt;/h3&gt;
     &lt;p&gt;在Profiles面板中，选择      &lt;em&gt;Take Heap Snapshot&lt;/em&gt;，然后点击      &lt;strong&gt;Start&lt;/strong&gt;或者按Cmd + E或者Ctrl + E：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/de732f1a9296e29bcdd89f5a2c70047d.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;快照最初是保存在渲染器进程内存中的。它们被按需导入到了DevTools中，当你点击快照按钮后就可以看到它们了。当快照被载入DevTools中显示后，快照标题下面的数字显示了      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101.html#retaining_paths"&gt;能够被引用到的(reachable)&lt;/a&gt;JavaScript对象占有内存总数。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/abf6242177ffc343aad9192a29dcf611.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试一下      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/demos/memory/example2.html"&gt;garbage collection in action&lt;/a&gt;的例子，在时间轴(Timeline)面板中监控内存的使用。&lt;/p&gt;
     &lt;h3&gt;清除快照&lt;/h3&gt;
     &lt;p&gt;点击Clear all按钮图标(      &lt;a target="_blank"&gt;       &lt;img alt=""&gt;&lt;/img&gt;&lt;/a&gt;)，就能清除掉所有快照：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/cf1cebe08614cd43202750e8f992851d.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;注意：&lt;/strong&gt;关闭DevTools窗口并不能从渲染内存中删除掉收集的快照。当重新打开DevTools后，之前的快照列表还在。&lt;/p&gt;
     &lt;p&gt;记住我们之前提到的，当你生成快照时你可以强制执行在DevTools中GC。当我们拍快照时，GC是自动执行的。在时间轴(Timeline)中点击垃圾桶(垃圾回收)按钮(      &lt;a target="_blank"&gt;       &lt;img alt=""&gt;&lt;/img&gt;&lt;/a&gt;)就可以轻松的执行垃圾回收了。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/c4877e505826b08ade0a22423dc69ddb.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试一下      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/demos/memory/example3.html"&gt;scattered objects&lt;/a&gt;并用堆分析仪(Heap Profiler)分析它。你可以看到(对象)项目的集合。&lt;/p&gt;
     &lt;h3&gt;切换快照视图&lt;/h3&gt;
     &lt;p&gt;一个快照可以根据不同的任务切换视图。可以通过如图的选择框切换：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/27046b406109e0213a2ae5bf92c1aa73.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;下面是三个默认视图：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;Summary(概要)&lt;/strong&gt; - 通过构造函数名分类显示对象；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;Comparison(对照)&lt;/strong&gt; - 显示两个快照间对象的差异；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;Containment(控制)&lt;/strong&gt; - 可用来探测堆内容；&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;strong&gt;Dominators(支配者)&lt;/strong&gt;视图可以在Settings面板中开启 – 显示      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101.html#dominators"&gt;dominators tree.&lt;/a&gt; 可以用来找到内存增长点。&lt;/p&gt;
     &lt;h3&gt;通过不同颜色区分对象&lt;/h3&gt;
     &lt;p&gt;对象的属性和属性值有不同的类型并自动的通过颜么进行了区分。每个属性都是以下四种之一：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;a:property&lt;/strong&gt; - 通过名称索引的普通属性，由.(点)操作符，或[](中括号)引用，如[&amp;quot;foo bar&amp;quot;]；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;0:element&lt;/strong&gt; - 通过数字索引的普通属性，由[](中括号)引用；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;a:context var&lt;/strong&gt; - 函数内的属性，在函数上下文内，通过名称引用；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;a:system prop&lt;/strong&gt; - 由JavaScript VM 添加的属性，JavaScript代码不能访问。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;命名为      &lt;code&gt;System&lt;/code&gt;的对象没有对应的JavaScript类型。它们是JavaScript VM对象系统内置的。V8将大多数内置对象和用户JS对象放在同一个堆中。但它们只是V8的内部对象。&lt;/p&gt;
     &lt;h2&gt;视图详解&lt;/h2&gt;
     &lt;h3&gt;Summary view(概要视图)&lt;/h3&gt;
     &lt;p&gt;打开一个快照，默认是以概要视图显示的，显示了对象总数，可以展开显示具体内容： Initially, a snapshot opens in the Summary view, displaying object totals, which can be expanded to show instances:&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/01bae1ecdebaae21d574087f63eb4501.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;第一层级是”总体”行，它们显示了：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;Constructor(构造函数)&lt;/strong&gt;表示所有通过该构造函数生成的对象&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;对象的实例数&lt;/strong&gt;在Objects Count列上显示&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;Shallow size&lt;/strong&gt;列显示了由对应构造函数生成的对象的       &lt;a href="https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101.html#object_sizes"&gt;shallow sizes(直接占用内存)&lt;/a&gt;总数&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;Retained size&lt;/strong&gt;列展示了对应对象所占用的最大内存&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;Distance&lt;/strong&gt;列显示的是对象到达GC根的最短距离&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;展开一个总体行后，会显示所有的对象实例。没一个实例的直接占用内存和占用总内存都被相应显示。@符号后的数字不对象的唯一ID，有了它你就可以逐个对象的在不同快照间作对比。&lt;/p&gt;
     &lt;p&gt;例子：尝试这个      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/heap-profiling-summary"&gt;例子&lt;/a&gt;(在新tab标签中打开)来了解如何使用概要视图。&lt;/p&gt;
     &lt;p&gt;记住黄色的对象被JavaScript引用，而红色的对象是由黄色背景色引用被分离了的节点。&lt;/p&gt;
     &lt;h3&gt;Comparison view(对照视图)&lt;/h3&gt;
     &lt;p&gt;该视图用来对照不同的快照来找到快照之间的差异，来发现有内存泄漏的对象。来证明对应用的某个操作没有造成泄漏(比如：一般一对操作和撤消的动作，像找开一个document，然后关闭，这样是不会造成泄漏的)，你可以按以下的步骤尝试：&lt;/p&gt;
     &lt;ol&gt;
      &lt;li&gt;在操作前拍一个堆快照；&lt;/li&gt;
      &lt;li&gt;执行一个操作(做你认为会造成泄漏的动作)；&lt;/li&gt;
      &lt;li&gt;撤消之前的操作(上一个操作相反的操作，多重复几次)；&lt;/li&gt;
      &lt;li&gt;拍第二个快照，将视图切换成对照视图，并同快照1进行对比。&lt;/li&gt;
&lt;/ol&gt;
     &lt;p&gt;在对照视图下，两个快照之间的不同就会展现出来了。当展开一个总类目后，增加和删除了的对象就显示出来了：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/1ec85dff4875e7f2d8e0a56178d4f2a3.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/heap-profiling-comparison"&gt;例子&lt;/a&gt;(在新tab标签中打开)来了解如何使用对照视图来定位内存泄漏。&lt;/p&gt;
     &lt;h3&gt;Containment view(控制视图)&lt;/h3&gt;
     &lt;p&gt;控制视图可以称作对你的应用的对象结构的”鸟瞰视图(bird’s eys view)”。它能让你查看function内部，跟你的JavaScript对象一样的观察VM内部对象，能让你在你的应用的非常低层的内存使用情况。&lt;/p&gt;
     &lt;p&gt;该视图提供了几个进入点：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;DOMWindow 对象&lt;/strong&gt; - 这些对象是JavaScript代码的”全局”对象；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;GC根&lt;/strong&gt; - VM的垃圾回收器真正的GC根；&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;Native对象&lt;/strong&gt; - 浏览器对象对”推入”JavaScript虚拟机中来进行自动操作，如：DOM节点，CSS规则(下一节会有详细介绍。)&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;下图是一个典型的控制视图：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/5c97748e4e854d6edbd63399930a3b0d.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/heap-profiling-containment"&gt;例子&lt;/a&gt;(在新tab标签中打开)来了解如何使用控制视图来查看闭包内部和事件处理。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;关于闭包的建议&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;给函数命名对你在快照中的闭包函数间作出区分会很用帮助。如：下面的例子中没有给函数命名：&lt;/p&gt;
     &lt;pre&gt;function createLargeClosure() {
  var largeStr = new Array(1000000).join(’x’);

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}&lt;/pre&gt;
而下面这个有给函数命名：
     &lt;pre&gt;function createLargeClosure() {
  var largeStr = new Array(1000000).join(’x’);

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}&lt;/pre&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/e85205e4443a733894c6f71d96988292.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试这个例子      &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example7.html"&gt;why eval is evil&lt;/a&gt;来分析内存中闭包的影响。你可能也对尝试下面这个例子，记录      &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example8.html"&gt;heap allocations(堆分配)&lt;/a&gt;有兴趣。&lt;/p&gt;
     &lt;h3&gt;揭露DOM内存泄漏&lt;/h3&gt;
     &lt;p&gt;这个工具独一无二的一点是展示了浏览器原生对象(DOM节点，CSS规则)和JavaScript对象之间的双向引用。这能帮助你发现因为忘记解除引用游离的DOM子节点而导致的难以发觉的内存泄漏。&lt;/p&gt;
     &lt;p&gt;DOM内存泄漏可能会超出你的想象。看下下面的例子 – #tree对象什么时候被GC呢？&lt;/p&gt;
     &lt;pre&gt;var select = document.querySelector;
  var treeRef = select(&amp;quot;#tree&amp;quot;);
  var leafRef = select(&amp;quot;#leaf&amp;quot;);
  var body = select(&amp;quot;body&amp;quot;);

  body.removeChild(treeRef);

  //#tree can’t be GC yet due to treeRef
  treeRef = null;

  //#tree can’t be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW can be #tree GC&lt;/pre&gt;
     &lt;p&gt;      &lt;code&gt;#leaf&lt;/code&gt;代表了对它的父节点的引用(parentNode)它递归引用到了      &lt;code&gt;#tree&lt;/code&gt;，所以，只有当leafRef被nullified后      &lt;code&gt;#tree&lt;/code&gt;代表的整个树结构才会被GC回收。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/339d23f72de9b36b9844e4ba10216f57.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试      &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example6.html"&gt;leaking DOM nodes&lt;/a&gt;来了解哪里DOM节点会内存泄漏并如何定位。你也可以看一下这个例子：      &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example9.html"&gt;DOM leaks being bigger than expected&lt;/a&gt;。&lt;/p&gt;
     &lt;p&gt;查看Gonzalo Ruiz de Villa的文章      &lt;a href="http://slid.es/gruizdevilla/memory"&gt;Finding and debugging memory leaks with the Chrome DevTools&lt;/a&gt;来阅读更多关于DOM内存泄漏和内存分析的基础。&lt;/p&gt;
     &lt;p&gt;原生对象在Summary和Containment视呼中更容易找到 – 有它们专门的类目：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/91a9a985f865acdb1fec0b5dad501a78.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试下这个      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/heap-profiling-dom-leaks"&gt;例子&lt;/a&gt;(在新tab标签中打开)来了解如何将DOM树分离。&lt;/p&gt;
     &lt;h3&gt;支配者视图(Dominators view)&lt;/h3&gt;
     &lt;p&gt;支配者视图显示了堆图的支配者树。支配者视图跟控制(Containment)视图很像，但是没有属性名。这是因为支配者可能会是一个没有直接引用的对象，就是说这个支配者树不是堆图的生成树。但这是个有用的视图能帮助我们很快的定位内存增长点。&lt;/p&gt;
     &lt;p&gt;注意：在Chrome Canary中，支配者视图能够在DevTools中的Settings &amp;gt; Show advanced heap snapshot properties 开启，重启DevTools生效。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/181c6faf28c37f53a7a32cf2bffd88be.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;例子：尝试这个      &lt;a href="https://developers.google.com/chrome-developer-tools/docs/heap-profiling-dominators"&gt;例子&lt;/a&gt;(在新tab标签中打开)来练习如何找到内存增长点。可以进一步尝试下一个例子      &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example10.html"&gt;retaining paths and dominators&lt;/a&gt;。&lt;/p&gt;
     &lt;h2&gt;对象分配跟踪器&lt;/h2&gt;
     &lt;p&gt;      &lt;strong&gt;对象跟踪器&lt;/strong&gt;整合了      &lt;a href="https://developer.chrome.com/devtools/docs/javascript-memory-profiling#heap_profiler"&gt;heap profiler&lt;/a&gt;的快照增量更新分析和Timeline面板的记录。跟其它工具一样，记录对象的堆配置需要启动记录，执行一系列操作，然后停止记录然后进行分析。&lt;/p&gt;
     &lt;p&gt;对象跟踪器不间断的记录堆快照(频率达到了每50毫秒！)，结束时记录最后一个快照。该堆分配分析器显示对象在哪被创建并定位它的保留路径。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/67f7f8dbdea7158123dc5f17ed471f18.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;开启并使用对象分析器&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;开始使用对象分析器： 1. 确认你使用的是最新版的      &lt;a href="https://www.google.com/intl/en/chrome/browser/canary.html"&gt;Chrome Canary&lt;/a&gt;。&lt;/p&gt;
     &lt;ol&gt;
      &lt;li&gt;打开DeveTools并点击齿轮图标(译者：没明白这步有什么用)。&lt;/li&gt;
      &lt;li&gt;现在，打开Profiler面板，你就能看到”Record Heap Allocations”的选项。&lt;/li&gt;
&lt;/ol&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/8fb25867f12926dbe0741a905be75714.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;上面的柱条表示在堆中生成的新对象。高度就对应了相应对象的大小，它的颜色表示了这个对象是否在最后拍的那个快照中还在：蓝色柱表示在timeline最后这个对象还在，灰色柱表示这个对象在timeline中生成，但结束前已经被内存回收了。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/2c91b35ba916b34162f031e0db116b74.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;上面的例子中，一个动作执行了10次。同一个程序保留了5个对象，所以最后5个蓝色柱条被保留了。但这最后留下的柱存在潜在的问题。你可以用timeline上的滑动条缩小到那个特定的快照并找到这个分配的对象。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/01fd194cac0590529df4941ee715eae6.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;点击一个堆中的对象就能在堆快照的下面部分显示它的保留总内存树。检查这个对象的保留总内存树能够给你足够的信息来了解为什么这个对象没有被回收，然后你就能对代码做相应的修改来去掉不必要的引用。&lt;/p&gt;
     &lt;h2&gt;内存分析FAQ&lt;/h2&gt;
     &lt;p&gt;      &lt;strong&gt;问：我不能看到对象的所有属性，我也看到它们的非字符串值！为什么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;并非所有属性都完整的保存在JavaScript堆中。其中有些是通过执行原生代码的getters方法来获取的。这些属性没有在堆快照中捕获，是为了防止对getters方法的调用和避免程序状态的改变，如果这些getters方法不是”纯(pure)”的functions。同样，非字符串的值，如数字，没有被捕获是为了减少快照的大小。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：&lt;/strong&gt;@      &lt;strong&gt;符号后面的数字是什么意思 – 是地址还是ID呢？这个ID值真的是唯一的么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;这是对象ID。显示对象的地址没有意义，因为一个对象会在垃圾回收的时候被移除。这些对象IDs是真正的IDs – 就是说，它们在不同的快照间是唯一表示的。这样就可以的堆状态间进行精确的对比。维持这些IDs会给GC流程增加额外的开支，但这仅在记录第一次堆快照时分配 – 如果堆分析仪没有用到，就不会有额外的开支。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：”死”(无法引用到的)对象被包含在快照中了么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;没有，只有可以引用到的对象才会显示在快照中。而且，拍快照前都会先自动执行GC操作。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;注意：&lt;/strong&gt;在写这篇文章的时候，我们计划在拍快照的时候不再GC，防止堆尺寸的减少。现在已经是这样了，但垃圾对象依然显示在快照之外。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：GC根是由什么组成的？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;由很多部分组成：&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;原生对象图；&lt;/li&gt;
      &lt;li&gt;符号表；&lt;/li&gt;
      &lt;li&gt;VM线程中的栈；&lt;/li&gt;
      &lt;li&gt;编辑缓存；&lt;/li&gt;
      &lt;li&gt;控制器上下文；&lt;/li&gt;
      &lt;li&gt;全局控制器。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/b80670c200b97fd33db401755ede3618.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：我得知可以使用Heap Profiler和Timeline Memory view来检测内存泄漏。但我应该先用哪个工具呢？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;Timeline面版，是在你第一次使用你的页面发现速度变慢了时用来论断过多的内存使用。网站变慢是比较典型的内存泄漏的信号，但也可能是其它的原因 – 可能是有渲染或网络传输方面的瓶颈，所以要确保解决你网页的真正问题。&lt;/p&gt;
     &lt;p&gt;论断是否是内存问题，就打开Timeline面板和Memory标签。点击record按钮，然后在你的应用上重复几次你认为可能导致内存泄漏的操作。停止记录。你应用的内存使用图就生成出来了。如果内存的使用一直在增长(而没有相应的下降)，这就表明你的应用可能有内存泄漏了。&lt;/p&gt;
     &lt;p&gt;一般一个正常的应用的内存使用图形是锯齿状的，因为内存使用后又会被垃圾回收器回收。不用担心这种锯齿形 – 因为总是会因为JavaScript而有内存的消耗，甚至一个空的      &lt;code&gt;requestAnimationFrame&lt;/code&gt;也会造成这种锯齿形，这是无法避免的。只要不是那种分配了持续很多内存的形状，那就表明生成了很多内存垃圾。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/1834e2cc26b4a278d36fd6a194a24069.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;上图的增长线是需要你警惕的。在诊断分析的时候Memory标签中的DOM node counter，Document counter和Event listener count也是很有用的。DOM节点数是使用的原生内存不会影响JavaScript内存图。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/c89453972a71ffa67fd893faf7147f72.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;一旦你确认你的应用有内存泄漏，堆分析仪就可以用来找到内存泄漏的地方。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：我发现堆快照中有的DOM节点的数字是用红色标记为”Detached DOM tree”，而其它的是黄色的，这是什么意思呢？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;你会发现有不同的颜色。红色的节点(有着深色的背景)没有从JavaScript到它们的直接的引用，但它们是分离出来的DOM结构的一部分，所以他们还是在内存中保留了。有可能有一个节点被JavaScript引用到了(可能是在闭包中或者一个变量)，这个引用会阻止整个DOM树被内存回收。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/5e58b46093afb0524e60edf91fad82c5.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;黄色节点(黄色背景)有JavaScript的直接引用。在同一个分离的DOM树中查看一个黄色的节点来定位你的JavaScript的引用。就可能看到从DOM window到那个节点的属性引用链(如：      &lt;code&gt;window.foo.bar[2].baz&lt;/code&gt;)。&lt;/p&gt;
     &lt;p&gt;下面的动态图显示了分离节点的处理过程：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/b0e13ff07f7c2a4cab23d59bebe7b0e9.gif"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;例子&lt;/strong&gt;：尝试这个例子      &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example4.html"&gt;detached nodes&lt;/a&gt;你可以查看节点在Timeline中的生命周期，然后拍堆快照来找到分离的节点。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：直接占用内存(Shallow Size)和占用总内存(Retained Size)分别代表什么，它们的区别是什么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;是这样的，对象可以在内存中以两种方式存在(be alive) – 直接的被别一个可访问的(alive)对象保留(window和document对象总是可访问的)或被原生对象(象DOM对象)隐含的包留引用。后一种方式会因为阻止对象被GC自动回收，而有导制内存泄泥漏的可能。对象自身占用的内存被称为直接占用内存(通常来说，数组和字符串会保留更多的直接占用内存(shallow size))。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/c8dd2b51ce6f93e4e1165a2f9364227d.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;一个任意大小的对象可以通过阻止其它对象内存被回收在保留很大的内存使用。当一个对象被删除后(它造成的一些依赖就无法被引用了)能够释放的内存的大小被称有占用总内存(retained size)。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：constructor和retained字段下有很多的数据。我应该从哪开始调查我是的否遇到了内存泄漏呢？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;一般来说最好是从通过retainers排序的第一个对象开始，retainers之间是通过距离排序的(是指到window对象的距离)。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/9fa14501c5d9debbb0fdbc17e54be2ad.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;距离最短的对象有可能是首选的可能导致内存泄漏的对象。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：Summary, Comparison, Dominators 和 Containment这些视图之间的不同是什么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;你可以通过切换视图来体验它们的区别。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/ed3d9ff0b68a142203255515b6124c7b.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;Summary(概要)视图能帮你通过构造函数分组寻找对象(和对象的内存使用)。该视图对找出DOM内存泄漏很有帮助。&lt;/li&gt;
      &lt;li&gt;Comparison(对照)视图能够通过显示哪些对象内存被正确的回收了来搜寻内存泄漏。通常在一个操作前后记录两个(或更多)的内存使用快照。它是通过察看释放的内存和引用数目的差导来察看是否有内存泄漏，并找到原因。&lt;/li&gt;
      &lt;li&gt;Containment(控制)视图对对象结构有更好的展示，帮助我们分析全局作用域(如 window)中对象引用情况来找到是什么保留了这些对象。它能让你分析闭包并深入到对象更深层去查看。&lt;/li&gt;
      &lt;li&gt;Dominators(支配者)视图能用来帮助我们确认没有多余的对象还挂在某个位置(如那些被引用了的)，和确认对象的删除/垃圾回收真正起了作用。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;      &lt;strong&gt;问：堆分析仪中的constructor(一组)内容代表什么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/bc6499d6c9ce44c82d445b006ea5a182.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;strong&gt;(global property)&lt;/strong&gt; - 全局对象(像 ‘window’)和引用它的对象之间的中间对象。如果一个对象由构造函数Person生成并被全局对象引用，那么引用路径就是这样的：[global] &amp;gt; (global property) &amp;gt; Person。这跟一般的直接引用彼此的对象不一样。我们用中间对象是有性能方面的原因，全局对象改变会很频繁，非全局变量的属性访问优化对全局变量来说并不适用。&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;(roots)&lt;/strong&gt; - constructor中roots的内容引用它所选中的对象。它们也可以是由引擎自主创建的一些引用。这个引擎有用于引用对象的缓存，但是这些引用不会阻止引用对象被回收，所以它们不是真正的强引用(FIXME)。&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;&lt;/strong&gt;       &lt;strong&gt;(closure)&lt;/strong&gt; - 一些函数闭包中的一组对象的引用&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;(array, string, number, regexp)&lt;/strong&gt; - 一组属性引用了Array,String,Number或正则表达式的对象类型&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;(compiled code)&lt;/strong&gt; - 简单来说，所有东西都与compoled code有关。Script像一个函数，但其实对应了&amp;lt;script&amp;gt;的内容。SharedFunctionInfos (SFI)是函数和compiled code之间的对象。函数通常有内容，而SFIS没有(FIXME)。&lt;/li&gt;
      &lt;li&gt;       &lt;strong&gt;&lt;/strong&gt;       &lt;strong&gt;HTMLDivElement&lt;/strong&gt;,        &lt;strong&gt;HTMLAnchorElement&lt;/strong&gt;,        &lt;strong&gt;DocumentFragment&lt;/strong&gt; 等 – 你代码中对elements或document对象的引用。&lt;/li&gt;
&lt;/ul&gt;
     &lt;p&gt;在你的程序的生命周期中生成的很多其它的对象，包括事件监听器或自定义对象，可以在下面的controllers中找到：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/788934b5291597b0e46bf3152866efa6.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;问：我在做内存分析时需要关闭Chrome里可能会产生影响的什么功能么？&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;我们建议在用Chrome DevTools做内存分析时，你可以使用关闭所有扩展功能的隐身模式，或      &lt;a href="http://www.chromium.org/developers/how-tos/run-chromium-with-flags"&gt;设置&lt;/a&gt;用户文件夹为(      &lt;code&gt;--user-data-dir=&amp;quot;&amp;quot;&lt;/code&gt;)后再打开Chrome。&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="" src="http://static.codeceo.com/images/2015/03/f25bfcc44392b68f03b71ddb191707c9.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
     &lt;p&gt;应用，扩展甚至console中的记录都会对你的分析有潜在的影响，如果你想让你的分析可靠的话，禁用这些吧。&lt;/p&gt;
     &lt;p&gt;      &lt;strong&gt;写在最后的话&lt;/strong&gt;&lt;/p&gt;
     &lt;p&gt;今天的JavaScript引擎已经具有很强的能力，能够自动回收代码产生的内存垃圾。就是说，它们只能做到这样了，但我们的应用仍然被证明会因为逻辑错误而产生内存泄漏。使用相应的工具来找到应用的瓶颈，记住，不要靠猜 – 测试它。&lt;/p&gt;
     &lt;h2&gt;帮助实例&lt;/h2&gt;
     &lt;h3&gt;诊断内存泄漏&lt;/h3&gt;
     &lt;p&gt;尽管很多内容在本文章中已经提到了，但一系列测试内存相关的问题的例子还是很有用的，下面是一组DOM节点内存泄漏的例子。你可能希望在测试你的更复杂的页面或应用前先用这些例子做试验。&lt;/p&gt;
     &lt;ul&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example1.html"&gt;Example 1: Growing memory&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example2.html"&gt;Example 2: Garbage collection in action&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example3.html"&gt;Example 3: Scattered objects&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example4.html"&gt;Example 4: Detached nodes&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example5.html"&gt;Example 5: Memory and hidden classes&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example6.html"&gt;Example 6: Leaking DOM nodes&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example7.html"&gt;Example 7: Eval is evil (almost always)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example8.html"&gt;Example 8: Recording heap allocations&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example9.html"&gt;Example 9: DOM leaks bigger than expected&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example10.html"&gt;Example 10: Retaining path&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;       &lt;a href="https://developer.chrome.com/devtools/docs/demos/memory/example11.html"&gt;Example 11: Last exercise&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>General</category>
      <guid isPermaLink="true">https://itindex.net/detail/52929-chrome-%E5%BC%80%E5%8F%91-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Fri, 13 Mar 2015 08:00:04 CST</pubDate>
    </item>
    <item>
      <title>原生体验挡不住！JavaScript开源跨平台框架NativeScript</title>
      <link>https://itindex.net/detail/52906-%E4%BD%93%E9%AA%8C-%E6%8C%A1%E4%B8%8D%E4%BD%8F-javascript</link>
      <description>&lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/news/66372/201503/101627378397389.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;NativeScript 是一款使用 JavaScript 语言来构建跨平台原生移动应用的开源框架，支持 iOS、Android 和 Windows Phone。且 NativeScript 的使用没有过多繁杂的要求，只需使用自己已经掌握的 JavaScript 和 CSS 技能就能开发出真正具有原生用户体验的移动应用。&lt;/p&gt;
 &lt;p&gt;作为免费开源项目的 NativeScript，它的源码已经托管至 Github 上，让开发者可以没有任何门槛约束的随意使用。除了无需学习新的编程语言，使用大家所熟识的 JavaScript 编码及 CSS 打造应用样式外，NativeScript 的强大之处还有使用跨平台的 UI 抽象、共享业务逻辑和第三方原生库。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;100% 访问原生平台 API：&lt;/strong&gt;整个原生平台功能在 JavaScript 层都是可用的。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/news/66372/201503/101627378241619.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于 ECMAScript5+JavaScript 和 CSS 标准：&lt;/strong&gt;使用现有的技能构建原生应用。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/news/66372/201503/101627378084848.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;100% 共享代码：&lt;/strong&gt;基于一次编码，构建 Android、iOS 和 Windows Phone 应用。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/news/66372/201503/101627382309676.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;使用 NativeScript 开发的应用展示：&lt;/p&gt;
 &lt;p&gt;Amroo：&lt;/p&gt;
 &lt;p&gt;Amroo 是一款专为养宠物的人员打造的应用，当你要出差或是度假时，可以通过这款应用找到可以帮你照看宠物的人。简单直观的界面设计与功能，都是通过 NativeScript 实现的，你也可以试试。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/news/66372/201503/101627384021962.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Tasks：&lt;/p&gt;
 &lt;p&gt;Tasks 是用于规划和监督事情进程的任务管理应用，它的构建当然也是来自 NativeScript 技术。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/news/66372/201503/101627384959990.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;传送门：  &lt;a href="https://www.nativescript.org/" target="_blank"&gt;NativeScript 官网&lt;/a&gt;、  &lt;a href="https://github.com/NativeScript" target="_blank"&gt;Github 托管地址&lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;a href="http://news.cnblogs.com/n/516560/" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt; &lt;img alt="" height="1" src="http://news.cnblogs.com/news/rssclick.aspx?id=516560" width="1"&gt;&lt;/img&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/52906-%E4%BD%93%E9%AA%8C-%E6%8C%A1%E4%B8%8D%E4%BD%8F-javascript</guid>
      <pubDate>Tue, 10 Mar 2015 16:27:09 CST</pubDate>
    </item>
  </channel>
</rss>

