lession1:环境搭建lession2:一个简单的express应用lession3:使用外部模块lession4:简单的superagent与cheerio爬虫eventproxy控制并发lession5:使用async控制并发lession6:测试:mocha, should, istanbullesssion7:浏览器端测试:mocha, chai, phantomjslession8:supertestlession9:正则表达式lession10: benchmarklession11:作用域和闭包 lession2:一个简单的express应用 lession3:使用外部模块 lession4:简单的superagent与cheerio爬虫 eventproxy控制并发 lession5:使用async控制并发 lession6:测试:mocha, should, istanbul lesssion7:浏览器端测试:mocha, chai, phantomjs lession8:supertest lession9:正则表达式 lession10: benchmark lession11:作用域和闭包

文章目录

想要深入实践JavaScript,所以尝试学习Node.js。

先上参考链接:Node.js 包教不包会

练习代码:Node.js练习

首先需要安装nvm,就是Node Version Manager,负责Node.js的版本管理。因为考虑不涉及Node.js新老版本的切换,因此这里没有安装nvm,直接进入了下一步。(主要是安装时遇到了问题不想花时间解决QAQ)

然后安装Node.js,之前已经借助brew安装过了。

最后需要安装npm,负责自动管理包的依赖,就是Node.js Package Manager的意思。之前用brew安装过。

lession2:一个简单的express应用

express是Node.js应用最广泛的web框架,express官网

Node.js的依赖是以项目为单位的,不是安装在全局的,直接安装在项目的node_modules目录下,每个依赖都可以有指定版本的其他依赖。

1
npm install express

写第一个express代码:

1
2
3
4
5
6
7
8
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000, function(){
console.log('app is listening at port 3000');
});

这里需要理解的知识点:

  • 端口:通过端口来区分出同一电脑内不同应用或者进程,从而实现一条物理网线同时连接多个程序(通过分组交换技术)。端口号是一个16位的uint,所以范围是1-65535,对TCP,port 0被保留,UDP的source端端口号是可选的,为0时表示无端口。
  • URL:常见的格式<scheme>://<host>:<port>/<url-path>

lession3:使用外部模块

package.json文件的用法:

定义了项目的各种元信息,包括项目的名称,git repo地址,作者等等,定义了项目的依赖,这样项目在部署的时候不需要将node_modules目录也上传到服务器,npm install即可。

安装依赖的具体步骤:

  1. npm init。初始化项目并帮我吗创建一个package.json文件;
  2. npm install express utility --save。安装依赖,同时--save命令会帮助我们把依赖写入package.json文件。

这里分不清http的query和body了,所以还是翻了《自顶向下》,有空了应该把这本再看一遍…query实际上就是URL上带着的para参数。

这里记录网络中应用层里几个之前有疑惑的点:

  • HTTP一定使用TCP支撑运输协议。
  • HTTP请求报文:
1
2
3
4
5
6
7
8
GET /somedir/page.html HTTP/1.1 
/* request line 请求行:方法字段,URL字段,HTTP版本字段 */
----------------------------------------------------
/* header line 首部行 */
Host: www.someschool.com
Connection: close /* 非持续连接 */
User-Agent: Mozilla/5.0
Accept-language: fr
  • HTTP响应报文:
1
2
3
4
5
6
7
8
9
10
11
/* 状态行 */
HTTP/1.1 200 OK
/* 首部行 */
Connection: close
Date: Tue, 09 Aug 2011 15:44:04 GMT
Server: Apache/2.2.3 (CentOS)
Last-Modified: Tur, 09 Aug 2011 15:11:03 GMT
Content-Length: 6821
Content-Type: text/html
/* 实体行 */
(data data data data data...)

这一部分的代码就不贴出来了。

lession4:简单的superagent与cheerio爬虫

  • superagent是一个http库,可以发起get或者post请求
  • cheerio用来从网页中以css selector取数据

这一段代码不是很理解,所以研究了一下cheerio这个库到底在干嘛,先贴代码:

1
2
3
4
5
6
7
8
9
var $ = cheerio.load(sres.text);
var items = [];
$('#topic_list .topic_title').each(function(inx, element){
var $element = $(element);
items.push({
title: $element.attr('title'),
href: $element.attr('href')
});
});

