本笔记的记录整理来自于【尚硅谷】最经典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中使用这两个方法 ?