<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Kimi</title>
    <link>https://bio.wuhh.cc/</link>
    <description>huihao 的个人博客，记录 JavaScript、HTML 与 CSS 的实践笔记</description>
    <language>zh-CN</language>
    <managingEditor>wuhuihao@qq.com</managingEditor>
    <webMaster>wuhuihao@qq.com</webMaster>
    
    <pubDate>Tue, 23 Oct 2018 11:00:10 +0000</pubDate>
    <lastBuildDate>Mon, 09 Nov 2026 09:48:42 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    <ttl>60</ttl>
    <atom:link href="https://bio.wuhh.cc/feed.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      <title>vue项目如何进行单元测试</title>
      <link>https://bio.wuhh.cc/2018/10/23/vue-test-unit.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2018/10/23/vue-test-unit.html</guid>
      <pubDate>Tue, 23 Oct 2018 11:00:10 +0000</pubDate>
      <description>单元测试所需要用到的npm包介绍</description>
      <content:encoded><![CDATA[<h2 id="单元测试所需要用到的npm包介绍">单元测试所需要用到的npm包介绍</h2>

<h3 id="vue-test-utils">Vue Test Utils</h3>

<p>Vue.js 官方提供了Vue Test Utils 测试实用工具库，我们可以单独安装进行使用，也可以在使用vue cli的时候进行配置安装。</p>

<h3 id="mocha--mocha-webpack">mocha &amp; mocha-webpack</h3>

<p>mocha 是一个运行在nodejs和浏览器上的测试框架，它提供了众多特性可以用于进行单元测试。</p>

<p>mocha-webpack 是 mocha 测试的管理工具，集成了webpack 预编译的功能。</p>

<h3 id="jsdom">jsdom</h3>

<p>一个DOM和HTML标准的Javascript实现，运行于Node.js环境，测试环境通过jsdom可以模拟真实浏览器的相关行为和调用相应的API进行单元测试。</p>

<h3 id="chai">Chai</h3>

<p>断言库</p>

<h3 id="axios-mock-adapter">axios-mock-adapter</h3>

<p>通过这个库测试中可以拦截axios发出的请求，并返回指定的mock数据，用于组件中包含异步请求数据的情况。</p>

<h3 id="flush-promises">flush-promises</h3>

<p>会清除所有等待完成的 Promise 具柄</p>

<h2 id="项目初始化">项目初始化</h2>

<p>如果你的项目是用vue cli进行初始化的，那么你可以根据官网的教程完成mocha-webpack单元测试环境的配置。如果你的项目原来并不支持mocha-webpack单元测试,那么可以根据这个官方的example https://github.com/vuejs/vue-test-utils-mocha-webpack-example 进行单元测试环境的配置。</p>

<p>在这里 我们使用vue cli创建一个项目用于介绍。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
npm install -g @vue/cli
vue create vue-test-unit

//Manually select features
//勾选Babel，Router，Vuex，Unit Testing
//Pick a unit testing solution: Mocha + Chai

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

<h2 id="vue-test-utils-使用介绍">Vue Test Utils 使用介绍</h2>

<h3 id="挂载">挂载</h3>

<p>Vue Test Utils 的基础操作是通过mount()函数和或shallowMount()函数挂载组件，函数会返回一个包裹器 Wrapper 对象，通过包裹器提供的API，我们可以对组件进行观察和测试。</p>

<p>mount() 和 shallowMount() 之间的区别在于被存根的子组件。shallowMount() 不会渲染子组件，而是用一个空的div块代替组件的渲染，因为没有渲染，所有子组件声明周期中的函数也不会被执行。Vue Test Utils 提供了stubs配置选项，通过这一选项替换默认的存根，方便我们在测试中测试子组件的渲染情况但又不会被子组件干扰。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>describe('HelloWorld.vue', () =&gt; {
  it('renders props.msg when passed', () =&gt; {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg },
	  stubs: {
		  child: "&lt;div&gt;child&lt;/div&gt;"   // child 组件就会被渲染成&lt;div&gt;child&lt;/div&gt;
	  }
    })
	console.log(wrapper.html());
    expect(wrapper.text()).to.include(msg)
  })
})

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

<h3 id="包裹器">包裹器</h3>

<p>Vue Test Utils 中另一个重要的概念就是包裹器，通过包裹器我们可以获得一种类似于JQuery操作的方式去对组件进行测试。find()和findAll()函数将会是测试中较为常用的一个函数，通过传入一个选择器,find()返回一个包裹器对象或dom节点，findAll()返回一个包含包裹器数组的对象（WrapperArray）。选择器可以是一个 CSS 选择器、一个 Vue 组件或是一个查找选项对象。如果返回的包裹器包裹了一个vue组件，可以通过访问wrapper.vm字段去访问到这个实例。包裹器提供了一个isVueInstance()方法用于判断该包裹器是否是一个vue组件包裹器。所有包裹器都拥有element属性，可以访问到包裹器的根 DOM 节点。</p>

<p>包裹器提供了众多方法可以获得挂载组件的渲染情况用于断言测试。比如isVisible()可以判断是否可以，exists()可以判断是否存在等等，都可以在官网的文档中找到。</p>

<h3 id="操作模拟">操作模拟</h3>

<p>除了验证组件的渲染情况外，通常我们还需要通过改变组件的数据流状态或者触发组件的事件去验证组件的功能，Vue Test Utils也提供了众多方法供我们去实现这方面的验证。</p>

<p>通过setData() 和 setProps()方法，可以改变组件的数据流状态。</p>

<p>通过trigger()可以触发组件绑定的事件。</p>

<p>但是在执行这些方法的过程中，经常会碰到异步渲染的问题，在这里我推荐使用flush-promises 配合 aync/await 去编写测试用例。flush-promises 会清除所有等待完成的 Promise 具柄。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
import { shallowMount } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import Foo from './Foo'
jest.mock('axios')

test('Foo', () =&gt; {
  it('fetches async when a button is clicked', async () =&gt; {
    const wrapper = shallowMount(Foo)
    wrapper.find('button').trigger('click')
    await flushPromises()
    expect(wrapper.vm.value).toBe('value')
  })
})

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

<h3 id="请求模拟">请求模拟</h3>

<p>通常项目中编写的组件是业务相关的，会包含异步请求在其中，为了验证组件的正确渲染，需要对这些请求行为进行模拟。如果你在项目中使用了axios，那么就可以通过axios-mock-adapter去实现这一需求。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import axios from "axios";
import MockAdapter from "axios-mock-adapter";
....

describe("请求模拟测试", () =&gt; {
	  beforeEach(() =&gt; {
		var mock = new MockAdapter(axios);

		mock.onGet("/test").reply(200,{
			msg: "test"
		   }
		);

	  });
	  it("正确模拟组件中的请求", async function () {
		 
			const wrapper = shallowMount(test);
			await flushPromises();
			expect(wrapper.find(".test").test()).to.equal('test');
	  });
});

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

<h3 id="单元测试编写中遇到的坑">单元测试编写中遇到的坑</h3>

<h4 id="element-ui-中-select-选择器无法mount的问题">Element-UI 中 Select 选择器无法Mount的问题</h4>

<p>在使用Vue 进行开发的过程中，难免会使用第三方的UI库，Element 就是其中较为常见的组件库之一。因为项目中使用了Element组件，进行测试的时候就必须引用它，所有就碰到了一些意想不到的状况，比如Select组件在渲染的过程中会报错，导致测试用例无法正常执行。</p>

<p>报错</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 
 try to mount a component with el-select inside throw :

---&gt; &lt;ElSelectDropdown&gt;
       &lt;Transition&gt;
         &lt;ElSelect&gt;
           &lt;ElFormItem&gt;
             &lt;ElForm&gt;
               &lt;Anonymous&gt;
                 &lt;Root&gt;
TypeError: Cannot read property '$el' of undefined
at VueComponent.mounted (/Users/mhanne/src/report-builder/viewer/node_modules/element-ui/lib/element-ui.common.js:9131:54)
at callHook (/Users/mhanne/src/report-builder/viewer/node_modules/vue/dist/vue.runtime.common.js:2919:21)
at Object.insert (/Users/mhanne/src/report-builder/viewer/node_modules/vue/dist/vue.runtime.common.js:4156:7)
at invokeInsertHook (/Users/mhanne/src/report-builder/viewer/node_modules/vue/dist/vue.runtime.common.js:5958:28)

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

<p>最后发现是因为Vue Test Utils 使用了默认的存根transitionStub，导致了组件中出现了错误，通过禁用默认后，解决这个错误。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  import { config } from '@vue/test-utils'
  config.stubs.transition = false
</code></pre></div></div>

<h4 id="jsdom-中getcomputedstyle调用报错">jsdom 中getComputedStyle()调用报错。</h4>

<p>重写window中的getComputedStyle方法后解决这个错误。</p>