以上代码做的其实是以下几件事:

  1. 装载html文件。
  2. 选择器:$(selelctor,[context],[root])。选择器在context范围内进行搜索,context又在root范围内搜索。
  3. attr(name, value)获得和修改属性。在匹配的元素中只能获得第一元素的属性,如果设置一个属性的值为null,则移除这个属性。只传第一个参数,则获得属性;传两个参数,则是修改属性。

ref: cheerio 笔记

eventproxy控制并发

Node.js的异步并发是一大亮点。eventproxy这个库是用来管理异步操作是否完成,监听异步操作,在他们都被完成之后自动调用提供的处理函数,并将抓取到的数据当参数传出来。

哈哈哈哈哈,这一段好逗,贴出来乐一下:

个人觉得js这种随意的语言,真的非常不符合我的严谨型人格…

这里解读一下eventproxy的用法(先会用再理解吧):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var ep = new eventproxy;
ep.after('topic_html', topicURLs.length, function(topics){
topics = topics.map(function(topicPair){
var topicURL = topicPair[0];
var topicHTML = topicPair[1];
var $ = cheerio.load(topicHTML);
return ({
title: $('.topic_full_title').text().trim(),
herf: topicURL,
comment: $('.reply_content').eq(0).text().trim(),
});
});
console.log('final:');
console.log(topics);
});
topicURLs.forEach(function(topicurl){
superagent.get(topicurl)
.end(function(err, res){
console.log('fetch '+topicurl+' successful');
ep.emit('topic_html', [topicurl, res.text]);
});
});
  1. 创建eventproxy的实例;
  2. .after()方法的三个参数,第一个参数表示监听的是'topic_html',第二个参数表示要监听达到topicURLs.length次才会执行回调函数,第三个参数就是要执行的回调函数。
  3. .map()方法是Array方法,他会返回一个新数组,数组中的元素为原数组元素调用函数处理后返回的值,并不会改变原数组的值。
  4. trim()函数用来去除字符串开头和结尾的空格。
  5. 这一段代码刚看到的时候没看懂,实际上eventproxy的使用是先执行ep.emit('topic_html', [topicurl, res.text]);语句,指定监听的事件,这些被监听的事件是异步完成的,当监听的事件全部完成后,再抛出事件,即执行after()。先监听,后抛出。
  6. topic_html实际上只是为监听事件起一个事件名,能够使回调时对应正确的监听事件。
  7. topicURLs.length才是实际上指定监听结束发起回调的时刻。
  8. .eq()方法将匹配元素集缩减至指定index上的一个。
  9. forEach()each()之间的区别:前者是用来遍历数组和json对象的,而后者是专门用来遍历jquery对象的。
  10. jquery到底是什么?jQuery API中文文档

jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。

jQuery库通常用于前端操作DOM节点,后端通常使用cheerio写爬虫脚本。cheerio是专门为服务端设计的快读、灵活精简实现的jQuery。cheerio模块引入了jquery核心的一部分,去除了jquery库中所有在dom和浏览器的不兼容部分,只保留了核心API。两者的API很类似,例如前文说到的each()也是用来遍历cheerio对象的。这样就说得通了。

有一个eventproxy的源码分析:eventproxy原理分析

lession5:使用async控制并发

当发起请求过多时,大部分网站会因此禁止恶意请求而封IP,因此需要控制并发数量。

async常用的控制并发数量的接口:mapLimit(arr, limit, iterator, callback)。另一个常用的控制并发数量的接口queue(worker, concurrency)

这里用到setTimeout(function, delay)这个函数,是在等待delay毫秒后执行function

lession6:测试:mocha, should, istanbul

直接上代码,用来跑测试用例:

1
2
3
4
5
describe('test/main.test.js', function () {
it('should equal 55 when n === 10', function () {
main.fibonacci(10).should.equal(55);
});
});

这里的教程看的比较懵了,使用mocha和istanbul的目的是什么。这里参考了阮一峰的代码覆盖率工具 Istanbul 入门教程.

测试的时候需要关注是否所有的代码都测试到了,所以引入“代码覆盖率”这个指标,从四个维度测试代码:

  • 行覆盖率:是否每一行都执行了
  • 函数覆盖率:是否每个函数都调用了
  • 分支覆盖率:是否每个if代码块都执行了
  • 语句覆盖率:是否每个语句都执行了

istabbul就是js中这样一个测试工具,常常和测试框架mocha结合使用。

相关的命令行:

1
istanbul cover _mocha

