<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://louyi.xyz/feed.xml" rel="self" type="application/atom+xml" /><link href="http://louyi.xyz/" rel="alternate" type="text/html" /><updated>2026-10-20T09:52:36+00:00</updated><id>http://louyi.xyz/feed.xml</id><title type="html">Hello JavaScript</title><subtitle>Hello Man</subtitle><author><name>xiedajian</name></author><entry><title type="html">绘制人物关系图谱的npm包</title><link href="http://louyi.xyz/2019/03/14/relation-chart/" rel="alternate" type="text/html" title="绘制人物关系图谱的npm包" /><published>2019-03-14T00:00:00+00:00</published><updated>2019-03-14T00:00:00+00:00</updated><id>http://louyi.xyz/2019/03/14/relation-chart</id><content type="html" xml:base="http://louyi.xyz/2019/03/14/relation-chart/"><![CDATA[<p><a href="https://www.jianshu.com/p/cd618edc11a8">旧版本的文章传送门</a></p>

<h1 id="relation-chart"><a href="https://github.com/xiedajian/relation-chart">relation-chart</a></h1>

<p>人物关系图生成器</p>

<hr />

<p><a href="https://xiedajian.github.io/relation-chart/examples/demo1/index.html">效果预览链接</a></p>

<p>可进行拖拽，缩放</p>

<p><a href="https://xiedajian.github.io/relation-chart/examples/demo1/index.html"><img src="https://upload-images.jianshu.io/upload_images/4263048-65b044b1cd05e821.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="demo.jpg" /></a></p>

<p>高亮选中的节点</p>

<p><a href="https://xiedajian.github.io/relation-chart/examples/demo1/index.html"><img src="https://upload-images.jianshu.io/upload_images/4263048-9ee2d8a51da074bf.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="高亮选中" /></a></p>

<h1 id="install">install</h1>

<p>npm:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install relation-chart --save
</code></pre></div></div>

<h1 id="引用">引用</h1>
<p>ES6:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">RelationChart</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">relation-chart</span><span class="dl">'</span>
</code></pre></div></div>

<p>Script tag:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;script src="/node_modules/relation-charts/dist/index.js"&gt;&lt;/script&gt;
</code></pre></div></div>

<h1 id="api">API</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>new RelationChart(domElement, data[, config])
</code></pre></div></div>
<p>参数说明：</p>
<ul>
  <li>domElement：容器dom元素</li>
  <li>data：人物关系图的数据，格式见下文</li>
  <li>config：自定义配置，可省略，默认详细配置见下文</li>
</ul>

<h1 id="数据格式">数据格式</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
    // 节点列表
    nodes:[
        {
            "name": "路人甲",
            "avatar": "./img/140646844806.jpg"
        },
        {
            "name": "路人乙",
            "avatar": "./img/141611471224.jpg"
        },
        {
            "name": "路人丙",
            "avatar": "./img/140848800133.jpg"
        },
    ],
    // 线条列表
    links:[
        {
            "source": 0,            // 起始节点在 nodes[] 中的索引
            "target": 1,            // 目标节点在 nodes[] 中的索引
            "relation": "朋友",      // 关系名称
            "color": "734646"       // 自定义细条颜色,#734646
        },
        {
            "source": 1,
            "target": 2,
            "relation": "女朋友",
            "color": "734646"
        },
    ],
}
</code></pre></div></div>

<h1 id="config">config</h1>

<p>默认配置</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">defaultConfig</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">width</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span>                 <span class="c1">// 总画布svg的宽,单位为px,默认为容器的宽</span>
    <span class="na">height</span><span class="p">:</span> <span class="mi">800</span><span class="p">,</span>                <span class="c1">// 高，默认为容器的高</span>
    <span class="na">isHighLight</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>        <span class="c1">// 是否启动 鼠标 hover 到节点上高亮与节点有关的节点，其他无关节点透明的功能</span>
    <span class="na">isScale</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>              <span class="c1">// 是否启用缩放平移zoom功能</span>
    <span class="na">scaleExtent</span><span class="p">:</span> <span class="p">[</span><span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">],</span>    <span class="c1">// 缩放的比例尺</span>
    <span class="na">chargeStrength</span><span class="p">:</span> <span class="o">-</span><span class="mi">300</span><span class="p">,</span>        <span class="c1">// 万有引力</span>
    <span class="na">collide</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>                 <span class="c1">// 碰撞力的大小 （节点之间的间距）</span>
    <span class="na">nodeWidth</span><span class="p">:</span> <span class="mi">160</span><span class="p">,</span>             <span class="c1">// 每个node节点所占的宽度，正方形</span>
    <span class="na">margin</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>                 <span class="c1">// node节点距离父亲div的margin</span>
    <span class="na">alphaDecay</span><span class="p">:</span> <span class="mf">0.0228</span><span class="p">,</span>          <span class="c1">// 控制力学模拟衰减率</span>
    <span class="na">r</span><span class="p">:</span> <span class="mi">45</span><span class="p">,</span>                      <span class="c1">// 头像的半径 [30 - 45]</span>
    <span class="na">relFontSize</span><span class="p">:</span> <span class="mi">12</span><span class="p">,</span>           <span class="c1">// 关系文字字体大小</span>
    <span class="na">linkSrc</span><span class="p">:</span> <span class="mi">30</span><span class="p">,</span>                <span class="c1">// 划线时候的弧度</span>
    <span class="na">linkColor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#bad4ed</span><span class="dl">'</span><span class="p">,</span>        <span class="c1">// 链接线默认的颜色</span>
    <span class="na">strokeColor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#7ecef4</span><span class="dl">'</span><span class="p">,</span>     <span class="c1">// 头像外围包裹的颜色</span>
    <span class="na">strokeWidth</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>             <span class="c1">// 头像外围包裹的宽度</span>
<span class="p">}</span>
</code></pre></div></div>
<p>自定义配置会覆盖默认配置</p>

<h1 id="example">example</h1>

<p>html 有个承载 svg 图的容器</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"map"</span> <span class="na">style=</span><span class="s">"width: 1000px;height: 800px"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">RelationChart</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">relation-chart</span><span class="dl">'</span>

<span class="c1">// 容器</span>
<span class="kd">let</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">#map</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// 关系图数据</span>

<span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">nodes</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span>
            <span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">路人甲</span><span class="dl">"</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">avatar</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">./img/140646844806.jpg</span><span class="dl">"</span>
        <span class="p">},</span>
        <span class="p">{</span>
            <span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">路人乙</span><span class="dl">"</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">avatar</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">./img/141611471224.jpg</span><span class="dl">"</span>
        <span class="p">},</span>
        <span class="p">{</span>
            <span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">路人丙</span><span class="dl">"</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">avatar</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">./img/140848800133.jpg</span><span class="dl">"</span>
        <span class="p">},</span>
    <span class="p">],</span>
    <span class="na">links</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span>
            <span class="dl">"</span><span class="s2">source</span><span class="dl">"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">target</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">relation</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">朋友</span><span class="dl">"</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">color</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">734646</span><span class="dl">"</span>
        <span class="p">},</span>
        <span class="p">{</span>
            <span class="dl">"</span><span class="s2">source</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">target</span><span class="dl">"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">relation</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">女朋友</span><span class="dl">"</span><span class="p">,</span>
            <span class="dl">"</span><span class="s2">color</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">734646</span><span class="dl">"</span>
        <span class="p">},</span>
    <span class="p">],</span>
<span class="p">}</span>
 
<span class="c1">// 创建人物关系图 svg</span>
<span class="k">new</span> <span class="nx">RelationChart</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span>
</code></pre></div></div>

<h1 id="vue-中使用案例">vue 中使用案例</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"page"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"map"</span> <span class="na">class=</span><span class="s">"map"</span> <span class="na">ref=</span><span class="s">"map"</span> <span class="na">style=</span><span class="s">"width: 1000px;height: 1000px;"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/template&gt;</span>
</code></pre></div></div>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span>
<span class="k">import</span> <span class="nx">RelationChart</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">relation-chart</span><span class="dl">'</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">data</span> <span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{}</span>
  <span class="p">},</span>
  <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">new</span> <span class="nx">RelationChart</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">map</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span>
  <span class="p">},</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p><a href="https://github.com/xiedajian/relation-chart">欢迎 star ❤❤❤</a></p>]]></content><author><name>xiedajian</name></author><category term="d3.js" /><summary type="html"><![CDATA[绘制人物关系图谱的npm包]]></summary></entry><entry><title type="html">JS代替ajax向服务端请求的新方案fetch</title><link href="http://louyi.xyz/2019/03/10/js-fetch/" rel="alternate" type="text/html" title="JS代替ajax向服务端请求的新方案fetch" /><published>2019-03-10T00:00:00+00:00</published><updated>2019-03-10T00:00:00+00:00</updated><id>http://louyi.xyz/2019/03/10/js-fetch</id><content type="html" xml:base="http://louyi.xyz/2019/03/10/js-fetch/"><![CDATA[<p>文档： https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API
