# nodejs 入门

# 什么是 Node.js

  • Node.js 的本质是 JavaScript 解析器
  • Node.js 是 JavaScript 的运行环境
  • Node.js 是一个服务器程序
  • Node.js 本身使用 V8 引擎
  • Node.js 不是 Web 服务器

# 为什么使用 nodejs

  • 为提供高性能的 web 服务
  • IO 性能强大
  • 事件处理机制完善
  • 天然能够处理 DOM
  • 社区活跃,生态日趋完善

# nodejs 优势

  • 处理大流量数据
  • 适合实时交互的应用
  • 完美支持对象数据库
  • 异步处理大量并发连接
const http = require("http");
http
  .createServer(function(req, res) {
    res.writeHead(200, { "content-type": "text/plan" });
    res.write("hello world");
    res.end();
  })
  .listen(8000);
console.log(111);

# Nodejs REPL(交互式解释器) 环境

node 常用命令

  • .help
  • .exit
  • .clear
  • .save
  • .load .load ./server.js

# npm

  • npm search express -g
  • npm help xxx

# node 回调机制

  • 什么是回调
  • 阻塞和非阻塞
  • node 回调

# 什么是回调

  • 函数调用方式分为三类:同步调用、回调和异步调用
  • 回调是一种双向调用模式
  • 可以通过回调函数来实现回调

# 阻塞和非阻塞

  • 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态
  • 阻塞就是做不完不准回来
  • 非阻塞就是你先做,我先看看有其他事没有,完了告诉我一声
// 阻塞
var fs = require("fs");
var data = fs.readFileSync("data.txt");
console.log(data.toString());
// 非阻塞
var fs = require("fs");
fs.readFile("data.txt", function(err, data) {
  // 匿名函数
  if (err) {
    return console.error(err);
  }
  // 回调
  console.log(data.toString()); // 后
});
console.log("代码执行完毕"); // 先

var fs = require("fs");
const p = new Promise((resolve, reject) => {
  fs.readFile("./index.html", (err, fd) => {
    if (err) {
      reject(err);
    }
    resolve(fd.toString());
  });
});
p.then((data) => {
  return data.substring(20);
}).then((data) => {
  console.log(data);
});

# Nodejs 事件驱动机制

# 事件驱动模型

非阻塞IO/事件驱动IO

  • nodejs 的 api 是异步执行,依据观察者模式实现的

# 事件与事件绑定

// 引入events对象,创建eventEmitter对象
var events = require("events");
var eventEmitter = new events.EventEmitter();
// 绑定事件处理程序
var connectHandler = () => {
  console.log("connected 被调用");
};
eventEmitter.on("connection", connectHandler);
// 触发事件
eventEmitter.emit("connection");

# Nodejs 模块化

# 模块化的概念和意义

  • 为了让 Nodejs 的文件可以相互调用,Nodejs 提供了一个简单的模块系统
  • 模块是 Nodejs 应用程序的基本组成部分
  • 文件和模块是一一对应的,一个 Nodejs 文件就是一个模块
  • 这个文件可能是 JavaScript 代码,Json 或编译过的 C/C++扩展
  • Nodejs 中存在 4 类模块(原生模块和 3 种文件模块)

# Nodejs 的模块加载流程

node_model

    1. 从文件模块缓存中加载
    1. 从原生模块加载
    1. 从文件加载

# 模块化代码案例

// hello.js
function Hello() {
  var name;
  this.setName = function(argName) {
    name = argName;
  };
  this.sayHello = function() {
    console.log("hello" + name);
  };
}
module.exports = Hello;
// main.js
var Hello = require("./hello");
hello = new Hello();
hello.setName("333");
hello.sayHello();

# Nodejs 函数

function say(word) {
  console.log(word);
}
function execute(someFunction, value) {
  someFunction(value);
}
// 回调函数
execute(say, "hello world!");
// 匿名函数 没有函数名
execute(function(word) {
  console.log(word);
}, "Hello");

# nodejs 路由

var url = require("url");
var querystring = require("querystring");
//localhost:8888/start?foo=bar&hello=world
url.parse(string).pathname; // start controller
url.parse(string).query; // foo=bar&hello=world
querystring.parse(queryString)["foo"]; // bar
querystring.parse(queryString)["hello"]; // world
// http.js
var http = require("http");
var url = require("url");
function start(route) {
  function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    console.log("request for " + pathname + " received.");
    route(pathname, response);
  }
  http.createServer(onRequest).listen(8888);
  console.log("server has start");
}
exports.start = start;
// router.js
function route(pathname, response) {
  console.log("about to route a request for " + pathname);
  if (pathname === "/") {
    response.writeHead(200, { "Context-Type": "text/plain" });
    response.write("Hello world");
    response.end();
  } else if (pathname === "/config") {
    response.end("index");
  }
}
exports.route = route;
// app.js
var server = require("./http");
var router = require("./router");
server.start(router.route);

