美文网首页
02、NodeJS-基础

02、NodeJS-基础

作者: 王梓懿_1fbc | 来源:发表于2018-10-20 16:02 被阅读4次

一、异步编程

  • 异步操作
  - Node 采用 Chrome V8 引擎处理 JavaScript 脚本, V8 最大特点就是单线程运行,一次只能运行一个任务
  - Node 大量采用异步操作(asynchronous operation),即任务不是马上执行,而是插在任务队列的尾部,等到前面的任务运行完后再执行
  - 提高代码的响应能力

Node大量采用异步操作,即任务不是马上执行,而是直接插入任务队列的尾部,等前面任务执行完成后再只执行。异步执行,而不是单线程执行(一次只能执行一个任务),这大大提高代码的响应能力。
【Node中,所有会发生阻塞的操作都是异步的。】

  - setTimeout()
  - ajax
  - 文件操作
  ...

  • 异步操作回调
    由于系统永远不知道用户什么时候会输入内容,所以代 码不能永远停在一个地方;
    Node 中的操作方式就是以异步回调的方式解决无状态 的问题;

  • 回调函数的设计

  - 回调函数一定作为参数的最后一个参数出现:
    function foo1(name, age, callback) { }
    function foo2(value, callback1, callback2) { }

  - 回调函数的第一个参数默认接收错误信息,第二个参数才是真正 的回调数据(便于外界获取调用的错误情况):
     foo1('李明', 19, function(error, data) { 
        if(error) throw error;
          console.log(data);
      });

***错误优先: ***因为之后的操作大多数都是异步的方式,无法通过 try catch 捕获异常; 所以在node中错误优先的回调函数,第一个参数为上一步的错误信息。

  • 异步回调的问题
  - 异步事件驱动的代码不容易阅读
  - 不容易调试
  - 不容易维护

二、进程和线程

  • 什么是进程
  - 一个正在运行 的应用程序都称之为进程;
  - 每一个应用程序都至少有一个进程;
  - 进程是用来给应用程序提供一个运行的环境;
  - 进程是操作系统为应用程序分配资源的一个单位;

  • 什么是线程
  - 用来执行应用程序中的代码;
  - 在一个进程内部,可以有很多的线程;
  - 在一个线程内部,同时只可以干一件事;
  - 而且传统的开发方式大部分都是 I/O 阻塞的;
  - 所以需要多线程来更好的利用硬件资源;
  - 给人带来一种错觉:线程越多越好;

多线程同时执行,真实情况并不是"同时",因为CPU只有一个;
线程问题: 线程创建需要耗费资源,线程数量也不能无限添加,线程同步操作,线程间数据共享,CPU中线程间的切换有上下文的转换是需要耗时的....
在node中,实现异步非阻塞操作,并不是使用多线程实现了(常规的异步非阻塞是通过多线程实现的)。*** Node.js在设计上也是比较大胆,它以单进程、单线程模式运行。事件驱动机制是Node.js通过内部单线程高效率地维护事件循环队列来实现的,没有多线程的资源占用和上下文切换,这意味着面对大规模的http请求,Node.js凭借事件驱动搞定一切。***

三、事件驱动

事件驱动是NodeJS中一大特性。事件驱动,就是通过监听事件的状态变化来作出相应的操作。例如文件存在,文件不存在,文件读取完毕,文件读取错误,触发对应的状态,之后通过回调函数进行处理。

  • 线程驱动和事件驱动

    • 线程驱动就是当收到一个请求的时候,将会为该请求开一个新的线程来处理请求。一般存在一个线程池,线程池中有空闲的线程,会从线程池中拿取线程来进行处理,如果线程池中没有空闲的线程,新来的请求将会进入队列排队,直到线程池中空闲线程;
    • 事件驱动就是当进来一个新的请求的时,请求将会被压入队列中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数;

在美国去看医生,需要填写大量表格,比如保险、个人信息之类,传统的基于线程的系统(thread-based system),接待员叫到你,你需要在前台填写完成这些表格,你站着填单,而接待员坐着看你填单。你让接待员没办法接待下一个客户,除非完成你的业务。想让这个系统能运行的快一些,只有多加几个接待员,人力成本需要增加不少。

基于事件的系统(event-based system)中,当你到窗口发现需要填写一些额外的表格而不仅仅是挂个号,接待员把表格和笔给你,告诉你可以找个座位填写,填完了以后再回去找他。你回去坐着填表,而接待员开始接待下一个客户。你没有阻塞接待员的服务。
你填完表格,返回队伍中,等接待员接待完现在的客户,你把表格递给他。如果有什么问题或者需要填写额外的表格,他给你一份新的,然后重复这个过程。
这个系统已经非常高效了,几乎大部分医生都是这么做的。如果等待的人太多,可以加入额外的接待员进行服务,但是肯定要比基于线程模式的少得多。

四、模块化结构

  • 模块与文件是一一对应关系,即加载一个模块,实际上
    就是加载对应的一个模块文件

  • 模块的分类

  - 文件模块
      就是我们自己写的功能模块文件
  - 核心模块
      Node 平台自带的一套基本的功能模块,也有人称之为 Node平台的 API
  - 第三方模块
      社区或第三方个人开发好的功能模块,可以直接拿回来用

  • 模块化开发的流程
  - new compute.js  创建模块(一个模块就一个文件)
  - module.exports = {}  导出成员
  - var comp = require('./compute.js')  载入模块
  - comp.add(1, 1)  使用模块

