Node.js 中的很多核心模块,都是基于事件来实现的。一些对象通过触发一些事件,使得相应的监听器函数能够被调用执行。所有能够触发事件的对象都是 EventEmitter
类的实例。这些对象可以通过 eventEmitter.on()
方法,将多个函数附加到由这个对象触发的对应命名事件中。当 EventEmitter
对象触发一个事件时,所有附着在那个事件上的函数都会被同步调用。
下面的例子展示了一个简单的 EventEmitter
实例,其中包含一个单独的监听器。eventEmitter.on()
方法用来注册监听器, eventEmitter.emit()
方法用来触发相应的事件。
一般不会直接使用 EventEmitter 作为构造实例的对象,而是会通过继承来自定义一个 EventEmitter,使用方式如下:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
// 允许对同一事件添加多个监听器函数,
// 也允许通过事件来传递多个参数:
myEmitter.on('event', (a, b, c) => {
// Something
})
myEmitter.emit('event', 'a', 'b', 'c');
在 TypedArray (ES6) 之前,JavaScript 语言还不能直接从二进制数据流中读取数据。Buffer
模块就被用来作为 Node 的核心 API,负责一些如 TCP 流和文件操作的功能。
在 ES6 的 TypedArray 出现后,Buffer
模块也继承了 Uint8Array
,并且让它更适用于 Node.js 的场景。
Buffer 模块是 Node.js 中的全局对象,所以不需要像其它模块那样通过 require('buffer').Buffer
这样来导入 Buffer 模块,直接就能作为已存在的全局对象够使用。
Buffer
的实例与整数数组很相似,但是却有着固定的长度,一旦被创建就不能够改变大小。Buffer
在内存分配的位置上也有所不同,它并不是被 V8 分配在堆上,所以不存在受到 V8 内存限制的问题。它是由 Node 的 C++ 层面上来实现对内存的申请的,然后通过 JavaScript 来对内存进行分配。
在浏览器中,顶级作用域就是全局作用域。也就是说在全局作用域中通过 var something
会定义一个全局可访问的变量,这个变量会附加到全局对象 window
成为它的一个属性。
Node.js 采用 CommonJS 模块加载机制,一个文件对应一个模块。顶级作用域并不是全局作用域,而是模块作用域。在一个模块被加载前,会被 Node.js 使用一个匿名函数包裹:
(function (exports, require, module, __filename, __dirname) {
// 模块文件的代码
})
因为有了这一个匿名函数的存在,而函数内部具有单独的作用域,因而有了模块作用域一说,这也是 Node.js 不同于浏览器的一点。
React 的好处很多,组件化简化了开发,虚拟 DOM 提升了页面性能,等等。当然,React 也有其缺点,其中影响最大的一点便是由于 React 是一个非常重型的框架,超过 2w 行的代码,即便是压缩后也超过 300k,导致了它的初始化加载非常费时。光是把这样的一个 JS 打包文件加载出来就要耗费一大笔时间,跟何况它的初始化执行过程也要达到百毫秒级,这样还是很影响用户体验的。
那么有没有什么方法能避免 React 这样的不足之处呢?
React 的渲染机制和其它类型的 MV * 框架有很大的区别。通过由数据构成的虚拟 DOM,React 便可在浏览器中一个根节点上渲染出一个完整的真实 DOM 树,从而完成整个页面的渲染。这是在浏览器客户端中完成的渲染操作。而在服务器(Node.js 环境)上,虽然没有 DOM,在客户端初次请求时,可以通过虚拟 DOM,将组件和数据渲染成真实 DOM 的 HTML 字符串,像其它后端语言如 PHP 那样把一个完整的 HTML 页面传输给浏览器,这就是服务端渲染。这同时也给了 React 一个很大的优势,让一个前端 View 层也有了 SEO 友好的展现方式。