Nodejs源码解析之module

2023-11-03

http://blog.csdn.net/leoleocs/article/details/50245677#

module管理是Nodejs中比较有特色的部分,官方有详细的文档https://nodejs.org/api/modules.html 哪里介绍了一些基本的使用规则,主要的内容如下,

  • 文件和模块之间是一一对应关系:使用方法就是require,后续源码解析中会详细介绍什么是require,以及如何实现的。 文件的名字就是一个id,也就是标志符。
  • 如何访问和被确认为主模块: 简单的说就是被nodejs启动的模块为主模块,其他都不是,这个可以在源码中说明。
  • 模块的导入:作为单个文件,目录,以及node_module如何导入。这里在源码中也有体现
  • 缓存: 被require导入的模块只导入一次,不会有多次,这个是靠cache来实现的,查找的关键字是文件(模块)的文件名(含全路径)。 同时需要主要循环依赖问题
  • module 对象的具体的含义与使用,这个也在源码中有实现

这里主要解析module.js 的全部源码和部分Node.js的代码,以说明上述的问题,在review module.js 的源码前,推荐下面两遍博文: 
http://stackoverflow.com/questions/9475792/how-does-require-in-node-js-work 
http://www.tuicool.com/articles/yE7zAv

module.js的导入

module.js是由node.js在Nodejs程序启动的时候导入的。module.js中使用的require函数是在node.js文件中定义的。 具体的代码在node.js中,代码分为两段:

// 程序启动时候,会利用NativeModule去require我们的module.js函数
      // 下面会详细介绍NativeModule
      var Module = NativeModule.require('module');
      .............(省去)
      } else {
        // Main entry point into most programs:
        // 这个就是文档中所说的主模块问题,也就是利用node app.js启动程序时候,
        // app.js就是主模块,也就是module.js里面定义的runMain函数。
        Module.runMain();
      }

下面详细说明NativeModule的使用

// 对象构造函数
    function NativeModule(id) {
    // 文件名,自动加上了js后缀,说明其仅仅解析js的module文件
    this.filename = id + '.js';
    // 用于记录本模块的标志符
    this.id = id;
    // 模块的导出对象
    this.exports = {};
    // 是否导出标志符
    this.loaded = false;
  }
  // 内部的模块, 具体可以查看 process.binding的用法
  NativeModule._source = process.binding('natives');
  // 用于做NativeModule的内部缓存
  NativeModule._cache = {};
  // 这个是在module.js中使用的require函数
  NativeModule.require = function(id) {
    // 特殊处理,如果是native_module,直接返回
    if (id == 'native_module') {
      return NativeModule;
    }
     // 查看是否有缓存,
    var cached = NativeModule.getCached(id);
    if (cached) {
     // 如果有,直接返回导出的对象
      return cached.exports;
    }
    // 是否在NativeModule中存在
    if (!NativeModule.exists(id)) {
      throw new Error('No such native module ' + id);
    }
    // 这个应该是和C/C++的系统模块交互
    process.moduleLoadList.push('NativeModule ' + id);
    // 生成一个新的NativeModule对象
    var nativeModule = new NativeModule(id);
     // 做缓存
    nativeModule.cache();
    // 编译模块
    nativeModule.compile();
    // 导出模块的对象
    return nativeModule.exports;
  };
  // 查找是否有缓存
  NativeModule.getCached = function(id) {
    return NativeModule._cache[id];
  }
  // 查找id是否存在,从代码上可以看出NativeModule要求在c/c++代码中有体现
  NativeModule.exists = function(id) {
    return NativeModule._source.hasOwnProperty(id);
  }
  // 获取source
  NativeModule.getSource = function(id) {
    return NativeModule._source[id];
  }
  // 对于script的封装,这个是后续理解module,exports等的关键。任何的require
  //一个文件或者模块其实就是将文件里面的内容,封装成一个函数
  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);
    // 这个是javascript虚拟机的功能 后续会有详细介绍
    var fn = runInThisContext(source, { filename: this.filename });
    // 查看上述的wrap 函数定义,也就是说在module.js中使用的module
    // 其实是NativeModule对象
    // 使用的require函数,其实是NativeModule.require
    fn(this.exports, NativeModule.require, this, this.filename);
     // 模块已经导入
    this.loaded = true;
  };
  // 增加cache
  NativeModule.prototype.cache = function() {
    NativeModule._cache[this.id] = this;
  };


