zoukankan      html  css  js  c++  java
  • Node.js 模块机制及常见面试问题解答

    Node.js 模块机制采用了 Commonjs 规范,弥补了当前 JavaScript 开发大型应用没有标准的缺陷,类似于 Java 中的类文件,Python 中的 import 机制,Node.js 中可以通过 module.exports、require 来导出和引入一个模块.

    在模块加载机制中,Node.js 采用了延迟加载的策略,只有在用到的情况下,系统模块才会被加载,加载完成后会放到 binding_cache 中。

    面试指南

    • require的加载机制?,参考:模块加载机制

    • module.exports与exports的区别,参考:对象引用关系考察

    • 假设有a.js、b.js两个模块相互引用,会有什么问题?是否为陷入死循环?,参考正文“模块循环引用问题1”

    • a模块中的undeclaredVariable变量在b.js中是否会被打印?,参考正文“模块循环引用问题2”

    • 模块在require的过程中是同步还是异步?,参考正文模块加载机制 “文件模块“

    模块的分类

    系统模块

    • C/C++ 模块,也叫 built-in 内建模块,一般用于 native 模块调用,在 require 出去

    • native 模块,在开发中使用的 Node.js 的 http、buffer、fs 等,底层也是调用的内建模块 (C/C++)。

    第三方模块

    非 Node.js 自带的模块称为第三方模块,其实还分为路径形式的文件模块(以 .../开头的)和自定义的模块(比如 express、koa 框架、moment.js 等)

    • javaScript 模块:例如 hello.js

    • json 模块:例如 hello.json

    • C/C++ 模块:编译之后扩展名为 .node 的模块,例如 hello.node

    目录结构

    ├── benchmark           一些 Node.js 性能测试代码
    ├── deps                Node.js 依赖
    ├── doc                 文档
    ├── lib                 Node.js 对外暴露的 js 模块源码
    ├── src                 Node.js 的 c/c++ 源码文件,内建模块
    ├── test                单元测试
    ├── tools               编译时用到的工具
    ├── doc                 api 文档
    ├── vcbuild.bat         win 平台 makefile 文件
    ├── node.gyp            node-gyp 构建编译任务的配置文件
    ...
    

    模块加载机制

    面试中可能会问到能说下require的加载机制吗?

    在 Node.js 中模块加载一般会经历 3 个步骤, 路径分析文件定位编译执行

    按照模块的分类,按照以下顺序进行优先加载:

    • 系统缓存:模块被执行之后会会进行缓存,首先是先进行缓存加载,判断缓存中是否有值。

    • 系统模块:也就是原生模块,这个优先级仅次于缓存加载,部分核心模块已经被编译成二进制,省略了 路径分析、 文件定位,直接加载到了内存中,系统模块定义在 Node.js 源码的 lib 目录下,可以去查看。

    • 文件模块:优先加载 .、 ..、 / 开头的,如果文件没有加上扩展名,会依次按照 .js、 .json、 .node 进行扩展名补足尝试,那么在尝试的过程中也是以同步阻塞模式来判断文件是否存在,从性能优化的角度来看待, .json、 .node最好还是加上文件的扩展名。

    • 目录做为模块:这种情况发生在文件模块加载过程中,也没有找到,但是发现是一个目录的情况,这个时候会将这个目录当作一个  来处理,Node 这块采用了 Commonjs 规范,先会在项目根目录查找 package.json 文件,取出文件中定义的 main 属性 ("main":"lib/hello.js") 描述的入口文件进行加载,也没加载到,则会抛出默认错误: Error: Cannot find module 'lib/hello.js'

    • node_modules 目录加载:对于系统模块、路径文件模块都找不到,Node.js 会从当前模块的父目录进行查找,直到系统的根目录

    image

    require 模块加载时序图

    模块缓存在哪

    上面讲解了模块的加载机制,中间有提到模块初次加载之后会缓存起来,有没有疑问,模块缓存在哪里?

    Node.js 提供了 require.cache API 查看已缓存的模块,返回值为对象,为了验证,这里做一个简单的测试,如下所示:

    新建 test-module.js 文件

    这里我导出一个变量和一个方法

    module.exports={
          a : 1,
          test : () => { }
    }
    

    新建 test.js 文件

    require('./test-module.js');
    console.log(require.cache);
    

    在这个文件里加载 test-module.js 文件,在之后打印下 require.cache 看下里面返回的是什么?看到以下结果应该就很清晰了,模块的文件名、地址、导出数据都很清楚。

    image

    模块循环引用

    问题1

    假设有 a.js、b.js 两个模块相互引用,会有什么问题?是否为陷入死循环?看以下例子

    // a.js
    console.log('a模块start');
    exports.test = 1;
    undeclaredVariable = 'a模块未声明变量'
    const b = require('./b');
    console.log('a模块加载完毕: b.test值:',b.test);
    

    // b.js
    console.log('b模块start');
    exports.test = 2;
    const a = require('./a');
    console.log('undeclaredVariable: ', undeclaredVariable);
    console.log('b模块加载完毕: a.test值:', a.test);
    

    问题2

    a 模块中的 undeclaredVariable 变量在 b.js 中是否会被打印?

    控制台执行 node a.js,查看输出结果:

    a模块start
    b模块start
    undeclaredVariable: a模块未声明变量
    b模块加载完毕: a.test值: 1
    a模块加载完毕: b.test值: 2
    

    问题1,启动 a.js 的时候,会加载 b.js,那么在 b.js 中又加载了 a.js,但是此时 a.js 模块还没有执行完,返回的是一个 a.js 模块的 exports 对象 未完成的副本 给到 b.js 模块(因此是不会陷入死循环的)。然后 b.js 完成加载之后将 exports 对象提供给了 a.js 模块

    问题2,因为 undeclaredVariable 是一个未声明的变量,也就是一个挂在全局的变量,那么在其他地方当然是可以拿到的。

    在执行代码之前,Node.js 会使用一个代码封装器进行封装,例如下面所示:

    (function(exports, require, module, __filename, __dirname) {
        // 模块的代码
    });
    

    对象引用关系考察

    也许是面试考察最多的问题:module.exports 与 exports 的区别?

    exports 相当于 module.exports 的快捷方式如下所示:

    const exports = modules.exports;
    

    但是要注意不能改变 exports 的指向,我们可以通过 exports.test='a' 这样来导出一个对象, 但是不能向下面示例直接赋值,这样会改变 exports 的指向

     // 错误的写法 将会得到 undefined
    exports = {
         'a': 1,
         'b': 2
    }
    // 正确的写法
    modules.exports = {
         'a': 1,
         'b': 2
    }
    

    更好的理解之间的关系,可以参考 JavaScript 中的对象引用 https://www.nodejs.red/#/javascript/object?

    文章来源

    转载自 Nodejs技术栈

  • 相关阅读:
    linux查看CPU和内存信息
    linux yum命令详解
    查看文件中关键字前后几行的内容
    vue.js+web storm安装及第一个vue.js
    android GPS: code should explicitly check to see if permission is available
    ASP.NET MVC Identity 使用自己的SQL Server数据库
    阿里云服务器,tomcat启动,一直卡在At least one JAR was scanned for TLDs yet contained no TLDs就不动了
    ASP.NET MVC4 MVC 当前上下文中不存在名称“Scripts”
    python 将windows字体中的汉字生成图片的方法
    Java android DES+Base64加密解密
  • 原文地址:https://www.cnblogs.com/ghostxbh/p/11416578.html
Copyright © 2011-2022 走看看