lyhper blog

node.js执行字符串代码

前言

提到js字符串代码,最容易想到的就是eval方法,比如eval('console.log(1)')。但是在node端,eval方法并不能很好的使用,因为在它的作用域里和原生模块的行为有一定的差异,比如不能如原生模块一样访问到exports变量;并且,它不能够模拟模块的行为。而module模块就能很好的解决这一问题。

module变量的介绍

在一个模块内部,module变量是当前模块的引用。在控制台打印出来,module变量含有如下属性:

module变量

  • id表示当前模块的唯一标识符;
  • exports表示当前模块输出的内容;
  • parent是第一个引用当前模块的模块;
  • filename就是当前模块的文件名;
  • loaded表示是否加载完成;
  • children表示当前模块引用过的模块;
  • paths表示当前模块的查找路径数组

module模块方法简介

module构造函数

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  updateChildren(parent, this, false);
  this.filename = null;
  this.loaded = false;
  this.children = [];
}

id表示模块的唯一标识符,parent表示第一个引用此模块的模块

_compile方法

// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {

  content = internalModule.stripShebang(content);

  // create wrapper function
  var wrapper = Module.wrap(content);

  var compiledWrapper = vm.runInThisContext(wrapper, {
    filename: filename,
    lineOffset: 0,
    displayErrors: true
  });

  var inspectorWrapper = null;
  if (process._breakFirstLine && process._eval == null) {
    if (!resolvedArgv) {
      // we enter the repl if we're not given a filename argument.
      if (process.argv[1]) {
        resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
      } else {
        resolvedArgv = 'repl';
      }
    }

    // Set breakpoint on module start
    if (filename === resolvedArgv) {
      delete process._breakFirstLine;
      inspectorWrapper = process.binding('inspector').callAndPauseOnStart;
      if (!inspectorWrapper) {
        const Debug = vm.runInDebugContext('Debug');
        Debug.setBreakPoint(compiledWrapper, 0, 0);
      }
    }
  }
  var dirname = path.dirname(filename);
  var require = internalModule.makeRequireFunction(this);
  var depth = internalModule.requireDepth;
  if (depth === 0) stat.cache = new Map();
  var result;
  if (inspectorWrapper) {
    result = inspectorWrapper(compiledWrapper, this.exports, this.exports,
                              require, this, filename, dirname);
  } else {
    result = compiledWrapper.call(this.exports, this.exports, require, this,
                                  filename, dirname);
  }
  if (depth === 0) stat.cache = null;
  return result;
};

content表示要执行的js代码,filename表示文件名。这个方法的作用就是在模块的作用域内执行js代码字符串,并暴露出响应的变量

举个🌰

获取module构造函数有两种办法:

  • const Module = require('module')
  • const Module = module.constructor

这里采用第一种方法

const Module = require('module')

const myModule = new Module('my-module')
const jsCode = 'console.log(1);module.exports = 1'
myModule._compile(jsCode, 'my-module')

console.log(myModule.exports) //我的模块暴露出的内容

参考文档