本笔记的记录整理来自于【尚硅谷】最经典Node.JS全套完整版教程(快速入门nodejs)
介绍
node采用v8引擎来运行,事件循环,异步,虽然是单进程,单线程,但是因为异步,没有IO的阻塞,性能也不错
单线程缺点:一个线程的奔溃,整个进程死掉,优点:开销小,不影响并发的同数据写入冲突
应用:webAPI,web应用,多人在线游戏,通信
适合:IO高并发,上传大文件,前后端分离
传统web服务:来一个请求,创建一个线程,处理过程中还可能需要等待IO,IO阻塞整个线程
node:来一个请求,单线程去接收进来,后台有一个IO线程池,处理IO读写时,这个单线程继续接收下一个请求,但是要清除,一个单线程最终不足以抗住大并发,所以还可以做分布式
webstorm的小配置
- 配置run
run(需要配置node的执行bin文件)setting--language & frameworks -- node.js and npm -- node interpreter -- node.exe路径
- 开启node代码提示
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中使用这两个方法 ?