Node.js 开发指南-读书笔记
1. Node.js 使用了单 线程、非阻塞的事件编程模式
Node.js 最大的特点就是采用异步式 I/O 与事件驱动的架构设计。对于高并发的解决方 案,传统的架构是多线程模型,也就是为每个业务逻辑提供一个系统线程,通过系统线程切 换来弥补同步式 I/O 调用时的时间开销。Node.js 使用的是单线程模型,对于所有 I/O 都采用 异步式的请求方式,避免了频繁的上下文切换。Node.js 在执行的过程中会维护一个事件队 列,程序在执行时进入事件循环等待下一个事件到来,每个异步式 I/O 请求完成后会被推送 到事件队列,等待程序进程进行处理。
Node.js 的异步机制是基于事件的,所有的磁盘 I/O、网络通信、数据库查询都以非阻塞的方式请求,返回的结果由事件循环来处理。
异步式 I/O (Asynchronous I/O)或非阻塞式 I/O (Non-blocking I/O)则针对 所有 I/O 操作不采用阻塞的策略。当线程遇到 I/O 操作时,不会以阻塞的方式等待 I/O 操作 的完成或数据的返回,而只是将 I/O 请求发送给操作系统,继续执行下一条语句。当操作 系统完成 I/O 操作时,以事件的形式通知执行 I/O 操作的线程,线程会在特定时候处理这个 事件。为了处理异步 I/O,线程必须有事件循环,不断地检查有没有未处理的事件,依次予以处理。
2. Node.js 的事件循环机制
Node.js 在什么时候会进入事件循环呢?答案是 Node.js 程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,程序入口就是
事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会发出 I/O 请求或
直接发射(emit)事件,执行完毕后再返回事件循环,事件循环会检查事件队列中有没有未
处理的事件,直到程序结束。图3-5说明了事件循环的原理。
3. 当我们要把某个包作为工程运行时的一部分时,通过本地模式获取,如果要在命令行下使用,则使用全局模式安装
创建全局链接:在工程目录下npm link [module],可以把全局包当本地包使用,不支持windows.
4. 全局对象:
在 Node.js 中能够直接访问到对象通常都是 global 的属性,如 console、process 等,当你定义一个全局变量时,这个变量同时也会成为全局对象的属性。
5.视图助手
Express 提供了一种叫做视图助手的工具,它的功能是允许在视图中访问一个全局的函数或对象,不用每次调用视图解析的时候单独传入。功能的解耦,减少代码的冗余。
6.JavaScript 高级特性
(1)变量的作用域为函数,而不是{ }封闭代码块,会先搜索当前函数作用域,或者称为“局部作用域”,如果没有找到则搜索其上层作用域,一直到全局作用域。作用域的嵌套关系不是在调用时确定的,而是在定义时确定的。
(2)在任何地方隐式定义的变量都会定义在全局作用域中,即不通过 var 声明直接赋值的变量。这一点经常被人遗忘,而模块化编程的一个重要原则就是避免使用全局变量,所以我们在任何地方都不应该隐式定义变量。
7. 闭包
当一个函数返回它内部定义的一个函数时,就产生了一个闭包, 闭包不但包括被返回的函数,还包括这个函数的定义环境。函数的内部变量也返回了,并在内存中生成了一个副本。
作用:一是实现嵌套的回调函数,二是隐藏对象的细节(private)
8. 对象
(1)JavaScript 只有对象,对象就是对象,不是类的实例。JavaScript中的对象是基于原型的。其对象实际上就是一个由属性组成的关联数组,属性由名称和值组成,值的类型可以是任何数据类型,或者函数和其他对象。
(2)其中,用句点运算符和关联数组引用是等价的。使用初始化器定义对象,对象属性名称是否加引号是可选的,除非属性名称中有空格或者其他可能造成歧义的字符,否则没有必要使用引号。
(3)提供构造函数,用new来新建对象。
(4)对象函数中的this 指针不属于某个函数,而是函数调用时所属的对象。
(5)call和apply:作用是允许一个对象去调用另一个对象的成员函数。call可以用于实现对象的继承。call 和 apply 的功能是一致的,两者细微的差别在于 call 以参数表来接受被调用函数的参数,而 apply 以数组来接受被调用函数的参数。
(6)bind的实现通过this指针。
9.原型
何时使用原型,构造函数内定义来创建属性呢?
(1)除非必须用构造函数闭包,否则尽量用原型定义成员函数,因为这样可以减少开销。
(2)尽量在构造函数内定义一般成员,尤其是对象或数组,因为用原型定义的成员是多个实例共享的。
10.原型链
JavaScript 中的对象分为三类,一类是用户创建的对象,一类是构造函数对象,一类是原型对象。
用户创建的对象,即一般意义上用 new 语句显式构造的对象。构造函数对象指的是普通的构造函数,即通过 new 调用生成普通对象的函数。原型对象特指构造函数 prototype 属性指向的对象。这三类对象中每一类都有一个 __proto__ 属 性,它指向该对象的原型,从任何对象沿着它开始遍历都可以追溯到 Object.prototype。 构造函数对象有 prototype 属性,指向一个原型对象,通过该构造函数创建对象时,被创建对象的 __proto__ 属性将会指向构造函数的 prototype 属性。原型对象有 constructor 属性,指向它对应的构造函数。
11.浅拷贝与深拷贝
对象浅拷贝(shallow copy),即只复制基本类型的属性,而共享对象类型的属性。浅拷贝的问题是两个对象共享对象类型的属性,例如上例中 likes 属性指向的是同一个数组。
实现一个完全的复制,或深拷贝(deep copy),除了基本数据类型,还有多种不同的对象,对象内部还有复杂的结构,因此需要用递归的方式来实现。
12命名函数
尽量给构造函数和回调函数命名,这样当你在调试的时候可以看见更清晰的调用栈