五、定义模块

  • 模块内全局环境
  -  __dirname
      用于获取当前文件所在目录的完整路径
      在 REPL 环境无效
  -  __filename
      用来获取当前文件的完整路径
      在 REPL 环境同样无效
  - module 
      模块对象
  - exports
      映射到module.exports的别名
  - require()
      require.cache
      require.extensions
      require.main
      require.resolve()

文件操作中必须使用绝对路径;

  • module 对象
    Node 内部提供一个 Module 构建函数。所有模块都是 Module 的实例;
  – module.id 模块的识别符,通常是带有绝对路径的模块文件名;
  - module.filename 模块定义的文件的绝对路径;
  – module.loaded 返回一个布尔值,表示模块是否已经完成加载;
  – module.parent 返回一个对象,表示调用该模块的模块;
  – module.children 返回一个数组,表示该模块要用到的其他模块;
  – module.exports 表示模块对外输出的值;

  • 模块的定义
  - 一个新的 JS 文件就是一个模块;
  - 一个合格的模块应该是有导出成员的,否则模块就失去了定义的价值;
  - 模块内部是一个独立(封闭)的作用域(模块与模块之间不会冲突);
  - 模块之间必须通过导出或导入的方式协同;
  - 导出方式
      exports.name = value;
      module.exports = { name: value };
  - module.exports是用于为模块导出成员的接口;
  - exports是指向module.exports的别名,相当于 在模块开始的时候执行
      var exports = module.exports;
  - 一旦为 module.exports 赋值,就会切断之前两者的相关性;
  - 最终模块的导出成员以 module.exports 为准;

1、每个模块的内部都是私有空间,不会污染全局作用域;
2、模块可以多次加载,但是只会在第一次加载时运行一次, 然后运行结果就被缓存了,以后再加载,就直接读取缓 存结果;
3、模块加载的顺序,按照其在代码中出现的顺序;

六、载入模块

  • require是什么
    require 的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象;

  • require扩展名
    require 加载文件时可以省略扩展名;

  require('./module');

  // 1、此时文件按 JS 文件执行
  require('./module.js');
  // 2、此时文件按 JSON 文件解析
  require('./module.json');
  // 3、此时文件预编译好的 C++ 模块执行
  require('./module.node');

  • require加载文件规则
  - 通过 ./ 或 ../ 开头:则按照相对路径从当前文件所在文件夹开始寻找模块
      require('../file.js');   // 上级目录下找 file.js 文件
  - 通过 / 开头:则以系统根目录开始寻找模块
      require('/Users/zyz/Documents/file.js');  // 以绝对路径的方式找
  - 如果参数字符串不以“./“ 或 ”/“ 开头,则表示加载 的是一个默认提供的核心模块(位于 Node 的系统安 装目录中)
      require('fs');   // 加载核心模块中的文件系统模块
  - 或者从当前目录向上搜索 node_modules 目录中的文件
      require('my_module'); // 各级 node_modules文件夹中搜索 my_module.js 文件;

require('my_module'); 开始是在核心模块中查找,这个其实不是核心模块;接着各级 node_modules文件夹中搜索

  • 模块的缓存
    • 第一次加载某个模块时,Node会缓存该模块。以后再 加载该模块,就直接从缓存取出该模块的 module.exports 属性(不会再次执行该模块)
    • 模块的缓存可以通过require.cache拿到,同样也可以删除
    //  模块的缓存的删除
    Object.keys(require.cache).forEach( (key) => {
        delete require.cache[key];
    } );

  • 如果需要多次执行模块中的代码,一般可以让模块暴露行为(函数)【避免每次都要清理缓存的问题】
  // 缓存的问题 -- 方式1
  module.exports = () => {
      return {time: new Date()};
  }
  // 缓存的问题 -- 方式2
    function fn(){
        return {time: new Date()};
     }
    module.exports = {fn};

作者:西门奄
链接:https://www.jianshu.com/u/77035eb804c3
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

相关文章

  • 02、NodeJS-基础

    一、异步编程 异步操作 Node大量采用异步操作,即任务不是马上执行,而是直接插入任务队列的尾部,等前面任务执行完...

  • 02、NodeJS-基础

    一、异步编程 异步操作 Node大量采用异步操作,即任务不是马上执行,而是直接插入任务队列的尾部,等前面任务执行完...

  • nodejs-流(stream)操作基础

    什么是流? 流是一种用来处理文件的字节传输手段 它不关心文件的整体内容,只关注是否从文件中读取到了数据,以及读到数...

  • NodeJS-简介&配置

    NodeJS-简介&配置 NodeJS NodeJS 中文网 NodeJS API 一、客户端的JavaScrip...

  • java多线程相关

    (一) 基础篇 01.Java多线程系列--“基础篇”01之 基本概念 02.Java多线程系列--“基础篇”02...

  • 3.get请求获取数据方式

    原生 split nodjs->querystring把键值对的字符串转化为键值对的对象 nodejs->query

  • Linux 之 NodeJs

    万金油 前序: NodeJs-官网[https://nodejs.org/] 前提: 搬运网上的教程,此处仅为个人...

  • 第一章 node.js 介绍

    本节内容 1 nodeJs-是什么 2 nodeJs 和 浏览器 JavaScript 的区别 3 V8 引擎 4...

  • Java 基础

    Java 基础01Java开发入门 Java 基础02Java编程基础 Java 基础03面向对象 Java 基础...

  • nodejs-准备

    1 - 安装nodejs(以Windows为例 ) 下载nodejs 打开cmd,输入node --version...

网友评论

      本文标题:02、NodeJS-基础

      本文链接:https://www.haomeiwen.com/subject/zndmzftx.html