# get/post 请求

var http = require("http");
var url = require("url");
var util = require("util");
function onRequest(request, response) {
  response.writeHead(200, { "context-type": "text/plain" });
  // util.inspect 展示请求信息
  response.end(util.inspect(url.parse(request.url, true)));
}
http.createServer(onRequest).listen(8888);
console.log("server has start");

# post

var http = require("http");
var querystring = require("querystring");
var util = require("util");
http
  .createServer(function(request, response) {
    var post = "";
    request.on("data", function(chunk) {
      post += chunk;
    });
    request.on("end", function() {
      post = querystring.parse(post);
      response.end(util.inspect(post));
    });
  })
  .listen(3000);

# 全局变量

# __filename

表示当前正在执行的脚本文件名,输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同,如果在模块中,返回的值是模块文件的路径

# __dirname

表示当前执行脚本所在的目录

# setTimeout / clearTimeout / setInterval/clearInterval

# console

  • console.trace(message[,...])当前执行代码在堆栈中的调用路径
var counter = 10;
console.log("计数:", counter);
console.time("获取数据");
// ....
console.timeEnd("获取数据");
console.info("程序执行完毕");
// 内存信息
console.memory;

# process

process 是全局变量,即 global 对象属性
它用于描述当前 nodejs 进程状态的对象,提供了一个与操作系统的简单接口。

  • exit 当进程准备退出是触发
  • beforeExit 当 node 清空事件循环,并没有其他安排的时候触发。通常来说,当没有进程安排时 node 退出,但 beforeExit 的监听器可以异步调用,这样 node 就会继续执行
  • uncaughtException 当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监听器,默认的操作(打印堆栈跟踪信息并退出)就不会发生
  • Signal 事件 当进程收到信号时触发,信号列表详见标准的 POSIX 信号名,如 SIGNT, SIGUSR1 等
process.on("exit", function(code) {
  setTimeout(function() {
    console.log("该段代码永远不会执行");
  }, 0);
  console.log("退出码为: ", code);
});
console.log("程序执行完毕");
// 输出到终端
process.stdout.write("hello world!\n");
// 通过参数读取
process.argv.forEach(function(val, index, array) {
  console.log(index + ":" + val);
});
// 获取执行路径
console.log(process.execPath);
// 平台信息
console.log(process.platform);

// 输出当前目录
console.log("当前目录: ", process.cwd());
// 输出当前版本
console.log("当前版本: ", process.version);
// 输出内存使用情况
console.log("当前内存使用情况: ", process.memoryUsage());

# 文件系统

Nodejs 提供 一组类似 UNIX(POSIX)标准的文件操作 API.Node 导入文件系统模块(fs)语法

var fs = require("fs");

# fs.readFile()异步读取,fs.readFileSync()同步读取

var fs = require("fs");
// 异步读取
fs.readFile("input.txt", function(err, data) {
  if (err) {
    return console.err(err);
  }
  console.log("异步读取: " + data.toString());
});
// 同步读取
var data = fs.readFileSync("input.txt");
console.log("同步读取:" + data.toString());
console.log("程序执行完毕");

# fs.open(path, flags[, mode], callback)

  • path 路径
  • flags 文件打开的行为
    • r+ 以读写模式打开文件,如果文件不存在,抛出异常
  • mode 设置文件模式(权限),文件创建的默认权限为 0666(可读,可写)
  • callback(err, fd) 回调函数
var fs = require("fs");
// 异步打开
console.log("准备打开文件");
fs.open("input.txt", "r+", function(err, fd) {
  if (err) {
    return console.err(err);
  }
  console.log("文件打开成功");
});

# 获取文件信息

var fs = require("fs");
fs.stat("./fs.js", function(err, stats) {
  console.log("是否为文件: " + stats.isFile());
  console.log("是否为目录: " + stats.isDirectory());
});

# fs.writeFile(file, data[, options], callback)写文件

  • file 文件名或文件描述符
  • data 要写入文件的数据,可以是 String 字符串,或 Buffer 缓冲对象
  • options 一个对象,包含{encoding, mode, flag}.默认为{encoding:"utf8", mode:0666, flag:"w"}
var fs = require("fs");
console.log("准备写入文件");
fs.writeFile("input.txt", "hhhhha", function(err) {
  if (err) {
    return console.err(err);
  }
  console.log("数据写入成功");
  fs.readFile("input.txt", function(err, data) {
    if (err) {
      return console.err(err);
    }
    console.log("异步读取: " + data.toString());
  });
});

