node(01)-基础概念

基础概念:

Node能够解决什么问题?

Node的首要目标是提供一种简单的,用于创建高性能服务器的开发工具
Web服务器的瓶颈在于并发的用户量,对比Java和Php的实现方式

Node是什么?

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,让JavaScript的执行效率与低端的C语言的相近的执行效率。。
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
Node.js 的包管理器 npm,是全球最大的开源库生态系统。

为什么JavaScript是单线程?

这是由 Javascript 这门脚本语言的用途决定的。
Web Worker并没有改变 JavaScript 单线程的本质。

浏览器模型
  • 用户界面-包括地址栏、前进/后退按钮、书签菜单等

  • 浏览器引擎-在用户界面和呈现引擎之间传送指令

  • 呈现引擎-又称渲染引擎,也被称为浏览器内核,在线程方面又称为UI线程

  • 网络-用于网络调用,比如 HTTP 请求

  • 用户界面后端-用于绘制基本的窗口小部件,UI线程和JS共用一个线程

  • JavaScript解释器-用于解析和执行 JavaScript 代码

    数据存储-这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie

浏览器模型

在这里插入图片描述

除JS线程和UI线程之外的其它线程

浏览器事件触发线程
定时触发器线程
异步HTTP请求线程

任务队列
  1. 所有同步任务都在主线程上执行,形成一个执行栈(事实上绝大部分代码都在栈中执行)
  2. 主线程之外,还存在一个任务队列。只要异步任务有了运行结果,就在任务队列之中放置一个事件
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面的第三步。

Event Loop

主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

Node.js的Event Loop
  1. V8引擎解析JavaScript脚本。
  2. 解析后的代码,调用Node API。
  3. libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event
    Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
  4. V8引擎再将结果返回给用户。

同步与异步

同步和异步关注的是消息通知机制
同步:就是发出调用后,没有得到结果之前,该调用不返回,一旦调用返回,就得到返回值了。 简而言之就是调用者主动等待这个调用的结果
异步:则相反,调用者在发出调用后这个调用就直接返回了,所以没有返回结果。换句话说当一个异步过程调用发出后,调用者不会立刻得到结果,而是调用发出后,被调用者通过状态、通知或回调函数处理这个调用。

阻塞与非阻塞

阻塞非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞:调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞:调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

组合

同步异步取决于被调用者,阻塞非阻塞取决于调用者

  1. 同步阻塞

  2. 异步阻塞

  3. 同步非阻塞

  4. 异步非阻塞

    什么场合下应该考虑使用Node框架

  5. 当应用程序需要处理大量并发的输入输出,而在向客户端响应之前,应用程序并不需要进行非常复杂的处理。

    聊天服务器

    电子商务网站

涉及技术点铺垫:

进程和线程

进程:程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位
线程:进程的一个执行实体,是CPU调度和分派的基本单位,比进程更小的能独立运行的基本单位
关系:一个进程可以创建和撤销多个线程,同一个进程中多个线程之间可以并发执行
并发:多线程程序在单核CPU运行
并行:多线程程序在多核CPU运行

在这里插入图片描述

栈内存和堆内存 

栈内存保存着JS的变量和指向堆内存中对象的指针堆内存保存着对象。
下面的执行栈和栈内存是两个概念。
在这里插入图片描述

任务队列 

一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都与一个函数相关联。当栈拥有足够内存时,从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因而创建了一个初始堆栈帧)。当栈再次为空的时候,也就意味着消息处理结束

