02、NodeJS-基础

一、异步编程

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

事件驱动1

事件驱动2

四、模块化结构

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

  • 模块的分类

  - 文件模块
      就是我们自己写的功能模块文件
  - 核心模块
      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};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宫若石阅读 1,064评论 0 1
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    w_zhuan阅读 3,612评论 2 41
  • 模块 Node 有简单的模块加载系统。在 Node 里,文件和模块是一一对应的。下面例子里,foo.js加载同一个...
    保川阅读 594评论 0 0
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    Myselfyan阅读 4,066评论 2 58
  • 素日里,我像只在黑夜偷生的蟑螂一样,爬行在杯盘中寻找别人牙缝里遗漏的残羹冷炙,津津有味的品尝着物质给我带来的那丁...
    竹鸿初阅读 182评论 0 0