由于module模块的内容比较多,分如下重点函数进行源码分析,然后再分析帮助函数

Require函数详解

下面是require函数的源码,也就是我们通常用require(./test)时候,这里需要强调的是,require不是关键字,而是一个函数

// Loads a module at the given file path. Returns that module's
// `exports` property.
// 导入模块,并且返回模块的exports属性。
Module.prototype.require = function(path) {
  // 参数检查,确保path不为空
  assert(path, 'missing path');
  // 参数检查,确保path为string类型
  assert(util.isString(path), 'path must be a string');
  // 直接调用_load函数返回,注意参数多了一个this,也就是本module的对象
  return Module._load(path, this);
};


// Check the cache for the requested file.
// 1. If a module already exists in the cache: return its exports object.
// 2. If the module is native: call `NativeModule.require()` with the
//    filename and return the result.
// 3. Otherwise, create a new module for the file and save it to the cache.
//    Then have it load  the file contents before returning its exports
//    object.
// 上述的介绍翻译。
// 对要求的文件检查缓存,
// @request就是对应的要导入的file或者目录,
// @parent其实是谁导入了该模块,从上述的源码可以看出,其实是this。
// @isMain 标志是否为主文件,这里只有从node.js 调用的才是,其他都不是。
// 对于缓存,会做下面的事情:
// 1. 如果模块以及存在在缓存中,直接返回。
// 2. 如果模块是native的,直接调用NativeModule.require()并且返回。
// 3. 否则,创建一个新的module对象,保存在缓存中,并且导入文件内容,然后返回exports对象。
Module._load = function(request, parent, isMain) {
  // 添加log,看谁导入了当前的模块
  if (parent) {
    debug('Module._load REQUEST  ' + (request) + ' parent: ' + parent.id);
  }
  //找到当前的需要解析的文件名,以后会详解_resolveFilename
  var filename = Module._resolveFilename(request, parent);
   // 步骤1:如果已经有的缓存,直接返回缓存的exports
  var cachedModule = Module._cache[filename];
  if (cachedModule) {
    return cachedModule.exports;
  }
  // 步骤2:如果在自然模块中存在,直接使用自然模块进行解析
  if (NativeModule.exists(filename)) {
    // REPL is a special case, because it needs the real require.
    if (filename == 'repl') {
      var replModule = new Module('repl');
      replModule._compile(NativeModule.getSource('repl'), 'repl.js');
      NativeModule._cache.repl = replModule;
      return replModule.exports;
    }


    debug('load native module ' + request);
    return NativeModule.require(filename);
  }
  // 创建Module对象。
  var module = new Module(filename, parent);
  // 是否为主模块,
  if (isMain) {
     // 主模块的话,需要将当前的module赋值给process.mainModule
    process.mainModule = module;
    // 主模块的id特殊的赋值为"."
    module.id = '.';
  }
  // 将创建的模块cache起来
  Module._cache[filename] = module;
  // 确保是否有异常
  var hadException = true;
  try {
    // 做真正的导入模块的操作,下面会详解该函数
    module.load(filename);
    hadException = false;
  } finally {
     // 如果有异常,直接删除上述的缓存
    if (hadException) {
      delete Module._cache[filename];
    }
  }
  // 返回新创建模块的exports
  return module.exports;
};


// Given a file name, pass it to the proper extension handler.
// 指定一个文件名,导入模块,调用适当扩展处理函数,当前主要是js,json,和node
Module.prototype.load = function(filename) {
  //增加log,当前导入什么文件,id是什么 
  debug('load ' + JSON.stringify(filename) +
        ' for module ' + JSON.stringify(this.id));
  // 确保当前模块没有被载入
  assert(!this.loaded);
  // 赋值当前模块的文件名
  this.filename = filename;
  // 当前的path
  this.paths = Module._nodeModulePaths(path.dirname(filename));
  // 当前文件的后缀
  var extension = path.extname(filename) || '.js';
  // 确认默认的后缀都*.js
  if (!Module._extensions[extension]) extension = '.js';
  // 根据后缀的解析函数来做解析
  Module._extensions[extension](this, filename);
  this.loaded = true;
};