fetch垫片： https://github.com/github/fetch</p>

<h1 id="fetch">fetch</h1>

<p>跨网络异步获取资源的功能以前是使用XMLHttpRequest对象实现的，Fetch API提供了更好的替代方案，可以很容易的被其他技术使用（如Servise Workers）</p>

<p>fetch，说白了，就是XMLHttpRequest的一种替代方案。如果有人问你，除了Ajax获取后台数据之外，还有没有其他的替代方案？</p>

<p>你就可以回答，除了XMLHttpRequest对象来获取后台的数据之外，还可以使用一种更优的解决方案fetch。</p>

<p><em>fetch的支持性还不是很好，但是在谷歌浏览器中已经支持了fetch</em></p>

<p>兼容性查看：https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API</p>

<h1 id="返回promise">返回promise</h1>

<p>Fetch API提供了一个全局的fetch()方法，该方法会返回一个Promise</p>

<p>当fetch请求接收到一个代表错误的状态码时（如404、500），返回的Promise不会被标记为reject，而是被标记为resolve，但是会将response的ok属性设置为false。只有当网络错误或请求被阻止时才会被标记为reject状态</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('https://127.0.0.1/125.jpg').then(function(res){
  if(res.ok) {
    return res.blob();
  }else {
    console.log('服务器响应出错了'); // 资源404、服务器500等
  }
}).catch(function(err){
  console.log('Network response was not ok.'); // 网络出错
})
</code></pre></div></div>

<h1 id="fetch-方法的两个参数">fetch() 方法的两个参数</h1>

<p>fetch()方法接收两个参数：第一个参数表示要获取的资源路径；第二个参数表示请求的配置项（可选）</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('https://127.0.0.1/api/articles/1/3').then(function(res){
  if(res.ok) {
    return res.json();
  }
})

// 定义第二个参数
fetch('https://127.0.0.1/api/articles/1/3', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    token:'token'
  },
  cache: 'default',
  mode: 'cors',
}).then(function(res){
  if(res.ok) {
    return res.json();
  }
})
</code></pre></div></div>

<h1 id="设置请求的头信息">设置请求的头信息</h1>

<p>在POST提交的过程中，一般是表单提交，可是，经过查询，发现默认的提交方式是：Content-Type:text/plain;charset=UTF-8，这个显然是不合理的,改为application/x-www-form-urlencoded</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('https://www.baidu.com/search/error.html', {
    method: 'POST',
    headers: new Headers({
      'Content-Type': 'application/x-www-form-urlencoded' // 指定提交方式为表单提交
    }),
    body: new URLSearchParams([["foo", 1],["bar", 2]]).toString()
  })
  .then((res)=&gt;{
    return res.text()
  })
  .then((res)=&gt;{
    console.log(res)
  })
</code></pre></div></div>

<h1 id="默认不使用-cookie">默认不使用 cookie</h1>

<p>默认情况下, fetch 不会从服务端发送或接收任何 cookies，要发送 cookies，必须设置 credentials 选项</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('http://127.0.0.1/search/name', {
    method: 'GET',
    credentials: 'include' // 强制加入凭据头
  })
  .then((res)=&gt;{
    return res.text()
  })
</code></pre></div></div>

<h1 id="get请求及传参">GET请求及传参</h1>

<p>GET请求中如果需要传递参数怎么办？这个时候，只能把参数写在URL上来进行传递了。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('http://127.0.0.1/search?a=1&amp;b=2', { // 在URL中写上传递的参数
    method: 'GET'
  })
  .then((res)=&gt;{
    return res.text()
  })
</code></pre></div></div>

<h1 id="post-请求及传参">POST 请求及传参</h1>

<p>POST请求的参数，放在第二个参数的body属性中</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('http://127.0.0.1/searchs', { 
     method: 'POST',
    body: new URLSearchParams([["foo", 1],["bar", 2]]).toString() // 这里是请求对象
  })
  .then((res)=&gt;{
    return res.text()
  })
</code></pre></div></div>

<p>POST提交改为application/x-www-form-urlencoded</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('http://127.0.0.1/searchs', { 
    method: 'POST',
    headers: new Headers({
      'Content-Type': 'application/x-www-form-urlencoded' // 指定提交方式为表单提交
    }),
    body: new URLSearchParams([["foo", 1],["bar", 2]]).toString() // 这里是请求对象
  })
  .then((res)=&gt;{
    return res.text()
  })
</code></pre></div></div>

<p>通过接口得到JSON数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fetch('http://127.0.0.1/searchs', { 
    method: 'POST',
    headers: new Headers({
      'Content-Type': 'application/x-www-form-urlencoded' // 指定提交方式为表单提交
      'Accept': 'application/json' // 通过头指定，获取的数据类型是JSON
    }),
    body: new URLSearchParams([["foo", 1],["bar", 2]]).toString() // 这里是请求对象
  })
  .then((res)=&gt;{
    return res.text()
  })
</code></pre></div></div>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS代替ajax向服务端请求的新方案fetch]]></summary></entry><entry><title type="html">JS中websocket双向通讯</title><link href="http://louyi.xyz/2019/03/04/websocket/" rel="alternate" type="text/html" title="JS中websocket双向通讯" /><published>2019-03-04T00:00:00+00:00</published><updated>2019-03-04T00:00:00+00:00</updated><id>http://louyi.xyz/2019/03/04/websocket</id><content type="html" xml:base="http://louyi.xyz/2019/03/04/websocket/"><![CDATA[<h1 id="websocket">webSocket</h1>

<p>WebSocket 是一种网络通信协议</p>

<p>WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议</p>

<p>WebSocket与http协议一样都是基于TCP的，所以他们都是可靠的协议，Web开发者调用的WebSocket的send函数在browser 的实现中最终都是通过TCP的系统接口进行传输的。WebSocket和Http协议一样都属于应用层的协议</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	var ws = new WebSocket("wss://echo.websocket.org");

	ws.onopen = function(evt) { 
	  console.log("Connection open ..."); 
	  ws.send("Hello WebSockets!");
	};

	ws.onmessage = function(evt) {
	  console.log( "Received Message: " + evt.data);
	  ws.close();
	};

	ws.onclose = function(evt) {
	  console.log("Connection closed.");
	};      

</code></pre></div></div>

<h3 id="websocketreadystate">webSocket.readyState</h3>

<p>readyState属性返回实例对象的当前状态，共有四种。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	CONNECTING：值为0，表示正在连接。
	OPEN：值为1，表示连接成功，可以通信了。
	CLOSING：值为2，表示连接正在关闭。
	CLOSED：值为3，表示连接已经关闭，或者打开连接失败。
</code></pre></div></div>
<p>下面是一个示例。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	switch (ws.readyState) {
	  case WebSocket.CONNECTING:
	    // do something
	    break;
	  case WebSocket.OPEN:
	    // do something
	    break;
	  case WebSocket.CLOSING:
	    // do something
	    break;
	  case WebSocket.CLOSED:
	    // do something
	    break;
	  default:
	    // this never happens
	    break;
	}
</code></pre></div></div>

<h3 id="websocketonopen">webSocket.onopen</h3>
<p>实例对象的onopen属性，用于指定连接成功后的回调函数。</p>

<p>ws.onopen = function () {
  ws.send(‘Hello Server!’);
}
如果要指定多个回调函数，可以使用addEventListener方法。</p>

<p>ws.addEventListener(‘open’, function (event) {
  ws.send(‘Hello Server!’);
});</p>

<h3 id="websocketonclose">webSocket.onclose</h3>
<p>实例对象的onclose属性，用于指定连接关闭后的回调函数。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	ws.onclose = function(event) {
	  var code = event.code;
	  var reason = event.reason;
	  var wasClean = event.wasClean;
	  // handle close event
	};

	ws.addEventListener("close", function(event) {
	  var code = event.code;
	  var reason = event.reason;
	  var wasClean = event.wasClean;
	  // handle close event
	});
</code></pre></div></div>

<h3 id="websocketonmessage">webSocket.onmessage</h3>
<p>实例对象的onmessage属性，用于指定收到服务器数据后的回调函数。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	ws.onmessage = function(event) {
	  var data = event.data;
	  // 处理数据
	};

	ws.addEventListener("message", function(event) {
	  var data = event.data;
	  // 处理数据
	});
</code></pre></div></div>
<p>注意，服务器数据可能是文本，也可能是二进制数据（blob对象或Arraybuffer对象）。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	ws.onmessage = function(event){
	  if(typeof event.data === String) {
	    console.log("Received data string");
	  }

	  if(event.data instanceof ArrayBuffer){
	    var buffer = event.data;
	    console.log("Received arraybuffer");
	  }
	}
