01小白学nodejs 介绍和模块

JavaScript/前端
329
0
0
2022-04-18
标签   NodeJs

本笔记的记录整理来自于【尚硅谷】最经典Node.JS全套完整版教程(快速入门nodejs)

介绍

node采用v8引擎来运行,事件循环,异步,虽然是单进程,单线程,但是因为异步,没有IO的阻塞,性能也不错
单线程缺点:一个线程的奔溃,整个进程死掉,优点:开销小,不影响并发的同数据写入冲突
应用:webAPI,web应用,多人在线游戏,通信
适合:IO高并发,上传大文件,前后端分离

传统web服务:来一个请求,创建一个线程,处理过程中还可能需要等待IO,IO阻塞整个线程

node:来一个请求,单线程去接收进来,后台有一个IO线程池,处理IO读写时,这个单线程继续接收下一个请求,但是要清除,一个单线程最终不足以抗住大并发,所以还可以做分布式

webstorm的小配置

  1. 配置run
  2. run(需要配置node的执行bin文件)setting--language & frameworks -- node.js and npm -- node interpreter -- node.exe路径
  3. 开启node代码提示
  4. setting--language & frameworks---node.js and npm---node.js core library is enabled

node的模块

首先我们的js是没有模块系统的概念的,对于前端来说,本身也没有业务的概念,仅仅是对浏览器的操作和渲染,大多数情况,一些库,框架,都是基于对js源码的封装,并没有实际对一个项目的整体规划和分层(现阶段有很大改善了),也没有一个包管理器;

但此时我们的nodejs是作为后端语言,因此如果还是单纯的js开发方式,无疑是不足以承担起一个中大型的项目;也就无法对其它后端语言产生任何威胁

因此nodejs引入commonjs规范,做了模块化,加入了npm,以及提供了方便开发者的大量的API;得益于这些东西,和运行机制,使得nodejs在后端语言中,也占有一席之位;

模块定义

项目中创建的一个js文件,就是一个模块;
文件中的内容其实是处于一个函数中,如果想让外部js使用这个模块中的变量和方法,则需要使用exports导出
exports.x = 'xxx'
exports.fn = function () {}

模块引用

var name = require('./path/module_name')  
name就是该模块对象
其实require()是作为函数的参数传进来的

底层模块,编译到node中,不需下载,不需引入,直接使用,如Buffer
核心模块,node提供,不需下载,需要引入,var fs = require('fs')
自己写的模块,需要传具体的路径
使用其它人写的模块,需要下载,引入

模块标识

模块标识就是模块的名字,也就是传递给require()的参数,它必须是符合驼峰命名法的字符串

验证一下模块文件中的内容确实是处于一个函数中(局部)

1. 在node中有一个全局对象global,它的作用和网页中的window类似,在全局中创建的变量,函数会作为global的属性,方法保存进去
    var a = 100 
    console.log(global.a)   //undefined  不存在全局中

2. 证明全局变量会保存到global中
    b = 100;
    console.log(global.b)  //100  

3. 证明模块文件中的代码是存在函数中的
    console.log(arguments)           函数的参数
    console.log(arguments.callee)    这个属性保存的是当前执行的函数对象 
    console.log(arguments.callee + "");

4. 实际上模块中的代码都是包装在一个函数中执行的,由node引擎调用,并且在函数执行时,传递进了5个实参
    exports  该对象用来将变量或函数暴露到外部
    require  函数,用来引入外部的模块
    module   代表当前模块本身,exports其实是module的属性
        console.log(exports, module.exports, exports == module.exports)
    __filename 当前模块的完整路径
    __dirname  当前模块所在目录

exports 和 module.exports ?

存的是同一个对象的指针,指向的是同一个对象

这里先说一下栈和堆:

  • 栈内存一般存数据量小的,数据结构单一的数据,比如字符串,数值,bool,数组;可以把它看作一个key-value结构,key中存的是变量名称,value中存的是值;默认都是值传递
var a = 10;
var b = a;
此时a和b的值都是10;其实是在栈内存中,申请了两个key-value,
| key  | value  |
|  a   |  10    |
|  b   |  10    |
a++
| key  | value  |
|  a   |  11    |
|  b   |  10    |
  • 堆内存一般存复杂的,大量的数据,比如对象,类,资源,其内部可以看作一个大空间,申请存数据时,划分一块空间,同时每块空间都有一个地址。
  • 一般这些数据都是引用传值,如下:
var a = {};
var b = a;
| key  | value  |
|  a   |  0x123    |
|  b   |  0x123    |

堆中
|  ox123  {} |

a.name = 'xqw';
|  ox123  {name:"xqw"} |
a对象的修改,对b一样起作用,反过来也一样,因为都是指向的同一个堆空间中

b = {"sex":"men"};  
|  a   |  0x123    |
|  b   |  0x124    |  

|  ox123  {name:"xqw"} |
|  ox124  {sex:"sex"} |

此时是直接重新赋值b变量了,断开了原指针,给了一个新指针,此时与a对象没有关系了;

可以这样看exports和module.exports

var module = {};
module.exports = {};//module对象,添加了一个exports属性
var exports = module.exports;//将module.exports的值赋值给exports变量,但是因为module.exports本身是一个对象,因此exports变量中存的是和module.exports相同的指针,即堆对象空间相同;

所以我们导出时可以这样:
exports.name = 'xqw';   等同于  module.exports.name = 'xqw';
exports.fn = ()=>{};  等同于  module.exports.fn = ()=>{};
其都是对一个对象里面的元素做调整;

但是假如这样写:
exports = {"name":"lyl"};  这是对变量的值修改,此时exports != module.exports 了,无法导出了;

我们总是可以使用这种写法,不会出错:
module.exports.attr = 'xxx';
module.exports = {...};

exports.xx 是改对象
exports = xx 是改变量

练习:定义一个模块math,在该模块中提供两个方法,add(a, b) 求和 ; mul(a, b)求积 , index.js中使用这两个方法 ?