# Node篇

# 1. 什么是 node(初级)

node 是一种后端语言,是基于谷歌浏览器的v8引擎的服务端语言。

node 是基于 javascript 语法的一套服务器。

  • 单线程:高并发使得 node 让 cpu 的利用率可以达到100%。
  • 非阻塞式i/o 就是 node 在访问高io(输入输出)的应用的时候 不会等待着它的完。
  • 事件驱动 把高 io 的操作放到一个事件列队当中 完成后通过回调函数的方式在进入到线程里。
  • 轻量级:因为 node.js 本身即是代码又是服务器,前后端可以使用同一套语法。

所以 node 一般适合开发高并发的,高 i/o 的。

不适合开发一些对稳定性要求较高的应用【银行,医院,电力等】。

# 2. node 的全局对象有哪些(中级)

  • global:定义整个 runtime 时期的全局变量。
  • console:定义控制台的标准输出。
  • module:模块化。
  • exports:导出。
  • process:用来描述当前 Node.js 进程状态的对象,提供一个与操作系统的简单接口。

# 3. node 的定时器有哪些(中级)

  • settimeout
  • setinterval
  • clearinterval

# 4. 通过什么方法可以防止程序的崩溃(中级)

  • try-catch-finally

# 5. npm 是什么(初级)

  • npm 是基于 node 的管理和分发包的工具,是目前最大的开放源代码的生态系统,可用于安装、卸载、发布、查看包等。
  • 通过 npm,可以安装和管理项目的所有依赖项,同时我可以指定版本。
  • 同时你也可以将自己的代码进行上传。

# 6. npm 的作用是什么(初级)

  • npm 是同 Node .js一起安装的包管理工具,能解决 Node. js 代码部署上的很多问题。常见的使用场景有以下几种:
    • 允许用户从 npm 服务器下载别人编写的第三方包到本地。
    • 允许用户从 npm 服务器下载并安装别人编写的命令行程序到本地。
    • 允许用户将自己编写的包或命令行程序上传到 npm 服务器供别人使用。

# 7.node 的 fs 模块(中级)

  • fs.readFile(文件名,回调函数)读文件。
  • fs.writeFile(路径,内容,回调函数)。
  • fs.appendFile(路径,内容,回调函数)。
  • fs.mkdir() 创建目录。
  • fs.rmdir() 删除目录。
  • fs.unlink()删除文件。

# 8. node 的 http 模块(中级)

let http=require("http")

// 2.创建服务
// 形参:req:请求, res:响应
http.createServer((req,res)=>{
    // 返回响应头
    res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"})
    // 设置返回内容
    res.end("我是一个服务器")
}).listen(3000)

# 9. express 项目的目录大致是什么结构的(中级)

首先,执行安装 express 的指令:npm install express-generator-g。

创建的项目目录结构如下:

  • ./app.js 应用核心配置文件(入口文件)。
  • ./bin 存放启动项目的脚本文件。
  • ./ package.json 存储项目的信息及模块依赖。
  • ./public 静态文件(css、js、img 等)。
  • ./routes 路由文件( MVC 中的 contro1ler)。
  • ./views 页面文件( jade 模板)。

# 10. express 常用函数有哪些(中级)

常用函数有以下几个:

  • express .Router—路由组件。
  • app.get—路由定向。
  • app. configure—配置。
  • app.set一设定参数。
  • app.use—使用中间件。

# 11. 模块化(中级)

  • commonjs 模块化

    暴露:exports 或者 module.exports 引入:require。

  • es6模块化:

    暴露export,引入 import {属性或函数…} from 路径;引入这个模块。

# 12. 请介绍一下 node 里的模块是什么(中级)

Node 中,每个文件模块都是一个对象,它的定义如下:

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

module.exports = Module;

var module = new Module(filename, parent);

所有的模块都是 Module 的实例。可以看到,当前模块(module.js)也是 Module 的一个实例。

# 13. 请介绍一下 require 的模块加载机制(高级)