</code></pre></div></div>
<p>除了动态判断收到的数据类型，也可以使用binaryType属性，显式指定收到的二进制数据类型。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	// 收到的是 blob 数据
	ws.binaryType = "blob";
	ws.onmessage = function(e) {
	  console.log(e.data.size);
	};

	// 收到的是 ArrayBuffer 数据
	ws.binaryType = "arraybuffer";
	ws.onmessage = function(e) {
	  console.log(e.data.byteLength);
	};
</code></pre></div></div>

<h3 id="websocketsend">webSocket.send()</h3>
<p>实例对象的send()方法用于向服务器发送数据。</p>

<p>发送文本的例子。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	ws.send('your message');
</code></pre></div></div>

<p>发送 Blob 对象的例子。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	var file = document
	  .querySelector('input[type="file"]')
	  .files[0];
	ws.send(file);
</code></pre></div></div>

<p>发送 ArrayBuffer 对象的例子。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	// Sending canvas ImageData as ArrayBuffer
	var img = canvas_context.getImageData(0, 0, 400, 320);
	var binary = new Uint8Array(img.data.length);
	for (var i = 0; i &lt; img.data.length; i++) {
	  binary[i] = img.data[i];
	}
	ws.send(binary.buffer);
</code></pre></div></div>

<h3 id="websocketbufferedamount">webSocket.bufferedAmount</h3>

<p>实例对象的bufferedAmount属性，表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	var data = new ArrayBuffer(10000000);
	socket.send(data);

	if (socket.bufferedAmount === 0) {
	  // 发送完毕
	} else {
	  // 发送还没结束
	}
</code></pre></div></div>

<h3 id="websocketonerror">webSocket.onerror</h3>
<p>实例对象的onerror属性，用于指定报错时的回调函数。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	socket.onerror = function(event) {
	  // handle error event
	};

	socket.addEventListener("error", function(event) {
	  // handle error event
	});
</code></pre></div></div>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS中websocket双向通讯]]></summary></entry><entry><title type="html">JS中SSE(Server-Sent-event)服务器向浏览器推送信息</title><link href="http://louyi.xyz/2019/03/03/server-sent-event/" rel="alternate" type="text/html" title="JS中SSE(Server-Sent-event)服务器向浏览器推送信息" /><published>2019-03-03T00:00:00+00:00</published><updated>2019-03-03T00:00:00+00:00</updated><id>http://louyi.xyz/2019/03/03/server-sent-event</id><content type="html" xml:base="http://louyi.xyz/2019/03/03/server-sent-event/"><![CDATA[<h1 id="sse">SSE</h1>

<p>服务器向浏览器推送信息，除了 WebSocket，还有一种方法：Server-Sent Events（以下简称 SSE）。本文介绍它的用法。</p>

<h2 id="sse-的本质">SSE 的本质</h2>
<p>严格地说，HTTP 协议无法做到服务器主动推送信息。但是，有一种变通方法，就是服务器向客户端声明，接下来要发送的是流信息（streaming）。</p>

<p>也就是说，发送的不是一次性的数据包，而是一个数据流，会连续不断地发送过来。这时，客户端不会关闭连接，会一直等着服务器发过来的新的数据流，视频播放就是这样的例子。本质上，这种通信就是以流信息的方式，完成一次用时很长的下载。</p>

<p>SSE 就是利用这种机制，使用流信息向浏览器推送信息。它基于 HTTP 协议，目前除了 IE/Edge，其他浏览器都支持。</p>

<h3 id="sse-的特点">SSE 的特点</h3>
<p>SSE 与 WebSocket 作用相似，都是建立浏览器与服务器之间的通信渠道，然后服务器向浏览器推送信息。</p>

<p>总体来说，WebSocket 更强大和灵活。因为它是全双工通道，可以双向通信；SSE 是单向通道，只能服务器向浏览器发送，因为流信息本质上就是下载。如果浏览器向服务器发送信息，就变成了另一次 HTTP 请求。</p>

<p>但是，SSE 也有自己的优点。</p>

<p>SSE 使用 HTTP 协议，现有的服务器软件都支持。WebSocket 是一个独立协议。
SSE 属于轻量级，使用简单；WebSocket 协议相对复杂。
SSE 默认支持断线重连，WebSocket 需要自己实现。
SSE 一般只用来传送文本，二进制数据需要编码后传送，WebSocket 默认支持传送二进制数据。
SSE 支持自定义发送的消息类型。
因此，两者各有特点，适合不同的场合。</p>

<h2 id="三客户端-api">三、客户端 API</h2>
<h4 id="31-eventsource-对象">3.1 EventSource 对象</h4>
<p>SSE 的客户端 API 部署在EventSource对象上。下面的代码可以检测浏览器是否支持 SSE。</p>

<p>if (‘EventSource’ in window) {
  // …
}
使用 SSE 时，浏览器首先生成一个EventSource实例，向服务器发起连接。</p>

<p>var source = new EventSource(url);
上面的url可以与当前网址同域，也可以跨域。跨域时，可以指定第二个参数，打开withCredentials属性，表示是否一起发送 Cookie。</p>

<p>var source = new EventSource(url, { withCredentials: true });
EventSource实例的readyState属性，表明连接的当前状态。该属性只读，可以取以下值。</p>

<p>0：相当于常量EventSource.CONNECTING，表示连接还未建立，或者断线正在重连。
1：相当于常量EventSource.OPEN，表示连接已经建立，可以接受数据。
2：相当于常量EventSource.CLOSED，表示连接已断，且不会重连。</p>

<h4 id="32-基本用法">3.2 基本用法</h4>
<p>连接一旦建立，就会触发open事件，可以在onopen属性定义回调函数。</p>

<p>source.onopen = function (event) {
  // …
};</p>

<p>// 另一种写法
source.addEventListener(‘open’, function (event) {
  // …
}, false);
客户端收到服务器发来的数据，就会触发message事件，可以在onmessage属性的回调函数。</p>

<p>source.onmessage = function (event) {
  var data = event.data;
  // handle message
};</p>

<p>// 另一种写法
source.addEventListener(‘message’, function (event) {
  var data = event.data;
  // handle message
}, false);
上面代码中，事件对象的data属性就是服务器端传回的数据（文本格式）。</p>

<p>如果发生通信错误（比如连接中断），就会触发error事件，可以在onerror属性定义回调函数。</p>

<p>source.onerror = function (event) {
  // handle error event
};</p>

<p>// 另一种写法
source.addEventListener(‘error’, function (event) {
  // handle error event
}, false);
close方法用于关闭 SSE 连接。</p>

<p>source.close();</p>

<h4 id="33-自定义事件">3.3 自定义事件</h4>
<p>默认情况下，服务器发来的数据，总是触发浏览器EventSource实例的message事件。开发者还可以自定义 SSE 事件，这种情况下，发送回来的数据不会触发message事件。</p>

<p>source.addEventListener(‘foo’, function (event) {
  var data = event.data;
  // handle message
}, false);
上面代码中，浏览器对 SSE 的foo事件进行监听。如何实现服务器发送foo事件，请看下文。</p>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS中SSE(Server-Sent-event)服务器向浏览器推送信息]]></summary></entry><entry><title type="html">JS单例模式</title><link href="http://louyi.xyz/2019/03/02/js-danli/" rel="alternate" type="text/html" title="JS单例模式" /><published>2019-03-02T00:00:00+00:00</published><updated>2019-03-02T00:00:00+00:00</updated><id>http://louyi.xyz/2019/03/02/js-danli</id><content type="html" xml:base="http://louyi.xyz/2019/03/02/js-danli/"><![CDATA[<h1 id="单例模式">单例模式</h1>

<p>在面向对象语言中，调用一个类的方法之前，必须先将这个类实例化，才能调用类方法。</p>

<p>单例模式能使得我们不需要每次都需要实例化一次，因为我们使用的对象都是同一个对象。</p>

<p>单例模式：只允许实例化一次的对象类。</p>

<h1 id="js的单例模式">JS的单例模式</h1>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const log = console.log;

let Leader = (()=&gt;{
    let _instance = null;
    //一个待实例化的类
    function _module(){
        this.name = 'xxx';
        this.callLeader = ()=&gt;{
            return 'The Leader Is ' + this.name;
        }
        this.setLeader = (name) =&gt; {
            this.name = name;
        }
    }

    return {
        getInstance:()=&gt;{
            if(!_instance){
                _instance = new _module();
            }
            return _instance;
        }
    }
})();

</code></pre></div></div>

<p>Leader返回一个包含getInstance方法的对象，执行这个方法可以获得_module的实例。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let leader_01 = Leader.getInstance();
leader_01.setLeader('hhh');

let leader_02 = Leader.getInstance();
log(leader_02.callLeader())

log(leader_01 === leader_02);   //true

