速通《了不起的Node.js》(一)
这是《了不起的Node.js》简记的第一篇,主要摘要一些JavaScript的基础知识和必须要注意的Node.js概念。
理解一个工具最好的方式是首先搞明白为什么会有这个工具
这是《了不起的Node.js》简记的第一篇,主要摘要一些JavaScript的基础知识和必须要注意的Node.js概念。当然,一些过于基础的知识我就不写出来了,相信对这几篇文章感兴趣的同好都有了解。
Node.js简述
2019年年末,Ryan Dahl在柏林的一个JavaScript大会上宣布了一项名为Node.js的新技术。这项技术是关于在服务器端运行JavaScript的,所有人在听闻后的第一个想法都是:
如果成真的话,以后开发Web应用就只需要一种语言了。
毫无疑问,Node.js引起了轰动,我还记得前几年很火的一句话:“所有能用JS写的东西都会使用JS”。虽然十来年过去了,这个目标并没有实现,但是Node.js带来的变化依旧在持久、缓慢的改变着技术圈子,拓宽着前端程序员的发展边界。
Node.js改变了Web开发模式,被称为是一个将设计网络应用导向正确道路的特殊工具。它快速又高效,这样的特性得益于一种叫事件轮询(event loop)的技术,以及其构建与V8之上,V8是Google为Chrome Web浏览器设计的JS解释器和虚拟机,它运行JS非常快。
JavaScript概览
JavaScript是基于原型、面向对象、弱类型的动态脚本语言。它从函数式语言中借鉴了一些强大的特性,如闭包和高阶函数。从技术层面上说,JavaScript是根据ECMAScript语言标准来实现的。
JavaScript基础
类型
JavaScript类型可以简单分为基本数据类型和复杂数据类型。
- 基本数据类型包括number、string、boolean、undefined、null和Symbol
- 复杂数据类型包括array、function和object
简单来说,访问JavaScript的基本类型,访问的是值;访问复杂类型,访问的是对值的引用。
特定的false值
在条件表达式中,一些特定的值会被判定为false:null、undefined、' '、0。
改变this
js中的this可以使用以下方法:
- .call 接受参数列表
- .apply 接受参数数组
- .bind 接受参数列表,改变this的引用
bind是返回对应函数,便于稍后调用;其他两者理解调用
闭包
自执行函数是一种机制,通过这种机制声明和调用一个匿名函数,能够达到仅定义一个新作用域的作用。自执行函数对声明私有变量是很有用的,这样可以让私有变量不被其他代码访问。——原书
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。——阮一峰
类相关
JavaScript在出现class语法糖前,是使用函数来定义和模仿类的。其中的继承特性则是通过基于原型的继承来模拟类继承。
// 继承
子类.prototype = new 父类();
// 上面的继承的缺陷是还需要初始化父类,可以参照下面的方法
子类.prototype.__proto__ = 父类.prototype
// 为子类定义属性和方法
子类.prototype.属性 = ""
// 重写父类函数
子类.prototype.方法 = function (a) {
父类.prototype.方法.call(this, a);
// 子类特有逻辑
// ...
}
Node.js的特别之处
阻塞与非阻塞IO
Node为JavaScript引入了一个复杂的概念:共享状态的并发。
Node采用一个长期运行的进程,相反,Apache会产生多个线程(每个请求一个线程),每次都会刷新状态。
事件轮询
Node会先注册事件,随后不停的询问内核这些事件是否已经分发。当时间分发时,对应的回调函数就会被触发,然后继续执行下去。如果没有事件触发,则继续执行其他代码,直到有新事件时,再去执行对应的回调函数。
总的来说:JavaScript中的事件循环机制是一种执行代码、处理事件和实现异步编程的方式。事件循环机制使得JavaScript能够处理异步操作,如setTimeout、Promise等,从而在单线程环境中实现非阻塞的行为。
事件循环机制主要包括以下几个部分:
- 调用栈(Call Stack):负责执行函数调用和执行上下文的管理。当一个函数被调用时,其执行上下文被推入调用栈;当函数执行完毕,其执行上下文从调用栈中弹出。
- 任务队列(Task Queue):负责存储待处理的任务。例如,当setTimeout到达指定延迟时间时,其回调函数就会被添加到任务队列中。
- 微任务队列(Microtask Queue):负责存储待处理的微任务,如Promise的then回调。微任务队列优先级高于任务队列,因此在执行任务队列中的任务之前,需要先清空微任务队列。
- 事件循环(Event Loop):事件循环是整个机制的核心。它的作用是检查调用栈是否为空,然后从任务队列或微任务队列中取出任务并执行。具体流程如下:
a. 首先检查调用栈是否为空,如果不为空,则执行栈顶的函数。
b. 如果调用栈为空,检查微任务队列是否有任务。如果有,执行并清空微任务队列中的所有任务。
c. 如果调用栈为空且微任务队列也为空,检查任务队列是否有任务。如果有,将任务队列中的第一个任务放入调用栈并执行。
d. 重复以上步骤。
这样,JavaScript可以在执行同步代码的同时,处理异步操作的回调,实现非阻塞行为。值得注意的是,虽然事件循环使得JavaScript可以执行异步操作,但过多的异步操作仍然可能导致性能问题。因此,在实际开发中,需要合理安排任务,以避免阻塞主线程。
Node中的JavaScript
global对象
我们知道,在浏览器中,全局对象指的是window对象。在window对象中定义的任何内容都可以被全局访问到。
Node中有两个类似但却各自代表着不同含义的对象:
- global:和window一样,任何global对象上的属性都可以被全局访问到。
- process:所有全局执行上下文中的内容都在process对象中。举例来说,在浏览器中,窗口的名字是window.name,类似的,在Node中进程的名字是process.title。
process.nextTick函数可以将一个函数的执行时间规划到下一个事件循环中。
事件
Node.js中的基础API之一就是EventEmitter。无论是在Node中还是是浏览器中,大量代码都依赖于所监听或者分发的事件。
浏览器中负责处理事件相关的DOMAPI主要包括addEventListener/removeEventListener以及dispatchEvent。
在Node中,你也可以通过Event EmitterAPI来随处进行事件的监听和分发。该API上定义了on/emit和removeListener方法,它以process.EventEmitter形式暴露出来。
事件是Node非阻塞设计的重要体现。Node通常不会直接返回数据(因为这样可能会在等待某个资源的时候发生线程阻塞),而是采用分发事件来传递数据的方式。在Node中,事件机制可以通知你尚未发生但即将要发生的事情。
buffer
除了模块之外,Node还补全了语言的另一个不足之处——对二级制数据的处理。
buffer是一个表示固定内存分配的全局对象(也就是说,要放到缓冲区中的字节数需要提前定下),它就好比是一个由八位字节元素组成的数组,可以有效地在JavaScript中表示二进制数据。
在Node.js中,绝大部分进行数据IO操作的API都用buffer来接受和返回数据。
注:文中还提到了base64,base64是一种仅用ASCII字符书写二进制数据的方式,换句话说,它可以让你用简单的英文字母来表示图片这样的复杂事物。
其他说明
一些已经过时或被废弃的东西比如存取器我并没有在文章里提到,留作之后单章特地说明。
END
本章入门篇到这里就结束了,这一篇主要是回顾了以下JavaScript的基础以及初探Node.js的部分奥妙,更深更好的内容接下来会慢慢揭晓,我会加快更新速度的!