<p>```</p>

<p>const { getComputedStyle } = window;
window.getComputedStyle = function getComputedStyleStub(el) {
	return {
		…getComputedStyle(el),
		transitionDelay: “”,
		transitionDuration: “”,
		animationDelay: “”,
		animationDuration: “”,
		getPropertyValue() {}
	};
};</p>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>vue</category>
      
      <category>test</category>
      
    </item>
    
    <item>
      <title>Javascript 中的继承</title>
      <link>https://bio.wuhh.cc/2017/10/22/javascript-about-prototype.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/10/22/javascript-about-prototype.html</guid>
      <pubDate>Sun, 22 Oct 2017 12:23:10 +0000</pubDate>
      <description>http://www.ecma-international.org/ecma-262/8.0/#sec-ordinary-and-exotic-objects-behaviours</description>
      <content:encoded><![CDATA[http://www.ecma-international.org/ecma-262/8.0/#sec-ordinary-and-exotic-objects-behaviours

### prototype

每一个普通的对象都有一个内部的slot被称作[[Prototype]].这个内部slot的值是null或者是一个对象，它被用作实现继承。prototype 对象中的属性值是被继承的（并且作为子对象的属性）为了获得访问权限，但是没有写入权限。存取器的继承能获得读取和写入的权限。

每一个普通的对象内部都有一个布尔类型的[[Extensible]] solt,它控制着属性能否被添加到对象中。如果对象内部solt[[Extensible]]的值是false,那么其它的属性值将不能被添加到这个对象中。 除此之外，如果[[Extensible]] 的值是false，那么这个对象的内部solt[[Prototype]]将不能被修改。当一个对象的[[Extensible]]内部slot被设为false，它可能将无法立马转换为true.

### 继承的实现方式

#### 原型继承

#### 构造方式继承

-- 未完成]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>javascript</category>
      
      <category>prototype</category>
      
    </item>
    
    <item>
      <title>介绍JavaScript中的Promise</title>
      <link>https://bio.wuhh.cc/2017/10/07/javascript-about-promise.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/10/07/javascript-about-promise.html</guid>
      <pubDate>Sat, 07 Oct 2017 17:23:10 +0000</pubDate>
      <description>一个Promise对象被用于作为某个延迟（或者异步）操作返回的最终结果的占位。</description>
      <content:encoded><![CDATA[<p>一个Promise对象被用于作为某个延迟（或者异步）操作返回的最终结果的占位。</p>

<p>每一个Promise对象会拥有三种互斥状态中的一种状态：fulfilled, rejected, 和 pending。</p>

<ul>
  <li>一个Promise对象的状态变为fulfilled,如果有p.then(f, r) ，会立即调用f函数。</li>
  <li>一个Promise对象的状态变为rejected，如果有p.then(f, r)，会立即执行r函数。</li>
  <li>一个Promise对象的的状态没有变为rejected或fulfilled，那么它的状态是pending。</li>
</ul>

<p>一个Promise对象被称为settled，如果它不处于pending状态或者说它的状态变为了fulfilled或者rejected。</p>

<p>一个Promise对象如果已经是settled或者为了匹配其他Promise对象的状态已经被“locked in”，那么可以称它已经是resolved。将一个已经是resolved的Promise对象变为fulfilled或者rejected的操作都将是无效的。一个Promise如果还没有resolved，那么它是unresolved的。一个unresolved的Promise对象的状态始终是pending。一个resolved的Promise对象的状态可能是 pending, fulfilled 或者 rejected。</p>

<h3 id="创建一个promise对象">创建一个Promise对象</h3>

<p>`
// mdn 上的例子
let myFirstPromise = new Promise(function(resolve, reject){
    //当异步代码执行成功时，我们才会调用resolve(…), 当异步代码失败时就会调用reject(…)
    //在本例中，我们使用setTimeout(…)来模拟异步代码，实际编码时可能是XHR请求或是HTML5的一些API方法.
    setTimeout(function(){
        resolve(“成功!”); //代码正常执行！
    }, 250);
});</p>

