《九浅一深node.js》Book Note

更新中。。。

模块机制

  • CommonJS希望JS可以在任何地方运行。
  • exports是唯一导出的接口,在模块中有一个module对象,代表自己,而exports就是module的属性。
  • require允许不包含扩展名,会按照.js, .node, .json尝试。
  • 同步配合缓存,可以大幅度缓解Node单线程阻塞调用的缺陷。
  • 18 ~

异步I/O

  • I/O disctributed I/O is pretty expensive
  • The cost of multi-thread when you create thread, and switch between them is expensive.
  • Asynchronous I/O
1
2
3
4
5
6
7
8
9
App OS
I/O call------> process request
/||
other call ||
||
||
||/
callback process done
  • repeatly to call I/O is 轮询
  • 55 ~

Asynchronous Programming

Continuation Passing Style

1
2
3
function foo(x, bar) {
return bar(x);
}

业务重点由返回值转移到了毁掉函数中。

  • 偏函数
1
2
3
4
5
6
7
8
9
10
var toString = Object.prototype.toString;
var isString = function(obj) {
return toString.call(obj) == '[object string]';
};
var isType = function(type) {
return function(obj) {
return toString.call(obj) == '[object' + type + ']';
};
};
  • 雪崩就是高访问并发的时候大量请求同时涌入数据库。
    用事件队列解决
1
2
3
4
5
6
7
8
9
10
11
12
13
var proxy = new events.EventEmitter();
var status = "ready";
var select = function(callback) {
proxy.once("selected", callback);
if (status == "ready") {
status = "pending";
db.select("SQL", function(results) {
proxy.emits("selected", results);
status = "ready";
});
}
};
// 保证相同的SQL语句从开始到结束永远只有一次,Node单线程执行所以无需担心状态同步问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var count = 0;
var results = {};
var done = unction(key, value) {
results[key] = value;
count++;
if (count === 3) {
render(results);
}
};
fs.readFile(template_path, "utf8", function(err, template) {
done("template", template);
});
db.query(sql, function(err, data) {
done("data", data);
});
/* L10n.get
Synchronous method for retrieving localization strings from app's resources. This function should
be used only when direct manipulation on the strings is needed (etc. date/time formatting) and in
all other UI related cases should be replaced with DOM attributes setting or L10n.setAttributes().
This method has also potential to generate race conditions so a developer has to make sure that any
code that uses this method is not fired before l10n resources are loaded using L10n.once() or L10n.
ready().*/
l10.get(function(err, resources) {
done("resources", resources);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
// 烧饼(哨兵)变量和偏函数结合
var after = function(times, callback) {
var count = 0, resutls = {};
return function(key, value) {
results[key] = value;
counts++;
if (count === times) {
callback(results);
}
};
};
var done = after(times, render);
1
2
3
4
5
6
7
8
9
10
// 多对一的收敛和事件订阅/发布
var emitter = new events.Emitter();
var done = after(times, render);
emitter.on("done", done);
emitter.on("done", other);
fs.readFile(template_path, "utf8", function(err, template) {
emitter.emit("done", "template", template);
})
  • 74 ~

构建Web应用

《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

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