《Node.js开发指南》 Book Note

更新 2014-11-29

Node.js 中,并不是所有的 API 都提供了同步和异步版本。Node.js 不鼓励使用同步 I/O。

模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的。

在 Node.js 中,创建一个模块非常简单,因为一个文件就是一个模块,我们要关注的问
题仅仅在于如何在其他文件中获取这个模块。Node.js 提供了 exports 和 require 两个对
象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获
取模块的 exports 对象。

覆盖 exports

1
2
3
exports.Hello = Hello;
->
module.exports = Hello;

在使用 npm 安装包的时候,有两种模式:本地模式和全局模式。

Node.js核心模块

常用工具 util

提供常用函数的集合,用于弥补核心 JavaScript 的功能过于精简的不足。

  • util.inherits
  • util.inspect

event

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点。首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

Node.js进阶话题

核心模块拥有最高的加载优先级,换言之如果有模块与其命名冲突,
Node.js 总是会加载核心模块。

BAD:

1
2
3
4
5
6
7
8
9
var fs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
for (var i = 0; i < files.length; i++) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files);
console.log(i);
console.log(files[i]);
});
}

可以利用
JavaScript 函数式编程的特性,手动建立一个闭包:

1
2
3
4
5
6
7
for (var i = 0; i < files.length; i++) {
(function(i) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files[i] + ': ' + contents);
});
})(i);
}

我之前在这个地方,不是特别懂,其实因为他这个例子,用了(i)让我有一些误解,我们可以用以下

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

cluster的功能是生成与当
前进程相同的子进程,并且允许父进程和子进程之间共享端口。Node.js 的另一个核心模块
child_process 也提供了相似的进程生成功能,但最大的区别在于cluster 允许跨进程端
口复用,给我们的网络服务器开发带来了很大的方便。

JS

闭包(closure)是函数式编程中的概念,出现于 20 世纪 60 年代,最早实现闭包的语言
是 Scheme,它是 LISP 的一种方言。之后闭包特性被其他语言广泛吸纳。

  1. 嵌套的回调函数
  2. 实现私有成员
    JavaScript通过约定在所有私有属性前加上下划线(例如_myPrivateProp), 表示这个属性是私有的
1
2
3
4
5
6
7
var generateClosure = function() { var count = 0;
var get = function() {
count ++;
return count; };
return get;
};
var counter = generateClosure(); console.log(counter()); // 输出 1 console.log(counter()); // 输出 2 console.log(counter()); // 输出 3

闭包的严格定义是“由函数(环境)及其封闭的自由变量组成的集合体。”

对象

JavaScript 中的对象实际上就是一个由属性组成的关联数组,属性由名称和值组成,值 的类型可以是任何数据类型,或者函数和其他对象。注意JavaScript具有函数式编程的特性, 所以函数也是一种变量,大多数时候不用与一般的数据类型区分。

call 和 apply 的功能是 以不同的对象作为上下文来调用某个函数。简而言之,就是允许一个对象去调用另一个对象 的成员函数。

1
2
3
4
5
6
7
8
var someuser = {
name: 'byvoid',
display: function(words) {
console.log(this.name + ' says ' + words); }
};
var foo = { name: 'foobar'
};
someuser.display.call(foo, 'hello'); // 输出 foobar says hello
1
2
3
4
5
6
7
8
9
10
11
12
var someuser = {
name: 'byvoid', func: function() {
console.log(this.name);
} };
var foo = { name: 'foobar'
};
foo.func = someuser.func;
foo.func(); // 输出 foobar
foo.func1 = someuser.func.bind(someuser);
foo.func1(); // 输出 byvoid func = someuser.func.bind(foo);
func(); // 输出 foobar func2 = func;
func2(); // 输出 foobar
1
2
3
4
5
6
var person = {
name: 'byvoid', 7 says: function(act, obj) {
console.log(this.name + ' ' + act + ' ' + obj); }
};
person.says('loves', 'diovyb'); // 输出 byvoid loves diovyb
byvoidLoves = person.says.bind(person, 'loves'); byvoidLoves('you'); // 输出 byvoid loves you

TODO

  • 构造函数内定义的属性继承方式与原型不同,子对象需要显式调用父对象才能继承构
    造函数内定义的属性。
  • 构造函数内定义的任何属性,包括函数在内都会被重复创
  • 同一个构造函数产生的
    两个对象不共享实例。
  • 构造函数内定义的函数有运行时闭包的开销,因为构造函数内的局部变量对其中定义的函数来说也是可见的。