下面是nodejs支持的三种后缀: 


// Native extension for .js
// js后缀的处理
Module._extensions['.js'] = function(module, filename) {
  //直接同步的读入文件的内容。 
  var content = fs.readFileSync(filename, 'utf8');
  // 然后调用_compile进行编译。下面会分析该函数
  module._compile(stripBOM(content), filename);
};




// Native extension for .json
// 对于json文件的处理
Module._extensions['.json'] = function(module, filename) {
  //直接同步的读入文件的内容。 
  var content = fs.readFileSync(filename, 'utf8');
  try {
   // 直接将模块的exports赋值为json文件的内容
    module.exports = JSON.parse(stripBOM(content));
  } catch (err) {
    // 异常处理
    err.message = filename + ': ' + err.message;
    throw err;
  }
};
//node文件的打开处理,通常为C/C++文件。
Module._extensions['.node'] = process.dlopen;


下面就分析最后的一个函数_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.
// 这个函数会给出require, module, exports等帮助变量给文件
// @content 主要是js文件的主要内容
// @filename 是js文件的文件名
Module.prototype._compile = function(content, filename) {
  // self就是一个帮助变量,代表的this
  var self = this;
  // remove shebang
  // 去掉一些注释(shebang)
  content = content.replace(/^\#\!.*/, '');
   // 其实模块中的require就是这个函数
   // 其仅仅是一个对module中的require函数的封装。
  function require(path) {
    return self.require(path);
  }
  //resolve 函数,这个会解释文档中的module导入的路径问题,是单个文件,目录还是模块 
  require.resolve = function(request) {
    return Module._resolveFilename(request, self);
  };
  // 禁止使用require中的paths路径
  Object.defineProperty(require, 'paths', { get: function() {
    throw new Error('require.paths is removed. Use ' +
                    'node_modules folders, or the NODE_PATH ' +
                    'environment variable instead.');
  }});
  //注意require.main就是主模块 
  require.main = process.mainModule;


  // Enable support to add extra extension types
  // 将Module._extensions赋值给require
  require.extensions = Module._extensions;
  require.registerExtension = function() {
    throw new Error('require.registerExtension() removed. Use ' +
                    'require.extensions instead.');
  };
  //将缓存也赋值给require
  // require一会是函数,一会又像是对象,其实都是对象:)
  require.cache = Module._cache;


  // 获取当前的文件的路径
  var dirname = path.dirname(filename);
  // 当NODE_MODULE_CONTEXTS为1的时候才可以调用,也就是说,所有的模块都在一个环境中,无需
  // 模块来,这个好像通常情况下不会被执行。
  // Module._contextLoad = (+process.env['NODE_MODULE_CONTEXTS'] > 0);
  if (Module._contextLoad) {
    if (self.id !== '.') {
      debug('load submodule');
      // not root module
      var sandbox = {};
      for (var k in global) {
        sandbox[k] = global[k];
      }
      sandbox.require = require;
      sandbox.exports = self.exports;
      sandbox.__filename = filename;
      sandbox.__dirname = dirname;
      sandbox.module = self;
      sandbox.global = sandbox;
      sandbox.root = root;


      return runInNewContext(content, sandbox, { filename: filename });
    }


    debug('load root module');
    // root module
    global.require = require;
    global.exports = self.exports;
    global.__filename = filename;
    global.__dirname = dirname;
    global.module = self;


    return runInThisContext(content, { filename: filename });
  }


  // create wrapper function
  // 这里的wrap函数就是node.js中的函数,会将文件中的内容封装成一个函数。
  var wrapper = Module.wrap(content);
  // 编译内容,返回函数
  var compiledWrapper = runInThisContext(wrapper, { filename: filename });
  // 处理debug模式,
  if (global.v8debug) {
    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);
      } else {
        resolvedArgv = 'repl';
      }
    }


    // Set breakpoint on module start
    if (filename === resolvedArgv) {
      global.v8debug.Debug.setBreakPoint(compiledWrapper, 0, 0);
    }
  }
  // 直接调用wrapper函数,将module模块中的exports,本函数中的require, 
  //self也就是新创建的module作为参数传递给模块,进行执行。
  // filename, dirname作为参数传递过去
  // 这就是为什么我们可以直接在module文件中,直接访问exports, module, require函数的原因
  var args = [self.exports, require, self, filename, dirname];
  return compiledWrapper.apply(self.exports, args);
};


