node.js脚本文件是node.js的js部分的入口。它里面有几个重要的对象:
1.NativeModule:
Nodejs的模块分为两部分,一部分是用C实现的模块,比如:buffer、stdio等,另一部分是用js实现的模块比如net、dns等。对于C的模块,nodejs通过node.h提供的NODE_MODULE方法将模块存储在变量_module里。在srcde_extensions.cc中提供了get_builtin_module(name)接口获取这些模块;而对于js的模块,nodejs在srcde.js中实现了一个NativeModule对象用于管理js模块,它通过调用process.binding(“natives”)把所有内置的js模块放在NativeModule._source上,并提供require接口供调用。
为了提高模块加载的效率,nodejs在binding函数和require函数中都增加了缓存机制,在首次加载模块的时候,模块会copy的缓存中,以后的模块加载实际上只是从缓存中获得模块的接口,这样的机制使得nodejs在模块加载方面十分高效。
Nodejs模块的调用形式如下:
加载C++模块(以stdio为例):
process.binding("stdio")->get_builtin_module("stdio")-> _module -> NODE_MODULE(node_stdio, node::Stdio::Initialize)(定义)
加载js模块(以net为例)
require("net") -> NativeModule.require("net") -> process.binding("natives")["net"] -> DefineJavaScript() -> natives[] -> node_natives.h
function NativeModule(id) {
this.filename = id + '.js';
this.id = id;
this.exports = {};
this.loaded = false;
}
NativeModule._source = process.binding('natives');// 加载内部模块
NativeModule._cache = {};
// 主要就是这个方法
NativeModule.require = function(id) {
if (id == 'native_module') {
return NativeModule;
}
var cached = NativeModule.getCached(id);
if (cached) {
return cached.exports; // 如果存在缓存,直接返回
}
if (!NativeModule.exists(id)) {
throw new Error('No such native module ' + id); // ID错误
}
process.moduleLoadList.push('NativeModule ' + id);
var nativeModule = new NativeModule(id); // 如果不存在缓存,就创建一个
nativeModule.cache(); // 缓存起来
nativeModule.compile(); // 加载
return nativeModule.exports;
};
NativeModule.getCached = function(id) {
return NativeModule._cache[id];
}
NativeModule.exists = function(id) {
return NativeModule._source.hasOwnProperty(id);
}
NativeModule.getSource = function(id) {
return NativeModule._source[id];
}
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
NativeModule.prototype.compile = function() {
var source = NativeModule.getSource(this.id);
source = NativeModule.wrap(source);
var fn = runInThisContext(source, this.filename, true);
fn(this.exports, NativeModule.require, this, this.filename);
this.loaded = true;
};
NativeModule.prototype.cache = function() {
NativeModule._cache[this.id] = this;
};
2.startup:
(function(process) {
this.global = this;
startup();
});
整个node.js文件就是一个函数对象的定义,而这个函数只做了两件事,所有到逻辑都在startup里面。
function startup() {
var EventEmitter = NativeModule.require('events').EventEmitter;
process.__proto__ = Object.create(EventEmitter.prototype, {
constructor: {
value: process.constructor
}
});
EventEmitter.call(process);
process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated 已经过时
// do this good and early, since it handles errors.
startup.processFatal(); // 异常处理模块
startup.globalVariables(); // 全局属性初始化
startup.globalTimeouts(); // 定义全局时间方法
startup.globalConsole(); // 加载控制台模块
startup.processAssert(); // 定义断言对象
startup.processConfig(); // 加载process.config里面的配置内容,保存做process.config中
startup.processNextTick(); // 定义process.nextTick方法,这个方法是让一个行为在下一次事件轮循的时候执行。
startup.processStdio(); // 标准流
startup.processKillAndExit(); // 定义进程到kill和exit方法
startup.processSignalHandlers(); // 定义为进程添加信号的方法,这里的信号是指事件
startup.processChannel(); // 这个暂时不清楚
startup.resolveArgv0();
// There are various modes that Node can run in. The most common two
// are running from a script and running the REPL - but there are a few
// others like the debugger or running --eval arguments. Here we decide
// which mode we run in.
if (NativeModule.exists('_third_party_main')) {
// To allow people to extend Node in different ways, this hook allows
// one to drop a file lib/_third_party_main.js into the build
// directory which will be executed instead of Node's normal loading.
process.nextTick(function() {
NativeModule.require('_third_party_main');
});
} else if (process.argv[1] == 'debug') {
// Start the debugger agent
var d = NativeModule.require('_debugger');
d.start();
} else if (process._eval != null) {
// User passed '-e' or '--eval' arguments to Node.
evalScript('[eval]');
} else if (process.argv[1]) { // 有第一个参数到时候
// make process.argv[1] into a full path
var path = NativeModule.require('path');
process.argv[1] = path.resolve(process.argv[1]);
// If this is a worker in cluster mode, start up the communiction
// channel.
if (process.env.NODE_UNIQUE_ID) {
var cluster = NativeModule.require('cluster');
cluster._setupWorker();
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}
var Module = NativeModule.require('module');
if (global.v8debug &&
process.execArgv.some(function(arg) {
return arg.match(/^--debug-brk(=[0-9]*)?$/);
})) {
// XXX Fix this terrible hack!
//
// Give the client program a few ticks to connect.
// Otherwise, there's a race condition where `node debug foo.js`
// will not be able to connect in time to catch the first
// breakpoint message on line 1.
//
// A better fix would be to somehow get a message from the
// global.v8debug object about a connection, and runMain when
// that occurs. --isaacs
var debugTimeout = +process.env.NODE_DEBUG_TIMEOUT || 50;
setTimeout(Module.runMain, debugTimeout);
} else {
// Main entry point into most programs:
Module.runMain();
}
} else { // 没有参数的时候
var Module = NativeModule.require('module');
// If -i or --interactive were passed, or stdin is a TTY.
if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
// REPL
var opts = {
useGlobal: true,
ignoreUndefined: false
};
if (parseInt(process.env['NODE_NO_READLINE'], 10)) {
opts.terminal = false;
}
if (parseInt(process.env['NODE_DISABLE_COLORS'], 10)) {
opts.useColors = false;
}
var repl = Module.requireRepl().start(opts);
repl.on('exit', function() {
process.exit();
});
} else {
// Read all of stdin - execute it.
process.stdin.setEncoding('utf8');
var code = '';
process.stdin.on('data', function(d) {
code += d;
});
process.stdin.on('end', function() {
process._eval = code;
evalScript('[stdin]');
});
}
}
startup.globalVariables = function() { // 这个方法很蛋疼啊,定义这么多重复变量吃啊
global.process = process;
global.global = global;
global.GLOBAL = global;
global.root = global;
global.Buffer = NativeModule.require('buffer').Buffer;
process.binding('buffer').setFastBufferConstructor(global.Buffer);
process.domain = null;
process._exiting = false;
};
startup.globalTimeouts = function() {
global.setTimeout = function() {
var t = NativeModule.require('timers');
return t.setTimeout.apply(this, arguments);
};
global.setInterval = function() {
var t = NativeModule.require('timers');
return t.setInterval.apply(this, arguments);
};
global.clearTimeout = function() {
var t = NativeModule.require('timers');
return t.clearTimeout.apply(this, arguments);
};
global.clearInterval = function() {
var t = NativeModule.require('timers');
return t.clearInterval.apply(this, arguments);
};
global.setImmediate = function() {
var t = NativeModule.require('timers');
return t.setImmediate.apply(this, arguments);
};
global.clearImmediate = function() {
var t = NativeModule.require('timers');
return t.clearImmediate.apply(this, arguments);
};
};
}
执行脚本部分代码:
function evalScript(name) {
var Module = NativeModule.require('module');
var path = NativeModule.require('path');
var cwd = process.cwd();
var module = new Module(name);
module.filename = path.join(cwd, name);
module.paths = Module._nodeModulePaths(cwd);
var script = process._eval;
if (!Module._contextLoad) {
var body = script;
script = 'global.__filename = ' + JSON.stringify(name) + ';\n' +
'global.exports = exports;\n' +
'global.module = module;\n' +
'global.__dirname = __dirname;\n' +
'global.require = require;\n' +
'return require("vm").runInThisContext(' +
JSON.stringify(body) + ', ' +
JSON.stringify(name) + ', true);\n';
}
var result = module._compile(script, name + '-wrapper');
if (process._print_eval) console.log(result);
}
nodejs执行脚本的方式有两种,一种是process进程对象的._compile方法,上面已经给出了。另外一种是evals模块的Script对象。module._compile(script, name + '-wrapper');。Script = process.binding('evals').Script,Script.runInThisContext(),Script.runInNewContext()。他们的本质是一样的,都是把内容传给V8解析执行。
var NativeModule = require('native_module');
var Script = process.binding('evals').NodeScript;
var runInThisContext = Script.runInThisContext;
var runInNewContext = Script.runInNewContext;
分享到:
相关推荐
《Node.js-深入理解Node.js核心思想与源码分析》 Node.js,作为一个基于Chrome V8引擎的JavaScript运行环境,自其诞生以来,就以其高效、异步非阻塞I/O以及事件驱动的特性,迅速在Web开发领域崭露头角。本资料将带...
5. **Node.js源码分析**: - **路由(Routing)**: Node.js服务器通过定义不同的路由来处理不同URL的请求,例如获取商品列表、创建订单等。开发者可以学习如何在Node.js中设置和处理路由。 - **中间件(Middleware...
在分析“node.js 源码 *nix版”时,我们将关注与 *nix(Unix-like)操作系统相关的源码编译过程,以及遇到的问题和解决方案。 标题中提到的“*nix版”意味着我们讨论的是 Node.js 在类 Unix 系统(如 Linux 或 ...
例如,《深入理解Node.js:核心思想与源码分析》这本书就基于node v6.0.0版本进行源码分析,书中详细阐述了Node.js异步IO和事件循环的核心思想,以及如何通过分析核心模块的实现来帮助开发者更好地使用Node.js。...
总的来说,Node.js的源码分析涵盖了事件循环机制、V8引擎的使用、模块系统的设计、异步I/O的实现等多个方面。深入学习这些内容,不仅可以帮助开发者更好地利用Node.js,还能为他们提供宝贵的系统级编程经验。通过...
总之,深入学习Node.js的源码,不仅可以帮助你更好地理解和利用这个强大的工具,还能提升你的系统编程和JavaScript编程能力,为你的职业发展打下坚实基础。记得在阅读过程中结合实际项目实践,理论与实践相结合,...
总之,《Node.js调试指南》涵盖了Node.js开发中的各种调试技术,从基础到高级,从同步到异步,从错误处理到性能分析,全面地指导开发者提升调试技能,从而编写更稳定、更高效的Node.js应用。通过学习和实践这些技巧...
这个"node.js博客案例详细源码 干货干货干货"项目,显然是一份使用Node.js开发个人博客系统的完整源代码,对于学习Node.js、Web开发以及数据库操作有着极高的实践价值。 首先,我们要理解Node.js的核心特性。Node....
在本资源中,你将学习如何结合 Vue.js 和 Node.js 进行实战项目的开发...通过这个实战项目,你将全面了解Vue.js和Node.js的结合使用,从需求分析、设计、编码到测试和部署,掌握完整的Web开发流程,提升实际开发能力。
11. **源码分析**:书中的源码部分可以帮助读者深入理解Node.js的内部工作原理,以及实际项目中的代码组织和设计模式。 12. **实时应用**:Node.js常用于构建实时应用,如聊天室、协作编辑工具,书中可能会有相关的...
Node.js是一种基于Chrome V8引擎的JavaScript运行环境,它允许开发者在服务器端使用JavaScript进行编程。这个"Node.js入门经典源代码"压缩包文件显然包含了用于学习和理解Node.js基础知识和实践的源代码示例。以下是...
Node.js 主要应用于构建 Web 服务器、实时通信应用(如聊天应用)、命令行工具、API 服务、构建工具(如 Webpack、Gulp)、数据处理和分析等场景。 7. **Node.js 与前端开发** 虽然 Node.js 主要用于后端开发,但...
深入理解Node.js:核心思想与源码分析 epub 电子书格式的
在本文中,我们将深入探讨如何使用Node.js构建一个远程监测系统,重点关注Node.js的性能监控。Node.js以其异步、事件驱动的特性在构建高性能...源码分析将帮助你理解实际的实现细节,进一步提升你的Node.js开发能力。
源码分析可以帮助我们深入了解Node.js的工作原理,提升对JavaScript运行机制的理解。 一、V8引擎升级 在Node.js v7.0.0中,使用的V8引擎也相应地进行了更新。V8引擎负责将JavaScript代码编译为机器码并执行。升级后...
Vue微商城项目源码分析与搭建指南 Vue.js是一个轻量级、高性能的前端JavaScript框架,它具有组件化、响应式、易于上手的特点。在本项目中,我们使用Vue.js构建了一个微商城应用,结合Node.js作为后端服务器,为用户...
3. **Node.js**:Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它让JavaScript可以在服务器端运行。在前端模块化开发中,Node.js用于搭建开发环境,安装和管理依赖,以及构建和打包工具。 4. **Webpack**...
Node.js是一个开源、跨平台的JavaScript运行环境,它允许开发者在服务器端运行JavaScript代码,具有高性能、非阻塞I/O和事件驱动的特性,适合构建实时、可扩展的网络应用。 2.2 MongoDB介绍 MongoDB是一种面向文档...
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它让开发者能够在服务器端使用 JavaScript 进行编程,极大地扩展了 JavaScript 的应用场景。而 NodeCanvas 是一个专为 Node.js 设计的 Canvas 库,它实现...
"Node.js-基于VUE2.x和Node.js的完整开源博客解决方案--ZOEBlog" 这个标题表明我们正在讨论一个开源的博客系统,它使用了Node.js作为服务器端技术,并结合了Vue.js 2.x版本作为前端框架。"ZOEBlog"可能是这个项目的...