标签: node 源码 学习javascript
源码: nianniuervue
Node.js主要分为四大部分,Node Standard Library,Node Bindings,V8,Libuv,架构图以下:java
树形结构查看,使用 tree 命令node
➜ nodejs git:(master) tree -L 1
.
├── AUTHORS
├── BSDmakefile
├── BUILDING.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── COLLABORATOR_GUIDE.md
├── CONTRIBUTING.md
├── GOVERNANCE.md
├── LICENSE
├── Makefile
├── README.md
├── ROADMAP.md
├── WORKING_GROUPS.md
├── android-configure
├── benchmark
├── common.gypi
├── config.gypi
├── config.mk
├── configure
├── deps
├── doc
├── icu_config.gypi
├── lib
├── node.gyp
├── out
├── src
├── test
├── tools
└── vcbuild.bat
复制代码
进一步查看 deps目录:python
➜ nodejs git:(master) tree deps -L 1
deps
├── cares
├── gtest
├── http_parser
├── npm
├── openssl
├── uv
├── v8
└── zlib
复制代码
node.js核心依赖六个第三方模块。其中核心模块 http_parser, uv, v8这三个模块在后续章节咱们会陆续展开。 gtest是C/C++单元测试框架。linux
node.js最初开始于2009年,是一个可让Javascript代码离开浏览器的执行环境也能够执行的项目。 node.js使用了Google的V8解析引擎和Marc Lehmann的libev。Node.js将事件驱动的I/O模型与适合该模型的编程语言(Javascript)融合在了一块儿。随着node.js的日益流行,node.js须要同时支持windows, 可是libev只能在Unix环境下运行。Windows 平台上与kqueue(FreeBSD)或者(e)poll(Linux)等内核事件通知相应的机制是IOCP。libuv提供了一个跨平台的抽象,由平台决定使用libev或IOCP。在node-v0.9.0版本中,libuv移除了libev的内容。android
libuv是一个高性能的,事件驱动的I/O库,而且提供了跨平台(如windows, linux)的API。 随着libuv的日益成熟,它成为了拥有卓越性能的系统编程库。除了node.js之外,包括Mozilla的Rust编程语言,和许多的语言都开始使用libuv。 libuv的官网:docs.libuv.org/en/v1.x/,英文…git
架构图 github
如今 JS 引擎的执行过程大体是:源代码 --->抽象语法树 --->字节码 --->JIT--->本地代码。一段代码的抽象语法树示例以下:web
function demo(name) {
console.log(name);
}
复制代码
V8 更加直接的将抽象语法树经过 JIT
技术转换成本地代码,放弃了在字节码阶段能够进行的一些性能优化,但保证了执行速度。 在 V8 生成本地代码后,也会经过 Profiler 采集一些信息,来优化本地代码。虽然,少了生成字节码这一阶段的性能优化, 但极大减小了转换时间。
JIT
就是即时编译器,能够根据字节码的使用频率对经常使用的字节码生成本地机器指令(运行时),而且保存下来
PS:
Tuborfan
将逐步取代Crankshaft
随着Web相关技术的发展,JavaScript所要承担的工做也愈来愈多,早就超越了“表单验证”的范畴,这就更须要快速的解析和执行JavaScript脚本。V8引擎就是为解决这一问题而生,在node中也是采用该引擎来解析JavaScript。
应用场景:Node的首要目标是提供一种简单的,用于建立高性能服务器的开发工具 Web服务器的瓶颈在于并发的用户量,对比Java和Php的实现方式
Node在处理高并发 , I/O密集场景有明显的性能优点
Chrome V8
引擎的 JavaScript 运行环境,让JavaScript的执行效率与低端的C语言的相近的执行效率。事件驱动
、非阻塞式 I/O
的模型,使其轻量又高效。npm
,是全球最大的开源库生态系统。进程是操做系统分配资源和调度任务的基本单位,线程是创建在进程上的一次程序运行单位,一个进程上能够有多个线程。
为何JavaScript是单线程?
// websocket
let worker = new Worker('./1.worker.js');
worker.postMessage(10000);
worker.onmessage = function (e) {
console.log(e.data);
}
console.log('main thread')
复制代码
因而可知浏览器是多进程的,而且从咱们的角度来看咱们更加关心浏览器渲染引擎
渲染引擎内部是多线程的,内部包含两个最为重要的线程ui线程和js线程。这里要特别注意ui线程和js线程是互斥的,由于JS运行结果会影响到ui线程的结果。ui更新会被保存在队列中等到js线程空闲时当即被执行.
javascript在最初设计时设计成了单线程,为何不是多线程呢?若是多个线程同时操做DOM那岂不会很混乱?这里所谓的单线程指的是主线程是单线程的(node其实也是多线程的),因此在Node中主线程依旧是单线程的。
什么是多线程?
什么是单线程?
什么是进程?
单线程不须要管锁的问题,这里简单说下锁的概念。例如你们都要去上厕所,厕所就一个,至关于全部人都要访问同一个资源。那么先进去的就要上锁。而对于node来讲。就一我的去厕所,因此免除了锁的问题!
主线程从任务队列中读取事件,这个过程是循环不断的,因此整个的这种运行机制又称为Event Loop(事件循环)
queue stack
同步和异步关注的是消息通知机制
补充知识:宏任务 && 微任务(vue $nextTick) 异步方法
// 宏任务 setTimeout
// 同步代码执行后才会执行异步
// 根据时间排序,当时间到达后把对应的回调放到队列
setTimeout(() => {
console.log(1);
setTimeout(() => {
console.log(4);
}, 1000);
}, 1000);
setTimeout(() => {
console.log(2);
}, 2000);
setTimeout(() => {
console.log(3);
}, 3000);
复制代码
// 宏任务 setImmedate
// setImmedate只兼容ie,默认是低于 setTimeout
setImmediate(function () {
console.log('setImmediate')
})
setTimeout(function () {
console.log('timeout')
}, 4);
console.log(1);
复制代码
// 宏任务 MessageChannel
let messageChannel = new MessageChannel();
let prot2 = messageChannel.port2;
// postMessage是异步执行的,要等待同步都执行完后才会被调用
messageChannel.port1.postMessage('111');
console.log(1);
prot2.onmessage = function (e) {
console.log(e.data);
}
console.log(2);
复制代码
// 宏任务 MutationObserver废弃了,兼容/性能有问题
let observe = new MutationObserver(function () {
alert('已经dom更新好了')
});
observe.observe(app,{childList:true});
for(let i = 0;i<1000;i++){
app.appendChild(document.createElement('span'));
}
复制代码
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
同步异步取决于被调用者,阻塞非阻塞取决于调用者
当应用程序须要处理大量并发的输入输出,而在向客户端响应以前,应用程序并不须要进行很是复杂的处理。