lesssion7:浏览器端测试:mocha, chai, phantomjs

总结一下这几个工具:

  • islanbul:js代码股概率测试工具npm i istanbul -g
  • mocha:测试框架,即运行测试的工具,使用describeit方法对不同的测试情况进行分组npm install mocha -g
  • chai:mocha本身不提供assert断言,chai是一个断言库
  • phantomjs:帮助搭建测试需要的前端浏览器环境npm i -g mocha-phantomjs

这里运行phantomjs出现了问题,运行mocha-phantomjs index.html --ssl-protocol=any --ignore-ssl-errors=true显示phantomjs terminated with signal SIGSEGV,随后执行mocha-phantomjs --path /usr/local/lib/node_modules/mocha-phantomjs/node_modules/phantomjs ./index.html抛出错误An error occurred trying to launch phantomjs at "/usr/local/lib/node_modules/mocha-phantomjs/node_modules/phantomjs": TypeError: Cannot read property 'pipe' of undefined.

官网上显示:PhantomJS development is suspended until further notice.

不再提供支持了。所以这里不再追究了,没有进行进一步的调研,直接进入下一节学习。

lession8:supertest

supertest和superagent的API一模一样,superagent是用来抓取页面的,supertest是专门用来配合express进行集成测试的。

这里区别一下npm install的时候--save</code>–save-dev的区别:

  • --save会把依赖包添加到package.json文件的dependencies键下,--save-dev会把依赖添加到package.jsondevDependencies键下;
  • dependencies是运行时依赖,而devDependencies是开发时依赖。如果发布后用不到这个模块,只有开发时用得到,使用后者。

lession9:正则表达式

正在表达式测试

不详述了,主要是之前看过,但是不用到的话又记不住那么多规则,用到的时候再查询吧。

lession10: benchmark

这个课程的缺点就是不解释这些都是啥东西,默认已经知道了…

benchmark:A robust benchmarking library that supports high-resolution timers & returns statistically significant results.

这个课程的目标是测试将字符串的100转换成Number类型的100的三种方法哪个更快,使用benchmark作为测试速度的工具。

benchmark文档

1
2
3
4
5
6
7
+ x 861,361,931 ops/sec ±0.64% (47 runs sampled)
index.js:29
parseint x 232,488,875 ops/sec ±1.68% (46 runs sampled)
index.js:29
Number x 862,891,767 ops/sec ±0.62% (47 runs sampled)
index.js:29
Fastest is Number,+

运行代码会显示每秒钟代码运行的次数(ops/sec),对比出最快的计算方式。

lession11:作用域和闭包

这一部分主要是理解两个点:

第一,js是没有块级作用域这个概念的。

第二,理解闭包。

1
2
3
4
5
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 5);
}

这个例子看到过很多次了,这里的解释我觉得还比较明白:

上面这个代码块会打印五个 5 出来,而我们预想的结果是打印 0 1 2 3 4。

之所以会这样,是因为 setTimeout 中的 i 是对外层 i 的引用。当 setTimeout 的代码被解释的时候,运行时只是记录了 i 的引用,而不是值。而当 setTimeout 被触发时,五个 setTimeout 中的 i 同时被取值,由于它们都指向了外层的同一个 i,而那个 i 的值在迭代完成时为 5,所以打印了五次 5。
为了得到我们预想的结果,我们可以把 i 赋值成一个局部的变量,从而摆脱外层迭代的影响。

1
2
3
4
5
6
7
for (var i = 0; i < 5; i++) {
(function (idx) {
setTimeout(function () {
console.log(idx);
}, 5);
})(i);
}



到此为止后面的部分不想再跟着研究了。觉得自己研究js这么长时间,理解上有进展,但是实际上没有什么长足的进步,主要原因就是自己并没有从头至尾的搭建一个完整的项目,始终停留在简单的demo阶段,这里主要原因是自己在写代码的思路上总是依赖模仿的,这是我停留在这个初级阶段始终不前进的原因之一吧,对于一个工程没有什么思路上的构想。

但是我想我也不可以急功近利吧,继续从模仿做总有一天也会有自己的想法产生,能够把握一个较为完整的程序。接下来想要写一个博客项目,希望能够在两周的时间内搭建起来基本功能,当然这取决于我找的这个例子的代码规范能不能满足我的要求 = =试试看咯。每一天都要加油鸭!