• 《Node.js高级编程》之Node 核心API基础


    Node 核心API基础

    • 第三章 加载模块
    • 第四章 应用缓冲区
    • 第五章 事件发射器模式简化事件绑定
    • 第六章 使用定时器制定函数执行计划

    第三章 加载模块

    本章提要

    • 加载模块
    • 创建模块
    • 使用node_modules文件夹

    问题:

    • 全局名称空间,共享
    • 安全性问题、冲突、难以跟踪与解决。

    Node解决

    • 使用了CommonJS模块标准。
    • 划分模块,根本没有全局作用域

    3.1 理解Node如何加载模块

    文件路径 + 名称
    核心模块会预先加载
    NPM安装模块

    var module = require('module_name');
    

    该对象表示模块对外暴露的 JavaScript API
    它可以是一个函数,也可以是一个具有若干属性的对象,
    属性可能是函数、数组,或者是其他类型。

    3.2 导出模块

    CommonJS模块系统

    文件共享对象,函数.
    模块和文件一一对应。

    function Circle(x,y,r){
        function r_squared(){
            return Math.pow(r,2);
        }
        
        function area(){
            return Math.PI * r_squared();
        }
        
        return {
            area : area
        }
    }
    
    module.exports = Circle;
    

    module.exports

    • 模块向需要它的脚本所导出的对象,可以是任意对象。

    模块代码

    function printA(){
        console.log('A');
    }
    
    function printB(){
        console.log('B');
    }
    
    function printC(){
        console.log('C');
    }
    
    module.exports.printA = printA;
    module.exports.printB = printB;
    module.exports.pi = Math.PI;
    
    

    引用模块

    var myModule2 = require('./myModule2');
    myModule2.printA();
    myModule2.printB();
    console.log(myModule2.pi);
    

    3.3 加载模块

    require函数不会改变全局名称空间的状态,Node种根本没有全局名称空间的概念。

    3.3.1 加载核心模块

    核心模块,二进制形式发布的模块。
    只能通过模块名,而不能通过文件路径。

    require(fs)
    

    3.3.2 加载文件模块

    提供绝对路径 或相对路径
    从文件系统加载非核心模块

    var myModule = require ('/home/carvendy/my_modules/my');
    

    可以省略.js的扩展名

    3.3.3 加载文件夹模块

    var myModule = require('./myModuleDir');
    

    查找包定义,package.json
    包不存在就会找 index.js

    3.3.4 从node_modules 文件夹加载

    如果一个模块既不是相对路径,也不是核心模块,
    那么就会尝试在当前目录下的 node_modules文件中查找。

    var myModule = require('myModule.js');
    

    当前目录没有myModule.js,就会往上一级父目录查找,持续到达根目录。
    推荐还是让NPM来管理

    3.3.4 缓存模块

    模块在首次加载时,会被缓存起来,这意味着能被解析为相同的文件名。

    console.log('module my_module initializing...');
    module.exports = function (){
        console.log('Hi!');
    }
    console.log('my_module initialized.');
    

    然后加载

    var myModule = require('./my_module');
    

    输出
    module my_module initializing...
    module my_module initialized.
    加载两次效果一样
    模块在初始化,可能会有副作用

    总结

    • CommonJS模块系统取代,js默认全局名称空间
    • 避免是安全性问题和错误。
    • 使用require()加载模块
    • 还可以编写 JavaSript导出的模块API

    第四章 应用缓冲区处理、编码和解码二进制数据


    #### 本章提要 - 理解Node 中为何需要缓冲区 - 用字符串创建缓冲区 - 将缓冲区转换成字符串 - 在缓冲区中处理数据 - 切分和复制缓冲区

    问题

    • js 善于处理字符串,但是它是被设计处理HTML,不擅长处理二进制数据。
    • js没有字节类型,没有结构化类型,甚至没有字节数组
    • 只有数值类型和字符串

    Node 解决

    • 处理HTTP文本协议
    • 数据库通信、图像操作。
    • 前期,使用字符串处理二进制,性能底下。
    • 于是,引入了二进制缓冲区。(字节为计量单位)

    补充

    Buffer类的另一特别之处是数据占用的内存并不是分配JavaScript VM内存堆,这些对象不会被垃圾收集算法处理。
    它会占据一个不会被修改的永久内存地址,这避免了因缓冲区内容的内存复制所造成的CPU浪费。

    4.1 创建缓冲区

    var buf = new Buffer('Hello World');//使用utf-8
    
    var buf = new Buffer('8b76fdes713ce','base64');
    

    编码

    • ascii, ASCII字符集
    • utf8, UTF-8
    • base64,Base64个可打印的ASCII字符

    如果缓冲区没有具体内容初始化,那么可以指定容量大小创建,备用

    var buf = new Buffer(1024);
    

    4.2 在缓冲区中获取和设置数据
    查看某个字节

    var buf = new Buffer('my buffer content');
    console.log(buf[10]);//-->99 (随机值)
    

    注意

    • 设置一个大于255的数,将会用256对该数取模,赋值。
    • 设置256,实际上被赋值为0
    • 设置为100.7的小数,只会存储100
    • 如果尝试给超出缓冲区边界的位置赋值,那么将会失败告终。
    var buf = new Buffer(100);
    console.log(buf.length);
    

    4.3 切分缓冲区

    对于已经创建的缓冲区,也许需要提取一部分。

    var buffer = new Buffer('this is the content of my buffer');
    var smallerBuffer = buffer.slice(8,9);
    console.log(smallerBuffer.toString());
    

    注意

    切缓冲区,并没有分配新的内存。只不过引用父缓冲区的起始和结束位置。
    修改父,删除父,可能会有错误,或内存泄漏。

    4.4 赋值缓冲区

    var buffer = new Buffer('this is the content of my buffer');
    var smallerBuffer = buffer.slice(8,19);
    console.log(smallerBuffer.toString());
    
    var buf1 = new Buffer('this is the content of my buffer');
    var buf2 = new Buffer(11);
    
    var targetStart = 0;
    var sourceStart = 8;
    var sourceEnd  = 19;
    
    buf1.copy(buf2, targetStart , sourceStart , sourceEnd);
    console.log(buf2.toString());
    

    ** 4.5 缓冲区解码**

    var buf = new Buffer('this is the content of my buffer');
    var str = buf.toString('base64');
    

    打印
    bXkgc3RyaW5n

    第五章 使用事件发射器模式简化事件绑定


    #### 本章提要: - 事件发射器模式简介 - 绑定和解绑事件监听器 - 创建自定义的事件发射器

    发射事件

    类似于发布/订阅模式。

    5.1 理解标准回调模式

    异步编程不使用函数返回值代表结束,而使用后继传递。
    每个函数执行完毕后都会调用一个回调函数。

    var fs =  require('fs');
    fs.readFile('/etc/passwd',function(err,fileContent){
        if(err){
            throw err;
        }
        console.info('file content',fileContent.toString());
    });
    

    5.2 理解事件发射器模式

    缺点

    如果在函数执行过程中发生了很多事件,或者事件反复多次出现。
    这样工作就不太好了。

    事件发射器

    看月发射事件的兑现,而事件监听器则是绑定到事件发射器上的代码,
    负责监听特定类型的事件

    var req = http.request(options,function(response){
        response.on("data",function(data){
            console.log("some data",data);
        });
        response.on("end",function(){
            console.log("response ended");
        });
    });
    

    PS

    • 当需要在请求操作完成智慧重新获取控制权看月使用CPS模式
    • 当事件可以发生多次时,就使用事件发射器模式

    5.3 理解事件类型

    事件类型

    按照一般约定,事件类型都是由不包含开个的小写单词组成的。
    无法通过编程判断发射了哪些类型的事件,API没有提供内省机制。

    error事件

    如果程序员选择不监听"error"事件,当事件发生是,事件发射器注意到它,并抛出未捕获异常。

    var em = new (require('events').EventEmitter)();
    em.emit('event1');//没有的事件
    em.emit('error', new Error('My mistake'));// error马上打印堆栈
    

    5.4 应用事件发生器API

    方法

    • .addListener 和.on —— 添加事件监听
    • .once ——指定,并只邦定一个近被调用一次
    • .removeEventListener ——删除
    • .removeAllEventListener ——删除所有绑定的事件

    5.4.1 使用.addListener或.on 绑回调函数

    function receiveData(data){
        console.info("got data from server");
    }
    readStream.addListener("data",receiveData);
    

    分析

    当有可用数据块时,文件可读流就会发射“data”事件

    可以使用on代替:

    readStream.on("data",receiveData);
    

    PS:

    data事件可能会传递数据缓冲区,error事件可能会传递一个错误对象,而流“end”事件则不向事件监听器传递任何参数。

    5.4.2 绑定多个事件监听器

    readStream.on("data",function(data){
        console.log('I have data');
    });
    readStream.on("data",function(data){
        console.log('I have data 2');
    });
    
    

    PS:

    • 有注册顺序
    • 监听器没有执行,可能是前面还有一个监听器
    • 异常,可能会永远不会被执行。
    • 第一个事件抛错误了,第二个监听器就不会执行。

    5.4.3 .removeListener 删除
    5.4.4. .once() 最多执行一次

    var event = require('events').EventEmitter;
    EventEmitter.prototype.once = function (type,callbak){
        var that = this;
        this.on(type,function listener(){
            that.removeListener(type,listener);
            callback.apply(that,arguments);//参数原封不动传递,再调回调函数
        });
    }
    
    

    .removeAllListeners

    5.5 创建事件发射器

    5.5.1 从Node事件发射器继承

    var util = require('util');
    var events = require('events').EventEmitter;
    var MyClass = function(){
        //原型链
        util.inherits(MyClass, EventEmitter);
    }
    

    发射事件

    MyClass.prototype.someMethod = function (){
        this.emit("custom event","argument 1","argument 2");
    }
    
    // 触发
    var myClazz = new MyClass();
    myClazz.on('custom event',function(str1,str2){
        console.log("one:%s,two:%s",str1,str2);
    });
    
    myClazz.someMethod();
    

    第六章 定时器指定函数执行计划


    #### 本章提要 - 推迟函数的执行 - 取消执行计划 - 指定函数的周期行执行计划 - 将函数执行推迟到下一轮事件循环

    Node全面实现一组函数,它们被用来再服务端辅助不通进程进行后期限或者推迟执行。

    6.1 使用 setTimeout推迟

    var timeout_ms = 2000;
    var timeout = setTimeout(functino({
        console.log('do sime');
    }),timeout_ms);
    
    clearTimeout(timeout);//取消函数计划
    

    6.2 指定和取消函数的重复执行计划

    var period = 1000;
    setInterval(function(){
        console.log("tick");
    },period);
    

    6.4 使用process.nextTick将函数执行推迟到下一个事件循环

    有时候不需要立刻执行。
    事件循环在一个处理事件队列的循环运行,事件循环每执行一次就被称为一个"tick"

    process.nextTick 比激活超时队列块。

    process.nextTick(function(){
        my_expensive_conmputation_function();
    });
    

    6.5 阻塞队列事件循环

    单线程事件循环。
    运行时调用相关回调函数来处理队列的下个事件。
    特殊情况,如果某个回调占用事件长,整个服务就会非常缓慢。
    [阻塞]

    process.nextTick(function nextTick1(){
        var a = 0;
        while(true){
            a++;
        }
    });
    
    process.nextTick(function nextTick2(){
        console.log("2.....");
    });
    
    setTimeout(function timeout(){
        console.log(1);
    },1000);
    

    6.6 推出事件循环

    释放事件

    6.7 使用 setTimeout 代替setInteval 前置函数串行

    var inteval = 1000;
    setInterval(function(){
        my_async_function(function(){
            console.log('finished...!');
        });
    });
    
    
  • 相关阅读:
    PHP 标量类型与返回值类型声明
    如何使用 PHP 语言来编码和解码 JSON 对象
    mongodb的读写分离
    [FWT] UOJ #310. 【UNR #2】黎明前的巧克力
    drcom 不耍流氓
    drcom 不耍流氓
    Visual Studio 自定义项目模板
    Visual Studio 自定义项目模板
    Visual Studio 自定义项目模板
    【广告】win10 uwp 水印图床 含代码
  • 原文地址:https://www.cnblogs.com/carvendy/p/7823436.html
Copyright © 2020-2023  润新知