所以,从上面的源码分析中,我们知道了缓存是如何实现的, module中的变量如exports,id,filename是如何能得到访问的。

module路径解析

这里先可以看一下官方文章中的内容:

require(X) from module at path Y
1. If X is a core module, // 如果是核心模块,
   a. return the core module // 直接返回核心模块,这里的核心模块是指nodejs下lib的内容
   b. STOP 返回
2. If X begins with './' or '/' or '../' // 如果文件以 "./", "/",或者 "../"形式,
   a. LOAD_AS_FILE(Y + X) // 导入一个文件 返回,如何处理导入文件
   b. LOAD_AS_DIRECTORY(Y + X) // 导入一个目录,返回 如何导入目录,看后面
3. LOAD_NODE_MODULES(X, dirname(Y)) // 导入一个NODE_MODULE,返回。
4. THROW "not found" // 上述都没找到,直接排出没找到的异常。


LOAD_AS_FILE(X) // 导入一个文件按
1. If X is a file, load X as JavaScript text.  STOP  // 如果X是一个文件,作为js文件导入,直接返回。直接停止
2. If X.js is a file, load X.js as JavaScript text.  STOP //加上js后缀为一个文件,直接作为js文件导入 直接停止
3. If X.json is a file, parse X.json to a JavaScript Object.  STOP//加上json后缀为一个文件,直接作为json文件导入 直接停止
4. If X.node is a file, load X.node as binary addon.  STOP//加上node后缀为一个文件,直接作为c/c++ addon文件导入 直接停止


LOAD_AS_DIRECTORY(X) 如何导入一个目录的处理方式
1. If X/package.json is a file, // 查找X/package.json是否存在
   a. Parse X/package.json, and look for "main" field. //查找json file中是否有main
   b. let M = X + (json main field) // 生成新的文件
   c. LOAD_AS_FILE(M) // 作为文件导入。
2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP // 查看是否存在 X/index.js,如果存在,作为js文件导入
3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP // 查看是否存在 X/index.json,如果存在,导入json文件作为js对象
4. If X/index.node is a file, load X/index.node as binary addon.  STOP// 查看是否存在 X/index.node,如果存在,导入c/c++的 addon导入


LOAD_NODE_MODULES(X, START) // 导入node_module的步骤
1. let DIRS=NODE_MODULES_PATHS(START) // 返回一个文件目录内容
2. for each DIR in DIRS:       // 对于目录里的内容
   a. LOAD_AS_FILE(DIR/X)      // 作为文件导入。 作为文件导入,查看LOAD_AS_FILE
   b. LOAD_AS_DIRECTORY(DIR/X) // 或者作为一个目录导入, 查看LOAD_AS_DIRECTORY


// 请注意,这里仅仅是说还是作为一个具体的文件或者目录,如果有一个找到了,就需要停止,而不是真的要导入文件文件里面的所有内容


NODE_MODULES_PATHS(START) // 具体NODE_MODULES文件目录算法
1. let PARTS = path split(START) 
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   c. DIR = path join(PARTS[0 .. I] + "node_modules")
   b. DIRS = DIRS + DIR
   c. let I = I - 1
5. return DIRS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
基本的思想都在文档中做了详细的说明, 那么下面分析源代码是如何实现的。废话少说,直接源代码分析:


