之前一直以传统后端的思维来写nodejs的代码,发现运行结果与我的同步思维方式不太一样,所以需要全面将nodejs学习一下。此学习笔记适合后端同学学习nodejs的时候观看,前端大佬可以多多指正。
环境变量
console.log(setTimeout);
console.log(setInterval);
console.log(setImmediate);
console.log(__filename); // 当前文件的全名
console.log(__dirname); //
console.log(process); // 进程信息,全局变量
process就是当前运行环境变量的集合体。例如process.argv就是获取用户输入的参数
数学库
与Java一样,叫Math
Math.random(); // 返回0~1的浮点数
模块规范之exports和module.exports的关系
需要解决的问题:
>脚本变多时,需要手动管理加载顺序
>不同脚本之间逻辑调用,需要通过全局变量的方式去交流,例如:JQUERY。将输出放到全局变量中,然后由其他部分进行使用
>没有html怎么办?
nodejs中使用CommonJs模块规范,加载另一个js,用require函数来获取即可。
// demo.js
console.log("-----------");
let lib = require('./lib');
console.log('---------',lib);
// lib.js
console.log('==========');
exports.hello = '++++++++++';
exports.word = '///////////////';
exports.add = function () {
console.log('1111111111111')
};
可以看见,模块中定义模块输出的方式:默认会注入一个叫export的变量,在该变量上挂参数就相当于给当前被require对象里面附一个值。还可以挂函数,对象等,理论上就是在输出对象中加对象。所以在exports中我们可以挂各种类型的结构
同时,对于exports来说,外面可以改变模块里面的内容,是同一个引用。
// demo.js
console.log("-----------");
let lib = require('./lib');
lib.addd = '*******************';
console.log('---------',lib);
// lib.js
console.log('==========');
exports.hello = '++++++++++';
exports.word = '///////////////';
exports.add = function () {
console.log('1111111111111')
};
setTimeout(()=>{
console.log(exports)
},2000);
运行结果如下,可以看见修改了外面被require的对象,里面的exports对象也被修改了
特定地,如果希望被require出来的不是一个对象,而是一个方法,或者变量啥的,可以使用module.exports,但是会覆盖掉之前怼exports变量的修改。
// demo.js
console.log("-----------");
let lib = require('./lib');
console.log('---------',lib);
// lib.js
console.log('==========');
exports.hello = '++++++++++';
exports.word = '///////////////';
exports.add = function () {
console.log('1111111111111')
};
module.exports = function dsa() {
return '123'
};
最终打印的结果为
可以看到结果中exports变量的值并没有被输出出来,得到这样的结果有两种可能性,第一是module.exports将exports对象给覆盖掉了,第二种是module.exports和exports在文件被require的时候其实是两个不同的东西,指向两个不同的内存,当模块被require的时候,如果module.exports没有被指定,那么就require出来的是exports对象,如果module.exports被指定了,那么就使用module,exports。我比较倾向于后面这种解释,下面来证实我的观点。
// demo.js
console.log("-----------");
let lib = require('./lib');
lib.addd = '*******************';
console.log('---------',lib);
// lib.js
console.log('==========');
exports.hello = '++++++++++';
exports.word = '///////////////';
exports.add = function () {
console.log('1111111111111')
};
setTimeout(()=>{
console.log(exports)
},2000);
module.exports = function dsa() {
return '123'
};
setTimeout(()=>{
console.log(module.exports)
},2000);
运行结果为:
可以看到,在demo.js中给引用的lib新增了addd属性,最终是作用到了module.exports上,同时,exports打印的结果与module.exports并不一样,说明,module.exports和exports不是一个对象,只是在一个模块被引用时,module.exports的优先级要高于exports。
获取控制台输入(标准输入输出)
process.stdin.on('data', (e) => {
const x = e.toString().trim();
console.log(x);
} );
包管理工具
每个语言都有包管理工具,不过nodejs有个坑,就是npm有时候版本和nodejs不匹配,这个时候就会抛出一些莫名其妙的错误。碰到这种情况,就只能去npm官网解决问题部分寻找结果了。
npm init
要使用npm,需要本身就是一个npm目录,所以需要使用 npm init 来声明为npm 包
package.json
dependencies:放在里面,npm install时会被自动下载,声明当前npm包所有的依赖
安装卸载
npm install xxx
npm uninstall xxx
国内镜像
npm是国外的镜像,可以使用淘宝的npm镜像,可以去NPM镜像查看相关的方法
值得注意的是,用cnpm的时候要加上–save指令,否则dependencies中不会加进去
npm search xxx
很多同学不知道自己适合用什么包,比如我想连接redis,该使用什么包呢?就可以使用 npm search redis来寻找了,相同的,docker search redis/composer search redis 等等都是这么玩的。
nodejs架构
>基于V8运行环境
>事件驱动:非阻塞式的IO模型
内置模块
底层能力是怎么完成的
以os为例,在源码的lib文件中:internalBinding(‘os’) 在 src/node_os.cc中再调用v8的能力
Event模块实现观察者模式,进而使得两个对象进行传输
EventEmitter,process继承与它,所以可以往上抛事件,也就是典型的观察者模式。底层的能力封装起来放到一个模块中,外面的模块拿到这个代码,通过事件的监听器,就能比较方便的,知道子模块里面发生的变化。
观察者模式,可以用来解决两个对象的交流问题。
其实这个方法也可以实现两个函数的先后执行,从而将代码过程化,我看到这个还是比较兴奋的,这样我就可以解决本来具有先后执行顺序的代码,由于nodejs的异步特性导致无法实现的,可以利用这个模式实现
const EventEmitter = require('events').EventEmitter;
class X extends EventEmitter{
constructor(){
super();
setInterval(()=>{
this.emit('newlesson',{
price:Math.random()*100
});
},3000)
}
}
const x = new X();
x.addListener('newlesson',(res)=>{
console.log('buy!',res);
});
>炒鸡辣鸡原创文章,转载请注明来源