队列和栈的区别
  1. 队列先进先出
  2. 栈先进后出
  3. 栈中代码执行完毕再执行队列任务(同步代码先执行,异步代码后执行
  4. 栈的执行跟函数进栈顺序无关,跟函数销毁顺序有关。

宏任务微任务

1、任务队列并不是只有一个,不同的任务对应着不同的任务队列,宏观任务放入宏观任务队列,
微观任务放入微观任务队列,
2、这些任务队列在栈空的时候被调入的优先级是微观任务队列优于宏观任务队列
3、当微观任务队列都清空的时候才执行宏观任务队列中的任务。

  浏览器的事件环

浏览器执行js代码,先执行主栈中的代码,待主栈内代码执行完成后,清空微任务,取出宏任务执行,执行完宏任务,再清空微任务,不断循环往复,这就是浏览器的事件环

如下代码:执行结果是先B后A
为什么呢?因为浏览器的事件环微任务先执行,而then使用了微任务的机制,所以会优先执行
在这里插入图片描述

例二

执行结果CABD,为什么会这样,

  1. then是一个微任务,所以先执行 得C
  2. 此时先放入宏任务队列得计时器紧接着执行得A,因为事件环机制,执行完后会清空微任务任务队列,所以接近着清空了then,得B,清空完了微任务就去执行宏任务得D,然后又回去清空为任务…

在这里插入图片描述

异步任务

常用有哪些宏任务?

1,setTimeout()
2,setImmediate() IE专属
3,setTimeout()
4,MessageChannel()

常用哪些是微任务?

1,Promise.then()
2,MutationObserver()

REPL

在Node.js中为了使开发者方便测试JavaScript代码,提供了一个名为REPL的可交互式运行环境。开发者可以在该运行环境中输入任何JavaScript表达式,当用户按下回车键后,REPL运行环境将显示该表达式的运行结果。(即写完的代码在此环境直接调用交互测试

如何进入REPL

在命令行容器中输入node命令并按下回车键,即可进入REPL运行环境。

REPL操作
  1. 变量的操作,声明普通变量和对象
  2. val
  3. 函数的书写
  4. 下划线访问最近使用的表达式
  5. 多行书写

REPL运行环境中的上下文对象

let repl = require('repl');
let con = repl.start().context;
con.msg = 'hello';
con.hello = function(){
console.log(con.msg);
}

REPL运行环境的基础命令

.break 退出当前命令
.clear 清除REPL运行环境上下文对象中保存的所有变量与函数
.exit 退出REPL运行环境
.save 把输入的所有表达式保存到一个文件中
.load 把所有的表达式加载到REPL运行环境中
.help 查看帮助命令

控制台

在Node.js中,使用console对象代表控制台(在操作系统中表现为一个操作系统指定的字符界面,比如 Window中的命令提示窗口)。

console.log
console.info
console.error 重定向到文件
console.warn
console.dir
console.time
console.timeEnd
console.trace
console.assert

全局作用域

全局作用域(global)可以定义一些不需要通过任何模块的加载即可使用的变量、函数或类
定义全局变量时变量会成为global的属性。
永远不要不使用var关键字定义变量,以免污染全局作用域

 process对象

在node.js里,process 对象代表node.js应用程序,可以获取应用程序的用户,运行环境等各种信息

process.argv.forEach(function(item){
  console.log(item);
});
process.on('exit',function(){
  console.log('clear');
});

process.on('uncaughtException',function(err){
  console.log(err);
})

console.log(process.memoryUsage());
console.log(process.cwd());
console.log(__dirname);
process.chdir('..');
console.log(process.cwd());
console.log(__dirname);

function err(){
 throw new Error('报错了');
}
err();

process.nextTick & setImmediate

process.nextTick()方法将 callback 添加到"next tick 队列"。 一旦当前事件轮询队列的任务全部完成,在next tick队列中的所有callbacks会被依次调用
setImmediate预定立即执行的 callback,它是在 I/O 事件的回调之后被触发

setImmediate(function(){
  console.log('4');
});
setImmediate(function(){
  console.log('5');
});
process.nextTick(function(){
  console.log('1');
  process.nextTick(function(){
    console.log('2');
    process.nextTick(function(){
      console.log('3');
    });
  });
});

EventEmitter

在Node.js的用于实现各种事件处理的event模块中,定义了EventEmitter类,所以可能触发事件的对象都是一个继承自EventEmitter类的子类实例对象。

addListener(event,listener)对指定事件绑定事件处理函数

on(event,listener)对指定事件绑定事件处理函数
once(event,listener)对指定事件指定只执行一次的事件处理函数
removeListener(event,listener)对指定事件解除事件处理函数
removeAllListeners([event])对指定事件解除所有的事件处理函数
setMaxListeners(n)指定事件处理函数的最大数量.n为整数值,代表最大的可指定事件处理函数的数量
listeners(event)获取指定事件的所有事件处理函数
emit(event,[arg1],[arg2],[...])手工触发指定事件

let EventEmitter = require('./events');
let util = require('util');
util.inherits(Bell,EventEmitter);
function Bell(){
  EventEmitter.call(this);
}
let bell = new Bell();
bell.on('newListener',function(type,listener){
  console.log(`对 ${type}  事件增加${listener}`);
});
bell.on('removeListener',function(type,listener){
  console.log(`对${type} 事件删除${listener}`);
});
function teacherIn(thing){
  console.log(`老师带${thing}进教室`);
}
function studentIn(thing){
  console.log(`学生带${thing}进教室`);
}
function masterIn(thing){
  console.log(`校长带${thing}进教室`);
}
bell.on('响',teacherIn);
bell.on('响',studentIn);
bell.once('响',masterIn);
bell.emit('响','书');
console.log('==============');
bell.emit('响','书');
console.log('==============');
bell.removeAllListeners('响');
console.log('==============');
bell.emit('响','书');
function EventEmitter(){
  this.events = {};//会把所有的事件监听函数放在这个对象里保存
  //指定给一个事件类型增加的监听函数数量最多有多少个
  this._maxListeners = 10;
}
EventEmitter.prototype.setMaxListeners = function(maxListeners){
  this._maxListeners = maxListeners;
}
EventEmitter.prototype.listeners = function(event){
  return this.events[event];
}

//给指定的事件绑定事件处理函数,1参数是事件类型 2参数是事件监听函数

EventEmitter.prototype.on = EventEmitter.prototype.addListener = function(type,listener){
  if(this.events[type]){
    this.events[type].push(listener);
    if(this._maxListeners!=0&&this.events[type].length>this._maxListeners){
      console.error(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${this.events[type].length} ${type} listeners added. Use emitter.setMaxListeners() to increase limit`);
    }
  }else{
    //如果以前没有添加到此事件的监听函数,则赋一个数组
    this.events[type] = [listener];
  }
}

EventEmitter.prototype.once = function(type,listener){
  //用完即焚
 let  wrapper = (...rest)=>{
   listener.apply(this);//先让原始的监听函数执行
   this.removeListener(type,wrapper);
 }
 this.on(type,wrapper);
}
EventEmitter.prototype.removeListener = function(type,listener){
  if(this.events[type]){
    this.events[type] = this.events[type].filter(l=>l!=listener)
  }
}

//移除某个事件的所有监听函数

EventEmitter.prototype.removeAllListeners = function(type){
  delete this.events[type];
}
EventEmitter.prototype.emit = function(type,...rest){
  this.events[type]&&this.events[type].forEach(listener=>listener.apply(this,rest));
}
module.exports = EventEmitter;

console.log('next');
process设置环境变量【process.env.环境变量名】

1、进入指定项目文件夹,添加命令windows下:set NODE_ENV=development 或者 set NODE_ENV=production 就能区别当前代码环境是开发环境还是生产环境
2、然后在webpack配置时process.env.NODE_ENV就能准确判断当前的环境

例如:

在这里插入图片描述

   process设置环境变量【process.argv】

在这里插入图片描述
未完待续…

发布了29 篇原创文章 · 获赞 16 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_38560742/article/details/88705013
今日推荐