// 这个函数就是在load的时候调用的,也就是说其负责具体filename的文件查找。
// 所以,从这里可以看出,无论怎么处理,require仅仅能导入的是一个文件模块,不能是一个目录,
// 有目录的说法,仅仅是因为可以在目录上查找具体的文件。这也就是文档中所说的文件和目录的一一对应关系
Module._resolveFilename = function(request, parent) {
  //如果是NativeModule中已经存在,直接用,这个其实就是core 模块,文档中的第一种情况
  if (NativeModule.exists(request)) {
    return request;
  }
  // 根据文件名称,调用_resolveLookupPaths来查找具体的文件模块
  var resolvedModule = Module._resolveLookupPaths(request, parent);
  // 上述返回的id
  var id = resolvedModule[0];
  // 上述返回的具体的目录文件夹
  var paths = resolvedModule[1];


  // look up the filename first, since that's the cache key.
  // 输出具体的id和paths
  debug('looking for ' + JSON.stringify(id) +
        ' in ' + JSON.stringify(paths));
  // 找到具体的文件名称,以返回。
  var filename = Module._findPath(request, paths);
  // 处理文件找不到的情况
  if (!filename) {
    var err = new Error("Cannot find module '" + request + "'");
    err.code = 'MODULE_NOT_FOUND';
    throw err;
  }
  // 返回具体的文件
  return filename;
};
// 从上面的文档可以看出,在处理核心模块后,其是依靠_resolveLookupPaths和_findPath这两个
// 帮助函数来查找具体的文件模块。 
// _resolveLookupPaths: 该函数用于查找当前文件“可能的路径”
// _findPath: 根据上述的路径,根据优先级选择一个具体的文件,如官方文档中的文件


Module._resolveLookupPaths = function(request, parent) {
  //依然先检查核心模块 
  if (NativeModule.exists(request)) {
    return [request, []];
  }
  // 查找request的标志符,如果不以"./" 或者'..'开头
  var start = request.substring(0, 2);
  if (start !== './' && start !== '..') {
    // 这种情况直接返回nodejs系统路径modulePaths,这个是在Module._initPaths 函数中设置的,
    // 有兴趣可以自己分析一下,很简单的函数
    var paths = modulePaths;
    if (parent) {
       // 设置一下父亲的路径,其实就是谁导入了当前模块
      if (!parent.paths) parent.paths = [];
      paths = parent.paths.concat(paths);
    }
    //直接返回
    return [request, paths];
  }


  // with --eval, parent.id is not set and parent.filename is null
  // 处理父亲模块为空的情况,这种情况我认为一般就是主模块
  if (!parent || !parent.id || !parent.filename) {
    // make require('./path/to/foo') work - normally the path is taken
    // from realpath(__filename) but with eval there is no filename
    // 生成新的目录, 在系统目录modulePaths,当前目录和"node_modules"作为候选的路径
    // 对于node_modules,可以参考_nodeModulePaths函数。
    var mainPaths = ['.'].concat(modulePaths);
    mainPaths = Module._nodeModulePaths('.').concat(mainPaths);
    return [request, mainPaths];
  }


  // Is the parent an index module?
  // We can assume the parent has a valid extension,
  // as it already has been accepted as a module.
  // 处理父亲模块是否为index模块,
  var isIndex = /^index\.\w+?$/.test(path.basename(parent.filename));
  var parentIdPath = isIndex ? parent.id : path.dirname(parent.id);
  // 返回request中的具体的id
  var id = path.resolve(parentIdPath, request);


  // make sure require('./path') and require('path') get distinct ids, even
  // when called from the toplevel js file
  // 确保require('./path') and require('path') 的id不一样。应该会做不同的缓存,所以,我们
  if (parentIdPath === '.' && id.indexOf('/') === -1) {
    id = './' + id;
  }


  debug('RELATIVE: requested:' + request +
        ' set ID to: ' + id + ' from ' + parent.id);
  //返回id和具体的父亲模块的文件夹
  //注意: 也就是说,当我们以"./" 等方式require是,都是以当前父模块为对象路径的
  return [id, [path.dirname(parent.filename)]];
};




接下来分析一下:_findPath 


