6 正则表达式
创建
字面量创建
const reg = /[a-z]\d+[a-z]/i;
优点
- 简单方便
- 不需要考虑二次转义
缺点
- 子内容无法重复使用
- 过长的正则导致可读性差
使用 RegExp 构造函数
const alphabet = '[a-z]';
const reg = new RegExp(`${alphabet}\\d+${alphabet}`, 'i');
优点
- 子内容可以重复使用
- 可以通过控制子内容的粒度提高可读性
缺点
- 二次转义的问题非常容易导致 bug
const reg = new RegExp(`\d+`);
reg.test('1'); // false
reg.test('ddd'); // true
正则表达式的常见用法
test()
RegExp.prototype.test()
const reg = /[a-z]\d+[a-z]/i;
reg.test('a1a'); // true
reg.test('1a1'); // false
reg.test(Symbol('a1a')); // TypeError
输入
要求输入字符串,如果输入的不是字符串类型,会尝试进行类型转换,转换失败会抛出 TypeError
输出
true 或者 false,表示匹配成功或失败
source 和 flags
source
与 flags
const reg = /[a-z]\d+[a-z]/ig;
reg.source; // "[a-z]\d+[a-z]"
reg.flags; // "gi"
get RegExp.prototype.source
返回当前正则表达式的模式文本的字符串
get RegExp.prototype.flags
es2015新增,返回当前正则表达式的修饰符的字符串,会对修饰符按照字母升序进行排序
exec() 和 match()
RegExp.prototype.exec() 和 String.prototype.match()
输入
RegExp.prototype.exec 要求输入字符串,遇到非字符串类型会尝试转换
String.prototype.match 要求输入正则表达式,遇到其它类型会先尝试转成字符串,再以字符串为 source 创建正则表达式
输出
匹配成功,返回匹配结果
匹配失败,返回 null
二者区别
const reg = /(a)/g;
reg.exec('a1a'); // ["a", "a", index: 0, input: "a1a", groups: undefined]
'a1a'.match(reg); // ["a", "a"]
当正则表达式含有 g 修饰符时,RegExp.prototype.exec 每次只返回一个匹配结果,数据格式和不含 g 修饰符相同。
String.prototype.match 会返回所有的匹配结果,数据格式会变为字符串数组。
由于 String.prototype.match 返回的数据格式不固定,因此大多数情况都建议使用 RegExp.prototype.exec
lastIndex
RegExp.prototype.lastIndex
const reg = /(a)/g;
const str = 'a1a';
reg.lastIndex; // 0
reg.exec('a1a'); // ["a", "a", index: 0, input: "a1a", groups: undefined]
reg.lastIndex; // 1
reg.exec('a1a'); // ["a", "a", index: 2, input: "a1a", groups: undefined]
reg.lastIndex; // 3
reg.exec('a1a'); // null
reg.lastIndex; // 0
当前正则表达式最后一次匹配成功的结束位置(也就是下一次匹配的开始位置)
注意:lastIndex 不会自己重置,只有当上一次匹配失败才会重置为 0 ,因此,当你需要反复使用同一个正则表达式的时候,请在每次匹配新的字符串之前重置 lastIndex!
replace()、search() 和 split()
String.prototype.replace()、String.prototype.search()、String.prototype.split()
'a1a'.replace(/a/, 'b'); // 'b1a'
'a1a'.replace(/a/g, 'b'); // 'b1b'
'a1a'.search(/a/); // 0
'a1a'.search(/a/g); // 0
'a1a'.split(/a/); // ["", "1", ""]
'a1a'.split(/a/g); // ["", "1", ""]
场景一:正则与数值
/[0-9]+/
[]
:字符集,使用连字符 - 表示指定的字符范围,如果想要匹配连字符,需要挨着方括号放置,或进行转义,0-9 表示匹配从 0 到 9 的数字字符,常用的还有 a-z 匹配小写字母,\u4e00-\u9fa5 匹配汉字等,如果只是匹配数字,还可以使用字符集缩写 \d
+
:限定符,匹配一个或多个
缺点:不是全字符匹配,包含就会捕获,存在误判,如 /[0-9]+/.test(‘a1’) === true
/^\d+$/
^
:匹配字符串开始位置,当结合 m 修饰符时,匹配某一行开始位置
$
:匹配字符串结束位置,当结合 m 修饰符时,匹配某一行结束位置
缺点:不能匹配带符号的数值,如 +1,-2,不能匹配小数,如 3.14159
/1?\d+(.\d+)?$/
()
:圆括号内是一个子表达式,当圆括号不带任何修饰符时,表示同时创建一个捕获组
?
:在正则中有多种含义,作为限定符时,表示匹配零到一个
\
.:. 可以匹配除换行符之外的任意字符,当结合 s 修饰符时,可以匹配包括换行符在内的任意字符当匹配小数点字符时需要转义
缺点:不能匹配无整数部分的小数,如 .123,捕获组会带来额外的开销
/2?(?:\d*.)?\d+$/
(?:)
:创建一个非捕获组
*
:限定符,匹配零个或多个
缺点:
不能匹配无小数部分的数值,如 2.
不能匹配科学计数法,如 1e2、3e-1、-2.e+4
7 Node.js 基础
概念
Node.js 是一个运行在 V8 的 JavaScript 运行时
Node.js is a JavaScript runtime build on Chrome’s V8
Node.js uses an event-driven, non-blocking I/O model
非阻塞 I/O
阻塞:I/O 时进程休眠等待 I/O 完成后进行下一步
非阻塞:I/O 时函数立即返回,进程不等待 I/O 完成
事件驱动
- I/O 等待异步操作结束后的通知
- 观察者模式
与 JavaScript 的区别
- 基于异步 I/O 相关接口
- 基于 node_modules 和 require 的模块依赖
- 提供 C++ addon API 与系统交互
Node.js 基础
运行 node.js
// index.js
console.log('Hello World');
全局安装 node 后,可以在控制台通过node index.js
运行
读写文件
通过内置模块fs
异步读取
const fs = require('fs');
fs.readFile('test.txt', (err, data) => {
console.log(data);
});
console.log('read file content');
模块
- 内置模块:编译进 Node 中,例如 http fs net process path 等
- 文件模块:build-in 模块之外的模块,和文件夹一一对应
内置模块和文件模块导入时路径书写方式不同,内置模块更为方便
内置模块
const fs = require('fs');
fs.readFile('a.text', (err, buffer) => {
console.log(buffer);
})
const {readFile} = require('fs');
readFile('a.txt', (err, buffer) => {
console.log(buffer);
})
文件模块
// app.js
var circle = require('./circle.js');
console.log('半径为4的圆面积是:' + circle.area(4));
定义模块
模块会将exports
对象导出,外部可以借此获得模块导出的方法
// circle.js
const pi = Math.PI;
exports.area = function (r) {
return pi * r * r;
};
exports.circumference = function (r) {
return 2 * pi * r;
};
模块加载
// 加载绝对路径文件
require('/foo/bar/a.js');
// 加载相对路径文件
require('../a.js');
// 加载无后缀的文件
require('../a');
// 加载外部模块
require('pkg-name');
模块路径查找
- 绝对路径
- 相对路径
- 和当前路径处理为绝对路径
- 模块/文件夹
- 原生模块,直接读取缓存
- [$NODE_PATH, ~/.node_modules,
./node_modules, …/node_modules, …] - 解析 package.json,查找 main 属性,没有则使用 index.js
- 如果未找到,则报错
JS 模块解析
// app.js
const circle = require('./circle.js');
- 通过 fs.readFileSync 同步拿到文件内容
- 对内容进行包装
(function (exports, require, module, __filename, __dirname) {
var circle = require('./circle.js');
console.log('The area is ' + circle.area(4));
});
- 通过 vm.runInThisContext 执行
- 获取 module 对象的值作为模块的返回值
vm
是 nodejs 里一个沙盒环境
模块缓存
- 模块加载后会将返回值缓存起来
- 下次加载时直接读取缓存结果,避免文件 I/O 和解析时间
- 导出对象缓存在 Module._cache 对象上
NPM
包管理
- 一个package.json文件应该存在于包顶级目录下
- 二进制文件应该包含在bin目录下
- JavaScript代码应该包含在lib目录下
- 文档应该在doc目录下
- 单元测试应该在test目录下
package.json
npm init -y
{
"name": "star-plan",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Tip: 可以使用
npm config set init.author.name
等命令修改初始化时的默认值
版本号
- 0.0.1:bug 的修复或补丁的修复
- 0.1.0:特性的增加
- 1.0.0:大版本修改,重构,不向后兼容
包依赖
-
1.0.0 Must match version exactly,准确版本
-
>1.0.0 Must be greater than version
-
>=1.0.0: 大于等于 1.0.0
-
~1.0.0: “Approximately equivalent to version”,允许小版本更新
-
^1.0.0: “Compatible with version” ,allow 1.3.1, reject 2.0.0
-
1.2.x: 1.2.0, 1.2.1, etc., but not 1.3.0
-
*: Matches any version
-
version1 - version2: Same as >=version1 <=version2.
基于 Node.js 开发
const http = require('http');
const server = http.createServer((req, res) => {
res.end('Hello World');
});
server.listen(3000);
Koa
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
Koa 无规范约束,不利于团队开发,中间件繁多,质量参差不齐,选择困难
thinkJS
安装
npm install -g think-cli
RESTful 接口规范
- 每个 API 都对应一种资源或资源集合
- 使用 HTTP Method 来表示对资源的动作
- 使用 HTTP Status Code 来表示资源操作结果