</code></pre></div></div>

<p>eader_01与leader_02完全相等，说明它们是同一个对象，并不是通过new新获取的对象。</p>

<p><em>但是在多页面情况下，通过import引入的并不是单例模式，因为</em></p>

<h2 id="用途">用途</h2>

<p>模态框弹窗的时候，不希望有两个弹窗</p>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS单例模式]]></summary></entry><entry><title type="html">JS工厂模式</title><link href="http://louyi.xyz/2019/03/01/js-gongchang/" rel="alternate" type="text/html" title="JS工厂模式" /><published>2019-03-01T00:00:00+00:00</published><updated>2019-03-01T00:00:00+00:00</updated><id>http://louyi.xyz/2019/03/01/js-gongchang</id><content type="html" xml:base="http://louyi.xyz/2019/03/01/js-gongchang/"><![CDATA[<h1 id="单例模式">单例模式</h1>

<p>单例模式解决了分组的问题，让每个对象有了自己独立的命名空间，但是不能批量生产，每一个新的对象都要重新写一份一模一样的代码。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var person1 = {
    name:'iceman',
    age:25,
    writeJs:function () {
        console.log(this.name + 'write js');
    }
};
person1.writeJs();

</code></pre></div></div>

<h1 id="工厂模式">工厂模式</h1>

<p>这时候就有了工厂模式</p>

<p>即：把实现同一事情的相同代码，放到一个函数中，以后如果再想实现这个功能，就不需要重新编写这些代码了，只要执行当前的函数即可，</p>

<p>这就是函数的封装，体现了高内聚、低耦合的思想：减少页面的中的冗余代码，提高代码的重复利用率：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function createPerson(name, age) {
    var obj = {};
    obj.name = name;
    obj.age = age;
    obj.writeJs = function () {
        console.log(this.name + 'write js');
    }
    return obj;
}

var p1 = createPerson('mengzhe' , 26);
p1.writeJs();

var p2 = createPerson('iceman' , 25);
p2.writeJs();

</code></pre></div></div>

<p>参考：https://www.jianshu.com/p/7b4d4318d533</p>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS工厂模式]]></summary></entry><entry><title type="html">JS实现函数重载</title><link href="http://louyi.xyz/2019/02/26/js-function-chongzai/" rel="alternate" type="text/html" title="JS实现函数重载" /><published>2019-02-26T00:00:00+00:00</published><updated>2019-02-26T00:00:00+00:00</updated><id>http://louyi.xyz/2019/02/26/js-function-chongzai</id><content type="html" xml:base="http://louyi.xyz/2019/02/26/js-function-chongzai/"><![CDATA[<h1 id="重载">重载</h1>

<p>做过后端的，可能有函数重载的概念</p>

<p>但是在JavaScript中不存在重载，如果方法名一样的话，后面的会把前面的覆盖掉，最后只保留一个方法的定义</p>

<p>不过我们可以根据传递的参数不一样，实现模拟重载的功能：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function sum(num) {
    if (typeof num === 'undefined') {
        return 0;
    }
    return num;
}
sum(100);
sum();

</code></pre></div></div>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS实现函数重载]]></summary></entry><entry><title type="html">JS自执行函数，匿名函数</title><link href="http://louyi.xyz/2019/02/25/js-zizhixing-function/" rel="alternate" type="text/html" title="JS自执行函数，匿名函数" /><published>2019-02-25T00:00:00+00:00</published><updated>2019-02-25T00:00:00+00:00</updated><id>http://louyi.xyz/2019/02/25/js-zizhixing-function</id><content type="html" xml:base="http://louyi.xyz/2019/02/25/js-zizhixing-function/"><![CDATA[<h1 id="自执行函数">自执行函数</h1>

<ol>
  <li>先来看个最简单的自执行函数
```
(function(){</li>
</ol>

<p>}());</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
相当于声明并调用

</code></pre></div></div>
<p>var b=function () {</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>} b() ``` 2. 自执行函数也可以有名字
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function b(){
    ...
}()
</code></pre></div></div>

<ol>
  <li>自执行函数也可以传参</li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function b(i){
    console.log(i)
}(5)
</code></pre></div></div>

<p>下面我们来总结一下自执行函数：</p>

<p>• 自执行函数在调用上与普通函数一样，可以匿名，可以传参。只不过是在声明的时候自调用了一次</p>

<p>• 自执行函数的写法有两种 , <em>推荐第二种写法</em></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    // 1.第一种方式： 两个()() ，function写在第一个()里面
    (function(){})()

    // 2.第二种方式： 一个() ,里面写 function(){}()
    (function(){}())
</code></pre></div></div>

<h1 id="匿名函数">匿名函数</h1>

<p>匿名函数就是没有名字的函数</p>

<p>匿名函数通常与自执行函数结合使用，因为匿名函数没有函数名，没办法调用，通过自执行调用</p>

<p>匿名函数的基本形式为</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function(){...})();
</code></pre></div></div>

<p>前面的括号包含函数体，后面的括号就是给匿名函数传递参数并立即执行之</p>

<p>匿名函数的作用是避免全局变量的污染以及函数名的冲突</p>

<h1 id="1小括号的作用">1.小括号的作用</h1>

<p>小括号能把我们的表达式组合分块，并且每一块，也就是每一对小括号，都有一个返回值。</p>

<p>这个返回值实际上也就是小括号中表达式的返回值。</p>

<p>所以，当我们用一对小括号把匿名函数括起来的时候，实际上小括号对返回的，就是一个匿名函数的Function 对象。</p>

<p>因此，小括号对加上匿名函数就如同有名字的函数般被我们取得它的引用位置了。</p>

<p>所以如果在这个引用变量后面再加上参数列表，就会实现普通函数的调用形式。</p>

<p>简单来说就是小括号有返回值，也就是小括号内的函数或者表达式的返回值，所以说小括号内的function返回值等于小括号的返回值</p>

<h1 id="2匿名函数的写法">2.匿名函数的写法</h1>

<p>方式1，调用函数，得到返回值。强制运算符使函数调用执行</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function(x,y){
    return x+y;
}(3,4)); 
</code></pre></div></div>

<p>方式2，调用函数，得到返回值。强制函数直接量执行再返回一个引用，引用再去调用执行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function(x,y){
    return x+y;
})(3,4); 
</code></pre></div></div>
<p>这种方式也是很多库爱用的调用方式，如jQuery，Mootools。</p>

<p>方式3，使用void</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>void function(x) {
    x = x-1;
}(9);
</code></pre></div></div>

<p>方式4，使用-/+运算符</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-function(x,y){
    return x+y;
}(3,4);

+function(x,y){
    return x+y;
}(3,4);

--function(x,y){
    return x+y;
}(3,4);

++function(x,y){
    return x+y;
}(3,4); 
</code></pre></div></div>

<p>方式5，使用波浪符(~)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~function(x, y) {
    return x+y;
}(3, 4); 
</code></pre></div></div>

<p>方式6，匿名函数执行放在中括号内</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[function(){
    console.log(this) // 浏览器得控制台输出window
}(this)] 
</code></pre></div></div>

<p>方式7，匿名函数前加typeof</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>typeof function(){
console.log(this) // 浏览器得控制台输出window
}(this) 
</code></pre></div></div>

<h1 id="匿名自执行函数">匿名自执行函数</h1>

<p>匿名自执行函数首先是一个匿名函数，但是这个函数是可以自己自动执行的，不需要借助其他的元素</p>

<p>匿名自执行函数的作用:</p>

<p>•  匿名自执行函数最常见的作用是用于实现闭包的情况中</p>

<p>•  匿名自执行函数还可以用于在js中模拟创建块级作用域</p>

<p>匿名函数</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function () {
   
    }
</code></pre></div></div>

<p>自执行</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function(){
          //代码
 })();
</code></pre></div></div>

<h2 id="匿名函数自执行-实现-异步函数递归">匿名函数自执行 实现 异步函数递归</h2>

<p>详情可参考 icode仓库的 js/异步递归调用.js</p>

<p>目标：在setTimeout异步函数中依次 打印 0 1 2 3 4</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function async(i) {
	
	if (i &gt;= 5)
		return
	else
		
		setTimeout(() =&gt; {
			console.log(i)
			i++
			async(i)
		}, 1000)
	
})(0)
</code></pre></div></div>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[JS自执行函数，匿名函数]]></summary></entry><entry><title type="html">浏览器同源政策及其规避方法</title><link href="http://louyi.xyz/2019/02/20/tongyuan/" rel="alternate" type="text/html" title="浏览器同源政策及其规避方法" /><published>2019-02-20T00:00:00+00:00</published><updated>2019-02-20T00:00:00+00:00</updated><id>http://louyi.xyz/2019/02/20/tongyuan</id><content type="html" xml:base="http://louyi.xyz/2019/02/20/tongyuan/"><![CDATA[<p>原文：http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
原文：http://www.ruanyifeng.com/blog/2016/04/cors.html</p>