// 从候选路径中选择
Module._findPath = function(request, paths) {
  // 等到具体的文件扩展,现在有js,json 和node
  var exts = Object.keys(Module._extensions);
  //如果以绝对路径开头,直接重置可选路径,所以这个windows下和linux下应该不一样,
  //这点应该是对linux来说的,所以,我们最好不要以/开头导入模块
  if (request.charAt(0) === '/') {
    paths = [''];
  }


  var trailingSlash = (request.slice(-1) === '/');
  // 这里是做文件扫描的cache,防止重复查找
  var cacheKey = JSON.stringify({request: request, paths: paths});
  if (Module._pathCache[cacheKey]) {
    // 如果cache已经有了,直接返回
    return Module._pathCache[cacheKey];
  }


  // For each path
  // 从每个候选路径中查找文件
  for (var i = 0, PL = paths.length; i < PL; i++) {
    var basePath = path.resolve(paths[i], request);
    var filename;


    if (!trailingSlash) {
      // try to join the request to the path
      // 尝试一下当前文件是否存在
      filename = tryFile(basePath);
      // 尝试一下当前文件加上后缀文件是否存在
      if (!filename && !trailingSlash) {
        // try it with each of the extensions
        filename = tryExtensions(basePath, exts);
      }
    }
    // 尝试一下当前的package.json文件是否存在
    if (!filename) {
      filename = tryPackage(basePath, exts);
    }
     // 尝试一下index文件加上后缀是否存在
    if (!filename) {
      // try it with each of the extensions at "index"
      filename = tryExtensions(path.resolve(basePath, 'index'), exts);
    }


    if (filename) {
      // 增加到文件缓存中
      Module._pathCache[cacheKey] = filename;
      return filename;
    }
  }
  return false;
};




// 所以从这里可以看出,对于具体的文件的优先级:
// 1. 具体文件。
// 2. 加上后缀。
// 3. package.json
// 4  index加上后缀
// 候选路径以当前文件夹,nodejs系统文件夹和node_module中的文件夹为候选,以上述顺序找到任意一个,
// 就直接返回

还有一些帮助函数,如readPackage, tryPackage,tryFile,_initPaths没有做详细的说明。


上述就是module的全部重要的源码说明。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Nodejs源码解析之module 的相关文章