<p>myFirstPromise.then(function(successMessage){
    //successMessage的值是上面调用resolve(…)方法传入的值.
    //successMessage参数不一定非要是字符串类型，这里只是举个例子
    console.log(“Yay! “ + successMessage);
});
`</p>

<p>Promise 构造器是一个内部对象企且是全局对象属性Promise的初始值。把Promise当作构造器使用的时候，它会创建并初始化一个新的Promise对象。当Promise被当作函数调用的时候，会抛出一个错误。</p>

<p>当使用Promise构造器的时候，需要传入一个executor参数，executor参数必须是一个函数对象。在executor中初始化并记录可能的延迟操作的完成状态。executor函数执行的时候将会带入两个参数resolve 和 reject。这两个方法可能被用于报告延迟操作的最终结果是完成还是失败。executor 函数中return并不表示这延迟操作已经完成,但是表示延迟操作向最终表现的请求已经被接受。</p>

<p>作为参数传入executor函数的resolve函数会接收到一个参数。executor中代码最终可能会调用resolve方法用以表明相关联的 Promise 对象已经解决。传入resolve函数的参数表示着延迟操作的最终结果，它可以是任意的实现的值或者是其他的会在完成时传值的Promise对象。</p>

<p>作为参数传入executor函数的reject函数会接收到一个参数。executor中代码最终可能会调用reject方法用以表明相关联的Promise已经被拒绝并且将永远无法将状态变为fulfilled。传入reject的参数被用作promise被拒绝的值，这个值通常是Error 对象。</p>

<p>被Promise构造器传入的resolve和reject方法能确实的resolve或reject相关联的promise。子类可能拥有不用的构造行为通过自定义的resolve和reject值。</p>

<p>https://github.com/mattdesl/promise-cookbook
 https://github.com/xieranmaya/blog/issues/3
 http://www.ecma-international.org/ecma-262/8.0/#sec-promise-objects
 http://www.ituring.com.cn/article/66566</p>

<p>### 原型方法</p>

<p>#### Promise.prototype.then ( onFulfilled, onRejected )</p>

<p>—未完成</p>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>promise</category>
      
      <category>asyn</category>
      
      <category>wait</category>
      
    </item>
    
    <item>
      <title>JavaScript中的数组操作总结</title>
      <link>https://bio.wuhh.cc/2017/09/01/javascript-about-array.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/09/01/javascript-about-array.html</guid>
      <pubDate>Fri, 01 Sep 2017 20:22:13 +0000</pubDate>
      <description>数组作为JavaScript中最为常用的几个内建对象之一，较好的掌握数组的用法，能在日常的编码中起到事半功倍的作用。</description>
      <content:encoded><![CDATA[<p>数组作为JavaScript中最为常用的几个内建对象之一，较好的掌握数组的用法，能在日常的编码中起到事半功倍的作用。</p>

<h2 id="执行后被操作数组发生改变">执行后被操作数组发生改变</h2>
<h3 id="arrayprototypepop--">Array.prototype.pop ( )</h3>

<p>移除数组中的最后一个元素并返回该元素。</p>

<p>如果你在一个空数组上调用 pop()，它返回  undefined。</p>

<h3 id="arrayprototypepush--items-">Array.prototype.push ( …items )</h3>

<p>push方法按照参数出现的次序，将所有参数添加到数组的末尾，并返回新的数组长度。</p>

<p>当参数个数+原数组长度大于2的53次方-1时，将会抛出TypeError错误。</p>

<p>push方法的length属性值为1.</p>

<h3 id="arrayprototypeshift--">Array.prototype.shift ( )</h3>

<p>shift 移除数组中的第一个元素并返回该元素。</p>

<h3 id="arrayprototypeunshift--items-">Array.prototype.unshift ( …items )</h3>

<p>push方法按照参数出现的次序，将所有参数添加到数组的头部，并返回新的数组长度。</p>

<p>当参数个数+原数组长度大于2的53次方-1时，将会抛出TypeError错误。</p>

<p>unshift方法的length属性值为1.</p>

<h3 id="arrayprototypereduce--callbackfn---initialvalue--">Array.prototype.reduce ( callbackfn [ , initialValue ] )</h3>

<p>传入的callbackfn应该是一个带有4个参数的函数。reduce 方法自数组的第1个元素开始，依次为每个元素调用一次callbackfn函数。</p>

<p>callbackfn函数被调用的时候将会传入4个参数：
previousValue（前一次callbackfn函数调用时返回的值）
currentValue（当前元素）
currentIndex（当前元素的坐标）
当前的执行对象</p>

<p>在callbackfn函数第一次被调用的时候，previousValue和currentValue可能会有不同的取值。如果reduce调用的时候有传入initialValue参数， previousValue将会等于传入的initialValue参数值，currentValue将会等于数组的第一个元素。如果没有传入initialValue参数，previousValue将会等于数组的第一个元素，currentValue将会等于数组的第2个元素。如果对空数组执行reduce方法，并且没有传入initialValue参数，将会抛出TypeError错误。</p>

<p>reduce并不会直接改变调用它的对象，但是对象有可能在callbackfn函数调用的时候被改变。</p>

<p>reducec所处理的数据的范围在第一次调用callbackfn之前就已经确定了，reduce方法开始执行之后所添加的元素都不会被callbackfn函数访问到。如果数组中某个元素的值发生了改变，传入callbackfn的值将是访问到这个元素时这个元素的取值。如果元素在被访问到之前或者reduce方法执行之前被删除了，那么这个元素将不会被访问到。</p>

<h3 id="arrayprototypesplice--start-deletecount-items-">Array.prototype.splice ( start, deleteCount, …items )</h3>

<p>splice在中文是绞接，剪接的意思。在splice方被调用的时候，start和deleteCount是必要的2个参数，这2个参数之后还可以传入0个或者多个参数。splice操作将会从下标start开始，删除deleteCount个元素，如果传入了items参数，items将会在start这个位置插入到数组中。操作返回一个包含被删除元素的数组。</p>

<h3 id="arrayprototypefill--value---start---end---">Array.prototype.fill ( value [ , start [ , end ] ] )</h3>

<p>fill方法将传入的value值填充到调用的数组中，start和end参数是可选的，如果没传，start默认是0，end默认是数组的长度。如果start的值为负数，则start值变为长度+start，如果end的值为负数，则end的值变为长度+end。方法调用后，自下标start开始一直到end为止(不包含end)，元素的值都转变为value。</p>

<h3 id="arrayprototypeevery--callbackfn---thisarg--">Array.prototype.every ( callbackfn [ , thisArg ] )</h3>

<h3 id="arrayprototypesome--callbackfn---thisarg--">Array.prototype.some ( callbackfn [ , thisArg ] )</h3>

<h2 id="执行后被操作数组维持不变">执行后被操作数组维持不变</h2>

<h3 id="arrayprototypeconcat--arguments-">Array.prototype.concat ( …arguments )</h3>

<p>concat方法可以传入0或多个参数，执行后返回一个新的数组，包含原来的数组元素以及传入的参数。传入的参数将会依次跟在原数组元素后面，如果参数一个数组，那么它将会被展开连接到数组后面。</p>

<h3 id="arrayprototypeslice--start-end-">Array.prototype.slice ( start, end )</h3>

<p>slice在中文是切片的意思。slice的调用需要start和end两个参数，截取原数组自下标start开始至end(不包括)的一段返回(如果end未传入，则截取到数组的最后)，原数组不会被改变。如果start是一个负数，则从数组的下标start+length开始（lenth为数组的长度）。如果end是一个负数，则截取到数组的下标end+length(lenth为数组的长度,不包括这个位置。)</p>

<h3 id="arrayprototypefilter--callbackfn---thisarg--">Array.prototype.filter ( callbackfn [ , thisArg ] )</h3>

<p>filter的调用需要传入callbackfn这个回调函数，callbackfn函数接受3个参数并且返回一个会被强制转换为布尔量的值。filter方法会为数组中的每个元素按升序调用一次callbackf函数，如果元素调用过callbackfn之后返回的值为true,那么这些元素会被放到一个新的数组中返回。callbackfn只会被数组中真正存在的元素调用，如果元素不存在，则callbackfn不会被调用。</p>

<p>如果传入了thisArg参数，那么它将作为被调用的callbackfn的this变量使用，如果没有传入thisArg，那么callbackfn调用时的this变量值为undefined。
callbackfn调用的时候会传入3个参数，当前元素的值，当前元素的下标，以及正在被遍历的对象。</p>

<p>filter 函数的调用并不会直接改变数组本身，但在callbackfn调用的过程中，对象可能会被改变。</p>

<p>filter所处理的数据的范围在第一次调用callbackfn之前就已经确定了，filter方法开始执行之后所添加的元素都不会被callbackfn函数访问到。如果数组中某个元素的值发生了改变，传入callbackfn的值将是访问到这个元素时这个元素的取值。如果元素在被访问到之前或者filter方法执行之前被删除了，那么这个元素将不会被访问到。</p>

<h3 id="arrayprototypemap--callbackfn---thisarg--">Array.prototype.map ( callbackfn [ , thisArg ] )</h3>

<p>http://www.ecma-international.org/ecma-262/8.0/#sec-array.prototype.reduce</p>

<p>回调函数 callbackfn 将会接受3个参数。map方法会对数组中的每个元素按升序调用一遍，并且返回一个新的数组。当数组中的元素真实存在的时候，callbackfn才会被调用，当元素不存在的时候，callbackfn不会被调用。</p>

<p>如果提供了thisArg参数，thisArg 将作为this的值在callbackfn被调用的时候被使用，如果没有传入thisArg参数，this的值将会是undefined。</p>

<p>callbackfn 被调用的时候将会传入3个参数，当前遍历的元素的值，元素的下标，以及被遍历的数组元素。</p>

<p>map 函数的调用并不会直接改变数组本身，但在callbackfn调用的过程中，对象可能会被改变。</p>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>数组</category>
      
    </item>
    
    <item>
      <title>JavaScript中的变量作用域</title>
      <link>https://bio.wuhh.cc/2017/08/11/javascript-scope.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/08/11/javascript-scope.html</guid>
      <pubDate>Fri, 11 Aug 2017 23:00:10 +0000</pubDate>
      <description>在这篇文章中，我们通过几个例子来理解学习Javascript中的变量作用域,所有的代码在chrome浏览器中执行。 //code-1 var a = &quot;a&quot;; function fun(){ var b = &quot;b&quot; console.log(a) console.log(b) } fun() console.log(a) concole.log(b)</description>
      <content:encoded><![CDATA[<p>在这篇文章中，我们通过几个例子来理解学习Javascript中的变量作用域,所有的代码在chrome浏览器中执行。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//code-1
var a = "a";

function fun(){
	var b = "b"
	console.log(a)
	console.log(b)
}

fun()
console.log(a)
concole.log(b)
</code></pre></div></div>

<p>code-1 代码段执行之后，我们获得了如下输出结果</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a
b
a
Uncaught ReferenceError: b is not defined
</code></pre></div></div>

<p>在fun函数执行的时候，不论是在函数体内定义的变量b,还是在全局代码中定义的变量a，都能取到它们的值，而在fun函数执行完毕后，fun方法体中定义的变量b在全局代码中是无法取得的。</p>

<p>从以上代码的执行结果看来，在全局代码中定义的变量a的作用域覆盖了整个fun函数体，而在fun函数体中定义的变量的作用域只局限于fun的函数体，并没有覆盖到全局代码中。</p>

<p>下面再来看一段代码：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//code-2
var b = 123
function fun(){
	var a = 12;
    baz();
}

function baz(){
  console.log(b)
  console.log(a);
}
fun()
</code></pre></div></div>

<p>code-2 代码段执行之后，我们获得如下的输入结果</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>123
Uncaught ReferenceError: b is not defined
</code></pre></div></div>

<p>baz函数在fun函数内被调用，也就说baz函数在fun函数执行的时候被调用。我们尝试在baz函数内输出fun函数内定义的变量a，但是我们得到了报错的信息。也就是说变量的作用域是在函数定义的时候确定的，与函数执行的位置无关。</p>

<p>下面我们通过ECMAScript规范中的定义，去解释函数能拥有独立作用域的原因。</p>

<h2 id="词法环境lexical-environments">词法环境（Lexical Environments）</h2>

<p>ECMAscript 规定了词法环境是一个用于定义特定变量和函数标识符在 ECMAScript 代码的词法嵌套结构上关联关系的规范类型。一个词法环境由一个环境记录项和可能为空的外部词法环境引用构成。</p>

<p>通常词法环境会与函数定义， catch代码块的句法结构相联系，比如说函数的定义语句的括号内会确定一个词法环境的范围，并且在代码执行的时候创建一个新的词法环境。</p>

<h4 id="环境记录项">环境记录项</h4>

<p>环境记录项记录了在其关联的词法环境范围中创建的标识符绑定。词法环境定义了一个范围，这个范围内定义的变量或函数会记录在改词法环境的环境记录项中。</p>

<h4 id="外部词法环境引用">外部词法环境引用</h4>

<p>外部词法环境引用用于表示词法环境的逻辑嵌套关系模型。如果函数a和函数c是定义在函数b内部的，那么b函数和c函数的外部词法环境就是a函数定义语句执行时候创建的词法环境。</p>

<p>外部词法环境引用的作用是在标识符解析的过程中形成一条作用域链。比如在上一段所描述的三个函数，如果在b函数和c函数中找不到所期待的变量，会到上一级作用域，也就是外部词法环境引用的a函数的作用域中去寻找。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	var wang = "wang"
	
	function a(x){
		
		var jay = "jay";
		
		function b(y){
			var jj = "jj"
		}
		
		function c(z){
			var lee = "lee"
		}
	}
	
	function d(){
		var jolin = "jolin"
	}
</code></pre></div></div>

<p>当然找到了标识符还不够，通常我们还需要取得这个变量的值，这就需要从标识符所在的词法环境所属的执行环境去查找了。</p>

<h4 id="全局环境">全局环境</h4>

<p>在任何 ECMAscript 代码执行之前会创建一个唯一的词法环境作为全局环境。全局环境使用宿主环境的全局对象作为绑定对象，在浏览器中就是window对象。作用域的链的顶端就是全局环境，如果查找到全局环境都找不到变量，程序便会返回undefined。</p>

<p>在非严格模式下的函数代码中，如果使用了没有var 语句创建的变量，该变量会被绑定到全局环境的环境记录项中，在浏览器中就是会被绑定到window对象中。</p>

<p>下面介绍一下执行环境，来了解整个变量查找的过程。</p>

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

<p>ECMA-262规定了3种可执行的代码，全局代码，Eval代码和函数代码。当控制器转入到ECMA脚本的可执行代码时，控制器会进入一个执行环境（execution context）。执行环境一个纯粹的标准机制，在脚本中我们是无法访问到执行环境的，它定义了变量或函数有权访问的其它数据，决定了它们各自的行为。</p>

<p>每个执行环境包含3个状态组件</p>

<ul>
  <li>词法环境组件（LexicalEnvironment）</li>
  <li>变量环境组件（VariableEnvironment）</li>
  <li>this 绑定（ThisBinding）</li>
</ul>

<h4 id="执行环境栈">执行环境栈</h4>

<p>当前活动的多个执行环境在逻辑上形成一个栈结构。例如当函数被调用的时候，控制器会进入到一段与原执行环境无关的可执行代码，此时会创建一个新的执行环境，这个新的执行环境会被压入到栈中，因为处在栈顶位置，此执行环境成为当前运行的执行环境。当代码执行完毕后将这个执行环境从堆栈中推出，并将控制权交还给上一个执行环境。</p>

<h4 id="变量的查找">变量的查找</h4>

<p>当代码中要查找一个变量时，需要找到当前正在运行的执行环境，也就是执行环境栈栈顶的执行环境。代码需要在这个执行环境的词法环境中查找到这个标识符，如果查不到则会到上一级的词法环境中查找。如果直到全局环境都找不到这个标识符，则会返回undefined。</p>

<h3 id="example">Example</h3>

<p>我们尝试通过对code-2代码进行一步步的解析，来对上面的概念有个相对的理解。</p>

<p>在全局代码执行之前，会初始化全局环境，在浏览器中执行会将window对象作为全局环境的绑定对象。</p>

<p>控制器解析执行全局代码创建并初始化全局环境。初始化全局环境的过程如下，控制器遍历全局代码，碰到一个变量定义和两个函数声明.对于var b = 123会将b的值设为undefined绑定到window对象也就是全局环境对象的绑定对象中。对于fun和baz的函数声明，控制器会根据函数定义的代码创建函数对象，并绑定到绑定对象中。在函数声明代码执行之前，会以当前运行环境的词法环境为外部词法环境引用，创建该函数的词法环境。所以在code-2中，函数fun和函数baz的词法环境的外部词法环境引用都是全局环境。</p>

<p>当代码执行到fun函数执行的时候，控制器开始进入到fun函数中，并且创建一个新的执行环境。新的执行环境将会被压入到执行环境栈中，作为当前运行的执行环境。变量a被添加到该执行环境的绑定对象中，并被赋予值2。</p>

<p>当代码执行到baz函数执行的时候，控制器开始进入到baz函数中，一样会创建一个新的执行环境并压入到执行环境栈中作为当前的运行环境。代码语句console.log(b)执行的时候，需要查找到变量b的值。控制器会先在当前执行环境的词法环境的环境数据项中查找b这个标识符，由于函数内没有定义这个变量，所以无法在当前执行环境的词法环境找到这个变量的引用。此时控制器会从该词法环境的外部词法环境中去查找，由于baz函数声明执行的时候是在全局环境中，所以此时执行环境的词法环境的外部词法环境便是全局环境。控制在全局环境的绑定对象中找到了b标识符，并返回了该标识符的引用给log函数使用。由于变量a没有在全局环境中定义，并且全局环境的外部词法环境为NULL，控制器在查找a这个标识符的最后会抛出一个ReferenceError 异常。</p>

<h3 id="总结">总结</h3>

<p>函数声明的执行能创建独立的作用域，它们相互之间的嵌套关系又形成了作用域链，通过这条作用域链能实现对标识符的查找。</p>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>作用域</category>
      
    </item>
    
    <item>
      <title>如何阅读VUE2.0的源码</title>
      <link>https://bio.wuhh.cc/2017/07/09/how-to-read-vue.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/07/09/how-to-read-vue.html</guid>
      <pubDate>Sun, 09 Jul 2017 22:42:10 +0000</pubDate>
      <description>阅读优秀程序员的代码是一种非常有用的学习方式，大到框架架构，小到代码片段，只要方法得当，相信肯定能从中汲取到自身所需的营养。Vue作为现今国内最火的前端框架之一，它的源码风格以优雅著称，从简练高效的API中就可以感受一二，自然阅读Vue的源码便成了工作之余提升的方式之一。</description>
      <content:encoded><![CDATA[<p>阅读优秀程序员的代码是一种非常有用的学习方式，大到框架架构，小到代码片段，只要方法得当，相信肯定能从中汲取到自身所需的营养。Vue作为现今国内最火的前端框架之一，它的源码风格以优雅著称，从简练高效的API中就可以感受一二，自然阅读Vue的源码便成了工作之余提升的方式之一。</p>

<h2 id="学习es6">学习ES6</h2>

<p>Vue2采用了ES6编写，学习ES6自然也成了阅读Vue源码的前提。</p>

<h2 id="阅读他人撰写的vue源码分析">阅读他人撰写的Vue源码分析</h2>

<p>面对一个庞大的框架，即使你已经有了丰富的Vue程序编写经验，要立马理解它的源码，也是一件非常苦难的事情。再此之前有不少人已经为后人铺好了路，我们可以顺着他们的路走下去，理解基本的原理，有了大概的思路之后，再进入到源码中便会轻松很多</p>

<h2 id="断点调试">断点调试</h2>

<p>在github上clone Vue项目到自己的电脑上，npm install 所需的依赖项，便可以开始在编辑器中浏览Vue的源码了。单纯的阅读源码，会是件十分枯燥的事情，我可以运行项目中的example或者自己编写事例在浏览器中运行去观察代码的运行。</p>

<p>我们可以看到package.json里的运行命令</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  "dev": "rollup -w -c build/config.js --environment     TARGET:web-full-dev",
</code></pre></div></div>

<p>Vue 使用了rollup 去打包源文件，我们可以加上soursesMap选项，生成sourcemap文件，更好的帮助我们去阅读源码。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  "dev": "rollup -w -c build/config.js --sourcemap --environment TARGET:web-full-dev",
</code></pre></div></div>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>vue</category>
      
      <category>es6</category>
      
    </item>
    
    <item>
      <title>JavaScript-作用域相关</title>
      <link>https://bio.wuhh.cc/2017/01/02/JavaScript-scopr.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/01/02/JavaScript-scopr.html</guid>
      <pubDate>Mon, 02 Jan 2017 00:00:00 +0000</pubDate>
      <description>当我们JavaScript代码定义一个变量，如何去确定这个变量的作用范围？当我使用一个变量的时候，如何去确定这个变量的值？这就是这篇文章所需要探讨的内容。</description>
      <content:encoded><![CDATA[<p>当我们JavaScript代码定义一个变量，如何去确定这个变量的作用范围？当我使用一个变量的时候，如何去确定这个变量的值？这就是这篇文章所需要探讨的内容。</p>

<h3 id="执行环境execution-context">执行环境(execution context)</h3>

<p>执行环境是一个规范机制用于追踪运行环境的代码，这些代码是基于ECMAScript的实现。在任何一个时刻，最多只有一个执行环境在处理代码，这就是所谓的运行环境。</p>

<p>每次当控制流转到某一段可执行代码的时候，即会进入到一个执行环境。执行环境定义了变量或函数有权访问的其它数据，决定了它们各自的行为。</p>

<p>执行环境的类型总共有两种，全局执行环境和局部执行环境（函数）。</p>

<h4 id="全局执行环境global-context">全局执行环境（global context）</h4>

<p>代码在宿主环境开始执行时所创建的执行环境成为全局执行环境，是最外围的执行环境。</p>

<h4 id="局部执行环境">局部执行环境</h4>

<p>控制流转入到一个函数中的代码时会创建一个执行环境，这个执行环境便是局部执行环境。</p>

<h4 id="变量对象">变量对象</h4>

<p>每个执行环境都有个与之相关联的变量对象（variable object），环境中所创建的变量和函数都保存在这个对象中。</p>

<h4 id="活动对象">活动对象</h4>

<p>当代码执行进入到函数时，会为当前函数创建局部执行环境，该函数的活动对象即为该环境的变量对象，活动对象初始包含arguments和该函数的命名参数。</p>

<h4 id="执行环境栈">执行环境栈</h4>

<p>当代码在宿主环境开始执行或者进入到一个函数中都会创建一个执行环境，并推入到一个堆栈中。当代码执行完毕后将这个执行环境从堆栈中推出，并将控制权交还给上一个执行环境。因为代码总是在宿主环境中开始执行的，所以堆栈的底部永远是全局执行环境。</p>

<h3 id="作用域">作用域</h3>

<h4 id="全局作用域">全局作用域</h4>

<p>在函数之外的范围为全局作用域，在全局作用域中定义的变量为全局变量。</p>

<h4 id="函数作用域">函数作用域</h4>

<p>与Java和C等语言不同的时，JavaScript只能通过函数创建独立的作用域,for和if等语句用花括号包裹的代码块等不能创建独立的块作用域。</p>

<h4 id="作用域模型词法作用域">作用域模型—词法作用域</h4>

<p>JavaScript采用的是词法作用域，即作用域在词法分析阶段就已经确定，由于只有函数才能创建独立的作用域，作用域的范围在函数定义的时候就已经确定了。</p>

<h4 id="作用域链">作用域链</h4>

<p>为了保障对执行环境内有权访问的变量和函数的有序访问，后台会为每个环境的变量对象创建一个作用域链。作用域链相当于一个指针数组，存储着指向各个执行环境的变量对象的引用。作用域链的最前端时当前的执行环境，接下去是当前环境的外围环境。当代码执行读取或写入操作需要根据标识符去获取一个变量的值的时候，会经历一个根据作用域链所有的过程。</p>

<p>###Example</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
var v1 = 1;

function scope1(){
    var v2 = 2,
        v3 = 3,
        foo;
    
    foo = function(x){
        var v4 = 4,
            v5 = 5,
            res;
        
        res = v1 + v2 + v3 + v4 + v5 +x;
    
        return res;
    }
	
    console.log(foo(6));
}
	
scope1();

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

<p>在上面的例子中，可以看到有2个局部作用域，分别由scope1函数和foo函数创建，foo函数创建的作用域被scope1函数创建的作用域包围，scope1函数创建的作用域被全局作用域包围。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
var x = 10;

function fn(){

    console.log(x);
}


function show(f){

    var x = 20;

    f();
}

show(fn);  //10

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

<p>在上面给出的例子中，可以看到有2个局部作用域，分别由函数fn和函数show创建，都被直接包含在全局作用域中。</p>]]></content:encoded>
      
      
    </item>
    
    <item>
      <title>2017 Happy New Year</title>
      <link>https://bio.wuhh.cc/2017/01/01/happy-new-year.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2017/01/01/happy-new-year.html</guid>
      <pubDate>Sun, 01 Jan 2017 00:00:00 +0000</pubDate>
      <description>今年的心情跟10年那时颇为相似，再一次的离家，虽不舍别离却也迎来重逢，兜兜转转之后也进入到了自己想要的生活轨道。</description>
      <content:encoded><![CDATA[<p>今年的心情跟10年那时颇为相似，再一次的离家，虽不舍别离却也迎来重逢，兜兜转转之后也进入到了自己想要的生活轨道。</p>

<p>半年内的2次的搬家，也初次感受到了漂泊的滋味，杭州是个让人生恋的城市，能在这里安定的生活也将是未来的目标之一。</p>

<p>过去习惯了一人埋头码码，新的环境令人振奋却也有些束手无措，更多的看到自身的不足和差距，越了解自己的无知越能感受到对过去的懊悔，却发现这些都是无法避免的，凡事终究要一步一步去践行，愚蠢也好，慵懒也罢，这就是自己，唯有不断的努力不懈怠才能避免再次落入深坑。</p>

<p>害怕的是自己成长的脚步赶不上父母老去的速度，不能相伴身旁只能付出更多辛苦在工作上，新的一年没有希望也要创造希望，新年快乐。</p>

<p><img src="//whhbio.oss-cn-beijing.aliyuncs.com/blog/c6a43f4ff5fa2f2fa951009f3f6f89748bcc1ff1.jpg" alt="顽固" /></p>]]></content:encoded>
      
      
    </item>
    
    <item>
      <title>什么是Vuex[翻译]</title>
      <link>https://bio.wuhh.cc/2016/12/01/vuex.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2016/12/01/vuex.html</guid>
      <pubDate>Thu, 01 Dec 2016 12:44:10 +0000</pubDate>
      <description>什么是Vuex</description>
      <content:encoded><![CDATA[<h1 id="什么是vuex">什么是Vuex</h1>

<p>Vuex是一个Vue.js程序的状态管理模式+类库。它作为一个中央仓库服务于程序中的所有组件，遵循一定的规则保证状态的变化发生在可预测的方法中。他也集成了Vue官方的devtools extension能提供一些高级的特性，类似零配置的time-travel调试和状态快照的导入导出。</p>

<h3 id="什么是state-management-pattern">什么是”State Management Pattern”?</h3>

<p>让我从一个简单的vue程序开始</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    &lt;div&gt;8&lt;/div&gt;
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})
</code></pre></div></div>

<p>它是一个包含如下部分的自给自足的app：</p>

<ol>
  <li>state:驱动你的app的真实数据来源。</li>
  <li>view：state映射的声明</li>
  <li>actions：可以根据从view中获取的用户输入从而改变state值的方法</li>
</ol>

<p>这是非常简单的一个例子体现了”one-way data flow”的概念</p>

<p><img src="http://vuex.vuejs.org/en/images/flow.png" alt="Alt text" /></p>

<p>当我们拥有多个共享同一state的组件之后，这种关系很快就被打破了。</p>

<ol>
  <li>多个view依赖于同一块state</li>
  <li>不同的view里面的方法可能需要改变同一块state</li>
</ol>

<p>对于问题1，在深层嵌套的组件中传递属性将会是单调乏味的，而且不能在相邻的组件之间起作用。对于问题2，会发现我们会常常借助于下面的一些方法，获取指向父级/子级实例的引用或者在事件中尝试改变和同步state的多个副本。这些方法都是脆弱的，而且容易导致很多不可维护的代码。</p>

<p>所以我们为什么不把共享的state从组件中提取出来，放在一个公共的单例中进行管理。如果这样，我们的组件树将会变成一个巨大的“view”, 我们组件不管在树中的哪里都能访问state或者触发事件.</p>

<p>除此之外，通过定义和分离状态管理中涉及的概念并执行一定的规则，使我们代码更具结构性和可维护性。</p>

<p>这是Vuex背后的基本概念，灵感来自于Flux, Redux 和 The Elm Architecture，与其他的模式不同，Vuex也是一个为Vue量身定做的类库，利用其颗粒反应系统进行有效的更新。</p>

<p><img src="http://vuex.vuejs.org/en/images/vuex.png" alt="Alt text" /></p>

<p>###Get Started</p>

<p>每一个Vuex程序的中心是Store。一个“store”从根本来说就是一个持有你程序state的容器。与单纯的公共对象比，Vuex Stroe有两点不同。</p>

<ol>
  <li>Vuex store是具有反应性的，当组件从它当中检索数据的时候，他会对此反应并高效的更新自己如果有state发生改变的话。</li>
  <li>你不能直接改变Store中state的值，明确的调用committing mutations是改变Store中state的唯一方法。这个保证了每次state的改变都留下了一条可追踪的记录，使得一个工具能帮我们更好的了解我们的程序。</li>
</ol>

<p>###The Simplest Store</p>

<p>安装完Vuex之后，创建一个非常简单的store，只提供一个初始的状态对象和一些简单的mutation。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//如果使用module系统要先确保调用了Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
</code></pre></div></div>

<p>先在我们可以访问定义为store.state的状态对象了，还可以通过调用store.commit方法触发状态的改变。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>store.commit('increment')

console.log(store.state.count) // -&gt; 1
</code></pre></div></div>

<p>再次说明，为了明确的追踪状态的变化，我们使用committing a mutation代替了直接去改变store.state.count的方法。这个简单的约定使你的意图更加明确，在你阅读代码的时候能更好的了解状态改变的原因。除此之外，还能使我们的工具记录所有状态改变的日志，记录状态的快照或者更好的实现代码时间穿越的调试。</p>

<p>要在组件中使用Store state，只需影响一个计算属性的返回状态，因为store state 是具有反应性的。触发事件只需简单的在组件的方法中调用committing mutations。</p>

<p>##state</p>

<p>###Single State Tree</p>

<p>Vuex使用一个单例状态树，这个单独的对象包含了所有应用级别的状态并且作为单独的真实数据来源。这意味在通常情况下你将在你的每个应用中只拥有一个store。使用一个单例状态树，能使定位某一块特殊的state的操作变得更加简单直接，而且允许我们能方便得得到一个当前应用状态的快照用于调试。</p>

<p>单例状态树并不与我们的模块化相冲突，在后面的章节中，我们将探讨如何去分离你的状态并变成子模块。</p>

<p>###Getting Vuex State into Vue Components</p>

<p>所以我们如何在vue的组件中展示Store中的state呢？因为Vuex store是具有反应性的，要从检索它里面检索state，最简单的方法就是在一个computed property返回一些store state。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// let's create a Counter component
const Counter = {
  template: `&lt;div&gt;8&lt;/div&gt;`,
  computed: {
    count () {
      return store.state.count
    }
  }
}
</code></pre></div></div>

<p>无论何时store.state.count发生改变，都将会导致computed property的重新评估，并触发关联DOM的更新。</p>

<p>然而这个模式导致组件将会依赖于一个公共store单例。当使用module系统时，需要在每个使用store state的组件中引入store，而且需要在测试组件的时候进行模拟。</p>

<p>Vuex提供一个机制使得跟组件能将store注入到每个子组件中，通过一个store选项配置（通过Vue.use(Vuex)启用）。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const app = new Vue({
  el: '#app',
  // provide the store using the "store" option.
  // this will inject the store instance to all child components.
  store,
  components: { Counter },
  template: `
    &lt;div class="app"&gt;
      &lt;counter&gt;&lt;/counter&gt;
    &lt;/div&gt;
  `
})
</code></pre></div></div>

<p>通过在根实例中配置store选项，store将会被注入到每一个子组件中，子组件就可以通过this.$store去使用，让我们更新我们的Counter去实现。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const Counter = {
  template: `&lt;div&gt;8&lt;/div&gt;`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}
</code></pre></div></div>

<p>###The mapState Helper</p>

<p>当一个组件需要利用多个store state 属性或getter时，在每个computed properties中声明它们将会是繁琐和重复的，我们可以使用mapState helper去应对这种情况，它生成了computed getter方法给我们，帮助我们保存一些按键信息。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// in standalone builds helpers are exposed as Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // arrow functions can make the code very succinct!
    count: state =&gt; state.count,

    // passing the string value 'count' is same as `state =&gt; state.count`
    countAlias: 'count',

    // to access local state with `this`, a normal function must be used
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
</code></pre></div></div>

<p>当映射的计算属性名称跟store状态树节点的名称相同时，我们可以只传递一个字符串的数组。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>computed: mapState([
  // map this.count to store.state.count
  'count'
])
</code></pre></div></div>
<p>###Object Spread Operator</p>

<p>mapState 返回的是一个整个Object，我们要如何与其他local计算属性配合使用。通常我们会使用utility去合并多个对象到一个对象，然后传给computed。然而通过object spread operator，我们可以大大的简化语法。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>computed: {
  localComputed () { /* ... */ },
  // mix this into the outer object with the object spread operator
  ...mapState({
    // ...
  })
}
</code></pre></div></div>

<p>###Components Can Still Have Local State</p>

<p>使用Vuex并不意味着你需要把所有的状态推送到Vuex，当然把更多的状态放到Vuex中管理，能让状态的变化变得更加明显和可调试。有时候这可能让代码变得更加啰嗦和迂回。如果一部分状态只属于一个独立的组件，那它就可以脱离只作为局部状态，你需要根据你的应用做出最合适的决定。</p>

<p>###Getters</p>

<p>有时候我们需要基于store state计算得出派生状态值，例如过滤一整个列表并计算它们：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo =&gt; todo.done).length
  }
}
</code></pre></div></div>

<p>如果多个组件都需要如此地使用，我们需要重复相同的函数，或者把它提取到一个共享的Helper中并在多个地方引用，两种方法都不理想。</p>

<p>Vuex允许我们在Stroe中定义 “getters” 。getters将会接收state作为第一个参数。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state =&gt; {
      return state.todos.filter(todo =&gt; todo.done)
    }
  }
})

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

<p>定义好的getters将会暴露在store.getters中:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>store.getters.doneTodos // -&gt; [{ id: 1, text: '...', done: true }]
</code></pre></div></div>

<p>Getters将其他定义好的getters最为第二个参数传入</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getters: {
  // ...
  doneTodosCount: (state, getters) =&gt; {
    return getters.doneTodos.length
  }
}
store.getters.doneTodosCount // -&gt; 1
</code></pre></div></div>

<p>我们可以轻松的在组件内部适应</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}
</code></pre></div></div>

<p>###The mapGetters Helper</p>

<p>mapGetters简单的映射store getters到local 计算属性：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
    // mix the getters into computed with object spread operator
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

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

<p>如果你想用一个不一样的名字去映射getter，可以使用一个对象：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
...mapGetters({
  // map this.doneCount to store.getters.doneTodosCount
  doneCount: 'doneTodosCount'
})

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

<p>提交一个mutation是改变一个Vuex store中状态的唯一方法，Vuex mutations与events十分相似，每一个mutation都有一个字符串名称和一个函数句柄。在句柄函数中我们执行状态的修改，并将状态作为第一个参数接收：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // mutate state
      state.count++
    }
  }
})

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

<p>你不能直接调用mutation的函数句柄，这里的操作会跟事件注册更相似：“当一个type为increment的mutation被触发，会调用它的句柄”。
要调用一个mutation的句柄，你需要调用store.commit并传入它的type</p>

<p>###Commit with Payload</p>

<p>调用store.commit的时候我们可以传入一个额外的参数，我们称之为mutation的payload。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

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

<p>在大多数情况下，传入的payload将会是一个对象，这将能包含更多的字段，并使得突变的记录变得更具可读性。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> // ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})
</code></pre></div></div>

<p>###Object-Style Commit</p>

<p>一个替代的方法去提交突变是传入一个带有type属性的对象</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>store.commit({
  type: 'increment',
  amount: 10
})
</code></pre></div></div>

<p>我们使用对象风格的提交，整个对象将会被当做payload传递给突变的句柄，随意句柄依旧是一样的。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
</code></pre></div></div>

<p>###Mutations Follow Vue’s Reactivity Rules</p>

<p>从一个Vuex store’s state被Vue变得具有反应性后，当我们改变一个状态，正在观察这个状态的Vue components将会自动的更新。这也意味着，Vuex mutations 也服从于相同的反应性告诫在一个单纯的vue环境中。</p>

<p>1.最好在你需要的所有字段之前初始化你的stroe的初始状态。</p>

<p>2.当你增加一个新的属性到一个对象的时候，你需要：</p>

<p>.使用Vue.set(obj, ‘newProp’, 123),
   或者使用一个新的对象替换</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   state.obj = { ...state.obj, newProp: 123 }
</code></pre></div></div>

<p>###Using Constants for Mutation Types</p>

<p>使用固定不变的Mutation Types 在各种Flux的实现中是十分常见的。这样做可以从像linters这样的工具中获取优势，并且把所有的常量放到一个文件中，能让你的合作同事更好地了解到有哪些mutations可以在整个应用中使用。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> // mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // we can use the ES2015 computed property name feature
    // to use a constant as the function name
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})
</code></pre></div></div>

<p>使用常量对于多人合作的大型项目来说是十分有益的，但这都是可配置取决于你是否喜欢。</p>

<p>###Mutations Must Be Synchronous</p>

<p>你需要记住一个非常重要的规则，mutation句柄函数必须是同步的。我们可以考虑如下的例子：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  mutations: {
  someMutation (state) {
    api.callAsyncMethod(() =&gt; {
      state.count++
    })
  }
}
</code></pre></div></div>

<p>想象一下我们在调试app，并且正在查看mutation日志信息，每一个突变被记录，开发者工具需要记录这个状态之前和之后的快照。但是，这个例子中异步回调的突变使得这个操作变得不可能。当mutation被提交的时候，回调还并未被调用，而且开发者并不能知道这个回调到底是否会被调用，将无法追踪在回调中发生的状态变化。</p>

<p>###Committing Mutations in Components</p>

<p>你可以使用this.$store.commit(‘xxx’)在组件中提交mutation,或者我们可以使用mapMutations Helper ，他将映射组件方法到store.commit调用</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // map this.increment() to this.$store.commit('increment')
    ]),
    ...mapMutations({
      add: 'increment' // map this.add() to this.$store.commit('increment')
    })
  }
}
</code></pre></div></div>

<p>###On to Actions</p>

<p>在状态mutation中结合异步操作，将会使你的程序变得难以推论。举个例子，当你在改变状态的方法中同时调用两个异步的方法，你不知道回调什么时候被调用，也不知道哪个回调会被先调用。这就是为什么我们会分开为两个概念，在Vuex中，mutations are synchronous transactions。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>store.commit('increment')
// any state change that the "increment" mutation may cause
// should be done at this moment.
</code></pre></div></div>

<p>Actions跟mutations 相似，有如下的两点不同：</p>

<ol>
  <li>不再直接修改state，而是提交mutations</li>
  <li>Actions可以包含任意的一步操作</li>
</ol>

<p>让我们注册一个简单的action</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})
</code></pre></div></div>

<p>Action 的句柄接收一个上下文对象作为参数，这个对象暴露了与Stroe相同的对象和属性，所以你可调用context.commit去提交一个mutation,或者通过context.state 和 context.getters获取state和getters。</p>

<p>在实际编写时，我们会通过使用ES2015的解构语法简化我们的代码。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>actions: {
  increment ({ commit }) {
    commit('increment')
  }
}
</code></pre></div></div>

<p>###Dispatching Actions</p>

<p>Action 被 store.dispatch方法触发</p>

<p>我们可以在Action实现异步操作：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>actions: {
  incrementAsync ({ commit }) {
    setTimeout(() =&gt; {
      commit('increment')
    }, 1000)
  }
}
</code></pre></div></div>

<p>Actions同样支持payload模式和对象风格的dispatch</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// dispatch with a payload
store.dispatch('incrementAsync', {
  amount: 10
})

// dispatch with an object
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})
</code></pre></div></div>

<p>一个更现实的例子是查看购物车，结合了调用异步API和committing multiple mutations：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>actions: {
  checkout ({ commit, state }, products) {
    // save the items currently in the cart
    const savedCartItems = [...state.cart.added]
    // send out checkout request, and optimistically
    // clear the cart
    commit(types.CHECKOUT_REQUEST)
    // the shop API accepts a success callback and a failure callback
    shop.buyProducts(
      products,
      // handle success
      () =&gt; commit(types.CHECKOUT_SUCCESS),
      // handle failure
      () =&gt; commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}
</code></pre></div></div>

<p>注意我们实现了一系列一步操作，并通过commit方法记录了副作用 (state mutations) ；</p>

<p>###Dispatching Actions in Components</p>

<p>你可以通过调用this.$store.dispatch(‘xxx’)分发Action在组件中，或者使用mapActions helper映射组件的方法到store.dispatch。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment' // map this.increment() to this.$store.dispatch('increment')
    ]),
    ...mapActions({
      add: 'increment' // map this.add() to this.$store.dispatch('increment')
    })
  }
}
</code></pre></div></div>

<p>Action通常是异步的，所以我们要怎么才能知道一个Action已经结束了？还有更重要的是，我们要如何组合多个action一起去处理更复杂的异步流。</p>

<p>你需要知道store.dispatch能处理由一个被触发的action返回的promise，而且它自身也能返回一个promise</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) =&gt; {
      setTimeout(() =&gt; {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

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

<p>所以你可以这么做：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>store.dispatch('actionA').then(() =&gt; {
  // ...
})
</code></pre></div></div>

<p>其他的action也一样</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() =&gt; {
      commit('someOtherMutation')
    })
  }
}

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

<p>如果使用 async / await，我们还可以如此组合actions</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for actionA to finish
    commit('gotOtherData', await getOtherData())
  }
}
</code></pre></div></div>

<p>store.dispatch可能会触发多个action不同的moudle中，再这样的情况下返回的值将会是一个promise，它将解决什么时候所有触发的句柄已经全部执行完毕的问题。</p>

<p>###Modules</p>

<p>由于使用一个仅有的状体树，程序所包含的所有状态全都在一个对象中，随着程序的壮大，store将会变得十分臃肿。</p>

<p>为了解决这个问题，Vuex允许我们将store分解到多个Moudle中。每一个Moudle拥有它的state, mutations, actions, getters, 和 nested modules。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -&gt; moduleA's state
store.state.b // -&gt; moduleB's state

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

<p>###Module Local State</p>

<p>在模块内的mutations 和 getters，接收到的参数将是模块自身的局部state</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const moduleA = {
  state: { count: 0 },
  mutations: {
    increment: (state) {
      // state is the local module state
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}
</code></pre></div></div>

<p>相似的，在模块内部的actions，context.state将会暴露局部state，根状态将通过context.rootState暴露。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const moduleA = {
  // ...
  actions: {
    incrementIfOdd ({ state, commit }) {
      if (state.count % 2 === 1) {
        commit('increment')
      }
    }
  }
}
</code></pre></div></div>

<p>在模块内的getters中，根状态将作为第三个参数传入。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}
</code></pre></div></div>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>vue</category>
      
      <category>vuex</category>
      
    </item>
    
    <item>
      <title>记一次性能优化（埋坑与填坑的过程）</title>
      <link>https://bio.wuhh.cc/2016/09/09/mark-chorme-layout.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2016/09/09/mark-chorme-layout.html</guid>
      <pubDate>Fri, 09 Sep 2016 12:44:10 +0000</pubDate>
      <description>接到一个需求，需要实现拖动单元格来自定义地调整宽度，啪啪啪，敲完代码，调试完工😊。</description>
      <content:encoded><![CDATA[<p>接到一个需求，需要实现拖动单元格来自定义地调整宽度，啪啪啪，敲完代码，调试完工😊。</p>

<p>测试发现在单页列表条数达到一定数量时会在拖动的过程中出现明显的卡顿，会影响到用户的体验。</p>

<p>根据情况，我猜想是由于在列表拖动的过程中，随着鼠标的拖动，列表每一行的宽度都跟随进行了调整，也随之导致了卡顿的情况。于是我决定在拖动的过程中，只调整显示区域列表的单元格的宽度，在拖动完毕之后再调整所有行的该列宽度。策略就是在列表上下滚动的过程中，为处于显示区域的行添加一个特征class，在拖动的过程中只调整拥有该特征class的行的列宽度。</p>

<p>写完代码之后发现，由于在监视scroll事件的过程中，需要遍历每一行，并对行进行class赋值，频繁的操作导致列表上下滚动的时候也出现了卡顿的问题。</p>

<p>为了解决这个问题，我决定用setTimeout实现只在列表停止上下滚动的时候执行遍历操作，从而减少卡顿的情况。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var $body = this.$el.parent()
    it = 0,
    self = this;
	$body.on("scroll",function(){
          clearTimeout(it);
          it = setTimeout(function(){
               self.getMethod("resizeSomeColumn")
          },500)   
    })
</code></pre></div></div>

<p>写完这些之后，再去查看列表宽度调整时的卡顿的情况，发现情况依旧😣。</p>

<p>于是想，不能这么瞎猜了，google 之后发现需要Chrome DevTools 去分析卡顿产生的原因。</p>

<p>在Timeline 面板，录制了宽度调整的过程，发现大多Frame的 fps只有2~5，在跳到Layout Tree(需在Chrome开启该工鞥) 查看之后发现，每次列表宽度的调整，都会导致整个列表的重新布局，如此导致了卡顿的情况，即使只调整了显示区域的行的列宽度，但整个列表还是重新布局了。</p>

<p>回过头来发现，为了在宽度调整的过程中不出现显示错位的情况，代码中有存在当行内列宽度调整的时候，就去的调整整个列表宽度的代码。</p>

<p>调整了这些代码，在列表宽度开始调整的时候就将列表包裹块的宽度增长到一定宽度，宽度调整完毕后在计算设置包裹块的宽度，移除了在拖动过程中的列表包裹块的宽度调整。</p>

<p>这样修改后，列表宽度调整过程中的卡顿不再明显，但Timeline面板中依旧显示存在很多long frame，还需要进行优化，以达到更好的显示效果。</p>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>chrome</category>
      
      <category>javascript</category>
      
    </item>
    
    <item>
      <title>使用VS Code调试JavaScript代码</title>
      <link>https://bio.wuhh.cc/2016/09/09/debugger-for-chrome.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2016/09/09/debugger-for-chrome.html</guid>
      <pubDate>Fri, 09 Sep 2016 12:44:10 +0000</pubDate>
      <description>在VS Code中，配合Debugger for Chrome插件，可以实现在编辑器中对JavaScript代码进行调试的功能，能更加方便的开发以Tatami为框架的单页面应用。</description>
      <content:encoded><![CDATA[<p>在VS Code中，配合Debugger for Chrome插件，可以实现在编辑器中对JavaScript代码进行调试的功能，能更加方便的开发以Tatami为框架的单页面应用。</p>

<p>安装插件</p>

<p><img src="//whhbio.oss-cn-beijing.aliyuncs.com/blog/install.gif" alt="" /></p>

<p>创建配置文件</p>

<p><img src="//whhbio.oss-cn-beijing.aliyuncs.com/blog/config.gif" alt="" /></p>

<p>插件存在两种运行模式，一是启动Chrome浏览器并跳转到你的应用页面，二是连接一个运行的Chrome实例。两种模式都可以通过.vscode/launch.json文件配置，VS Code可以为你自动生成一个配置文件，当然你也可以自己手动编写。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Chrome against localhost, with sourcemaps",
            "type": "chrome",
            "request": "launch",
            "url": "http://localhost:8088",
            "sourceMaps": true,
            "webRoot": "${workspaceRoot}"
        },
        {
            "name": "Attach to Chrome, with sourcemaps",
            "type": "chrome",
            "request": "attach",
            "port": 9222,
            "sourceMaps": true,
            "webRoot": "${workspaceRoot}"
        }
    ]
}
</code></pre></div></div>

<p>上面的样例配置文件中，分别对应了两种启动模式，可以通过更改webRoot属性改变项目的根目录。request属性为launch的配置，可以指定url作为你应用所需要启动的页面地址。</p>

<p>启动调试前，需要利用命令行，打开Chrome的远程调试功能</p>

<p><img src="//whhbio.oss-cn-beijing.aliyuncs.com/blog/iterm.gif" alt="" /></p>

<p><strong>Windows</strong></p>

<ul>
  <li>Right click the Chrome shortcut, and select properties</li>
  <li>In the “target” field, append <code class="language-plaintext highlighter-rouge">--remote-debugging-port=9222</code></li>
  <li>Or in a command prompt, execute <code class="language-plaintext highlighter-rouge">&lt;path to chrome&gt;/chrome.exe --remote-debugging-port=9222</code></li>
</ul>

<p><strong>OS X</strong></p>

<ul>
  <li>In a terminal, execute <code class="language-plaintext highlighter-rouge">/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222</code></li>
</ul>

<p><strong>Linux</strong></p>

<ul>
  <li>In a terminal, launch <code class="language-plaintext highlighter-rouge">google-chrome --remote-debugging-port=9222</code></li>
</ul>

<p>启动调试</p>

<p><img src="//whhbio.oss-cn-beijing.aliyuncs.com/blog/attach.gif" alt="" /></p>]]></content:encoded>
      
      <category>ide</category>
      
      
      <category>microsoft</category>
      
      <category>javascript</category>
      
    </item>
    
    <item>
      <title>Learn from jQuery</title>
      <link>https://bio.wuhh.cc/2016/08/11/jquery.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2016/08/11/jquery.html</guid>
      <pubDate>Thu, 11 Aug 2016 15:44:10 +0000</pubDate>
      <description>  如果说jQuery是一把瓦雷利亚利剑，众多骑士靠着这把宝剑在前端界披荆斩棘，那JavaScript就是锻造这把剑的瓦雷利亚钢。瓦雷利亚钢是一种注入了魔法的钢铁，需要特殊的制作工艺制造，当然javascript并不是一种自身拥有魔法的语言，是靠着众多的“工匠”们的聪明才智给其注入了神奇的魔法，才能锻造出众多像jQuery一样的神兵利器。</description>
      <content:encoded><![CDATA[<p>  如果说jQuery是一把瓦雷利亚利剑，众多骑士靠着这把宝剑在前端界披荆斩棘，那JavaScript就是锻造这把剑的瓦雷利亚钢。瓦雷利亚钢是一种注入了魔法的钢铁，需要特殊的制作工艺制造，当然javascript并不是一种自身拥有魔法的语言，是靠着众多的“工匠”们的聪明才智给其注入了神奇的魔法，才能锻造出众多像jQuery一样的神兵利器。</p>

<p>  不知到多少人跟我一样，接触web脚本编写的初始并不是从javascript入手的，而是一上场就拿着jQuery，一阵糊捅蛮砍之后发现很多的效果也都实现了。当然久而久之，我们会不再满足于万事都依赖jQuery的日子，会想摆脱jQuery的阴影，去创造一把属于自己的宝剑。那么如何去做呢？系统的学习一遍javascript是十分有必要的，像红宝书这样的“圣经”应当时常拿出来诵读。除此之外，既然我们有jQuery这么好的导师，我们为什么不从他开始呢。</p>

<p>  jQuery的源码很轻松的能在github上找到,进入到页面中，我看到了src文件夹，那jQuery的源码自然就在其中，但是打开文件夹看到众多文件和文件夹，平时使用的jQuery不是就只引用了一个文件吗，这么多文件是怎么并成一个文件的呢？这时候，我看到了熟悉的jQuery.js文件，打开这个文件之后，看到了如下的代码</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>define( [
	"./core",
	"./selector",
	...//省略部分
	"./dimensions",
	"./deprecated",
	"./exports/amd"
], function( jQuery ) {

	"use strict";

	return ( window.jQuery = window.$ = jQuery );

} ); &amp;emsp;&amp;emsp;这应该是一个入口文件，谷歌了define这个关键词之后，我知道这是一种被称作AMD的模块加载方式。 &gt;&amp;emsp;&amp;emsp;异步模块定义规范（AMD）制定了定义模块的规则，这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应（浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题）。
</code></pre></div></div>

<p>  jQuery这样做除了将代码模块化管理之外，也可以方便其他开发者重用代码，以及可以配合<a href="http://requirejs.org/" title="RequireJS">RequireJS</a> 等加载器对资源进行加载。jQuery的功能十分丰富，单在某些场景下其实并不需要所有的功能，模块化之后，可以定义自己需要的模块，减少网页所需加载资源的大小。但是到这里我还不知道jQuery如何将这些小的模块打包成一个文件的，网上翻阅资料后，在“<a href="http://hzxiaosheng.bitbucket.org/work/2014/02/17/jQuery-build-process.html" title="jQuery小文件合并生成大文件过程">jQuery小文件合并生成大文件过程</a>”这篇文章中了解到了详细的打包过程，文章介绍的十分详细，具体内容可以点文章中进行查阅。</p>

<p>  前面提到的jQuery.js便是入口文件，依赖的子模块都在define中定义，看到依赖的一个子模块是core.js,那么就从这个文件着手。</p>

<p>  在这里我们先回顾一下工作中是如果使用jQuery的,举个最简单的例子：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$("#container").height(100);
</code></pre></div></div>

<p>  上面这段代码中，实现了将Id为container的元素的高度设置为100。看到$()返回的对象执行了height方法，为指定的元素设置了高度属性。这里先看下，$()方法实现的是什么。</p>

<p>jquery.js</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>return ( window.jQuery = window.$ = jQuery );
</code></pre></div></div>

<p>core.js</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jQuery = function( selector, context ) {

	// The jQuery object is actually just the init constructor 'enhanced'
	// Need init if jQuery is called (just allow error to be thrown if not included)
	return new jQuery.fn.init( selector, context );
}

jQuery.fn = jQuery.prototype = {...};

jQuery.extend = jQuery.fn.extend = function() {...}
</code></pre></div></div>

<p>init.js</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var init = jQuery.fn.init = function( selector, context, root ) {
...
};


init.prototype = jQuery.fn;
</code></pre></div></div>

<p>  如上几个代码段实现了我们在上面使用的$()方法，我们大致用JavaScript模拟一下：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//构造函数
var Foo=function(name){
	
	//返回以init方法为构造函数的一个实例
	return new Foo.fn.init(name); 
}
</code></pre></div></div>

   	//定义原型
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Foo.prototype=Foo.fn={
  say:function(){
     console.log(this.name);
  }
}

//定义原型的一个方法
Foo.fn.init=function(name){

	this.name=name
}

//将原型赋给的init方法的原型
Foo.fn.init.prototype=Foo.fn;

$=Foo;


$("123").say();
</code></pre></div></div>

<p>  在写下上面这段代码之前，我先去了解了有关JavaScript原型的知识，查阅相关资料以及教程之后做出的简单模拟。</p>]]></content:encoded>
      
      <category>javascript</category>
      
      
      <category>jquery</category>
      
      <category>javascript</category>
      
    </item>
    
    <item>
      <title>在Github上使用Jekyll建立博客站点</title>
      <link>https://bio.wuhh.cc/2015/07/05/hello.html</link>
      <guid isPermaLink="true">https://bio.wuhh.cc/2015/07/05/hello.html</guid>
      <pubDate>Sun, 05 Jul 2015 20:48:10 +0000</pubDate>
      <description>翻阅多篇网络日志，花费一晚上的时间完成个人站点的建设，虽很简陋，却也是百废待兴，待我慢慢完善，现将经验分享如下。</description>
      <content:encoded><![CDATA[<blockquote>
  <p>翻阅多篇网络日志，花费一晚上的时间完成个人站点的建设，虽很简陋，却也是百废待兴，待我慢慢完善，现将经验分享如下。</p>
</blockquote>

<h3 id="域名申请">域名申请</h3>

<p>登陆万网使用支付宝购买所需要的域名即可，本来想买.me的域名，但考虑价格因素还是购买了更为便宜的.com域名。</p>

<h3 id="github建立个人主页">Github建立个人主页</h3>

<ul>
  <li>
    <p>建立Github账户，新建仓库（New repository），仓库名称需采用yourname.github.io格式，将yourname替换为自己项目的名称。</p>
  </li>
  <li>
    <p>跳转到仓库设置(Settings)页面，在GitHub Pages框中点击Launch automatic page generatro按钮生成基础模板页面。</p>
  </li>
  <li>
    <p>根据指引进行站点信息设置，选择个性模板，完成静态页面生成，访问yourname.github.io</p>
  </li>
</ul>

<h3 id="git-本地安装windows环境">Git 本地安装（windows环境）</h3>
<ul>
  <li>安装<a href="https://git-scm.com/">Git</a>和<a href="http://sourceforge.net/projects/gitextensions/">Git Extensions</a>。</li>
  <li>选定一个文件目录，右击选择GitExt Clone，填入主页仓库的clone URL，将静态页面克隆到本地。</li>
</ul>

<h3 id="安装jekyllwindows环境需要安装rubypython环境">安装Jekyll(windows环境，需要安装ruby，python)环境</h3>
<ul>
  <li>
    <p>安装<a href="https://www.ruby-lang.org/en/">Ruby 和 Ruby Development Kit</a>,并且运行以下命令。</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby dk.rb init
ruby dk.rb install
</code></pre></div>    </div>
  </li>
  <li>
    <p>运行一下命令安装Jeklly</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install jekyll
</code></pre></div>    </div>
  </li>
  <li>
    <p>安装<a href="https://www.python.org/">Python</a>,</p>
  </li>
  <li>
    <p>安装easy_install，从https://pypi.python.org/pypi/setuptools#windows 处下载 distribute_setup.py 文件，并通过以下命令运行该脚本文件。命令执行完毕后，将Python目录下的Scripts添加到系统环境中。</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  python distribute_setup.py
</code></pre></div>    </div>
  </li>
  <li>
    <p>通过运行下面的命令安装Pygments</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>easy_install Pygments
</code></pre></div>    </div>
  </li>
  <li>
    <p>删除上一步中克隆到本地的仓库中的所有文件（如果想要使用自动生成的模板文件，可以将这些文件进行备份），运行如下命令新建Jekyll项目</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll new myblog
</code></pre></div>    </div>
  </li>
  <li>
    <p>运行Jekyll,并通过http://localhost:4000 地址预览新生成的博客。</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd myblog
jekyll serve
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="推送jkyll博客至github">推送Jkyll博客至Github</h3>

<ul>
  <li>在仓库右击选择打开GitExt Browser,提交修改的文件，并推送到Github远程仓库中，Github将会自动生成博客静态页面，通过yourname.github.io地址访问。</li>
</ul>

<p>###解析域名至yourname.github.io ###</p>

<ul>
  <li>
    <p>注册<a href="https://www.dnspod.cn/">DNSpod</a>服务。<a href="https://support.dnspod.cn/Kb/showarticle/tsid/177/">点击查看教程</a></p>
  </li>
  <li>
    <p>在购买域名的注册商处添加DNSPod的2个DNS短地址</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>f1g1ns1.dnspod.net
f1g1ns2.dnspod.net
</code></pre></div>    </div>
  </li>
  <li>
    <p>博客仓库根目录下新建CNAME文件（不带后缀）,文件内只需填写你所购买的域名www.youname.com,推送到Github上完成域名解析。</p>
  </li>
</ul>

<h2 id="参考网址">参考网址</h2>

<p>http://www.cellier.me/2015/01/04/jekyll%E6%90%AD%E5%BB%BA%E5%8D%9A%E5%AE%A2%E6%95%99%E7%A8%8B/</p>

<p>http://justcoding.iteye.com/blog/1959737</p>

<p>http://blog.csdn.net/u012844814/article/details/28869997</p>

<p>http://www.madhur.co.in/blog/2011/09/01/runningjekyllwindows.html</p>]]></content:encoded>
      
      <category>skill</category>
      
      
      <category>web</category>
      
      <category>jekyll</category>
      
    </item>
    
  </channel>
</rss>