这道题基本上就可以了解到面试者对Node模块机制的了解程度 基本上面试提到:

  • 1、先计算模块路径

  • 2、如果模块在缓存里面,取出缓存

  • 3、加载模块

  • 4、的输出模块的 exports 属性即可

    // require 其实内部调用 Module._load 方法
    Module._load = function(request, parent, isMain) {
      //  计算绝对路径
      var filename = Module._resolveFilename(request, parent);
    
      //  第一步:如果有缓存,取出缓存
      var cachedModule = Module._cache[filename];
      if (cachedModule) {
        return cachedModule.exports;
    
      // 第二步:是否为内置模块
      if (NativeModule.exists(filename)) {
        return NativeModule.require(filename);
      }
      
      /********************************这里注意了**************************/
      // 第三步:生成模块实例,存入缓存
      // 这里的Module就是我们上面的1.1定义的Module
      var module = new Module(filename, parent);
      Module._cache[filename] = module;
    
      /********************************这里注意了**************************/
      // 第四步:加载模块
      // 下面的module.load实际上是Module原型上有一个方法叫Module.prototype.load
      try {
        module.load(filename);
        hadException = false;
      } finally {
        if (hadException) {
          delete Module._cache[filename];
        }
      }
    
      // 第五步:输出模块的exports属性
      return module.exports;
    };
    

# 14. 我们知道node导出模块有两种方式,是 exports.xxx=xxx 和 Module.exports={} 他们有什么区别(高级)

  • exports其实就是module.exports
  • 其实1.3问题的代码已经说明问题了,接着我引用廖雪峰大神的讲解,希望能讲的更清楚
module.exports vs exports
// 很多时候,你会看到,在Node环境中,有两种方法可以在一个模块中输出变量:

// 方法一:对module.exports赋值:

// hello.js

function hello() {
    console.log('Hello, world!');
}

function greet(name) {
    console.log('Hello, ' + name + '!');
}

module.exports = {
    hello: hello,
    greet: greet
};
// 方法二:直接使用exports:

// hello.js

function hello() {
    console.log('Hello, world!');
}

function greet(name) {
    console.log('Hello, ' + name + '!');
}

function hello() {
    console.log('Hello, world!');
}

exports.hello = hello;
exports.greet = greet;
// 但是你不可以直接对exports赋值:

// 代码可以执行,但是模块并没有输出任何变量:
exports = {
    hello: hello,
    greet: greet
};
// 如果你对上面的写法感到十分困惑,不要着急,我们来分析Node的加载机制:

// 首先,Node会把整个待加载的hello.js文件放入一个包装函数load中执行。在执行这个load()函数前,Node准备好了module变量:

var module = {
    id: 'hello',
    exports: {}
};
// load()函数最终返回module.exports:

var load = function (exports, module) {
    // hello.js的文件内容
    ...
    // load函数返回:
    return module.exports;
};

var exportes = load(module.exports, module);
// 也就是说,默认情况下,Node准备的exports变量和module.exports变量实际上是同一个变量,并且初始化为空对象{},于是,我们可以写:


exports.foo = function () { return 'foo'; };
exports.bar = function () { return 'bar'; };
// 也可以写:

module.exports.foo = function () { return 'foo'; };
module.exports.bar = function () { return 'bar'; };
// 换句话说,Node默认给你准备了一个空对象{},这样你可以直接往里面加东西。

// 但是,如果我们要输出的是一个函数或数组,那么,只能给module.exports赋值:

module.exports = function () { return 'foo'; };
// 给exports赋值是无效的,因为赋值后,module.exports仍然是空对象{}。

// 结论
// 如果要输出一个键值对象{},可以利用exports这个已存在的空对象{},并继续在上面添加新的键值;

// 如果要输出一个函数或数组,必须直接对module.exports对象赋值。

// 所以我们可以得出结论:直接对module.exports赋值,可以应对任何情况:

module.exports = {
    foo: function () { return 'foo'; }
};
// 或者:

module.exports = function () { return 'foo'; };
// 最终,我们强烈建议使用module.exports = xxx的方式来输出模块变量,这样,你只需要记忆一种方法。

# 15. 请介绍一下 Node 事件循环的流程(高级)

  • 在进程启动时,Node 便会创建一个类似于 while(true) 的循环,每执行一次循环体的过程我们成为 Tick。

  • 每个 Tick 的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程。

    37

# 16. 在每个tick的过程中,如何判断是否有事件需要处理(高级)

  • 每个事件循环中有一个或者多个观察者,而判断是否有事件需要处理的过程就是向这些观察者询问是否有要处理的事件。
  • 在 Node 中,事件主要来源于网络请求、文件的 I/O 等,这些事件对应的观察者有文件 I/O 观察者,网络 I/O的观察者。
  • 事件循环是一个典型的生产者/消费者模型。异步 I/O,网络请求等则是事件的生产者,源源不断为Node提供不同类型的事件,这些事件被传递到对应的观察者那里,事件循环则从观察者那里取出事件并处理。
  • 在 windows 下,这个循环基于 IOCP 创建,在Lnix下则基于多线程创建。