随机推荐

  • 不拆无损,在北汽EU5,EU7,EX3,EX7安装app应用

    本文是破解过程 仅供学习参考 如果您只是安装软件 请直接看https blog csdn net robinhunan article details 105963936 这篇文章就可以了 app安装仅需要一个U盘 不需要输入任何命令 即可
  • RV1126 isp开发文档记录

    Rockchip IQ Tools Guide ISP2x v1 3 pdf 主要介绍 RKISP2 x Tuner 以下简称 Tuner 提供了一套便于用户调试 ISP 参数的工具 用户可以在 Tuner 中对所有 ISP 模块开展标定
  • Redis沙盒逃逸漏洞(CVE-2022-0543)复现以及流量特征分析

    Redis简介 Redis Labs Redis是美国Redis Labs公司的一套开源的使用ANSI C编写 支持网络 可基于内存亦可持久化的日志型 键值 Key Value 存储数据库 并提供多种语言的API 漏洞介绍 Redis 存在
  • JdbcTemplate防注入的几种方式

    使用数组 public void deleteItemByName String name Object obj new Object name String sql DELETE FROM t item WHERE name jdbcTe
  • php开启opcache

    1 简介 OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能 存储预编译字节码的好处就是 省去了每次加载和解析 PHP 脚本的开销 注意 如果需要将 Xdebug 扩展和 OPcache 一起使用 必须
  • C++ 11学习

    原始字符串字面量 就不需要转义引脚 原始字符串字面量以R 开头 以 结尾 string str R Hello World c 中 nullptr无法意识转换为整形 但是可以隐式匹配指针类型 在C 11校准下 相比NULL和0 使用null
  • Nginx 状态页详解

    Nginx 状态页详解 基于nginx 模块 ngx http stub status module 实现 在编译安装nginx的时候需要添加编译参数 with http stub status module 否则配置完成之后监测会是提示语
  • Linux命令大全,唯一以案例详解文,持续更新中

    系列文章目录 软件测试功能到自动化学习路线图 2022年最新版技术栈 软件测试01 从了解测试岗位职能和测试流程开始 附作业 软件测试02 6大实际案例手把手教你设计测试点 软件测试03 用例执行以及缺陷管理的学习 附禅道下载使用流程 软件
  • 信贷风险指标你都懂吗?

    转自 简书 信贷指标 风控基本概念 贷款不良率 不良贷款率 逾期率Vintage统计法 Now和Ever DPD1 DPD30 DPD60 DPD90 引言 17年3月份的时候 有一篇文章曾经引爆金融界 称某国内知名互联网金融逾期率高达30
  • linux下解压zip文件命令

    Linux下的压缩解压缩命令详解及实例 实例 压缩服务器上当前目录的内容为xxx zip文件 zip r xxx zip 解压zip文件到当前目录 unzip filename zip 另 有些服务器没有安装zip包执行不了zip命令 但基
  • Apache Tomcat AJP 文件包含漏洞复现(CVE-2020-10487)

    影响范围 tomcat 7 lt 7 0 100 tomcat 8 lt 7 5 51 tomcat 9 lt 9 0 31 利用条件 1 漏洞版本范围内 2 默认开启ajp服务 漏洞复现 nmap扫描 默认端口8009 经测试存在更改默认
  • AI绘画调用OpenAI-api接口【人工智能里的未来之城】:4 座未来派塔楼,天桥上覆盖着茂密的树叶,数字艺术

    OpenAI绘画数字艺术是一种利用人工智能算法生成数字艺术的技术 该技术使用了一种称为GAN Generative Adversarial Networks 生成对抗网络 的深度学习模型 这种模型由两个神经网络组成 生成器和判别器 生成器的
  • 07-2_Qt 5.9 C++开发指南_二进制文件读写(stm和dat格式)

    文章目录 1 实例功能概述 2 Qt预定义编码文件的读写 2 1 保存为stm文件 2 2 stm文件格式 2 3 读取stm文件 3 标准编码文件的读写 3 1 保存为dat文件 3 2 dat文件格式 3 3 读取dat文件 4 框架及
  • 【Maven】Maven下载,配置以及基本概念

    文章目录 1 Maven简介 2 Maven下载 3 Maven环境配置 4 Maven的基本概念 4 1 仓库 4 2 坐标 4 3 仓库配置 修改IDEA默认Maven库 1 Maven简介 Maven是一个Java项目管理工具和构建工
  • 本博客全文目录索引

    本专栏博文索引 目录 涵盖 C C STL Data Structure Algorithm TCP IP Linux Interface Driver Kernel Netfilter 和 Projects C C 详解C指针 C 对象模
  • Java BufferedInputStream原理及设计模式分析

    文章目录 背景 源码分析 FileInputStream BufferedInputStream 装饰器模式 总结 背景 BufferedInputStream和其他InputStream常常放在一起使用 BufferedInputStre
  • 梯度有关问题

    1 偏导数 方向导数 导数是函数随自变量的变化率 对于 一元函数 只有一个自变量x 那么函数y f x 的导数是 在某一点处沿x轴正方向的变化率 多元函数 多个自变量 是多维向量 那么函数随自变量的变化怎么刻画呢 一个方法 就是衡量函数在给
  • 【终极区分】iterable和iterator

    1 凡是可作用于for循环的对象都是Iterable类型 2 凡是可作用于next 函数的对象都是Iterator类型 它们表示一个惰性计算的序列 3 集合数据类型如list dict str等是Iterable但不是Iterator 不过
  • C#中的拆箱与装箱

    1 什么是拆箱和装箱 在C 中 值类型是直接将数据存储在栈空间中 而引用类型是将数据存储在堆空间中 同时在栈空间中存储一个对该数据的引用 那么如果将一个值类型转换为一个它实现的某个接口或object会发生什么 结果必然是对一个存储位置的引用
  • Nodejs源码解析之module

    http blog csdn net leoleocs article details 50245677 module管理是Nodejs中比较有特色的部分 官方有详细的文档https nodejs org api modules html