update title

This commit is contained in:
Fengda HUANG 2015-10-23 23:33:54 +08:00
parent bf41b3926a
commit 5d6af08c47
4 changed files with 1060 additions and 15 deletions

View file

@ -1,4 +1,4 @@
#Github项目分析
#Github流行项目分析
之前曾经分析过一些Github的用户行为现在我们先来说说Github上的Star吧。(截止: 2015年3月9日23时。)

View file

@ -8,8 +8,6 @@
今天就来说说是怎样做的。
##Github项目组成
以之前造的[Lettuce](https://github.com/phodal/lettuce)为例,里面有:
- 代码质量(Code Climate)
@ -25,7 +23,7 @@
等等。
##Skillock模块化
###Skillock模块化
在SkillTree的源码里大致分为三部分:
@ -122,8 +120,6 @@ return {
当然函数也是一个对象。
##Skillock测试
###自动化测试
一直习惯用Travis CI于是也继续用Travis Ci``.travis.yml``配置如下所示:
@ -161,7 +157,7 @@ branches:
最后的``test/spec``是指定测试的目录。
##Jshint
###Jshint
> JSLint定义了一组编码约定这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验并以一条年代久远的编程原则 作为宗旨能做并不意味着应该做。JSLint会对它认为有的编码实践加标志另外还会指出哪些是明显的错误从而促使你养成好的 JavaScript编码习惯。
@ -254,7 +250,7 @@ it('should return book label & url', function () {
这就是个问题了于是偶然间看到了一个叫code climate的网站。
##Code Climate
###Code Climate
> Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.
@ -284,11 +280,11 @@ A | lib/url_handler.js | 9 | 0 | 5 | 2.2 | 94.1% | 0
![Coverage][1]
##代码的坏味道
###代码的坏味道
于是我们就打开``lib/database/sqlite_helper.js``,因为其中有两个坏味道
###Similar code found in two :expression_statement nodes (mass = 86)
Similar code found in two :expression_statement nodes (mass = 86)
在代码的 ``lib/database/sqlite_helper.js:58…61 < >``

View file

@ -1133,7 +1133,7 @@ def get_points(usernames):
真看不出来两者有什么相似的地方 。。。。
#Github项目分析
#Github流行项目分析
之前曾经分析过一些Github的用户行为现在我们先来说说Github上的Star吧。(截止: 2015年3月9日23时。)
@ -1199,12 +1199,550 @@ C | 2
#构建Github项目
##从模块分离到测试
在之前说到
> 奋斗了近半个月后将fork的代码读懂、重构、升级版本、调整添加新功能、添加测试、添加CI、添加分享之后终于almost finish。
今天就来说说是怎样做的。
以之前造的[Lettuce](https://github.com/phodal/lettuce)为例,里面有:
- 代码质量(Code Climate)
- CI状态(Travis CI)
- 测试覆盖率(96%)
- 自动化测试(npm test)
- 文档
按照[Web Developer路线图](https://github.com/phodal/awesome-developer)来说,我们还需要有:
- 版本管理
- 自动部署
等等。
###Skillock模块化
在SkillTree的源码里大致分为三部分:
- namespace函数: 故名思意
- Calculator也就是TalentTree主要负责解析、生成url头像依赖等等
- Skill 主要是tips部分。
而这一些都在一个js里对于一个库来说是一件好事但是对于一个项目来说并非如此。
依赖的库有
- jQuery
- Knockout
好在Knockout可以用Require.js进行管理于是使用了``Require.js``进行管理:
```html
<script type="text/javascript" data-main="app/scripts/main.js" src="app/lib/require.js"></script>
```
``main.js``配置如下:
```javascript
require.config({
baseUrl: 'app',
paths:{
jquery: 'lib/jquery',
json: 'lib/json',
text: 'lib/text'
}
});
require(['scripts/ko-bindings']);
require(['lib/knockout', 'scripts/TalentTree', 'json!data/web.json'], function(ko, TalentTree, TalentData) {
'use strict';
var vm = new TalentTree(TalentData);
ko.applyBindings(vm);
});
```
text、json插件主要是用于处理web.json即用json来处理技能于是不同的类到了不同的js文件。
.
|____Book.js
|____Doc.js
|____ko-bindings.js
|____Link.js
|____main.js
|____Skill.js
|____TalentTree.js
|____Utils.js
加上了后来的推荐阅读书籍等等。而Book和Link都是继承自Doc。
```javascript
define(['scripts/Doc'], function(Doc) {
'use strict';
function Book(_e) {
Doc.apply(this, arguments);
}
Book.prototype = new Doc();
return Book;
});
```
而这里便是后面对其进行重构的内容。Doc类则是Skillock中类的一个缩影
```javascript
define([], function() {
'use strict';
var Doc = function (_e) {
var e = _e || {};
var self = this;
self.label = e.label || (e.url || 'Learn more');
self.url = e.url || 'javascript:void(0)';
};
return Doc;
});
```
或者说这是一个AMD的Class应该有的样子。考虑到this的隐性绑定作者用了self=this来避免这个问题。最后Return了这个对象我们在调用的就需要new一个。大部分在代码中返回的都是对象除了在Utils类里面返回的是函数:
```javascript
return {
getSkillsByHash: getSkillsByHash,
getSkillById: getSkillById,
prettyJoin: prettyJoin
};
```
当然函数也是一个对象。
###自动化测试
一直习惯用Travis CI于是也继续用Travis Ci``.travis.yml``配置如下所示:
```yml
language: node_js
node_js:
- "0.10"
notifications:
email: false
branches:
only:
- gh-pages
```
使用gh-pages的原因是我们一push代码的时候就可以自动测试、部署等等好处一堆堆的。
接着我们需要在``package.json``里面添加脚本
```javascript
"scripts": {
"test": "mocha"
}
```
这样当我们push代码的时候便会自动跑所有的测试。因为mocha的主要配置是用``mocha.opts``,所以我们还需要配置一下``mocha.opts``
--reporter spec
--ui bdd
--growl
--colors
test/spec
最后的``test/spec``是指定测试的目录。
###Jshint
> JSLint定义了一组编码约定这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验并以一条年代久远的编程原则 作为宗旨能做并不意味着应该做。JSLint会对它认为有的编码实践加标志另外还会指出哪些是明显的错误从而促使你养成好的 JavaScript编码习惯。
当我们的js写得不合理的时候这时测试就无法通过:
line 5 col 25 A constructor name should start with an uppercase letter.
line 21 col 62 Strings must use singlequote.
这是一种驱动写出更规范js的方法。
###Mocha
> Mocha 是一个优秀的JS测试框架支持TDD/BDD结合 should.js/expect/chai/better-assert能轻松构建各种风格的测试用例。
最后的效果如下所示:
Book,Link
Book Test
✓ should return book label & url
Link Test
✓ should return link label & url
###测试用例
简单地看一下Book的测试:
```javascript
/* global describe, it */
var requirejs = require("requirejs");
var assert = require("assert");
var should = require("should");
requirejs.config({
baseUrl: 'app/',
nodeRequire: require
});
describe('Book,Link', function () {
var Book, Link;
before(function (done) {
requirejs(['scripts/Book'、], function (Book_Class) {
Book = Book_Class;
done();
});
});
describe('Book Test', function () {
it('should return book label & url', function () {
var book_name = 'Head First HTML与CSS';
var url = 'http://www.phodal.com';
var books = {
label: book_name,
url: url
};
var _book = new Book(books);
_book.label.should.equal(book_name);
_book.url.should.equal(url);
});
});
});
```
因为我们用``require.js``来管理浏览器端,在后台写测试来测试的时候,我们也需要用他来管理我们的依赖,这也就是为什么这个测试这么从的原因,多数情况下一个测试类似于这样子的。(用Jasmine似乎会是一个更好的主意但是用习惯Jasmine了)
```javascript
describe('Book Test', function () {
it('should return book label & url', function () {
var book_name = 'Head First HTML与CSS';
var url = 'http://www.phodal.com';
var books = {
label: book_name,
url: url
};
var _book = new Book(books);
_book.label.should.equal(book_name);
_book.url.should.equal(url);
});
});
```
最后的断言,也算是测试的核心,保证测试是有用的。
##Code Climate来clean code与重构
- 当你写了一大堆代码,你没有意识到里面有一大堆重复。
- 当你写了一大堆测试,却不知道覆盖率有多少。
这就是个问题了于是偶然间看到了一个叫code climate的网站。
###Code Climate
> Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.
Code Climate整合一组静态分析工具的结果到一个单一的实时的报告让您的团队需要识别热点探讨新的方法提高代码质量的信息。
简单地来说:
- 对我们的代码评分
- 找出代码中的坏味道
于是,我们先来了个例子
Rating | Name | Complexity | Duplication | Churn | C/M | Coverage | Smells
--------|------|--------------|-------------|----------|---------|---------------------
A | lib/coap/coap_request_handler.js | 24 | 0 | 6 | 2.6 | 46.4% | 0
A | lib/coap/coap_result_helper.js | 14 | 0 | 2 | 3.4 | 80.0% | 0
A | lib/coap/coap_server.js | 16 | 0 | 5 | 5.2 | 44.0% | 0
A | lib/database/db_factory.js | 8 | 0 | 3 | 3.8 | 92.3% | 0
A | lib/database/iot_db.js | 7 | 0 | 6 | 1.0 | 58.8% | 0
A | lib/database/mongodb_helper.js | 63 | 0 | 11 | 4.5 | 35.0% | 0
C | lib/database/sqlite_helper.js | 32 | 86 | 10 | 4.5 | 35.0% | 2
B | lib/rest/rest_helper.js | 19 | 62 | 3 | 4.7 | 37.5% | 2
A | lib/rest/rest_server.js | 17 | 0 | 2 | 8.6 | 88.9% | 0
A | lib/url_handler.js | 9 | 0 | 5 | 2.2 | 94.1% | 0
分享得到的最后的结果是:
![Coverage][1]
###代码的坏味道
于是我们就打开``lib/database/sqlite_helper.js``,因为其中有两个坏味道
Similar code found in two :expression_statement nodes (mass = 86)
在代码的 ``lib/database/sqlite_helper.js:58…61 < >``
```javascript
SQLiteHelper.prototype.deleteData = function (url, callback) {
'use strict';
var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
```
lib/database/sqlite_helper.js:64…67 < >
```javascript
SQLiteHelper.prototype.getData = function (url, callback) {
'use strict';
var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
```
只是这是之前修改过的重复。。
原来的代码是这样的
```javascript
SQLiteHelper.prototype.postData = function (block, callback) {
'use strict';
var db = new sqlite3.Database(config.db_name);
var str = this.parseData(config.keys);
var string = this.parseData(block);
var sql_command = "insert or replace into " + config.table_name + " (" + str + ") VALUES (" + string + ");";
db.all(sql_command, function (err) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
callback();
});
};
SQLiteHelper.prototype.deleteData = function (url, callback) {
'use strict';
var db = new sqlite3.Database(config.db_name);
var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
db.all(sql_command, function (err) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
callback();
});
};
SQLiteHelper.prototype.getData = function (url, callback) {
'use strict';
var db = new sqlite3.Database(config.db_name);
var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
db.all(sql_command, function (err, rows) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
callback(JSON.stringify(rows));
});
};
```
说的也是大量的重复,重构完的代码
```javascript
SQLiteHelper.prototype.basic = function(sql, db_callback){
'use strict';
var db = new sqlite3.Database(config.db_name);
db.all(sql, function (err, rows) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
db_callback(JSON.stringify(rows));
});
};
SQLiteHelper.prototype.postData = function (block, callback) {
'use strict';
var str = this.parseData(config.keys);
var string = this.parseData(block);
var sql_command = "insert or replace into " + config.table_name + " (" + str + ") VALUES (" + string + ");";
SQLiteHelper.prototype.basic(sql_command, callback);
};
SQLiteHelper.prototype.deleteData = function (url, callback) {
'use strict';
var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
};
SQLiteHelper.prototype.getData = function (url, callback) {
'use strict';
var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
};
```
重构完后的代码比原来还长,这似乎是个问题~~
#创建项目文档
#测试
#重构
或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。
有一天我发现当我需要我一次又一次地重复讲述某些内容于是我就计划着把这些应该掌握的技能放到Github上也就有了[Artisan Stack](https://github.com/artisanstack) 计划。
每个程序员都不可避免地是一个Coder一个没有掌握好技能的Coder算不上是手工艺人但是是手工人。
艺,需要有创造性的方法。
#[前端技能训练: 重构一](http://www.phodal.com/blog/frontend-improve-refactor-javascript-code/)
##为什么重构?
> 为了更好的代码。
在经历了一年多的工作之后我平时的主要工作就是修Bug。刚开始的时候觉得无聊后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码有时候你可能要花几天的时间去阅读代码。而你重写那几十代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做你的修改只会带来更多的bug。修Bug更多的是维护代码。还是前人总结的那句话对:
> 写代码容易,读代码难。
假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。
如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试,``没有测试的重构``。
从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。
让我们来看看我们的第一个训练,相当有挑战性。
##重构uMarkdown
代码及setup请见github: [js-refactor](https://github.com/artisanstack/js-refactor)
###代码说明
``uMarkdown``是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:
```javascript
/* code */
while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
str = str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, '&nbsp;') + '</code>\n');
}
/* headlines */
while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
count = stra[1].length;
str = str.replace(stra[0], '<h' + count + '>' + stra[2] + '</h' + count + '>' + '\n');
}
/* mail */
while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
str = str.replace(stra[0], '<a href="mailto:' + stra[1] + '">' + stra[1] + '</a>');
}
```
选这个做重构的开始,不仅仅是因为之前在写[EchoesWorks](https://github.com/phodal/echoesworks)的时候进行了很多的重构。而且它更适合于,``重构到设计模式``的理论。让我们在重构完之后给作者进行pull request吧。
Markdown的解析过程有点类似于``Pipe and Filters``模式(架构模式)。
Filter即我们在代码中看到的正规表达式集:
```javascript
regexobject: {
headline: /^(\#{1,6})([^\#\n]+)$/m,
code: /\s\`\`\`\n?([^`]+)\`\`\`/g
```
他会匹配对应的Markdown类型随后进行替换和处理。而``str```,就是管理口的输入和输出。
接着,我们就可以对其进行简单的重构。
###重构
(ps: 推荐用WebStrom来做重构自带重构功能)
作为一个示例我们先提出codeHandler方法即将上面的
```javascript
/* code */
while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
str = str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, '&nbsp;') + '</code>\n');
}
```
提取方法成
```javascript
codeFilter: function (str, stra) {
return str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, '&nbsp;') + '</code>\n');
},
```
while语句就成了
```javascript
while ((stra = regexobject.code.exec(str)) !== null) {
str = this.codeFilter(str, stra);
}
```
然后,运行所有的测试。
```
grunt test
```
同理我们就可以``mail``、``headline``等方法进行重构。接着就会变成类似于下面的代码,
```javascript
/* code */
while ((execStr = regExpObject.code.exec(str)) !== null) {
str = codeHandler(str, execStr);
}
/* headlines */
while ((execStr = regExpObject.headline.exec(str)) !== null) {
str = headlineHandler(str, execStr);
}
/* lists */
while ((execStr = regExpObject.lists.exec(str)) !== null) {
str = listHandler(str, execStr);
}
/* tables */
while ((execStr = regExpObject.tables.exec(str)) !== null) {
str = tableHandler(str, execStr, strict);
}
```
然后你也看到了上面有一堆重复的代码接着让我们用JavaScript的``奇技浮巧``即apply方法把上面的重复代码变成。
```javascript
['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
while ((stra = regexobject[type].exec(str)) !== null) {
str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
}
});
```
进行测试blabla都是过的。
```javascript
Markdown
✓ should parse h1~h3
✓ should parse link
✓ should special link
✓ should parse font style
✓ should parse code
✓ should parse ul list
✓ should parse ul table
✓ should return correctly class name
```
快来试试吧, [https://github.com/artisanstack/js-refactor](https://github.com/artisanstack/js-refactor)
#Github连击
##100天

View file

@ -117,16 +117,35 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
</ul></li>
<li><a href="#邻近算法">邻近算法</a></li>
</ul></li>
<li><a href="#github项目分析">Github项目分析</a></li>
<li><a href="#github流行项目分析">Github流行项目分析</a></li>
<li><a href="#创建pull-request">创建Pull Request</a><ul>
<li><a href="#第一个">第一个</a></li>
<li><a href="#google-ngx-pagespeed">Google Ngx Pagespeed</a></li>
</ul></li>
<li><a href="#创建你的项目">创建你的项目</a></li>
<li><a href="#构建github项目">构建Github项目</a></li>
<li><a href="#构建github项目">构建Github项目</a><ul>
<li><a href="#从模块分离到测试">从模块分离到测试</a><ul>
<li><a href="#skillock模块化">Skillock模块化</a></li>
<li><a href="#自动化测试">自动化测试</a></li>
<li><a href="#jshint">Jshint</a></li>
<li><a href="#mocha">Mocha</a></li>
<li><a href="#测试用例">测试用例</a></li>
</ul></li>
<li><a href="#code-climate来clean-code与重构">Code Climate来clean code与重构</a><ul>
<li><a href="#code-climate">Code Climate</a></li>
<li><a href="#代码的坏味道">代码的坏味道</a></li>
</ul></li>
</ul></li>
<li><a href="#创建项目文档">创建项目文档</a></li>
<li><a href="#测试-1">测试</a></li>
<li><a href="#重构-1">重构</a></li>
<li><a href="#前端技能训练-重构一"><a href="http://www.phodal.com/blog/frontend-improve-refactor-javascript-code/">前端技能训练: 重构一</a></a><ul>
<li><a href="#为什么重构">为什么重构?</a></li>
<li><a href="#重构umarkdown">重构uMarkdown</a><ul>
<li><a href="#代码说明">代码说明</a></li>
<li><a href="#重构-2">重构</a></li>
</ul></li>
</ul></li>
<li><a href="#github连击">Github连击</a><ul>
<li><a href="#天">100天</a><ul>
<li><a href="#天的提升">40天的提升</a></li>
@ -973,7 +992,7 @@ pipe.execute()</code></pre></div>
0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. ]</code></pre>
<p>真看不出来两者有什么相似的地方 。。。。</p>
<h1 id="github项目分析">Github项目分析</h1>
<h1 id="github流行项目分析">Github流行项目分析</h1>
<p>之前曾经分析过一些Github的用户行为现在我们先来说说Github上的Star吧。(截止: 2015年3月9日23时。)</p>
<table>
<thead>
@ -1100,9 +1119,501 @@ pipe.execute()</code></pre></div>
fi</code></pre>
<h1 id="创建你的项目">创建你的项目</h1>
<h1 id="构建github项目">构建Github项目</h1>
<h2 id="从模块分离到测试">从模块分离到测试</h2>
<p>在之前说到</p>
<blockquote>
<p>奋斗了近半个月后将fork的代码读懂、重构、升级版本、调整添加新功能、添加测试、添加CI、添加分享之后终于almost finish。</p>
</blockquote>
<p>今天就来说说是怎样做的。</p>
<p>以之前造的<a href="https://github.com/phodal/lettuce">Lettuce</a>为例,里面有:</p>
<ul>
<li>代码质量(Code Climate)</li>
<li>CI状态(Travis CI)</li>
<li>测试覆盖率(96%)</li>
<li>自动化测试(npm test)</li>
<li>文档</li>
</ul>
<p>按照<a href="https://github.com/phodal/awesome-developer">Web Developer路线图</a>来说,我们还需要有:</p>
<ul>
<li>版本管理</li>
<li>自动部署</li>
</ul>
<p>等等。</p>
<h3 id="skillock模块化">Skillock模块化</h3>
<p>在SkillTree的源码里大致分为三部分:</p>
<ul>
<li>namespace函数: 故名思意</li>
<li>Calculator也就是TalentTree主要负责解析、生成url头像依赖等等</li>
<li>Skill 主要是tips部分。</li>
</ul>
<p>而这一些都在一个js里对于一个库来说是一件好事但是对于一个项目来说并非如此。</p>
<p>依赖的库有</p>
<ul>
<li>jQuery</li>
<li>Knockout</li>
</ul>
<p>好在Knockout可以用Require.js进行管理于是使用了<code>Require.js</code>进行管理:</p>
<div class="sourceCode"><pre class="sourceCode html"><code class="sourceCode html"><span class="kw">&lt;script</span><span class="ot"> type=</span><span class="st">&quot;text/javascript&quot;</span><span class="ot"> data-main=</span><span class="st">&quot;app/scripts/main.js&quot;</span><span class="ot"> src=</span><span class="st">&quot;app/lib/require.js&quot;</span><span class="kw">&gt;&lt;/script&gt;</span></code></pre></div>
<p><code>main.js</code>配置如下:</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="va">require</span>.<span class="at">config</span>(<span class="op">{</span>
<span class="dt">baseUrl</span><span class="op">:</span> <span class="st">&#39;app&#39;</span><span class="op">,</span>
<span class="dt">paths</span><span class="op">:{</span>
<span class="dt">jquery</span><span class="op">:</span> <span class="st">&#39;lib/jquery&#39;</span><span class="op">,</span>
<span class="dt">json</span><span class="op">:</span> <span class="st">&#39;lib/json&#39;</span><span class="op">,</span>
<span class="dt">text</span><span class="op">:</span> <span class="st">&#39;lib/text&#39;</span>
<span class="op">}</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="at">require</span>([<span class="st">&#39;scripts/ko-bindings&#39;</span>])<span class="op">;</span>
<span class="at">require</span>([<span class="st">&#39;lib/knockout&#39;</span><span class="op">,</span> <span class="st">&#39;scripts/TalentTree&#39;</span><span class="op">,</span> <span class="st">&#39;json!data/web.json&#39;</span>]<span class="op">,</span> <span class="kw">function</span>(ko<span class="op">,</span> TalentTree<span class="op">,</span> TalentData) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> vm <span class="op">=</span> <span class="kw">new</span> <span class="at">TalentTree</span>(TalentData)<span class="op">;</span>
<span class="va">ko</span>.<span class="at">applyBindings</span>(vm)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span></code></pre></div>
<p>text、json插件主要是用于处理web.json即用json来处理技能于是不同的类到了不同的js文件。</p>
<pre><code>.
|____Book.js
|____Doc.js
|____ko-bindings.js
|____Link.js
|____main.js
|____Skill.js
|____TalentTree.js
|____Utils.js</code></pre>
<p>加上了后来的推荐阅读书籍等等。而Book和Link都是继承自Doc。</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="at">define</span>([<span class="st">&#39;scripts/Doc&#39;</span>]<span class="op">,</span> <span class="kw">function</span>(Doc) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">function</span> <span class="at">Book</span>(_e) <span class="op">{</span>
<span class="va">Doc</span>.<span class="at">apply</span>(<span class="kw">this</span><span class="op">,</span> arguments)<span class="op">;</span>
<span class="op">}</span>
<span class="va">Book</span>.<span class="at">prototype</span> <span class="op">=</span> <span class="kw">new</span> <span class="at">Doc</span>()<span class="op">;</span>
<span class="cf">return</span> Book<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span> </code></pre></div>
<p>而这里便是后面对其进行重构的内容。Doc类则是Skillock中类的一个缩影</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="at">define</span>([]<span class="op">,</span> <span class="kw">function</span>() <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> Doc <span class="op">=</span> <span class="kw">function</span> (_e) <span class="op">{</span>
<span class="kw">var</span> e <span class="op">=</span> _e <span class="op">||</span> <span class="op">{};</span>
<span class="kw">var</span> self <span class="op">=</span> <span class="kw">this</span><span class="op">;</span>
<span class="va">self</span>.<span class="at">label</span> <span class="op">=</span> <span class="va">e</span>.<span class="at">label</span> <span class="op">||</span> (<span class="va">e</span>.<span class="at">url</span> <span class="op">||</span> <span class="st">&#39;Learn more&#39;</span>)<span class="op">;</span>
<span class="va">self</span>.<span class="at">url</span> <span class="op">=</span> <span class="va">e</span>.<span class="at">url</span> <span class="op">||</span> <span class="st">&#39;javascript:void(0)&#39;</span><span class="op">;</span>
<span class="op">};</span>
<span class="cf">return</span> Doc<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span></code></pre></div>
<p>或者说这是一个AMD的Class应该有的样子。考虑到this的隐性绑定作者用了self=this来避免这个问题。最后Return了这个对象我们在调用的就需要new一个。大部分在代码中返回的都是对象除了在Utils类里面返回的是函数:</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="cf">return</span> <span class="op">{</span>
<span class="dt">getSkillsByHash</span><span class="op">:</span> getSkillsByHash<span class="op">,</span>
<span class="dt">getSkillById</span><span class="op">:</span> getSkillById<span class="op">,</span>
<span class="dt">prettyJoin</span><span class="op">:</span> prettyJoin
<span class="op">};</span></code></pre></div>
<p>当然函数也是一个对象。</p>
<h3 id="自动化测试">自动化测试</h3>
<p>一直习惯用Travis CI于是也继续用Travis Ci<code>.travis.yml</code>配置如下所示:</p>
<pre class="yml"><code>language: node_js
node_js:
- &quot;0.10&quot;
notifications:
email: false
branches:
only:
- gh-pages</code></pre>
<p>使用gh-pages的原因是我们一push代码的时候就可以自动测试、部署等等好处一堆堆的。</p>
<p>接着我们需要在<code>package.json</code>里面添加脚本</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="st">&quot;scripts&quot;</span><span class="op">:</span> <span class="op">{</span>
<span class="st">&quot;test&quot;</span><span class="op">:</span> <span class="st">&quot;mocha&quot;</span>
<span class="op">}</span></code></pre></div>
<p>这样当我们push代码的时候便会自动跑所有的测试。因为mocha的主要配置是用<code>mocha.opts</code>,所以我们还需要配置一下<code>mocha.opts</code></p>
<pre><code>--reporter spec
--ui bdd
--growl
--colors
test/spec </code></pre>
<p>最后的<code>test/spec</code>是指定测试的目录。</p>
<h3 id="jshint">Jshint</h3>
<blockquote>
<p>JSLint定义了一组编码约定这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验并以一条年代久远的编程原则 作为宗旨能做并不意味着应该做。JSLint会对它认为有的编码实践加标志另外还会指出哪些是明显的错误从而促使你养成好的 JavaScript编码习惯。</p>
</blockquote>
<p>当我们的js写得不合理的时候这时测试就无法通过:</p>
<pre><code>line 5 col 25 A constructor name should start with an uppercase letter.
line 21 col 62 Strings must use singlequote.</code></pre>
<p>这是一种驱动写出更规范js的方法。</p>
<h3 id="mocha">Mocha</h3>
<blockquote>
<p>Mocha 是一个优秀的JS测试框架支持TDD/BDD结合 should.js/expect/chai/better-assert能轻松构建各种风格的测试用例。</p>
</blockquote>
<p>最后的效果如下所示:</p>
<pre><code>Book,Link
Book Test
✓ should return book label &amp; url
Link Test
✓ should return link label &amp; url</code></pre>
<h3 id="测试用例">测试用例</h3>
<p>简单地看一下Book的测试:</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">/* global describe, it */</span>
<span class="kw">var</span> requirejs <span class="op">=</span> <span class="at">require</span>(<span class="st">&quot;requirejs&quot;</span>)<span class="op">;</span>
<span class="kw">var</span> assert <span class="op">=</span> <span class="at">require</span>(<span class="st">&quot;assert&quot;</span>)<span class="op">;</span>
<span class="kw">var</span> should <span class="op">=</span> <span class="at">require</span>(<span class="st">&quot;should&quot;</span>)<span class="op">;</span>
<span class="va">requirejs</span>.<span class="at">config</span>(<span class="op">{</span>
<span class="dt">baseUrl</span><span class="op">:</span> <span class="st">&#39;app/&#39;</span><span class="op">,</span>
<span class="dt">nodeRequire</span><span class="op">:</span> require
<span class="op">}</span>)<span class="op">;</span>
<span class="at">describe</span>(<span class="st">&#39;Book,Link&#39;</span><span class="op">,</span> <span class="kw">function</span> () <span class="op">{</span>
<span class="kw">var</span> Book<span class="op">,</span> Link<span class="op">;</span>
<span class="at">before</span>(<span class="kw">function</span> (done) <span class="op">{</span>
<span class="at">requirejs</span>([<span class="st">&#39;scripts/Book&#39;</span>、]<span class="op">,</span> <span class="kw">function</span> (Book_Class) <span class="op">{</span>
Book <span class="op">=</span> Book_Class<span class="op">;</span>
<span class="at">done</span>()<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="at">describe</span>(<span class="st">&#39;Book Test&#39;</span><span class="op">,</span> <span class="kw">function</span> () <span class="op">{</span>
<span class="at">it</span>(<span class="st">&#39;should return book label &amp; url&#39;</span><span class="op">,</span> <span class="kw">function</span> () <span class="op">{</span>
<span class="kw">var</span> book_name <span class="op">=</span> <span class="st">&#39;Head First HTML与CSS&#39;</span><span class="op">;</span>
<span class="kw">var</span> url <span class="op">=</span> <span class="st">&#39;http://www.phodal.com&#39;</span><span class="op">;</span>
<span class="kw">var</span> books <span class="op">=</span> <span class="op">{</span>
<span class="dt">label</span><span class="op">:</span> book_name<span class="op">,</span>
<span class="dt">url</span><span class="op">:</span> url
<span class="op">};</span>
<span class="kw">var</span> _book <span class="op">=</span> <span class="kw">new</span> <span class="at">Book</span>(books)<span class="op">;</span>
<span class="va">_book</span>.<span class="va">label</span>.<span class="va">should</span>.<span class="at">equal</span>(book_name)<span class="op">;</span>
<span class="va">_book</span>.<span class="va">url</span>.<span class="va">should</span>.<span class="at">equal</span>(url)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span></code></pre></div>
<p>因为我们用<code>require.js</code>来管理浏览器端,在后台写测试来测试的时候,我们也需要用他来管理我们的依赖,这也就是为什么这个测试这么从的原因,多数情况下一个测试类似于这样子的。(用Jasmine似乎会是一个更好的主意但是用习惯Jasmine了)</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="at">describe</span>(<span class="st">&#39;Book Test&#39;</span><span class="op">,</span> <span class="kw">function</span> () <span class="op">{</span>
<span class="at">it</span>(<span class="st">&#39;should return book label &amp; url&#39;</span><span class="op">,</span> <span class="kw">function</span> () <span class="op">{</span>
<span class="kw">var</span> book_name <span class="op">=</span> <span class="st">&#39;Head First HTML与CSS&#39;</span><span class="op">;</span>
<span class="kw">var</span> url <span class="op">=</span> <span class="st">&#39;http://www.phodal.com&#39;</span><span class="op">;</span>
<span class="kw">var</span> books <span class="op">=</span> <span class="op">{</span>
<span class="dt">label</span><span class="op">:</span> book_name<span class="op">,</span>
<span class="dt">url</span><span class="op">:</span> url
<span class="op">};</span>
<span class="kw">var</span> _book <span class="op">=</span> <span class="kw">new</span> <span class="at">Book</span>(books)<span class="op">;</span>
<span class="va">_book</span>.<span class="va">label</span>.<span class="va">should</span>.<span class="at">equal</span>(book_name)<span class="op">;</span>
<span class="va">_book</span>.<span class="va">url</span>.<span class="va">should</span>.<span class="at">equal</span>(url)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span></code></pre></div>
<p>最后的断言,也算是测试的核心,保证测试是有用的。</p>
<h2 id="code-climate来clean-code与重构">Code Climate来clean code与重构</h2>
<ul>
<li>当你写了一大堆代码,你没有意识到里面有一大堆重复。</li>
<li>当你写了一大堆测试,却不知道覆盖率有多少。</li>
</ul>
<p>这就是个问题了于是偶然间看到了一个叫code climate的网站。</p>
<h3 id="code-climate">Code Climate</h3>
<blockquote>
<p>Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.</p>
</blockquote>
<p>Code Climate整合一组静态分析工具的结果到一个单一的实时的报告让您的团队需要识别热点探讨新的方法提高代码质量的信息。</p>
<p>简单地来说:</p>
<ul>
<li>对我们的代码评分</li>
<li>找出代码中的坏味道</li>
</ul>
<p>于是,我们先来了个例子</p>
<table>
<thead>
<tr class="header">
<th style="text-align: left;">Rating</th>
<th style="text-align: left;">Name</th>
<th style="text-align: left;">Complexity</th>
<th style="text-align: left;">Duplication</th>
<th style="text-align: left;">Churn</th>
<th style="text-align: left;">C/M</th>
<th style="text-align: left;">Coverage</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/coap/coap_request_handler.js</td>
<td style="text-align: left;">24</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">6</td>
<td style="text-align: left;">2.6</td>
<td style="text-align: left;">46.4%</td>
</tr>
<tr class="even">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/coap/coap_result_helper.js</td>
<td style="text-align: left;">14</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">2</td>
<td style="text-align: left;">3.4</td>
<td style="text-align: left;">80.0%</td>
</tr>
<tr class="odd">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/coap/coap_server.js</td>
<td style="text-align: left;">16</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">5</td>
<td style="text-align: left;">5.2</td>
<td style="text-align: left;">44.0%</td>
</tr>
<tr class="even">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/database/db_factory.js</td>
<td style="text-align: left;">8</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">3</td>
<td style="text-align: left;">3.8</td>
<td style="text-align: left;">92.3%</td>
</tr>
<tr class="odd">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/database/iot_db.js</td>
<td style="text-align: left;">7</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">6</td>
<td style="text-align: left;">1.0</td>
<td style="text-align: left;">58.8%</td>
</tr>
<tr class="even">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/database/mongodb_helper.js</td>
<td style="text-align: left;">63</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">11</td>
<td style="text-align: left;">4.5</td>
<td style="text-align: left;">35.0%</td>
</tr>
<tr class="odd">
<td style="text-align: left;">C</td>
<td style="text-align: left;">lib/database/sqlite_helper.js</td>
<td style="text-align: left;">32</td>
<td style="text-align: left;">86</td>
<td style="text-align: left;">10</td>
<td style="text-align: left;">4.5</td>
<td style="text-align: left;">35.0%</td>
</tr>
<tr class="even">
<td style="text-align: left;">B</td>
<td style="text-align: left;">lib/rest/rest_helper.js</td>
<td style="text-align: left;">19</td>
<td style="text-align: left;">62</td>
<td style="text-align: left;">3</td>
<td style="text-align: left;">4.7</td>
<td style="text-align: left;">37.5%</td>
</tr>
<tr class="odd">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/rest/rest_server.js</td>
<td style="text-align: left;">17</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">2</td>
<td style="text-align: left;">8.6</td>
<td style="text-align: left;">88.9%</td>
</tr>
<tr class="even">
<td style="text-align: left;">A</td>
<td style="text-align: left;">lib/url_handler.js</td>
<td style="text-align: left;">9</td>
<td style="text-align: left;">0</td>
<td style="text-align: left;">5</td>
<td style="text-align: left;">2.2</td>
<td style="text-align: left;">94.1%</td>
</tr>
</tbody>
</table>
<p>分享得到的最后的结果是:</p>
<p>[Coverage][1]</p>
<h3 id="代码的坏味道">代码的坏味道</h3>
<p>于是我们就打开<code>lib/database/sqlite_helper.js</code>,因为其中有两个坏味道</p>
<p>Similar code found in two :expression_statement nodes (mass = 86)</p>
<p>在代码的 <code>lib/database/sqlite_helper.js:58…61 &lt; &gt;</code></p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"> <span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">deleteData</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;DELETE FROM &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; where &quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getKeyFromURL</span>(url) <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getValueFromURL</span>(url)<span class="op">;</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">basic</span>(sql_command<span class="op">,</span> callback)<span class="op">;</span></code></pre></div>
<p>lib/database/sqlite_helper.js:64…67 &lt; &gt;</p>
<p></p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">getData</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;SELECT * FROM &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; where &quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getKeyFromURL</span>(url) <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getValueFromURL</span>(url)<span class="op">;</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">basic</span>(sql_command<span class="op">,</span> callback)<span class="op">;</span></code></pre></div>
<p>只是这是之前修改过的重复。。</p>
<p>原来的代码是这样的</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">postData</span> <span class="op">=</span> <span class="kw">function</span> (block<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> db <span class="op">=</span> <span class="kw">new</span> <span class="va">sqlite3</span>.<span class="at">Database</span>(<span class="va">config</span>.<span class="at">db_name</span>)<span class="op">;</span>
<span class="kw">var</span> str <span class="op">=</span> <span class="kw">this</span>.<span class="at">parseData</span>(<span class="va">config</span>.<span class="at">keys</span>)<span class="op">;</span>
<span class="kw">var</span> string <span class="op">=</span> <span class="kw">this</span>.<span class="at">parseData</span>(block)<span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;insert or replace into &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; (&quot;</span> <span class="op">+</span> str <span class="op">+</span> <span class="st">&quot;) VALUES (&quot;</span> <span class="op">+</span> string <span class="op">+</span> <span class="st">&quot;);&quot;</span><span class="op">;</span>
<span class="va">db</span>.<span class="at">all</span>(sql_command<span class="op">,</span> <span class="kw">function</span> (err) <span class="op">{</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">errorHandler</span>(err)<span class="op">;</span>
<span class="va">db</span>.<span class="at">close</span>()<span class="op">;</span>
<span class="at">callback</span>()<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">};</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">deleteData</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> db <span class="op">=</span> <span class="kw">new</span> <span class="va">sqlite3</span>.<span class="at">Database</span>(<span class="va">config</span>.<span class="at">db_name</span>)<span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;DELETE FROM &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; where &quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getKeyFromURL</span>(url) <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getValueFromURL</span>(url)<span class="op">;</span>
<span class="va">db</span>.<span class="at">all</span>(sql_command<span class="op">,</span> <span class="kw">function</span> (err) <span class="op">{</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">errorHandler</span>(err)<span class="op">;</span>
<span class="va">db</span>.<span class="at">close</span>()<span class="op">;</span>
<span class="at">callback</span>()<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">};</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">getData</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> db <span class="op">=</span> <span class="kw">new</span> <span class="va">sqlite3</span>.<span class="at">Database</span>(<span class="va">config</span>.<span class="at">db_name</span>)<span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;SELECT * FROM &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; where &quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getKeyFromURL</span>(url) <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getValueFromURL</span>(url)<span class="op">;</span>
<span class="va">db</span>.<span class="at">all</span>(sql_command<span class="op">,</span> <span class="kw">function</span> (err<span class="op">,</span> rows) <span class="op">{</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">errorHandler</span>(err)<span class="op">;</span>
<span class="va">db</span>.<span class="at">close</span>()<span class="op">;</span>
<span class="at">callback</span>(<span class="va">JSON</span>.<span class="at">stringify</span>(rows))<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">};</span></code></pre></div>
<p>说的也是大量的重复,重构完的代码</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">basic</span> <span class="op">=</span> <span class="kw">function</span>(sql<span class="op">,</span> db_callback)<span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> db <span class="op">=</span> <span class="kw">new</span> <span class="va">sqlite3</span>.<span class="at">Database</span>(<span class="va">config</span>.<span class="at">db_name</span>)<span class="op">;</span>
<span class="va">db</span>.<span class="at">all</span>(sql<span class="op">,</span> <span class="kw">function</span> (err<span class="op">,</span> rows) <span class="op">{</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">errorHandler</span>(err)<span class="op">;</span>
<span class="va">db</span>.<span class="at">close</span>()<span class="op">;</span>
<span class="at">db_callback</span>(<span class="va">JSON</span>.<span class="at">stringify</span>(rows))<span class="op">;</span>
<span class="op">}</span>)<span class="op">;</span>
<span class="op">};</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">postData</span> <span class="op">=</span> <span class="kw">function</span> (block<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> str <span class="op">=</span> <span class="kw">this</span>.<span class="at">parseData</span>(<span class="va">config</span>.<span class="at">keys</span>)<span class="op">;</span>
<span class="kw">var</span> string <span class="op">=</span> <span class="kw">this</span>.<span class="at">parseData</span>(block)<span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;insert or replace into &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; (&quot;</span> <span class="op">+</span> str <span class="op">+</span> <span class="st">&quot;) VALUES (&quot;</span> <span class="op">+</span> string <span class="op">+</span> <span class="st">&quot;);&quot;</span><span class="op">;</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">basic</span>(sql_command<span class="op">,</span> callback)<span class="op">;</span>
<span class="op">};</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">deleteData</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;DELETE FROM &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; where &quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getKeyFromURL</span>(url) <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getValueFromURL</span>(url)<span class="op">;</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">basic</span>(sql_command<span class="op">,</span> callback)<span class="op">;</span>
<span class="op">};</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">getData</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span>
<span class="st">&#39;use strict&#39;</span><span class="op">;</span>
<span class="kw">var</span> sql_command <span class="op">=</span> <span class="st">&quot;SELECT * FROM &quot;</span> <span class="op">+</span> <span class="va">config</span>.<span class="at">table_name</span> <span class="op">+</span> <span class="st">&quot; where &quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getKeyFromURL</span>(url) <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">+</span> <span class="va">URLHandler</span>.<span class="at">getValueFromURL</span>(url)<span class="op">;</span>
<span class="va">SQLiteHelper</span>.<span class="va">prototype</span>.<span class="at">basic</span>(sql_command<span class="op">,</span> callback)<span class="op">;</span>
<span class="op">};</span></code></pre></div>
<p>重构完后的代码比原来还长,这似乎是个问题~~</p>
<h1 id="创建项目文档">创建项目文档</h1>
<h1 id="测试-1">测试</h1>
<h1 id="重构-1">重构</h1>
<p>或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。</p>
<p>有一天我发现当我需要我一次又一次地重复讲述某些内容于是我就计划着把这些应该掌握的技能放到Github上也就有了<a href="https://github.com/artisanstack">Artisan Stack</a> 计划。</p>
<p>每个程序员都不可避免地是一个Coder一个没有掌握好技能的Coder算不上是手工艺人但是是手工人。</p>
<p>艺,需要有创造性的方法。</p>
<h1 id="前端技能训练-重构一"><a href="http://www.phodal.com/blog/frontend-improve-refactor-javascript-code/">前端技能训练: 重构一</a></h1>
<h2 id="为什么重构">为什么重构?</h2>
<blockquote>
<p>为了更好的代码。</p>
</blockquote>
<p>在经历了一年多的工作之后我平时的主要工作就是修Bug。刚开始的时候觉得无聊后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码有时候你可能要花几天的时间去阅读代码。而你重写那几十代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做你的修改只会带来更多的bug。修Bug更多的是维护代码。还是前人总结的那句话对:</p>
<blockquote>
<p>写代码容易,读代码难。</p>
</blockquote>
<p>假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。</p>
<p>如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试,<code>没有测试的重构</code></p>
<p>从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。</p>
<p>让我们来看看我们的第一个训练,相当有挑战性。</p>
<h2 id="重构umarkdown">重构uMarkdown</h2>
<p>代码及setup请见github: <a href="https://github.com/artisanstack/js-refactor">js-refactor</a></p>
<h3 id="代码说明">代码说明</h3>
<p><code>uMarkdown</code>是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">/* code */</span>
<span class="cf">while</span> ((stra <span class="op">=</span> <span class="va">micromarkdown</span>.<span class="va">regexobject</span>.<span class="va">code</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="va">str</span>.<span class="at">replace</span>(stra[<span class="dv">0</span>]<span class="op">,</span> <span class="st">&#39;&lt;code&gt;</span><span class="sc">\n</span><span class="st">&#39;</span> <span class="op">+</span> <span class="va">micromarkdown</span>.<span class="at">htmlEncode</span>(stra[<span class="dv">1</span>]).<span class="at">replace</span>(<span class="ss">/</span><span class="sc">\n</span><span class="ss">/gm</span><span class="op">,</span> <span class="st">&#39;&lt;br/&gt;&#39;</span>).<span class="at">replace</span>(<span class="ss">/</span><span class="sc">\ </span><span class="ss">/gm</span><span class="op">,</span> <span class="st">&#39;&amp;nbsp;&#39;</span>) <span class="op">+</span> <span class="st">&#39;&lt;/code&gt;</span><span class="sc">\n</span><span class="st">&#39;</span>)<span class="op">;</span>
<span class="op">}</span>
<span class="co">/* headlines */</span>
<span class="cf">while</span> ((stra <span class="op">=</span> <span class="va">micromarkdown</span>.<span class="va">regexobject</span>.<span class="va">headline</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
count <span class="op">=</span> stra[<span class="dv">1</span>].<span class="at">length</span><span class="op">;</span>
str <span class="op">=</span> <span class="va">str</span>.<span class="at">replace</span>(stra[<span class="dv">0</span>]<span class="op">,</span> <span class="st">&#39;&lt;h&#39;</span> <span class="op">+</span> count <span class="op">+</span> <span class="st">&#39;&gt;&#39;</span> <span class="op">+</span> stra[<span class="dv">2</span>] <span class="op">+</span> <span class="st">&#39;&lt;/h&#39;</span> <span class="op">+</span> count <span class="op">+</span> <span class="st">&#39;&gt;&#39;</span> <span class="op">+</span> <span class="st">&#39;</span><span class="sc">\n</span><span class="st">&#39;</span>)<span class="op">;</span>
<span class="op">}</span>
<span class="co">/* mail */</span>
<span class="cf">while</span> ((stra <span class="op">=</span> <span class="va">micromarkdown</span>.<span class="va">regexobject</span>.<span class="va">mail</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="va">str</span>.<span class="at">replace</span>(stra[<span class="dv">0</span>]<span class="op">,</span> <span class="st">&#39;&lt;a href=&quot;mailto:&#39;</span> <span class="op">+</span> stra[<span class="dv">1</span>] <span class="op">+</span> <span class="st">&#39;&quot;&gt;&#39;</span> <span class="op">+</span> stra[<span class="dv">1</span>] <span class="op">+</span> <span class="st">&#39;&lt;/a&gt;&#39;</span>)<span class="op">;</span>
<span class="op">}</span></code></pre></div>
<p>选这个做重构的开始,不仅仅是因为之前在写<a href="https://github.com/phodal/echoesworks">EchoesWorks</a>的时候进行了很多的重构。而且它更适合于,<code>重构到设计模式</code>的理论。让我们在重构完之后给作者进行pull request吧。</p>
<p>Markdown的解析过程有点类似于<code>Pipe and Filters</code>模式(架构模式)。</p>
<p>Filter即我们在代码中看到的正规表达式集:</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript">regexobject<span class="op">:</span> <span class="op">{</span>
<span class="dt">headline</span><span class="op">:</span> <span class="ss">/</span><span class="sc">^(\#{1,6})([^\#\n]+)$</span><span class="ss">/m</span><span class="op">,</span>
<span class="dt">code</span><span class="op">:</span> <span class="ss">/</span><span class="sc">\s\`\`\`\n?([^`]+)\`\`\`</span><span class="ss">/g</span></code></pre></div>
<p>他会匹配对应的Markdown类型随后进行替换和处理。而``str```,就是管理口的输入和输出。</p>
<p>接着,我们就可以对其进行简单的重构。</p>
<h3 id="重构-2">重构</h3>
<p>(ps: 推荐用WebStrom来做重构自带重构功能)</p>
<p>作为一个示例我们先提出codeHandler方法即将上面的</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">/* code */</span>
<span class="cf">while</span> ((stra <span class="op">=</span> <span class="va">micromarkdown</span>.<span class="va">regexobject</span>.<span class="va">code</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="va">str</span>.<span class="at">replace</span>(stra[<span class="dv">0</span>]<span class="op">,</span> <span class="st">&#39;&lt;code&gt;</span><span class="sc">\n</span><span class="st">&#39;</span> <span class="op">+</span> <span class="va">micromarkdown</span>.<span class="at">htmlEncode</span>(stra[<span class="dv">1</span>]).<span class="at">replace</span>(<span class="ss">/</span><span class="sc">\n</span><span class="ss">/gm</span><span class="op">,</span> <span class="st">&#39;&lt;br/&gt;&#39;</span>).<span class="at">replace</span>(<span class="ss">/</span><span class="sc">\ </span><span class="ss">/gm</span><span class="op">,</span> <span class="st">&#39;&amp;nbsp;&#39;</span>) <span class="op">+</span> <span class="st">&#39;&lt;/code&gt;</span><span class="sc">\n</span><span class="st">&#39;</span>)<span class="op">;</span>
<span class="op">}</span></code></pre></div>
<p>提取方法成</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript">codeFilter<span class="op">:</span> <span class="kw">function</span> (str<span class="op">,</span> stra) <span class="op">{</span>
<span class="cf">return</span> <span class="va">str</span>.<span class="at">replace</span>(stra[<span class="dv">0</span>]<span class="op">,</span> <span class="st">&#39;&lt;code&gt;</span><span class="sc">\n</span><span class="st">&#39;</span> <span class="op">+</span> <span class="va">micromarkdown</span>.<span class="at">htmlEncode</span>(stra[<span class="dv">1</span>]).<span class="at">replace</span>(<span class="ss">/</span><span class="sc">\n</span><span class="ss">/gm</span><span class="op">,</span> <span class="st">&#39;&lt;br/&gt;&#39;</span>).<span class="at">replace</span>(<span class="ss">/</span><span class="sc">\ </span><span class="ss">/gm</span><span class="op">,</span> <span class="st">&#39;&amp;nbsp;&#39;</span>) <span class="op">+</span> <span class="st">&#39;&lt;/code&gt;</span><span class="sc">\n</span><span class="st">&#39;</span>)<span class="op">;</span>
<span class="op">},</span> </code></pre></div>
<p>while语句就成了</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="cf">while</span> ((stra <span class="op">=</span> <span class="va">regexobject</span>.<span class="va">code</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="kw">this</span>.<span class="at">codeFilter</span>(str<span class="op">,</span> stra)<span class="op">;</span>
<span class="op">}</span></code></pre></div>
<p>然后,运行所有的测试。</p>
<pre><code>grunt test</code></pre>
<p>同理我们就可以<code>mail</code><code>headline</code>等方法进行重构。接着就会变成类似于下面的代码,</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">/* code */</span>
<span class="cf">while</span> ((execStr <span class="op">=</span> <span class="va">regExpObject</span>.<span class="va">code</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="at">codeHandler</span>(str<span class="op">,</span> execStr)<span class="op">;</span>
<span class="op">}</span>
<span class="co">/* headlines */</span>
<span class="cf">while</span> ((execStr <span class="op">=</span> <span class="va">regExpObject</span>.<span class="va">headline</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="at">headlineHandler</span>(str<span class="op">,</span> execStr)<span class="op">;</span>
<span class="op">}</span>
<span class="co">/* lists */</span>
<span class="cf">while</span> ((execStr <span class="op">=</span> <span class="va">regExpObject</span>.<span class="va">lists</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="at">listHandler</span>(str<span class="op">,</span> execStr)<span class="op">;</span>
<span class="op">}</span>
<span class="co">/* tables */</span>
<span class="cf">while</span> ((execStr <span class="op">=</span> <span class="va">regExpObject</span>.<span class="va">tables</span>.<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> <span class="at">tableHandler</span>(str<span class="op">,</span> execStr<span class="op">,</span> strict)<span class="op">;</span>
<span class="op">}</span></code></pre></div>
<p>然后你也看到了上面有一堆重复的代码接着让我们用JavaScript的<code>奇技浮巧</code>即apply方法把上面的重复代码变成。</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript">[<span class="st">&#39;code&#39;</span><span class="op">,</span> <span class="st">&#39;headline&#39;</span><span class="op">,</span> <span class="st">&#39;lists&#39;</span><span class="op">,</span> <span class="st">&#39;tables&#39;</span><span class="op">,</span> <span class="st">&#39;links&#39;</span><span class="op">,</span> <span class="st">&#39;mail&#39;</span><span class="op">,</span> <span class="st">&#39;url&#39;</span><span class="op">,</span> <span class="st">&#39;smlinks&#39;</span><span class="op">,</span> <span class="st">&#39;hr&#39;</span>].<span class="at">forEach</span>(<span class="kw">function</span> (type) <span class="op">{</span>
<span class="cf">while</span> ((stra <span class="op">=</span> regexobject[type].<span class="at">exec</span>(str)) <span class="op">!==</span> <span class="kw">null</span>) <span class="op">{</span>
str <span class="op">=</span> that[(type <span class="op">+</span> <span class="st">&#39;Handler&#39;</span>)].<span class="at">apply</span>(that<span class="op">,</span> [stra<span class="op">,</span> str<span class="op">,</span> strict])<span class="op">;</span>
<span class="op">}</span>
<span class="op">}</span>)<span class="op">;</span></code></pre></div>
<p>进行测试blabla都是过的。</p>
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"> Markdown
✓ should parse h1<span class="op">~</span>h3
✓ should parse link
✓ should special link
✓ should parse font style
✓ should parse code
✓ should parse ul list
✓ should parse ul table
✓ should <span class="cf">return</span> correctly <span class="kw">class</span> name</code></pre></div>
<p>快来试试吧, <a href="https://github.com/artisanstack/js-refactor" class="uri">https://github.com/artisanstack/js-refactor</a></p>
<h1 id="github连击">Github连击</h1>
<h2 id="天">100天</h2>
<p>我也是蛮拼的虽然我想的只是在Github上连击100~200天然而到了今天也算不错。</p>
@ -1308,7 +1819,7 @@ pipe.execute()</code></pre></div>
<h3 id="编程的基础能力">编程的基础能力</h3>
<p>虽说算法很重要但是编码才是基础能力。算法与编程在某种程度上是不同的领域算法编程是在编程上面的一级。算法写得再好如果别人很难直接拿来复用在别人眼里就是shit。想出能work的代码一件简单的事学会对其重构使之变得更易读就是一件有意义的事。</p>
<p>于是在某一时刻在Github上创建了一个组织<a href="https://github.com/artisanstack">Artisan Stack</a>。当时想的是在Github寻找一些JavaScript项目对其代码进行重构。但是到底是影响力不够哈参与的人数比较少。</p>
<h4 id="重构-2">重构</h4>
<h4 id="重构-3">重构</h4>
<p>如果你懂得如何写出高可读的代码那么我想你是不需要这个的但是这意味着你花了更多的时候在思考上了。当谈论重构的时候让我想起了TDD(测试驱动开发)。即使不是TDD那么如果你写着测试那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章<a href="https://www.phodal.com/blog/intellij-idea-refactor-extract-method/">提炼函数</a><a href="https://www.phodal.com/blog/intellij-idea-refactor-replace-temp-with-query/">以查询取代临时变量</a><a href="https://www.phodal.com/blog/thoughtworks-refactor-and-intellij-idea/">重构与Intellij Idea初探</a><a href="https://www.phodal.com/blog/intellij-idea-refactor-inline-method/">内联函数</a>)</p>
<p>在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。</p>
<h4 id="测试-2">测试</h4>