<h1 id="浏览器同源政策及其规避方法">浏览器同源政策及其规避方法</h1>

<p>浏览器安全的基石是”同源政策”（same-origin policy）。很多开发者都知道这一点，但了解得不全面。</p>

<p>本文详细介绍”同源政策”的各个方面，以及如何规避它。</p>

<h2 id="一概述">一、概述</h2>
<p>1.1 含义
1995年，同源政策由 Netscape 公司引入浏览器。目前，所有浏览器都实行这个政策。</p>

<p>最初，它的含义是指，A网页设置的 Cookie，B网页不能打开，除非这两个网页”同源”。所谓”同源”指的是”三个相同”。</p>

<p>协议相同
域名相同
端口相同
举例来说，http://www.example.com/dir/page.html这个网址，协议是http://，域名是www.example.com，端口是80（默认端口可以省略）。它的同源情况如下。</p>

<p>http://www.example.com/dir2/other.html：同源
http://example.com/dir/other.html：不同源（域名不同）
http://v2.www.example.com/dir/other.html：不同源（域名不同）
http://www.example.com:81/dir/other.html：不同源（端口不同）
1.2 目的
同源政策的目的，是为了保证用户信息的安全，防止恶意的网站窃取数据。</p>

<p>设想这样一种情况：A网站是一家银行，用户登录以后，又去浏览其他网站。如果其他网站可以读取A网站的 Cookie，会发生什么？</p>

<p>很显然，如果 Cookie 包含隐私（比如存款总额），这些信息就会泄漏。更可怕的是，Cookie 往往用来保存用户的登录状态，如果用户没有退出登录，其他网站就可以冒充用户，为所欲为。因为浏览器同时还规定，提交表单不受同源政策的限制。</p>

<p>由此可见，”同源政策”是必需的，否则 Cookie 可以共享，互联网就毫无安全可言了。</p>

<p>1.3 限制范围
随着互联网的发展，”同源政策”越来越严格。目前，如果非同源，共有三种行为受到限制。</p>

<p>（1） Cookie、LocalStorage 和 IndexDB 无法读取。</p>

<p>（2） DOM 无法获得。</p>

<p>（3） AJAX 请求不能发送。</p>

<p>虽然这些限制是必要的，但是有时很不方便，合理的用途也受到影响。下面，我将详细介绍，如何规避上面三种限制。</p>

<h2 id="cookie">Cookie</h2>

<p>Cookie 是服务器写入浏览器的一小段信息，只有同源的网页才能共享。但是，两个网页一级域名相同，只是二级域名不同，浏览器允许通过设置document.domain共享 Cookie。</p>

<p>举例来说，A网页是http://w1.example.com/a.html，B网页是http://w2.example.com/b.html，那么只要设置相同的document.domain，两个网页就可以共享Cookie。</p>

<p>document.domain = ‘example.com’;</p>

<p>现在，A网页通过脚本设置一个 Cookie。</p>

<p>document.cookie = “test1=hello”;</p>

<p>B网页就可以读到这个 Cookie。</p>

<p>var allCookie = document.cookie;</p>

<p>注意，这种方法只适用于 Cookie 和 iframe 窗口，LocalStorage 和 IndexDB 无法通过这种方法，规避同源政策，而要使用下文介绍的PostMessage API。</p>

<p>另外，服务器也可以在设置Cookie的时候，指定Cookie的所属域名为一级域名，比如.example.com。</p>

<p>Set-Cookie: key=value; domain=.example.com; path=/</p>

<p>这样的话，二级域名和三级域名不用做任何设置，都可以读取这个Cookie。</p>

<h2 id="iframe">iframe</h2>

<p>如果两个网页不同源，就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口，它们与父窗口无法通信。</p>

<p>比如，父窗口运行下面的命令，如果iframe窗口不是同源，就会报错。</p>

<p>document.getElementById(“myIFrame”).contentWindow.document
// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.</p>

<p>上面命令中，父窗口想获取子窗口的DOM，因为跨源导致报错。</p>

<p>反之亦然，子窗口获取主窗口的DOM也会报错。</p>

<p>window.parent.document.body
// 报错
如果两个窗口一级域名相同，只是二级域名不同，那么设置上一节介绍的document.domain属性，就可以规避同源政策，拿到DOM。</p>

<p>对于完全不同源的网站，目前有三种方法，可以解决跨域窗口的通信问题。</p>

<ul>
  <li>
    <p>片段识别符（fragment identifier）</p>
  </li>
  <li>
    <p>window.name</p>
  </li>
  <li>
    <p>跨文档通信API（Cross-document messaging）</p>
  </li>
</ul>

<h4 id="31-片段识别符">3.1 片段识别符</h4>

<p>片段标识符（fragment identifier）指的是，URL的#号后面的部分，比如http://example.com/x.html#fragment的#fragment。如果只是改变片段标识符，页面不会重新刷新。</p>

<p>父窗口可以把信息，写入子窗口的片段标识符。</p>

<p>var src = originURL + ‘#’ + data;
document.getElementById(‘myIFrame’).src = src;</p>

<p>子窗口通过监听hashchange事件得到通知。</p>

<p>window.onhashchange = checkMessage;</p>

<p>function checkMessage() {
  var message = window.location.hash;
  // …
}
同样的，子窗口也可以改变父窗口的片段标识符。</p>

<p>parent.location.href= target + “#” + hash;</p>

<h4 id="32-windowname">3.2 window.name</h4>

<p>浏览器窗口有window.name属性。这个属性的最大特点是，无论是否同源，只要在同一个窗口里，前一个网页设置了这个属性，后一个网页可以读取它。</p>

<p>父窗口先打开一个子窗口，载入一个不同源的网页，该网页将信息写入window.name属性。</p>

<p>window.name = data;</p>

<p>接着，子窗口跳回一个与主窗口同域的网址。</p>

<p>location = ‘http://parent.url.com/xxx.html’;</p>

<p>然后，主窗口就可以读取子窗口的window.name了。</p>

<p>var data = document.getElementById(‘myFrame’).contentWindow.name;</p>

<p>这种方法的优点是，window.name容量很大，可以放置非常长的字符串；缺点是必须监听子窗口window.name属性的变化，影响网页性能。</p>

<h4 id="33-windowpostmessage">3.3 window.postMessage</h4>

<p>上面两种方法都属于破解，HTML5为了解决这个问题，引入了一个全新的API：跨文档通信 API（Cross-document messaging）。</p>

<p>这个API为window对象新增了一个window.postMessage方法，允许跨窗口通信，不论这两个窗口是否同源。</p>

<p>举例来说，父窗口http://aaa.com向子窗口http://bbb.com发消息，调用postMessage方法就可以了。</p>