# fs.read(fd, buffer, offset, length, position, callback) 异步读取文件

  • fd 通过fs.open()方法返回的文件描述符
  • buffer 数据写入缓冲区
  • offset 缓冲区写入的写入偏移量
  • length 要从文件中读取的字节数
  • position 文件读取的起始位置 null ->从当前文件指针的位置读取
  • callback(err, bytesRead, buffer) bytesRead 表示读取的字节数,buffer 为缓冲区对象
var fs = require("fs");
var buf = new Buffer.alloc(1024);
fs.open("input.txt", "r+", function(err, fd) {
  if (err) {
    return console.err(err);
  }
  fs.read(fd, buf, 0, buf.length, 0, function(err, bytes) {
    if (err) {
      return console.err(err);
    }
    console.log(bytes + "字节被读取");
    // 仅输出读取的字节
    if (bytes > 0) {
      console.log(buf.slice(0, bytes).toString());
    }
    fs.close(fd, function(err) {
      if (err) {
        return console.err(err);
      }
      console.log("文件关闭成功");
    });
  });
});

# fs.close(fd, function(err))

# fs.ftruncate(fd, len, callbeck)异步模式下的文件截取

var fs = require("fs");

console.log("准备删除文件!");
fs.unlink("input.txt", function(err) {
  if (err) {
    return console.error(err);
  }
  console.log("文件删除成功!");
});

# fs.mkdir(path[, options], callback)创建目录

  • path 文件路径
  • options
    • recursive 是否一递归方式创建目录,默认为 false
    • mode 设置目录权限 默认 0777
  • callback(err)
var fs = require("fs");
console.log("创建目录 /tmp/test/");
// 不管/tmp/ 和 /tmp/a/ 目录是否存在
fs.mkdir("/tmp/a/test/", { recursive: true }, (err) => {
  if (err) throw err;
});

# fs.readdirr(path,callbeck(err,files)) 读取目录

  • files 为目录下的文件数组列表

# fs.rmdir(path, callback)删除目录

# Nodejs 常用工具

const util = require("util");

# util.callbackify

将 async 异步函数(或一个返回值为 Promise 的函数)转换为遵循异常优先的回调风格函数。

const util = require("util");
async function fn() {
  return "hello world";
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
  if (err) throw err;
  console.log(ret);
});

function fn() {
  return Promise.rejcet(null);
}
const callbackFunction = util.callbackify(fn);

callbackFunction((err, ret) => {
  // 当Promise被以 null 拒绝时,它被包装为Error并且原始值存储在"reason"中,
  err && err.hasOwnProperty("reason") && err.reason == null;
});

# util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数

var util = require("util");
function Base() {
  this.name = "base";
  this.base = 1001;
  this.sayHello = function() {
    console.log("Hello " + this.name);
  };
}
Base.prototype.showName = function() {
  console.log(this.name);
};
function Sub() {
  this.name = "sub";
}
util.inherits(Sub, Base);
var objBase = new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);
var objSub = new Sub();
objSub.showName();
// Sub 仅继承Base原型中定义的函数,而构造函数内部的base属性和sayHello函数没有被继承。
// objSub.sayHello();
console.log(objSub.base); // undefined
console.log(objSub);

# util.inspect(object, [showHidden], [depth], [colors])

将任意对象转换为字符串的方法,通常由于调试和错误输出

  • showHidden 是可选参数,如果为 true,将会输出更多隐藏信息
  • depth 表示最大递归,默认为 2 层,null 为不限递归层数完整遍历对象
  • colors 值为 true,输出格式将以 ANSI 颜色编码
  • util.inspect 并不是调用toString方法
var util = require("util");
function Person() {
  this.name = "ssss";
  this.toString = function() {
    return this.name;
  };
}
var obj = new Person();
console.log(util.inspect(obj));
console.log(util.inspect(obj, true, 2, true));

# util.isArray(object)

var util = require("util");
util.isArray([]);
util.isArray(new Array());
util.isArray({});

# util.isRegExp(object)

检查object是否是正则表达式

# util.isDate(object)

检查object是否是一个日期

# web module

// client.js
var http = require("http");
// 用于请求的选项
var options = {
  host: "loaclhost",
  port: "8080",
  path: "/index.html",
};
// 处理响应的回调函数
var callback = function(response) {
  // 不断更新数据
  var body = "";
  response.on("data", function(data) {
    body += data;
  });
  reponse.on("end", function() {
    // 数据接收完成
    console.log(body);
  });
};
// 向服务器发送请求
var req = http.request(options, callback);
req.end();
// server.js
var http = require("http");
var fs = require("fs");
var url = require("url");
http
  .createServer(function(request, response) {
    var pathname = url.parse(request.url).pathname;
    fs.readFile(pathname.substr(1), function(err, data) {
      if (err) {
        console.log(err);
        response.writeHead(404, { "Content-Type": "text/html" });
      } else {
        response.writeHead(200, { "Content-Type": "text/html" });
        response.write(data.toString());
      }
      response.end();
    });
  })
  .listen(8080);