<p>var popup = window.open(‘http://bbb.com’, ‘title’);
popup.postMessage(‘Hello World!’, ‘http://bbb.com’);</p>

<p>postMessage方法的第一个参数是具体的信息内容，第二个参数是接收消息的窗口的源（origin），即”协议 + 域名 + 端口”。也可以设为*，表示不限制域名，向所有窗口发送。</p>

<p>子窗口向父窗口发送消息的写法类似。</p>

<p>window.opener.postMessage(‘Nice to see you’, ‘http://aaa.com’);
父窗口和子窗口都可以通过message事件，监听对方的消息。</p>

<p>window.addEventListener(‘message’, function(e) {
  console.log(e.data);
},false);
message事件的事件对象event，提供以下三个属性。</p>

<p>event.source：发送消息的窗口
event.origin: 消息发向的网址
event.data: 消息内容</p>

<p>下面的例子是，子窗口通过event.source属性引用父窗口，然后发送消息。</p>

<p>window.addEventListener(‘message’, receiveMessage);
function receiveMessage(event) {
  event.source.postMessage(‘Nice to see you!’, ‘*’);
}
event.origin属性可以过滤不是发给本窗口的消息。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	window.addEventListener('message', receiveMessage);
	function receiveMessage(event) {
	  if (event.origin !== 'http://aaa.com') return;
	  if (event.data === 'Hello World') {
	      event.source.postMessage('Hello', event.origin);
	  } else {
	    console.log(event.data);
	  }
	}
</code></pre></div></div>

<h4 id="34-localstorage">3.4 LocalStorage</h4>

<p>通过window.postMessage，读写其他窗口的 LocalStorage 也成为了可能。</p>

<p>下面是一个例子，主窗口写入iframe子窗口的localStorage。</p>

<p>window.onmessage = function(e) {
  if (e.origin !== ‘http://bbb.com’) {
    return;
  }
  var payload = JSON.parse(e.data);
  localStorage.setItem(payload.key, JSON.stringify(payload.data));
};</p>

<p>上面代码中，子窗口将父窗口发来的消息，写入自己的LocalStorage。</p>

<p>父窗口发送消息的代码如下。</p>

<p>var win = document.getElementsByTagName(‘iframe’)[0].contentWindow;
var obj = { name: ‘Jack’ };
win.postMessage(JSON.stringify({key: ‘storage’, data: obj}), ‘http://bbb.com’);</p>

<p>加强版的子窗口接收消息的代码如下。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	window.onmessage = function(e) {
	  if (e.origin !== 'http://bbb.com') return;
	  var payload = JSON.parse(e.data);
	  switch (payload.method) {
	    case 'set':
	      localStorage.setItem(payload.key, JSON.stringify(payload.data));
	      break;
	    case 'get':
	      var parent = window.parent;
	      var data = localStorage.getItem(payload.key);
	      parent.postMessage(data, 'http://aaa.com');
	      break;
	    case 'remove':
	      localStorage.removeItem(payload.key);
	      break;
	  }
	};
</code></pre></div></div>

<p>加强版的父窗口发送消息代码如下。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	var win = document.getElementsByTagName('iframe')[0].contentWindow;
	var obj = { name: 'Jack' };
	// 存入对象
	win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com');
	// 读取对象
	win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
	window.onmessage = function(e) {
	  if (e.origin != 'http://aaa.com') return;
	  // "Jack"
	  console.log(JSON.parse(e.data).name);
	};
</code></pre></div></div>

<h2 id="ajax">AJAX</h2>

<p>同源政策规定，AJAX请求只能发给同源的网址，否则就报错。</p>

<p>除了架设服务器代理（浏览器请求同源服务器，再由后者请求外部服务），有三种方法规避这个限制。</p>

<ul>
  <li>JSONP</li>
  <li>WebSocket</li>
  <li>CORS</li>
</ul>

<h4 id="41-jsonp">4.1 JSONP</h4>
<p>JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用，老式浏览器全部支持，服务器改造非常小。</p>

<p>它的基本思想是，网页通过添加一个 script 元素，向服务器请求JSON数据，这种做法不受同源政策限制；服务器收到请求后，将数据放在一个指定名字的回调函数里传回来。</p>

<p>首先，网页动态插入 script 元素，由它向跨源网址发出请求。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	function addScriptTag(src) {
	  var script = document.createElement('script');
	  script.setAttribute("type","text/javascript");
	  script.src = src;
	  document.body.appendChild(script);
	}

	window.onload = function () {
	  addScriptTag('http://example.com/ip?callback=foo');
	}

	function foo(data) {
	  console.log('Your public IP address is: ' + data.ip);
	};
</code></pre></div></div>
<p>上面代码通过动态添加script元素，向服务器example.com发出请求。注意，该请求的查询字符串有一个callback参数，用来指定回调函数的名字，这对于JSONP是必需的</p>

<p>服务器收到这个请求以后，会将数据放在回调函数的参数位置返回。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	foo({
	  "ip": "8.8.8.8"
	});
</code></pre></div></div>
<p>由于script元素请求的脚本，直接作为代码运行。这时，只要浏览器定义了foo函数，该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象，而不是字符串，因此避免了使用JSON.parse的步骤。</p>

<h4 id="42-websocket">4.2 WebSocket</h4>
<p>WebSocket是一种通信协议，使用ws://（非加密）和wss://（加密）作为协议前缀。该协议不实行同源政策，只要服务器支持，就可以通过它进行跨源通信。</p>

<p>下面是一个例子，浏览器发出的WebSocket请求的头信息（摘自维基百科）。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	GET /chat HTTP/1.1
	Host: server.example.com
	Upgrade: websocket
	Connection: Upgrade
	Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
	Sec-WebSocket-Protocol: chat, superchat
	Sec-WebSocket-Version: 13
	Origin: http://example.com
</code></pre></div></div>

<p>上面代码中，有一个字段是Origin，表示该请求的请求源（origin），即发自哪个域名。</p>

<p>正是因为有了Origin这个字段，所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段，判断是否许可本次通信。如果该域名在白名单内，服务器就会做出如下回应。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	HTTP/1.1 101 Switching Protocols
	Upgrade: websocket
	Connection: Upgrade
	Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
	Sec-WebSocket-Protocol: chat
</code></pre></div></div>

<h4 id="43-cors">4.3 CORS</h4>

<p>CORS是跨源资源分享（Cross-Origin Resource Sharing）的缩写。它是W3C标准，是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求，CORS允许任何类型的请求。</p>

<p>它允许浏览器向跨源服务器，发出XMLHttpRequest请求，从而克服了AJAX只能同源使用的限制。</p>

<h6 id="一简介">一、简介</h6>

<p>CORS需要浏览器和服务器同时支持。目前，所有浏览器都支持该功能，IE浏览器不能低于IE10。</p>

<p>整个CORS通信过程，都是浏览器自动完成，不需要用户参与。对于开发者来说，CORS通信与同源的AJAX通信没有差别，代码完全一样。浏览器一旦发现AJAX请求跨源，就会自动添加一些附加的头信息，有时还会多出一次附加的请求，但用户不会有感觉。</p>

<p>因此，实现CORS通信的关键是服务器。只要服务器实现了CORS接口，就可以跨源通信。</p>

<h6 id="二两种请求">二、两种请求</h6>

<p>浏览器将CORS请求分成两类：简单请求（simple request）和非简单请求（not-so-simple request）。</p>

<p>只要同时满足以下两大条件，就属于简单请求。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	（1) 请求方法是以下三种方法之一：

	HEAD
	GET
	POST
	（2）HTTP的头信息不超出以下几种字段：

	Accept
	Accept-Language
	Content-Language
	Last-Event-ID
	Content-Type：只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

</code></pre></div></div>
<p>凡是不同时满足上面两个条件，就属于非简单请求。</p>

<p>浏览器对这两种请求的处理，是不一样的。</p>

<h6 id="三简单请求">三、简单请求</h6>

<p>3.1 基本流程</p>

<p>对于简单请求，浏览器直接发出CORS请求。具体来说，就是在头信息之中，增加一个Origin字段。</p>

<p>下面是一个例子，浏览器发现这次跨源AJAX请求是简单请求，就自动在头信息之中，添加一个Origin字段。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	GET /cors HTTP/1.1
	Origin: http://api.bob.com
	Host: api.alice.com
	Accept-Language: en-US
	Connection: keep-alive
	User-Agent: Mozilla/5.0...
</code></pre></div></div>
<p>上面的头信息中，Origin字段用来说明，本次请求来自哪个源（协议 + 域名 + 端口）。服务器根据这个值，决定是否同意这次请求。</p>

<p>如果Origin指定的源，不在许可范围内，服务器会返回一个正常的HTTP回应。浏览器发现，这个回应的头信息没有包含Access-Control-Allow-Origin字段（详见下文），就知道出错了，从而抛出一个错误，被XMLHttpRequest的onerror回调函数捕获。注意，这种错误无法通过状态码识别，因为HTTP回应的状态码有可能是200。</p>

<p>如果Origin指定的域名在许可范围内，服务器返回的响应，会多出几个头信息字段。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	Access-Control-Allow-Origin: http://api.bob.com
	Access-Control-Allow-Credentials: true
	Access-Control-Expose-Headers: FooBar
	Content-Type: text/html; charset=utf-8
</code></pre></div></div>
<p>上面的头信息之中，有三个与CORS请求相关的字段，都以Access-Control-开头。</p>

<p>（1）Access-Control-Allow-Origin</p>

<p>该字段是必须的。它的值要么是请求时Origin字段的值，要么是一个*，表示接受任意域名的请求。</p>

<p>（2）Access-Control-Allow-Credentials</p>

<p>该字段可选。它的值是一个布尔值，表示是否允许发送Cookie。默认情况下，Cookie不包括在CORS请求之中。设为true，即表示服务器明确许可，Cookie可以包含在请求中，一起发给服务器。这个值也只能设为true，如果服务器不要浏览器发送Cookie，删除该字段即可。</p>

<p>（3）Access-Control-Expose-Headers</p>

<p>该字段可选。CORS请求时，XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段：Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段，就必须在Access-Control-Expose-Headers里面指定。上面的例子指定，getResponseHeader(‘FooBar’)可以返回FooBar字段的值。</p>

<p>3.2 withCredentials 属性</p>

<p>上面说到，CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器，一方面要服务器同意，指定Access-Control-Allow-Credentials字段。</p>

<p>Access-Control-Allow-Credentials: true</p>

<p>另一方面，开发者必须在AJAX请求中打开withCredentials属性。</p>

<p>var xhr = new XMLHttpRequest();
xhr.withCredentials = true;</p>

<p>否则，即使服务器同意发送Cookie，浏览器也不会发送。或者，服务器要求设置Cookie，浏览器也不会处理。</p>

<p>但是，如果省略withCredentials设置，有的浏览器还是会一起发送Cookie。这时，可以显式关闭withCredentials。</p>

<p>xhr.withCredentials = false;</p>

<p>需要注意的是，如果要发送Cookie，Access-Control-Allow-Origin就不能设为星号，必须指定明确的、与请求网页一致的域名。同时，Cookie依然遵循同源政策，只有用服务器域名设置的Cookie才会上传，其他域名的Cookie并不会上传，且（跨源）原网页代码中的document.cookie也无法读取服务器域名下的Cookie。</p>

<h5 id="四非简单请求">四、非简单请求</h5>

<p>4.1 预检请求
非简单请求是那种对服务器有特殊要求的请求，比如请求方法是PUT或DELETE，或者Content-Type字段的类型是application/json。</p>

<p>非简单请求的CORS请求，会在正式通信之前，增加一次HTTP查询请求，称为”预检”请求（preflight）。</p>

<p>浏览器先询问服务器，当前网页所在的域名是否在服务器的许可名单之中，以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复，浏览器才会发出正式的XMLHttpRequest请求，否则就报错。</p>

<p>下面是一段浏览器的JavaScript脚本。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	var url = 'http://api.alice.com/cors';
	var xhr = new XMLHttpRequest();
	xhr.open('PUT', url, true);
	xhr.setRequestHeader('X-Custom-Header', 'value');
	xhr.send();
</code></pre></div></div>
<p>上面代码中，HTTP请求的方法是PUT，并且发送一个自定义头信息X-Custom-Header。</p>

<p>浏览器发现，这是一个非简单请求，就自动发出一个”预检”请求，要求服务器确认可以这样请求。下面是这个”预检”请求的HTTP头信息。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	OPTIONS /cors HTTP/1.1
	Origin: http://api.bob.com
	Access-Control-Request-Method: PUT
	Access-Control-Request-Headers: X-Custom-Header
	Host: api.alice.com
	Accept-Language: en-US
	Connection: keep-alive
	User-Agent: Mozilla/5.0...
</code></pre></div></div>
<p>“预检”请求用的请求方法是OPTIONS，表示这个请求是用来询问的。头信息里面，关键字段是Origin，表示请求来自哪个源。</p>

<p>除了Origin字段，”预检”请求的头信息包括两个特殊字段。</p>

<p>（1）Access-Control-Request-Method</p>

<p>该字段是必须的，用来列出浏览器的CORS请求会用到哪些HTTP方法，上例是PUT。</p>

<p>（2）Access-Control-Request-Headers</p>

<p>该字段是一个逗号分隔的字符串，指定浏览器CORS请求会额外发送的头信息字段，上例是X-Custom-Header。</p>

<p>4.2 预检请求的回应
服务器收到”预检”请求以后，检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后，确认允许跨源请求，就可以做出回应。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	HTTP/1.1 200 OK
	Date: Mon, 01 Dec 2008 01:15:39 GMT
	Server: Apache/2.0.61 (Unix)
	Access-Control-Allow-Origin: http://api.bob.com
	Access-Control-Allow-Methods: GET, POST, PUT
	Access-Control-Allow-Headers: X-Custom-Header
	Content-Type: text/html; charset=utf-8
	Content-Encoding: gzip
	Content-Length: 0
	Keep-Alive: timeout=2, max=100
	Connection: Keep-Alive
	Content-Type: text/plain
</code></pre></div></div>
<p>上面的HTTP回应中，关键的是Access-Control-Allow-Origin字段，表示http://api.bob.com可以请求数据。该字段也可以设为星号，表示同意任意跨源请求。</p>

<p>Access-Control-Allow-Origin: *
如果浏览器否定了”预检”请求，会返回一个正常的HTTP回应，但是没有任何CORS相关的头信息字段。这时，浏览器就会认定，服务器不同意预检请求，因此触发一个错误，被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。</p>

<p>XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
服务器回应的其他CORS相关字段如下。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	Access-Control-Allow-Methods: GET, POST, PUT
	Access-Control-Allow-Headers: X-Custom-Header
	Access-Control-Allow-Credentials: true
	Access-Control-Max-Age: 1728000
</code></pre></div></div>

<p>（1）Access-Control-Allow-Methods</p>

<p>该字段必需，它的值是逗号分隔的一个字符串，表明服务器支持的所有跨域请求的方法。注意，返回的是所有支持的方法，而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。</p>

<p>（2）Access-Control-Allow-Headers</p>

<p>如果浏览器请求包括Access-Control-Request-Headers字段，则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串，表明服务器支持的所有头信息字段，不限于浏览器在”预检”中请求的字段。</p>

<p>（3）Access-Control-Allow-Credentials</p>

<p>该字段与简单请求时的含义相同。</p>

<p>（4）Access-Control-Max-Age</p>

<p>该字段可选，用来指定本次预检请求的有效期，单位为秒。上面结果中，有效期是20天（1728000秒），即允许缓存该条回应1728000秒（即20天），在此期间，不用发出另一条预检请求。</p>

<p>4.3 浏览器的正常请求和回应
一旦服务器通过了”预检”请求，以后每次浏览器正常的CORS请求，就都跟简单请求一样，会有一个Origin头信息字段。服务器的回应，也都会有一个Access-Control-Allow-Origin头信息字段。</p>

<p>下面是”预检”请求之后，浏览器的正常CORS请求。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	PUT /cors HTTP/1.1
	Origin: http://api.bob.com
	Host: api.alice.com
	X-Custom-Header: value
	Accept-Language: en-US
	Connection: keep-alive
	User-Agent: Mozilla/5.0...
</code></pre></div></div>
<p>上面头信息的Origin字段是浏览器自动添加的。</p>

<p>下面是服务器正常的回应。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	Access-Control-Allow-Origin: http://api.bob.com
	Content-Type: text/html; charset=utf-8
</code></pre></div></div>
<p>上面头信息中，Access-Control-Allow-Origin字段是每次回应都必定包含的。</p>

<p>五、与JSONP的比较
CORS与JSONP的使用目的相同，但是比JSONP更强大。</p>

<p>JSONP只支持GET请求，CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器，以及可以向不支持CORS的网站请求数据。</p>

<p>（完）</p>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[浏览器同源政策及其规避方法]]></summary></entry><entry><title type="html">js介绍，运行环境，运行机制，线程</title><link href="http://louyi.xyz/2019/02/16/js-jizhi/" rel="alternate" type="text/html" title="js介绍，运行环境，运行机制，线程" /><published>2019-02-16T00:00:00+00:00</published><updated>2019-02-16T00:00:00+00:00</updated><id>http://louyi.xyz/2019/02/16/js-jizhi</id><content type="html" xml:base="http://louyi.xyz/2019/02/16/js-jizhi/"><![CDATA[<h1 id="js">js</h1>

<p>JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”（script language），指的是它不具备开发操作系统的能力，而是只用来编写控制其他大型应用程序（比如浏览器）的“脚本”。</p>

<p>JavaScript 也是一种嵌入式（embedded）语言。它本身提供的核心语法不算很多，只能用来做一些数学和逻辑运算。JavaScript 本身不提供任何与 I/O（输入/输出）相关的 API，都要靠宿主环境（host）提供，所以 JavaScript 只合适嵌入更大型的应用程序环境，去调用宿主环境提供的底层 API。</p>

<p>目前，已经嵌入 JavaScript 的宿主环境有多种，最常见的环境就是浏览器，另外还有服务器环境，也就是 Node 项目。</p>

<p>JavaScript 的核心语法部分相当精简，只包括两个部分：基本的语法构造和标准库。除此之外，各种宿主环境提供额外的 API，以便 JavaScript 调用。</p>

<h2 id="使用领域">使用领域</h2>

<p>依靠宿主环境，向通用的系统语言发展</p>

<ol>
  <li>
    <p>浏览器平台化</p>

    <p>随着 HTML5 的出现，浏览器本身的功能越来越强。JavaScript 因此得以调用许多系统功能，比如操作本地文件、操作图片、调用摄像头和麦克风等等。这使得 JavaScript 可以完成许多以前无法想象的事情。</p>
  </li>
  <li>
    <p>Node</p>

    <p>Node 项目使得 JavaScript 可以用于开发服务器端的大型项目，网站的前后端都用 JavaScript 开发已经成为了现实。有些嵌入式平台（Raspberry Pi）能够安装 Node，于是 JavaScript 就能为这些平台开发应用程序。</p>
  </li>
  <li>
    <p>数据库操作</p>

    <p>NoSQL 数据库这个概念，本身就是在 JSON（JavaScript Object Notation）格式的基础上诞生的，大部分 NoSQL 数据库允许 JavaScript 直接操作。</p>
  </li>
  <li>
    <p>移动平台开发</p>

    <p>JavaScript 也正在成为手机应用的开发语言。
 一般来说，安卓平台使用 Java 语言开发，iOS 平台使用 Objective-C 或 Swift 语言开发。许多人正在努力，让 JavaScript 成为各个平台的通用开发语言。
 PhoneGap 项目就是将 JavaScript 和 HTML5 打包在一个容器之中，使得它能同时在 iOS 和安卓上运行。
 Facebook 公司的 React Native 项目则是将 JavaScript 写的组件，编译成原生组件，从而使它们具备优秀的性能</p>
  </li>
  <li>
    <p>跨平台的桌面应用</p>

    <p>Chromium OS、Windows 8 等操作系统直接支持 JavaScript 编写应用程序。Mozilla 的 Open Web Apps 项目、Google 的 Chrome App 项目、Github 的 Electron 项目、以及 TideSDK 项目，都可以用来编写运行于 Windows、Mac OS 和 Android 等多个桌面平台的程序，不依赖浏览器。</p>
  </li>
</ol>

<h2 id="运行环境">运行环境</h2>

<p>脚本语言，需要借助引擎（解释器）来运行，所以需要封装了引擎的环境.</p>

<p>封装了js引擎的环境分为两类，浏览器环境，非浏览器环境（nodejs, mongoDB）</p>

<p>浏览器环境下js有三部分组成，分别是ECMAScript，DOM，BOM</p>

<p>非浏览器环境，比如node，也是以ECMAScript为基础，扩展出了I/O操作，文件操作，数据库操作等等</p>

<h2 id="运行机制">运行机制</h2>

<p>了解了JavaScript的运行环境，我们来看看运行机制。这里我们不再谈微软的JScript，一方面写本文时我没有找到详尽的介绍JScript的资料，另一方面JScript的应用现在不常见。</p>

<p>JavaScript是个什么样子，取决于它初始应用于哪里，它是作为浏览器的脚本出现，主要用途是解决网页中的用户交互。页面中的用户交互行为会让页面中的DOM元素产生变化，比如用户输入信息后的反馈提示等等。JavaScript在浏览器环境中操作DOM，为避免复杂的同步问题，决定了它采用单线程。如果同时有多个线程，有的在DOM节点上添加内容，有的修改了整个节点，甚至有的删除了整个节点，这个时候很难判断到底采用哪个线程的结果。</p>

<p>JavaScript最大的特点就是单线程，在浏览器环境中中是，在非浏览器环境中同样也是。单线程也就意味着JavaScript在同一时间只能进行一项任务，如果有多项任务的话，需要对任务进行排队，完成一个才能继续下一个。</p>

<p>不同的浏览器、不同的引擎、不同的执行环境，执行JavaScript的细节会有差异，但是不变的是单线程和队列。</p>

<h2 id="运行过程----预编译期预解析期和执行期">运行过程   // 预编译期（预解析期）和执行期</h2>

<p>在浏览器环境中，JavaScript引擎按 script 标签代码块从上到下的顺序加载并立即解释执行。</p>

<p>我们在这里不探究引擎的详尽解释执行细节，比如词法分析、语法分析以及语法树的构造等等，只说它解释执行过程中非常重要的两个时期预编译期（预解析期）和执行期。理解这两个阶段十分有助于理解JavaScript中的一些“奇特”的现象。</p>

<p>在预编译期JavaScript会对var和function的声明在其所在作用域内进行提升，提升的位置相当于所在作用域开始位置。预编译期需要注意下面几个问题：</p>

<ol>
  <li>预编译首先是全局预编译，函数体在未调用时不进行预编译</li>
  <li>只有var和function声明会提升</li>
  <li>注意是在所在作用域内提升，不会扩展到其他作用域</li>
  <li>预编译后顺序执行</li>
</ol>

<h2 id="为什么javascript是单线程">为什么JavaScript是单线程？</h2>

<p>JavaScript语言的一大特点就是单线程，也就是说，同一个时间只能做一件事。那么，为什么JavaScript不能有多个线程呢？这样能提高效率啊。</p>

<p>JavaScript的单线程，与它的用途有关。作为浏览器脚本语言，JavaScript的主要用途是与用户互动，以及操作DOM。这决定了它只能是单线程，否则会带来很复杂的同步问题。比如，假定JavaScript同时有两个线程，一个线程在某个DOM节点上添加内容，另一个线程删除了这个节点，这时浏览器应该以哪个线程为准？</p>

<p>所以，为了避免复杂性，从一诞生，JavaScript就是单线程，这已经成了这门语言的核心特征，将来也不会改变。</p>

<p>为了利用多核CPU的计算能力，HTML5提出Web Worker标准，允许JavaScript脚本创建多个线程，但是子线程完全受主线程控制，且不得操作DOM。所以，这个新标准并没有改变JavaScript单线程的本质。</p>

<h2 id="任务队列">任务队列</h2>

<p>单线程就意味着，所有任务需要排队，前一个任务结束，才会执行后一个任务。如果前一个任务耗时很长，后一个任务就不得不一直等着。</p>

<p>如果排队是因为计算量大，CPU忙不过来，倒也算了，但是很多时候CPU是闲着的，因为IO设备（输入输出设备）很慢（比如Ajax操作从网络读取数据），不得不等着结果出来，再往下执行。</p>

<p>JavaScript语言的设计者意识到，这时主线程完全可以不管IO设备，挂起处于等待中的任务，先运行排在后面的任务。等到IO设备返回了结果，再回过头，把挂起的任务继续执行下去。</p>

<p>于是，所有任务可以分成两种，一种是同步任务（synchronous），另一种是异步任务（asynchronous）。同步任务指的是，在主线程上排队执行的任务，只有前一个任务执行完毕，才能执行后一个任务；异步任务指的是，不进入主线程、而进入”任务队列”（task queue）的任务，只有”任务队列”通知主线程，某个异步任务可以执行了，该任务才会进入主线程执行。</p>

<p>具体来说，异步执行的运行机制如下。（同步执行也是如此，因为它可以被视为没有异步任务的异步执行。）</p>

<p>（1）所有同步任务都在主线程上执行，形成一个执行栈（execution context stack）。</p>

<p>（2）主线程之外，还存在一个”任务队列”（task queue）。只要异步任务有了运行结果，就在”任务队列”之中放置一个事件。</p>

<p>（3）一旦”执行栈”中的所有同步任务执行完毕，系统就会读取”任务队列”，看看里面有哪些事件。那些对应的异步任务，于是结束等待状态，进入执行栈，开始执行。</p>

<p>（4）主线程不断重复上面的第三步。</p>

<p>只要主线程空了，就会去读取”任务队列”，这就是JavaScript的运行机制。这个过程会不断重复。</p>

<h2 id="javascript-是unicode-字符串编写的">JavaScript 是Unicode 字符串编写的</h2>

<h2 id="区分大小写的语言">区分大小写的语言</h2>

<p>注意： html 不区分大小写</p>

<h2 id="js-会忽略程序中标识之间的空格和换行可以在代码中随意使用空格和换行">js 会忽略程序中标识之间的空格和换行，可以在代码中随意使用空格和换行。</h2>

<h2 id="js-预定义了很多全局变量和函数">js 预定义了很多全局变量和函数，</h2>

<p>arguments		encodeURI		encodeURIComponent			decodeURI		decodeURIComponent</p>

<p>NaN 		Ininity		Number		RegExp		Array		Boolean			Object 		 	String			Date		Math		undefined 		Json		Function</p>

<p>isNaN		isFinite		parseFlote		parseInt		eval</p>

<p>Error		SyntaxError		RangeRrror		 	ReferenceError 		URIError		 EvalError		TypeError</p>

<h2 id="js-使用分号将语句分隔开-分号不是必须的加不加都可以但是有些会混淆的地方还是要加">js 使用分号（；）将语句分隔开。 分号不是必须的，加不加都可以。但是有些会混淆的地方还是要加</h2>

<h2 id="js-数据类型-分为两类">js 数据类型 分为两类</h2>

<ol>
  <li>
    <p>原始类型 string number bool null undefined</p>
  </li>
  <li>
    <p>对象类型 object array function Date RegExp</p>
  </li>
</ol>

<p>特殊值 ：  null undefined , 无法拥有方法的值</p>

<p>注意： 如果函数用来初始化一个对象 （使用new运算符），我们称之为构造函数，每个构造函数定义了一个类 （class） 对象</p>

<h2 id="全局变量--不在任何函数中声明的变量">全局变量 ： 不在任何函数中声明的变量</h2>

<h2 id="在客户端js中在其表示的浏览器窗口中window对象充当全局对象">在客户端js中，在其表示的浏览器窗口中window对象充当全局对象</h2>]]></content><author><name>xiedajian</name></author><category term="js" /><summary type="html"><![CDATA[js介绍，运行环境，运行机制，线程]]></summary></entry></feed>