node

node

Node

Node 简介

基于 Chrome V8 引擎的 JavaScript 运行环境

  • Node 与 浏览器均存在互不拥有的 API
  • Node.js 中你可以控制环境即控制运行时的 JS 版本
  • Node.js 使用 CommonJS 模块系统,浏览器使用 ES Modules 标准

Node 内置 API

process 核心进程
fs
path
http

Node 第三方框架

express 构建 Web 应用
electron 构建桌面应用
restify 构建 API 接口项目
读写数据库
实用命令行工具

Node 简单命令

node -v 查看 Node 版本号

node -h 查看 Node 的命令帮助

node <filename|filepath> 执行给定文件名或路径的 js 文件

node 进入 node 交互运行模式

终端快捷键
↑ 自动定位至上一条命令
TAB 自动补全路径
ESC 快速清空当前输入命令
cls 清空终端

process 模块

node 内置模块
隐式导入

process.exit(<exitCode>) 关闭正在执行的 node 进程

process.exitCode 规定 node 进程的退出码

process.env 读取 node 进程启动时设置的所有环境变量

fs 模块

node 内置模块

fs.open(<filepath>, <open-mode>, (<err>, <fd>) => {}) 获取文件描述符(?)

fs.readFile(<filepath>, (<err>, <stats>) => {}) 读取文件属性

stats.isFile()stats.isDirectory() 判断文件是否目录或文件
stats.size 获取文件的大小

fs.readFile(<filepath>, [options], (<err>, <data>) => {}) 读取文件内容

会将文件的全部内容读取至内存中,对于大文件使用流更好

fs.writeFile(<filepath>, data,[options], (<err>) => {}) 写文件内容

仅在将全部内容写入文件才会结束执行,对于数据量较大的使用流更好

以上函数均有sync后缀的同步版本函数

在读取文件时,存在相对路径 ./ ../,运行时可能会存在问题,一般推荐使用 path 模块动态拼接文件完整路径(如 __dirname + '/<filename>')或使用绝对路径

__dirname 脚本文件当前的目录路径
D:\程序\前端学习new\Node-eg
__filename 脚本文件当前的绝对路径
D:\程序\前端学习new\Node-eg\index.js

path 模块

node 内置模块

path.join([...paths]) 将多个路径片段拼接为完整的路径字符串

path.basename(<filepath>) 获取文件名部分
path.basename(<filepath>, path.extname(<filepath>)) 获取文件名不含扩展名部分

path.dirname(<filepath>) 获取文件的父文件夹部分

path.extname(<filepath>) 获取文件扩展名部分

path.resolve() 获得相对路径的绝对路径计算
可以在第一个参数的基础上指定路径,以使第二个参数附加第一个参数

需要注意的是,path模块方法仅做单纯的计算而不会主动判断路径的存在与否

http 模块

node 内置模块

基于js语言创建本地服务器

  • IP 地址
    ip 地址是互联网上各计算机的唯一地址
    192.169.1.1
    有 ipv4 与 ipv6
  • 域名
    使用字符串对域名进行唯一的标识
  • 域名服务器
    负责解析域名并转换为IP地址
  • 端口
    区分服务器上不同的服务
    一个服务器只能对应单个服务
    默认端口是80端口

http.createServer() 创建 http 服务器对象

Serve.prototype.on() 规定监听http请求的回调函数

Serve.prototype.listen() 启动 HTTP 服务器并监听http请求

实现基于url的响应
1 获取url地址
2 设置默认返回内容
3 处理数据并返回

express 库

基于 node 的 web 开发框架

快速创建 API 接口服务器与 Web 网站服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 导入
const express = require('express');

// 创建服务器
const app = express();

// 启动服务器
app.listen(8000, () => {
console.log('express server running at http://127.0.0.1:8000');
});

// 监听请求
// req 请求对象
// res 响应对象
app.get('/:id/user', (req, res) => {

// 获取查询参数 `req.query`

// 获取动态参数{如 /:id/} `req.params`

// 发送请求内容 `res.send()`
res.send('get!!!');

});

// 托管静态资源|注册全局中间件
// 外部 http://localhost:8000/0.css
// 内部 /public/0.css
// 托管多个静态资源目录,只需多次调用 express.static() 方法
app.use(express.static('public'));
// 挂载路径前缀
// 外部 http://localhost:8000/static/0.css
// 内部 /public/0.css
app.use('/static/', express.static('public'));

nodemon

监听本地项目文件变动并自动重启项目(类似前端 live server 的作用)

直接使用 nodemon file 指令执行项目

express 路由

一种映射关系,express中指的是客户端的请求与服务器处理函数之间的映射关系
app.METHOD(PATH, HANDLER)

路由模块化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// router.js
const express = require('express');

const router = express.Router();

router.get('/', (req, res) => {});
router.post('/', (req, res) => {});

module.exports = router;

// main.js
...
const router = require('./router.js');
app.use(router);

express 中间件

请求过程中可以调用中间件对请求进行多次预处理

实质是一个 function

next 函数实现多个中间件连续调用,表示启用下一个中间件

1
2
3
4
5
6
7
8
9
// 全局注册中间件
app.use((req, res, next) => {
console.log('mid');
next();
});
// 同时注册多个中间件只需多次调用app.use即可

// 局部注册中间件
app.post('/', (req, res, next) => {}, (req, res) => {});

需在路由之前注册中间件
可以对请求调用多个中间件进行处理
注意执行完中间件代码,需调用next方法
调用next方法后不应执行其他的任何的代码
连续多个中间件之间共享req与res对象

CORS

使用 cors 包

1
2
3
const cors = require('cors');

app.use(cors());

只需服务器做配置,客户端无需做额外的配置

响应头部带 Access-Control-Allow-Origin 字段,表示允许来源的域的名称
Access-Control-Allow-Headers 声明允许的请求头字段,Accept Accept-Language Content-Language DPR Downlink Save-Data Viewport-Width Width Content-Type
Access-Control-Allow-Methods 声明允许的请求方法,默认GET POST HEAD可,其他均需声明

  • 简单请求
    请求方式与请求头部字段有限

  • 预检请求(预先发送OPTION请求确定是否可请求,仅成功后才会发送实际请求)
    其他

mysql.js

在 node 中访问 MySQL 数据库的 js 库

Web 开发模式

  1. 基于服务端渲染的传统 Web 开发模式
    页面由服务器动态生成
    前端耗时少
    利于SEO
    占用服务器端资源
    不利于前后端分离开发效率低
  2. 基于前后端分离的新型 Web 开发模式
    依赖于 Ajax 技术,即前端使用接口,后端提供接口
    开发体验好
    用户体验好
    减轻服务器端渲染压力
    不利于SEO

身份认证

出于安全性考虑,需要对用户身份进行认证

(服务器渲染)Session 认证机制
(前后端分离)JWT 认证机制

Session 认证机制

  • HTTP 协议的无状态性

HTTP 请求独立,服务器无法主动保留每次 HTTP 请求的状态

  • 突破无状态性

使用 Cookie

  • Cookie

一段存储在用户浏览器中的字符串
可以存储一些特有信息
由名称、值及一些可选属性(有效期、安全性、使用范围等)构成
不同域名下Cookie相互独立
每次客户端请求均会自动发送当前域名下所有未过期的Cookie

1
2
3
4
<!-- 设置cookie -->
Set-Cookie: token=1dw21qde43s;
<!-- 发送cookie -->
Cookie: token=1dw21qde43s;

客户端登录,服务器生成对应的cookie发回客户端保存;之后客户端每次请求均将带cookie,服务器需验证通过后再进行操作返回响应内容

es6

es6

ES6 标准入门

2022.03.22
No.1

let 与 const

  • 具有块状作用域,仅在声明的代码块内可使用

  • 不存在变量提升现象

  • 仅可在变量声明后使用,声明之前不可使用,即暂时性死区(TDZ)现象

  • 不允许重复声明

  • const 声明常量,且需立即初始化

  • 原理可以认为是在声明仍会提到块顶部创建变量区域,但会阻止在声明位置后的使用

  • for 循环的条件部分可视为父作用域,循环体部分可视为子作用域

块作用域

  • 内层作用域可以覆盖外层作用域的变量
  • 外层作用域无法使用内层作用域的变量
  • 函数声明在块作用域的行为类似于 var(浏览器)

顶层对象

globalThis

  • 浏览器:window

  • Node:global

  • var 命令与 function 命令声明全局变量是顶层对象属性

  • let 命令、const 命令与 class 命令声明的不是顶层对象属性

解构赋值

  • 数组

let [a, [b], c = true, ...d] = [1, [2], 3, 4, 5, 6]4
a = 1 b = 2 c = 3 d = [4, 5, 6]

根据数组的位置提取值,匹配不到返回 undefined
允许默认值,仅 undefined 会起效
相当于模式匹配

应为可遍历结构(包含数组、Set 实例)

  • 对象

变量应与对象属性同名
取值没有次序

let { log: loge, sin, cos } = Math;

允许重命名变量

2022.03.23
No.2

Promise

Promise 含义

promise 语法上来说是一个对象,它保存了一个未来才会结束的事件的结果,可以用于获取异步操作的结果,是用于异步编程的一种常用的强大的工具,可以解决“回调地狱”问题

promise 对象具有三种状态:pending进行中、fulfilled已成功、rejected已失败,它的状态仅由异步执行的结果决定

promise 对象状态改变只能为 pending->fulfilledpending->rejected,且状态改变一旦发生,就无法改变,会一直保持这个结果

promise 对象创建后即无法取消,pending 状态时无法得知事件进展,无法在未设置回调函数时捕获错误

new Promise()

1
2
3
4
5
6
7
const promise = new Promise(function(resolve, reject) {
if(/*success*/) {
resolve(result);
} else {
reject(error);
}
});

promise 对象一旦新建就会立即执行
promise 对象接受一个函数作为参数,该函数参数分别为 resolve 和 reject,它们都可以根据需要接收参数,并包装成 Promise 对象返回(若已是 Promise 对象则直接返回)
resolve 函数将 promise 对象状态从 pending 改变为 fulfilled
reject 函数将 promise 对象状态从 pending 改变为 rejected

Promise.prototype.then()

1
2
3
4
5
6
7
8
promise.then(
function (result) {
// do something when success
},
function (error) {
// do something when fail
}
);

then 方法指定 promise 对象的处理回调函数,接收两个方法作为参数,第一个方法处理 fulfilled 状态的回调函数,第二个方法处理 rejected 状态的回调函数,两个方法都是可选的
then 方法返回一个新的 Promise 实例,因此 Promise 可以具有链式写法,链式的 then 方法会依次调用

Promise.prototype.catch()

相当于 .then(null | undefined, rejection) 指定 Promise 返回错误时的回调函数,包括异步操作抛出错误引起状态改变为 rejected 和回调函数运行时抛出的错误

1
2
3
4
promise.catch((error) => {
// do something to deal with the error
console.log(error);
});

一般建议不在 then 方法内定义错误的处理方法,而是统一在最后的 catch 方法统一处理
(也比较符合 try-catch 语句的写法)

处理完成后返回的 Promise 实例的状态会重置为 fulfilled

Promise.prototype.finally()

finally 方法绑定无论 fulfilled 状态还是 rejected 状态都会执行的方法

ES2018 引入

1
2
3
promise.finally(() => {
// do something to deal with the error
});

该方法无法接收参数
实质为 then 方法的特例

2022.03.26
No.3

Promise.resolve()

返回一个 Promise 实例

  • 参数是 Promise 实例 :直接返回,不做修改
  • 参数具有 then() 方法 :转为 Promise 实例并立即调用 then() 方法
  • 参数不具有 then() 方法 :返回一个 fulfilled 状态的 Promise 实例
  • 无参 :返回一个 fulfilled 状态的 Promise 实例

Promise.reject()

返回一个 rejected 状态下的 Promise 实例

Promise.all()

const promiseAll = Promise.all([promise1, promise2]);

该方法将多个 Promise 实例合并为一个新的 Promise 实例
以数组形式接收需包装的 Promise 实例,若接收到非 Promise 实例参数会自动转为 Promise 实例

  • 若所有 Promise 实例状态均变成 fulfilled,包装后的新 Promise 实例状态才会变成 fulfilled
  • 反之有任一一个 Promise 实例状态变成 rejected,包装后的新 Promise 实例状态即会变成 rejected

需要注意的是,Promise 实例的 catch 方法会阻碍装后的新 Promise 实例 catch 方法的错误捕获

1
2
3
4
5
Promise.all([p1, p2])
.then(([pp1, pp2]) => {
// do something with pp1 & pp2
})
.catch(console.error);

Promise.race()

语法及参数返回值格式同 all

  • 若有一个 Promise 实例状态均变成 fulfilled,包装后的新 Promise 实例状态就会变成 fulfilled
  • 反之若所有的 Promise 实例状态变成 rejected,包装后的新 Promise 实例状态才会变成 rejected

Promise.allSettled()

语法及参数返回值格式同 all

  • 类似 all 方法
  • 仅在所有 Promise 实例状态变更后才发生状态变更

ES2020 引入

1
2
3
4
5
6
7
Promise.allSettled([p1, p2]).then((res) => {
// res:
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
});

res 是对象数组,均具有 status 属性代表 Promise 实例的状态;fulfilled 状态具有 value 属性,rejected 状态具有 reason 属性,分别代表 Promise 实例操作的返回值

Promise.any()

语法及参数返回值格式同 all

  • 类似 race 与 allSettled 方法
  • 仅在所有 Promise 实例状态变更后才发生状态变更

ES2021 引入

error 是 AggregateError 实例数组,代表各 Promise 实例的错误信息

2022.03.27
No.4

Generator 函数

Generator

  • Generator 函数是一个状态机,封装了多个内部状态
  • 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数还是一个遍历器对象生成函数
  • 返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态(通过 yield 定义)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* helloWorldGenerator() {
yield "hello";
yield "world";
return "ending";
}
let hw = helloWorldGenerator();

hw.next();
// { value: 'hello', done: false }
hw.next();
// { value: 'world', done: false }
hw.next();
// { value: 'ending', done: true }
hw.next();
// { value: undefined, done: true }
  • 调用 Generator 函数时会返回一个遍历器 Iterator 对象实例
  • 每次调用返回的 Iterator 对象实例的 next() 方法时从函数头部或上次停止位置开始执行,直至遇到下一个 yield 表达式或 return 语句为止
  • 每次调用 next() 方法返回的是一个对象,具有 done 属性,表示 Iterator 是否执行完毕;具有 value 属性,代表 yield 后的表达式或 return 后的表达式值(惰性求值)

yield

  • 若 Generator 函数不使用 yield,相当于单纯的暂缓执行函数
  • 关键字 yield 只能使用在 Generator 函数内
  • 若 yield 在表达式内使用需加小括号
    console.log('y' + (yield 'eah'))

next()

  • next() 方法可以带一个参数,会给出上一个 yield 表达式的返回值(因此第一次 next() 方法参数无效,可以理解是因为第一次调用 next() 方法会启动遍历器)

for-of

  • 可以使用 for-of 循环遍历 Generator 函数运行时生成的 Iterator 对象,且无需调用 next() 方法
  • 特别的, for-of 循环不会返回 return 语句的表达式值
  • 可以应用于遍历任意对象
1
2
3
4
for (let v of hw()) {
console.log(v);
}
// 'hello' 'world' 'ending'

throw

Generator 函数运行时生成的 Iterator 对象可以调用 throw 方法,在函数体外抛出错误并可被函数体内的 try-catch 块捕获
hw().throw();

return

Generator 函数运行时生成的 Iterator 对象可以调用 return 方法结束 Generator 函数执行,返回值是 return 的参数
hw().return();

next & throw & return

  • next 方法将 yield 表达式替换成一个值

  • throw 方法将 yield 表达式替换成一个 throw 语句

  • return 方法将 yield 表达式替换成一个 return 语句

    2022.03.28
    No.5

yield*

用于在 Generator 函数内执行另一个 Generator 函数

1
2
3
4
5
6
7
8
9
10
function* bar() {
yield "x";
yield* foo();
/**
* for (let v of foo()) {
* yield v;
* }
*/
yield "y";
}

语法上认为 yield* 表达式用于声明返回的是一个遍历器对象且是希望被遍历的,实际效果相当于一个 for-of 循环

可以利用 yield* 取出嵌套数组的所有成员

1
2
3
4
5
6
7
8
9
function* iterTree(tree) {
if (Array.isArray(tree)) {
for (let i = 0; i < tree.length; i++) {
yield* iterTree(tree[i]);
}
} else {
yield tree;
}
}

Generator 函数的 函数对象属性

可简写为

1
2
3
4
5
let obj = {
*myGeneratorMethod() {
// do something
},
};

Generator 函数的 this

Generator 函数 g 返回的遍历器 obj,是 g 的实例,而且继承了 g.prototype

  • Generator 函数无法被用作构造函数,即在 Generator 函数内部利用 this 添加属性无法起效
  • Generator 函数无法被 new 使用

状态机&协程&上下文

可以使用 Generator 函数实现状态机

1
2
3
4
5
6
7
8
var clock = function* () {
while (true) {
console.log("Tick!");
yield;
console.log("Tock!");
yield;
}
};

Generator 函数是一个半协程的函数,即只有 Generator 函数的调用者,才能将程序的执行权还给 Generator 函数

Generator 函数的上下文在暂停时会暂时退出堆栈但会冻结在当前状态,启动执行时会再次加入调用栈回复执行

Generator 与异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 封装异步常规方法
var fetch = require("node-fetch");
function* gen() {
var url = "https://api.github.com/users/github";
var result = yield fetch(url);
console.log(result.bio);
}
var g = gen();
var result = g.next();
// 介于 fetch 模块返回 promise 对象实例
result.value
.then(function (data) {
return data.json();
})
.then(function (data) {
g.next(data);
});

2022.03.29
No.6

async await

async let a function return a promise, await let a expression wait for a promise

async 函数是 Generator 函数的语法糖

  • 内置执行器,无需像 Generator 函数一样调用 next 方法
  • 语义清晰化,async 表明函数内含异步操作,await 表明其后的表达式需等待结果
  • 适用性更广,允许 Promise 对象和原始状态的值
  • async 函数返回 Promise 对象

async 函数内部遇到 await 会先返回一个 Promise 对象,当 await 后的异步操作完成时,才会继续执行函数体后面的语句

await 命令遇到非 promise 对象会直接返回对应的值
await 命令后的 promise 对象变为 rejected 状态,所在的整个 async 函数都会终止执行

建议将 await 命令放在 try-catch 代码块中

1
2
3
4
5
6
7
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}

可以让多个并发的异步任务使用 Promise.all 进行包装

1
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

await 命令只能在 async 函数内使用

async 函数可以保留运行堆栈,错误信息不会被遗漏

Iterator

简介

Iterator 为各种不同的数据结构(部署了 Iterator 接口)提供了统一的访问接口

Iterator 遍历时,首先创建一个指向当前数据结构起始位置的指针对象,随后调用指针对象的 next 方法,依次访问数据结构的各个成员,直至指向数据结构的结束位置

每次遍历返回一个对象,包含 value 属性和 done 属性(同 Generator 函数)

Iterator 接口部署在 Symbol.iterator 属性上,其亦是判断是否可遍历的标准

原生 Iterator 接口数据结构有 Array、Map、Set、String、函数的 arguments 对象、NodeList 对象

1
2
3
4
5
// 使用数组的 iterator
let iter = [1, 2][Symbol.iterator]();
iter.next(); // { value: 1, done: false }
iter.next(); // { value: 2, done: false }
iter.next(); // { value: undefined, done: true }

调用

  • 解构赋值(数组、Set)
  • 扩展运算符 …
  • yield*
  • 数组遍历 for…of、Array.from()、Map()、Set()、WeakMap()、WeakSet()、Promise.all()、Promise.race()

实现

1
2
3
4
5
6
7
8
9
// 使用 Generator 函数
let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
},
};
[...myIterable]; // [1, 2, 3]

return 与 throw 方法

  • return 遍历提前结束时调用,如 break 或 Error 发生
  • throw 配合 Generator 函数使用

for - of

可以遍历任意具有 Symbol.iterator 的对象或对象实例

1
2
3
for (let v of ["red", "green", "blue"]) {
console.log(v); // red green blue
}
  • for-in 循环获取对象的键名
  • for-of 循环获取对象的键值

遍历的对象方法

  • Object.entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组

  • Object.keys() 返回一个遍历器对象,用来遍历所有的键名

  • Object.values() 返回一个遍历器对象,用来遍历所有的键值

    2022.03.30
    No.7

字符串扩展

字符串 Unicode 表示更新

1
2
3
4
"\u{20BB7}";
// "𠮷"
"\u{41}\u{42}\u{43}";
// "ABC"

字符串遍历器接口

可以识别大于 0xFFFF 的码点而不是分开识别

1
2
3
for (let c of "foo") {
console.log(c);
}

模板字符串

使用反引号标识字符串

  • 可以定义多行字符串
  • 可以在字符串内嵌入变量乃至表达式及函数
  • 会保留多行字符串内部的空格和缩进
  • 内部使用反引号需转义
1
`${x} + ${y} = ${x + y}`;

标签模板

相当于向函数传入一个特殊的模板字符串数组,其具有 raw 属性,代表转义后的原字符串

1
2
3
4
5
6
7
8
9
// 两者等价
alert`hello`;
alert(["hello"]);

let a = 5,
b = 10;
tag`Hello ${a + b} world ${a * b}`;
// 等同于
tag(["Hello ", " world ", ""], 15, 50);

可以用于过滤 HTML 字符串,防止用户输入恶意内容(过滤 < > & 等特殊字符)
可用于多语言转换

字符串新增方法

  • includes(SearchString, [StartIndex]) 表示是否找到参数字符串
  • startsWith(SearchString, [StartIndex]) 表示参数字符串是否在原字符串头部
  • endsWith(SearchString, [StartIndex]) 表示参数字符串是否在原字符串尾部

方法均返回布尔值
方法均支持第二个参数,代表开始搜索的位置

  • repeat(RepeatCount) 将给定字符串重复指定的次数

参数应为有限的正整数
小数会取整
0 会返回空串
负数及 Infinity 会报错
NaN 等价于 0

  • padStart() 用于头部补全
  • padEnd() 用于尾部补全

ES2017 引入

第一个参数指定补全后字符串的长度
第二个参数指定用于补全的字符串,默认为一个空格

原字符串长度超出指定的长度,不起效
补全用字符串与原字符串长度之和超出最大长度会截取补全用的字符串

常用于为数值补全指定位数,或用于提示字符串格式

  • trimStart() 消除字符串头部空格,浏览器中同 trimLeft()

  • trimEnd() 消除字符串尾部空格,浏览器中同 trimRight()

  • matchAll() 返回正则表达式在当前字符串的所有匹配

  • replaceAll(searchValue, replacement) 替换字符串中所有的匹配到的字符串

searchValue 可以为字符串或带g修饰符的正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// $& 表示匹配的字符串,即`b`本身
// 所以返回结果与原字符串一致
"abbc".replaceAll("b", "$&");
// 'abbc'

// $` 表示匹配结果之前的字符串
// 对于第一个`b`,$` 指代`a`
// 对于第二个`b`,$` 指代`ab`
"abbc".replaceAll("b", "$`");
// 'aaabc'

// $' 表示匹配结果之后的字符串
// 对于第一个`b`,$' 指代`bc`
// 对于第二个`b`,$' 指代`c`
"abbc".replaceAll("b", `$'`);
// 'abccc'

// $1 表示正则表达式的第一个组匹配,指代`ab`
// $2 表示正则表达式的第二个组匹配,指代`bc`
"abbc".replaceAll(/(ab)(bc)/g, "$2$1");
// 'bcab'

// $$ 指代 $
"abc".replaceAll("b", "$$");

// replacement可以是一个函数
"aabbcc".replaceAll("b", () => "_");
// 'aa__cc'
  • at() 选取字符串指定位置的字符,允许负索引

    2022.03.31
    No.8

数值表示方法

  • 二进制 0b11
  • 八进制 0o11

转为十进制可使用 Number() 方法

数值分隔符

特点

  • 允许数值使用 _ 作为分隔符
  • 分隔符之间没有指定间隔的位数 12_34_56 12_345_567
  • 允许应用于小数与指数 0.123_21 1e1_234
  • 允许应用于其他进制的数值

注意点

  • 数值分隔符无法放在数值最前面或最后面
  • 数值分隔符不能连在一起使用
  • 小数的小数点前后不能有分隔符
  • 指数的 e 符号前后不能有分隔符
  • 进制前缀后不能加分隔符
  • 字符串转数值函数不支持数值分隔符 Number('123_456') // NaN parseInt('123_456') // 123

Number 对象新增方法

  • Number.isFinite()
    检测数值是否为有限的

    • 数值为 ±Infinity 或 NaN 返回 false,其他返回 true
    • 非数值一律返回 false
  • Number.isNaN()
    检测数值是否为 NaN

    • 仅 NaN 返回 true,其他均为 false

    与传统的全局方法 isFinite()和 isNaN()的区别,对于非数值类型,全局方法会调用 Number()方法转换再比较,而 Number 对象方法一律直接返回 false

  • Number.parseInt()

  • Number.parseFloat()
    将数值转换为整数与浮点数
    使用与相应的全局方法相同

  • Number.isInteger()
    判断数值是否为整数
    若数值的小数部分过小,可能会发生误判
    非数值一律返回 false

  • Number.EPSILON
    代表 js 所能够表示的最小精度数,实质是可接受的最小误差范围
    两个浮点数的差小于该数时,可以认为两浮点数相等

  • Number.MAX_SAFE_INTEGER 2^53-1

  • Number.MIN_SAFE_INTEGER -(2^53-1)
    表示 js 能够精确表示的整数的范围

  • Number.isSafeInteger()
    检测数值是否为能够精确表示的
    非数值或非整数一律返回 false

    2022.04.01
    No.9

Math 对象新增方法

大多数非数值参数会先调用 Number() 方法尝试转换

  • Math.trunc()
    对浮点数执行取整操作,去除小数部分
    空值或无法转为整数的值返回 NaN
  • Math.sign()
    判断数值的符号
    • 正数返回 +1
    • 负数返回 -1
    • 0 返回 0
    • -0 返回-0
    • 其他返回 NaN
  • Math.cbrt()
    计算数值的立方根
  • Math.clz32()
    “count leading zero bits in 32-bit binary representation of a number”
    将参数转为 32 位无符号整数形式,并返回 32 位数的前导 0 的个数
    小数只会考虑整数部分
    Math.clz32(0) // 32
    Math.clz32(1) // 31
    Math.clz32(1000) // 22
    Math.clz32(0b01000000000000000000000000000000) // 1
    Math.clz32
    Math.clz32(3.2) // 30
  • Math.imul()
    返回两数以 32 位有符号整数相乘的结果
    正常情况 Math.imul(a, b)与 a _ b 效果相同
    (0x7fffffff _ 0x7fffffff)|0 // 0 Math.imul(0x7fffffff, 0x7fffffff) // 1
    主要应用于获取大数相乘时的正确的低位数值
  • Math.fround()
    返回数的 32 位单精度浮点数形式
    绝对值超出 2^24 的数字会丢失精度
    Math.fround(0) // 0
    Math.fround(2 ** 24 - 1) // 16777215
    Math.fround(2 ** 24) // 16777216
    Math.fround(2 ** 24 + 1) // 16777216
    主要应用于将 64 位双精度浮点数转为 32 位单精度浮点数
  • Math.hypot()
    返回所有参数的平方和的平方根
    对于非数值类型参数,会先转为数值,若任一参数无法转换,即会返回 NaN
    Math.hypot(3, 4); // 5
    Math.hypot(); // 0
    Math.hypot(NaN); // NaN
    Math.hypot(3, 4, 'foo'); // NaN
    Math.hypot(-3); // 3
  • Math.log2()
    返回以 2 为底的 x 的对数
  • Math.log10()
    返回以 10 为底的 x 的对数
  • Math.log1p()
    返回 ln(1 + x)
  • Math.expm1()
    返回 e^x - 1
  • Math.sinh(x)
    返回 x 的双曲正弦
  • Math.cosh(x)
    返回 x 的双曲余弦
  • Math.tanh(x)
    返回 x 的双曲正切
  • Math.asinh(x)
    返回 x 的反双曲正弦
  • Math.acosh(x)
    返回 x 的反双曲余弦
  • Math.atanh(x)
    返回 x 的反双曲正切

BigInt 类型

ES2020 引入

  • 只能用于表示整数

  • 可以表示任意位数的整数
    2172141653n * 15346349309n // 33334444555566667777n
    2172141653 * 15346349309 // 33334444555566670000

  • 允许使用各种进制表示整数
    0b1101n // 二进制
    0o777n // 八进制
    0xFFn // 十六进制

  • 通过后缀 n 声明整数
    1234 // 普通整数
    1234n // BigInt
    1n + 2n // 3n

  • typeof 运算符返回 ‘bigint’
    typeof 123n // 'bigint'

  • 可以使用-,不可以使用+
    -42n // CORRECT
    +42n // ERROR

  • 可以使用 BigInt()将其他类型的值转换为 BigInt 类型的变量

  • BigInt.asUintN(width, BigInt)
    给定的 BigInt 转为 0 到 2^width - 1 之间对应的值。

  • BigInt.asIntN(width, BigInt)
    给定的 BigInt 转为 -2^(width-1) 到 2^(width-1) - 1 之间对应的值。

  • BigInt.parseInt(string[, radix])
    近似于 Number.parseInt(),将一个字符串转换成指定进制的 BigInt

  • 大多数运算符,除>>>与+外,均可应用于 BigInt 对象

    2022.04.02
    No.10

数组扩展

Array 对象新增方法

  • Array.from()
    将类数组对象(具有 length
    属性)及可遍历对象(具有 Iterator 接口)转为数组
    允许接受第二个参数即回调函数,作用类似 map 方法,对元素进行处理并放入返回的数组

    • 转换各种值为数组
    • 将字符串转为数组以判断字符串正确的长度
    1
    2
    3
    4
    5
    6
    7
    8
    let arr2 = Array.from({
    0: "a",
    1: "b",
    2: "c",
    length: 3,
    }); // ['a', 'b', 'c']
    Array.from([1, 2, 3], (x) => x * x);
    // [1, 4, 9]
  • Array.of()
    将一组值转换为数组
    Array.of(3, 11, 8) // [3,11,8]

  • Array.prototype.copyWithin()
    将当前数组内部指定位置成员复制至其他位置并返回当前数组
    Array.prototype.copyWithin(target, start = 0, end = this.length)

    • target 开始替换数据位置
    • start 开始读取数据位置
    • end 结束读取数据位置
      [1, 2, 3, 4, 5].copyWithin(0, 3, 4)
  • Array.prototype.find()
    传入一个回调函数,返回第一个符合条件的值

  • Array.prototype.findIndex()
    传入一个回调函数,返回第一个符合条件的值的下标

  • Array.prototype.includes()
    查找数组内是否包含指定的值

  • Array.prototype.fill()
    使用给定值填充一个数组
    new Array(3).fill(7) // [7, 7, 7]
    ['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']

  • Array.prototype.entries()

  • Array.prototype.keys()

  • Array.prototype.values()
    keys()是对键名的遍历
    values()是对键值的遍历
    entries()是对键值对的遍历
    可以使用 for-of 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (let index of ["a", "b"].keys()) {
console.log(index);
}
// 0
// 1

for (let elem of ["a", "b"].values()) {
console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ["a", "b"].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
  • Array.prototype.flat()
    展平多维数组形成低维的数组
    默认展平一层
    可以传入一个数值或 Infinity,代表期望展平的层数
    [1, [2, [3]]].flat(Infinity) // [1, 2, 3]

  • Array.prototype.flatMap()
    先对原数组各元素进行遍历后执行展平操作
    接受一个回调函数参数,只能展平一层

  • Array.prototype.at()
    根据数组下标获取元素
    允许负值

数组空位

不同方法对数组空位的处理方法不同

  • forEach(), filter(), reduce(), every() 和 some()都会跳过空位
  • map()会跳过空位,但会保留这个值
  • join()和 toString()会将空位视为 undefined,最终被处理成空字符串
  • Array.from()方法与扩展运算符会将数组的空位,转为 undefined
  • copyWithin()会连空位一起拷贝
  • fill()会将空位视为正常的数组位置
  • for…of 循环也会遍历空位
  • entries()、keys()、values()、find()和 findIndex()会将空位处理成 undefined

建议数组中不要出现空位


2022.04.05
No.11

运算符扩展

指数运算符 **

ES2016

右结合
2 ** 3 ** 2 // 相当于 2 ** (3 ** 2)

指数赋值运算符 **=

链判断运算符 ?.

ES2020

const firstName = (message && message.body && message.body.user && message.body.user.firstName) || 'default';
const firstName = message?.body?.user?.firstName || 'default';

在链式调用的时候判断左侧的对象是否为 null 或 undefined。如果是的,就不再往下运算,而是返回 undefined

  • obj?.prop // 对象属性是否存在
  • obj?.[expr] // 同上
  • func?.(…args) // 函数或对象方法是否存在

空值合并运算符 ??

ES2020

只有运算符左侧的值为 null 或 undefined 时,才会返回右侧的值

逻辑赋值运算符 &&= ||= ??=

可以用于为变量或属性设置默认值

opts.foo = opts.foo ?? 'bar';
opts.foo ??= 'bar';

对象 Object 新增方法

Object.is()

用来比较两个值是否严格相等
基本类似 === 运算符

区别:+0 不等于-0 ; NaN 等于本身

Object.assign()

用于对象合并,将源对象所有可枚举自身属性复制到目标对象

第一个参数是目标对象,剩余的参数是源对象

若只有第一个参数,会直接返回该对象
若该参数不是对象,会转为对象再处理
若该参数是 null 或 undefined 会报错
若非对象参数是剩余参数,会尝试转为对象,若失败则忽略跳过(实际只有字符串能以字符数组合入)
该方法只能拷贝源对象自身的可枚举属性,不能拷贝源对象继承属性,也不能拷贝不可枚举的属性

  • 浅拷贝
    源对象的对象属性拷贝的是引用

  • 替换同名属性
    对于同名属性,该方法会直接替换该属性而不是添加

  • 取值函数
    对于 get 函数,该方法会先求值再复制,不会复制 get 函数

  • 为对象添加属性

1
2
3
4
5
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
},
}
  • 为对象添加方法
1
2
3
4
5
6
7
8
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
},
});
  • 克隆对象
1
2
3
function clone(origin) {
return Object.assign({}, origin);
}
  • 合并多个对象
1
2
const merge = (target, ...sources) => Object.assign(target, ...sources);
const merge = (...sources) => Object.assign({}, ...sources);
  • 指定属性默认值
1
2
3
4
5
6
7
8
const DEFAULTS = {
logLevel: 0,
outputFormat: "html",
};
function processContent(options) {
options = Object.assign({}, DEFAULTS, options);
// ...
}

2022.04.06
No.12

Object.getOwnPropertyDescriptors(Object)

ES2017 引入

返回指定对象的所有非继承的自身属性的描述对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Object.getOwnPropertyDescriptors({
foo: 123,
get bar() {
return "abc";
},
});
/**
* {
* foo: {
* value: 123,
* writable: true,
* enumerable: true,
* configurable: true,
* },
* bar: {
* get: [Function: get bar],
* set: undefined,
* enumerable: true,
* configurable: true,
* },
* }
*/
  • 可以用于配合 Object.defineProperties()方法方法正确拷贝对象(特别是对象的 get 和 set 属性)
  • 配合 Object.create()方法,将对象属性克隆到一个新对象(浅拷贝)
  • 实现对象之间的继承

__proto__ Object.setPrototypeOf() Object.getPrototypeOf()

__proto__ 用于读取并设置对象的原型对象
特别注意的是它仅规定在浏览器需要实现

Object.setPrototypeOf() Object.getPrototypeOf()
分别用于设置和获取对象的原型对象
不是对象的参数会自动转为对象

Object.keys() Object.values() Object.entries()

Object.keys() 返回包含对象自身所有的非继承的可遍历的属性键名的数组
Object.values() 返回包含对象自身所有的非继承的可遍历的属性键值的数组
Object.entries() 返回包含对象自身所有的非继承的可遍历的属性键值对数组的数组

1
2
3
4
var obj = { foo: "bar", baz: 42 };
Object.keys(obj); // ["foo", "baz"]
Object.values(obj); // ["bar", 42]
Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ]

Object.fromEntries()

相当于 Object.entries() 的逆操作
将一个键值对数组转为对象

1
2
3
4
Object.fromEntries([
["foo", "bar"],
["baz", 42],
]); // { foo: "bar", baz: 42 }

可以利用该方法与其逆方法实现map 数据结构对象之间的相互转换

对象扩展

属性的简洁表示方法

可以在大括号里面,直接写入变量和函数,作为对象的属性和方法,此时属性名就是变量名,属性值就是变量值

1
2
3
4
const foo = "bar";
const baz = {
foo,
};

方法的简洁表示方法

1
2
3
4
5
const o = {
method() {
return "Hello!";
},
};

get 与 set 的显示方法

1
2
3
4
5
6
7
8
9
10
11
12
const cart = {
_wheels: 4,
get wheels() {
return this._wheels;
},
set wheels(value) {
if (value < this._wheels) {
throw new Error("数值太小了!");
}
this._wheels = value;
},
};

属性名表达式

允许将变量值作为对象的属性名
注意:默认会将非对象变量值转为字符串

1
2
3
4
5
let propKey = "foo";
let obj = {
[propKey]: true,
["a" + "bc"]: 123,
};

2022.04.07
No.13

方法的 name 属性

  • 普通方法 方法名
  • 普通对象方法 方法名
  • get set 对象方法 get|set+方法名
  • Function 构造函数方法 anonymous
  • bind 方法 bound+方法名
  • Symbol 方法 Symbol 的描述

属性可枚举性与遍历

对象属性描述对象

对象的各属性均有一个相应的描述对象
可以使用 Object.getOwnPropertyDescriptor() 方法获取属性的描述对象

enumerable属性

属性 enumerable 用于描述对象属性是否可枚举

遍历方法

  • for…in 遍历对象自身的和继承可枚举非 Symbol 属性的属性
  • Object.keys(obj) 遍历对象自身可枚举非 Symbol 属性的属性
  • Object.getOwnPropertyNames(obj) 遍历对象自身非 Symbol 属性的属性
  • Object.getOwnPropertySymbols(obj) 遍历对象自身Symbol 属性的属性
  • Reflect.ownKeys(obj) 遍历对象自身的所有属性

遍历次序

  • 首先遍历所有数值键,按照数值升序排列
  • 其次遍历所有字符串键,按照加入时间升序排列
  • 最后遍历所有Symbol键,按照加入时间升序排列

super 关键字

指向当前对象的原型对象
(只能用于对象的方法内,只能在对象方法的简写法可使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// correct
const obj = {
foo: "world",
find() {
return super.foo;
},
};

// error
const obj = {
foo: super.foo,
};
const obj = {
foo: () => super.foo,
};
const obj = {
foo: function () {
return super.foo;
},
};

super.foo 原理上等同于 Object.getPrototypeOf(this).foo (属性)或 Object.getPrototypeOf(this).foo.call(this) (方法)

对象扩展运算符

ES2018 引入

对象的解构赋值

1
2
3
4
5
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }
/* ...必须为最后一个参数 */

扩展运算符

取出对象的所有可遍历属性并拷贝到当前对象内,也可以应用于合并对象

1
2
3
4
5
6
7
let z = { a: 3, b: 4 };
let n = { ...z };
n; // { a: 3, b: 4 }

let ab = { ...a, ...b };
// 同名属性会依照出现的顺序被覆盖
// 可以设置对象属性的默认值以及修改现有对象的属性

实际效果等同于使用 Object.assign()方法

AggregateError 错误对象

ES2021 引入

AggregateError 在一个错误对象里面,封装了多个错误

AggregateError(errors[, message])
errors 以数组形式接收产生的错误对象(必须)
message 以字符串形式接收抛出时的提示信息(可选)
以上亦是对象实例的属性

1
2
3
4
5
6
7
8
9
const error = new AggregateError(
[
new Error("ERROR_11112"),
new TypeError("First name must be a string"),
new RangeError("Transaction value must be at least 1"),
new URIError("User profile link must be https"),
],
"Transaction cannot be processed"
);

2022.04.09
No.14

函数的扩展

函数默认值

1
2
3
4
5
6
7
8
// 形参
function f(a, b = 12) {
/* ... */
}
// 解构赋值
function g({ a, b = 1 }) {
/* ... */
}
  • 形参默认声明,因而无法再次声明
  • 不允许出现重名参数
  • 参数默认值是惰性求值,即每次重新计算值
  • 建议函数默认值放在末尾(不然为取到默认值,需要显式传参 undefined)
  • 指定默认值的函数,length 属性表示未指定默认值的参数个数(非末尾参数的默认值后的参数不再计入)【length 属性指的是函数预期传入的参数个数】
1
2
3
4
// 双重默认值
function fetch(url, { body = "", method = "GET", headers = {} } = {}) {
/* ... */
}

另外函数的形参部分运行时会生成一个独立的作用域

1
2
3
4
5
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2); // 2

可以利用默认值强制实现函数参数不可省略的特性

1
2
3
function foo(mustBeProvided = () => throw new Error("Missing parameter")) {
/* ... */
}

rest 参数

用于搭配一个数组变量,获取函数多余的参数
格式 ...变量名

函数的 length 属性,不包括 rest 参数

1
const sortNumbers = (...numbers) => numbers.sort();

另外,rest 参数必须为函数的最后一个参数

函数的严格模式

可以在函数内部设定严格模式

但是若函数参数使用了默认值解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式

  1. 可以设定全局的严格模式规避
  2. 可以将函数包裹在无参数的立即执行函数内部

函数的 name 属性

返回函数的函数名称

  • 匿名函数会返回实际的函数名
  • Function 构造函数创建的函数返回 ‘anonymous’
  • bind 返回的函数返回的有 bind 前缀

箭头函数

() => {}

尾调用优化

函数式编程
函数的返回调用另一个函数得到的结果(且应是函数的最后一步操作)

1
2
3
function f(x) {
return g(x);
}

它可以提高性能节省内存,尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了

若尾调用自身,即尾递归
优点在于不易发生栈溢出错误

1
2
3
4
5
6
7
8
9
10
11
12
// 阶乘
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
// Fibonacci 数列
function Fibonacci2(n, ac1 = 1, ac2 = 1) {
if (n <= 1) {
return ac2;
}
return Fibonacci2(n - 1, ac2, ac1 + ac2);
}

尾递归优化起效必须开启严格模式

函数参数尾逗号

ES2017 生效

允许函数最后一个参数出现逗号

Function.prototype.toString() 优化

ES2019 生效

会完全返回一模一样的原始代码(包括注释、空格等等)

catch 的参数忽略

允许 catch 语句忽略参数

1
2
3
4
5
try {
// ...
} catch {
// ...
}

2022.04.10
No.15

Map 与 Set

Set

Set 类似于数组,但是 Set 成员的值都是唯一的,没有重复的值

Set 构造方法

  • new Set()
  • new Set([1, 2, 3])
    构造方法可以接受一个数组或其他具有 Iterator 接口的数据结构参数

去除数组重复成员

[...new Set(array)] > > Array.from(new Set(array))
去除字符串重复成员
[...new Set(string)].join('')

Set 对象添加值时,不会发生类型转换,且使用 Same-value-zero equality 判断值是否重复(类似===),特别的是任意对象均不相同

可以使用 Array.from() 方法将 Set 结构转为数组
可以利用扩展运算符对 Set 实例转为数组执行一些操作

Set 原型属性

  • Set.prototype.constructor Set
    构造函数,即 Set 函数
  • Set.prototype.size
    返回 Set 实例的成员数量

Set 原型{操作}方法

  • Set.prototype.add(value)
    向 Set 实例添加一个值
    返回 Set 结构本身
    因此该方法可以链式调用
  • Set.prototype.delete(value)
    删除某个值
    返回一个布尔值,表示删除是否成功
  • Set.prototype.has(value)
    检测该值是否为 Set 实例的成员
    返回一个布尔值
  • Set.prototype.clear()
    清除所有成员
    没有返回值
1
2
3
4
5
6
7
s.add(1).add(2).add(2);
s.size; // 2
s.has(1); // true
s.has(2); // true
s.has(3); // false
s.delete(2);
s.has(2); // false

Set 原型{遍历}方法

  • Set.prototype.keys()
    返回键名的遍历器

  • Set.prototype.values()
    返回键值的遍历器

  • Set.prototype.entries()
    返回键值对的遍历器

  • Set.prototype.forEach()
    使用回调函数遍历每个成员,分别执行某种操作
    没有返回值
    接受第一个参数作为将执行的回调函数,第二个参数作为绑定的 this 对象

    特别的,Set 结构键名和键值是同一个值,因此 keys 方法和 values 方法的行为完全一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let set = new Set(["red", "green", "blue"]);
for (let item of set.keys()) {
console.log(item);
// red green blue
}
for (let item of set.values()) {
console.log(item);
// red green blue
}
for (let item of set.entries()) {
console.log(item);
// ["red", "red"] ["green", "green"] ["blue", "blue"]
}
new Set([1, 4, 9]).forEach((value, key) => console.log(key + " : " + value));
// 1 : 1 4 : 4 9 : 9

Map

Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现(因为 Object 对象的键名只允许为字符串)

Map 构造方法

构造方法类似 Set 的构造方法,可以接受任意具有 Iterator 接口且每个成员都是一个双元素的数组的数据结构作为参数
Map 亦不允许出现键值相同的元素
特别的,Map 判断键值相同是基于内存地址的,因此对于对象而言,相同内容的对象可能被 Map 认为是不同的键值

Map 原型属性及方法

  • size
    返回 Map 结构的成员总数
  • Map.prototype.set(key, value)
    设置键对应的值
    若键已存在会覆盖已有的值
    返回当前的 Map 结构
    因此可以采取链式写法
  • Map.prototype.get(key)
    读取键对应的值
    查找成功返回键对应的值,否则返回 undefined
  • Map.prototype.has(key)
    检测某个键是否存在于当前 Map 对象内
    返回一个布尔值
  • Map.prototype.delete(key)
    删除某个键
    返回一个布尔值,表示是否删除成功
  • Map.prototype.clear()
    清除所有成员
    方法没有返回值
1
2
3
4
5
6
7
8
const map = new Map();
map.set("foo", "val");
map.size; // 1
map.get("foo"); // 'val'
map.has("foo"); // true
map.delete("foo"); // true
map.delete("foo"); // false
map.clear();
  • Map.prototype.keys()
    返回键名的遍历器
  • Map.prototype.values()
    返回键值的遍历器
  • Map.prototype.entries()
    返回所有成员的遍历器
  • Map.prototype.forEach()
    遍历 Map 的所有成员

同样,Map 的遍历顺序即插入顺序

WeakSet 及 WeakMap

WeakSet 只接受对象作为成员
WeakMap 只接受对象作为键名(null 除外),键值则没有限制
WeakSet 中的对象与 WeakMap 的键名所指向的对象,不计入垃圾回收机制

因此 WeakSet 和 WeakMap 是不可遍历的,且不具有 size 属性,且无法执行清空操作
WeakSet 和 WeakMap 的其他方法基本相同

它们主要应用于解决使用 Set 和 Map 时可能出现的内存泄漏问题

2022.04.11
No.16

WeakRef

ES2021 引入

对象的弱引用

直接使用 new WeakRef(target) 创建基于对象 target 的新对象,该对象是对对象 target 的弱引用,垃圾回收机制不会计入这个引用
新对象的引用不会妨碍原始对象 target 被垃圾回收机制清除

实例 deref() 方法可以判断原始对象是否已被清除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 作为缓存
function makeWeakCached(f) {
const cache = new Map();
return (key) => {
const ref = cache.get(key);
if (ref) {
const cached = ref.deref();
if (cached !== undefined) return cached;
}
const fresh = f(key);
cache.set(key, new WeakRef(fresh));
return fresh;
};
}
const getImageCached = makeWeakCached(getImage);

FinalizationRegistry

指定目标对象被垃圾回收机制清除以后,所要执行的回调函数

1
2
3
4
5
6
7
8
// 新建注册表实例,接受一个回调函数参数作为当该对象被回收时的回调函数
const registry = new FinalizationRegistry((heldValue) => {
// ....
});
// 注册表实例的register()方法注册所要观察的目标对象并确定将传入回调函数的参数
registry.register(theObject, "some value", theObject);
// unregister()方法可以取消注册当前的对象
registry.unregister(theObject);

Symbol

概述

原始数据类型 Symbol,表示独一无二的值,是一种类似于字符串的数据类型
Symbol 值通过 Symbol() 函数生成
可以添加一个字符串参数表示对 Symbol 实例的描述(若为对象则会调用 toString() 方法转为字符串)

1
2
let s = Symbol(); // typeof s === "symbol"
let s1 = Symbol("foo"); // Symbol(foo)

任意两个 Symbol 变量使用 === 运算符比较都是不等的

Symbol 无法与其他类型的值进行运算,否则会报错;但可以显式转为字符串与布尔值(恒为 true)

Symbol.prototype.description

ES2019 引入

表示 Symbol 的描述(实际上就是传入 Symbol 的参数)

1
2
const sym = Symbol("foo");
sym.description; // "foo"

Symbol 作属性名

Symbol 值可以作为标识符,用于对象的属性名,从而保证不会出现同名的属性

1
2
3
let a = {
[mySymbol]: "bar",
};

注意:Symbol 值作为对象属性名时,不能用点运算符,只能使用中括号运算符

Symbol 类型可以用于定义一组常量,可以替代魔术字符串的使用

Symbol 的遍历

Symbol 成员属性不会被大多数循环所遍历
但可以使用 Object.getOwnPropertySymbols() 方法与 Reflect.ownKeys() 获取用作属性名的 Symbol 值
因此可以用于为对象定义一些非私有的、但又希望只用于内部的方法

2022.04.12
No.17

Symbol 的方法

Symbol.for()

接受一个字符串作为参数,然后搜索有没有以该参数作为名称的全局环境的 Symbol 值,若存在,就返回这个 Symbol 值;否则就新建一个以该字符串为名称的 Symbol 值,并将其全局注册

1
2
3
4
Symbol.for("foo") === Symbol.for("foo");
// true
Symbol("bar") === Symbol("bar");
// false

Symbol.keyFor()

返回一个已登记的 Symbol 类型值的 key,否则返回 undefined

Symbol 的内置值

Symbol.hasInstance

对象的 Symbol.hasInstance 属性,指向一个内部方法,该内部方法会在其他对象使用 instanceof 运算符,判断是否为该对象的实例时调用

1
2
3
4
5
6
7
8
9
10
11
12
13
class M {
[Symbol.hasInstance](foo) {
return foo instanceof Array;
},
};
[1, 2] instanceof new MyClass() // true
class N {
static [Symbol.hasInstance](obj) {
return Number(obj) > 0;
},
};
1 instanceof N // true
-1 instanceof N // true

Symbol.isConcatSpreadable

Symbol.isConcatSpreadable 属性是一个布尔值
表示该对象用于Array.prototype.concat()时,是否可以展开

  • 默认数组可以展开,值为 undefined(true 亦可)
  • 类数组对象默认不可展开,值为 false,(设为 true 可展开)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A1 extends Array {
constructor(args) {
super(args);
this[Symbol.isConcatSpreadable] = true;
},
};
class A2 extends Array {
constructor(args) {
super(args);
},
get [Symbol.isConcatSpreadable] () {
return false;
},
};
/* 注意:实例内与类内的区别 */

Symbol.species

Symbol.species 属性,指向一个构造函数,在创建衍生对象时调用

1
2
3
4
5
class MyArray extends Array {
static get [Symbol.species]() {
return Array;
}
}

可以指定衍生对象的类型(相当于指定原型对象)

Symbol.match

Symbol.match 属性,指向一个函数,在执行str.match(myObject)时,如果该属性存在,会调用该属性,返回该方法的返回值

Symbol.replace

Symbol.replace 属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值
Symbol.replace 方法会收到两个参数,第一个参数是 replace 方法正在作用的对象;第二个参数是替换后的值

对象的 Symbol.search 属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值

Symbol.split

对象的 Symbol.search 属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值

Symbol.iterator

对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法
对象进行 for…of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器

1
2
3
4
myIterable[Symbol.iterator] = function* () {};
class Collection {
*[Symbol.iterator]() {},
};

特别的,这个方法应当是一个 Generator 函数

Symbol.toPrimitive

对象的 Symbol.toPrimitive 属性,指向一个方法,该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case "number":
return 123;
case "string":
return "str";
case "default":
return "default";
default:
throw new Error();
}
},
};
2 * obj; // 246
3 + obj; // '3default'
obj == "default"; // true
String(obj); // 'str'

该方法可以接受一个字符串参数

  • Number:该场合需要转成数值
  • String:该场合需要转成字符串
  • Default:该场合可以转成数值,也可以转成字符串

Symbol.toStringTag

对象的 Symbol.toStringTag 属性,指向一个属性的 get 方法,在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在 toString 方法返回的字符串之中,表示对象的类型
可以用来定制[object Object][object Array]中 object 后面的那个字符串

部分特殊的对象已具有自带的 Symbol.toStringTag 属性

Symbol.unscopables

对象的 Symbol.unscopables 属性,指向一个对象,该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyClass {
foo() {
return 1;
}
get [Symbol.unscopables]() {
return { foo: true };
}
}
var foo = function () {
return 2;
};

with (MyClass.prototype) {
foo(); // 2 (没有时为1)
}

2022.04.13
No.18

class 语法

class 简介

ES6 引入了 class 关键字用于定义类,作为对象的模板
可以认为是构造函数的另外一种写法,用法完全和构造函数一样

1
2
3
4
5
6
7
8
9
10
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return "(" + this.x + ", " + this.y + ")";
}
}
const point = new Point(1, 2);
  • 实际上类模板内的所有方法实质是定义在构造方法的 prototype 对象上
  • prototype 对象的 constructor()属性,直接指向’类’的本身
  • 特别的,类内部定义的所有方法,都是不可枚举
  • 特别的,类必须使用new指令调用,否则会报错
  • 类内部默认是严格模式
  • 类不存在变量提升现象
  • 类的 name 属性返回类名
  • 类成员方法的 this 默认指向类的实例(因此不建议单独使用成员方法,除非绑定了 this 的指向)

constructor 方法

类的默认方法(必须的),使用 new 命令时会被调用

默认返回实例对象,即 this

class 实例

实例的属性除非显式定义在其本身(即定义在 this 对象上),否则都是定义在原型上(即 class 上)

类的所有实例共享一个原型对象(浏览器可以使用proto属性访问,亦可使用 Object.getPrototypeOf()获取实例的原型对象)

另外,如果需要修改原型对象,需要非常非常谨慎

取值函数与存值函数

存值函数和取值函数直接设置在属性的 Descriptor 对象上,会拦截赋值和读取行为

1
2
3
4
5
6
7
8
9
10
11
class MyClass {
constructor() {
// ...
}
get prop() {
return "getter";
}
set prop(value) {
console.log("setter: " + value);
}
}

属性表达式与 class 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 属性表达式 */
let methodName = "getArea";
class A {
[methodName]() {}
}

/* class表达式 */
const B = class {};

/* 立即执行的class表达式 */
let c = new (class {
constructor() {}
})();

2022.04.14
No.19

静态方法

在 class 内定义的方法加 static 关键字,使该方法不被实例继承而是直接通过类调用
静态方法的 this 关键字指向类本身
继承时父类的静态方法可以被子类继承,亦可通过 super 关键字调用

1
2
3
4
5
6
class Foo {
static classMethod() {
return "hello";
}
}
Foo.classMethod(); // 'hello'

实例属性

实例属性可以定义在类的顶层

1
2
3
4
5
6
class Foo {
count = 0;
get value() {
return this.count;
}
}

静态属性

定义在 class 本身的属性,而不是定义在实例对象上的属性

1
2
class Foo {}
Foo.prop = 1;

static 修饰静态属性

私有方法及私有属性

  1. 通过命名时前加下划线_加以提示
  2. 将变量名命名为 Symbol 值,增大获取的难度
  3. 将私有方法放在类外

新 method,使用#前缀

1
2
3
4
class Foo {
#count = 0;
#cause() {}
}

同样可以使用 in 运算符判断私有属性的存在(但只能在类内部使用),常规的继承也可以使用

静态块

在类生成时运行一次,且仅运行一次
主要用于对静态属性进行初始化
(内部可以使用类名或 this 指代当前类)

1
2
3
4
5
6
class C {
static x = 1;
static {
this.x; // 等价于C.x
}
}

new.target 属性

在构造函数之中,返回 new 命令作用于的那个构造函数,若不是 new 调用的,会返回 undefined
可以用来确定构造函数的调用方式
子类继承父类时,new.target 会返回子类

1
2
3
4
5
6
7
function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error("必须使用 new 命令生成实例");
}
}

2022.04.15
No.20

class 继承

使用 extends 关键字声明继承

1
2
3
4
5
6
7
8
class Point {
constructor() {};
}
class ColorPoint extends Point {
constructor() {
super();
};
}
  • constructor

    必须在显式声明的 constructor()方法内部显式调用 super()方法,否则会报错(未指定 constructor()方法会默认添加且内部会调用 super()方法)

    原因是需要先调用父类的构造方法才可以生成 this 对象,从而子类才可在 this 上添加方法及属性
    可以认为,任意子类方法都有一个 constructor()方法

  • 属性及方法

    子类会继承父类所有属性方法,包括静态属性与静态方法(不包括私有属性与私有方法)
    但如果子类定义了私有属性的读写非私有方法,即可通过方法读写私有属性

super 关键字

  • super 作为函数调用时,代表父类的构造函数

    super 方法只能用于子类的构造函数内

  • super 作为对象使用时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类

    实例方法调用 super 内部 this 指向调用 super 的子类的实例
    静态方法调用 super 内部 this 指向 super 所在的子类

  • 由于对象总是继承其他对象,因此实际上任意对象中均可使用 super 关键字

prototype 属性和 __proto__ 属性

class 同时具有以上两种属性

子类

  • 子类 __proto__ 属性指向父类
  • 子类 prototype 属性的 __proto__ 属性,指向父类的 prototype 属性
1
2
3
4
5
class A {}
class B extends A {}

B.__proto__ === A; // true
B.prototype.__proto__ === A.prototype; // true
  • 作为一个对象,子类(B)的原型(proto属性)是父类(A)
  • 作为一个构造函数,子类(B)的原型对象(prototype 属性)是父类的原型对象(prototype 属性)的实例

子类实例

  • 子类实例的proto属性的proto属性,指向父类实例的proto属性
1
2
3
4
var a = new A(2, 3);
var b = new B(2, 3, "red");

b.__proto__.__proto__ === a.__proto__; // true
  • 子类的原型的原型,是父类的原型

    2022.04.16
    No.21

Module

模块

  1. CommonJS 服务器端
  2. AMD 浏览器端

ES6 模块通过 export 显式指定输出的代码,并使用 import 导入

这是编译时加载,可以实现静态分析,可以提高加载效率

模块自动采用严格模式

export命令

规定模块的对外接口

  • 可以直接输出变量、函数、对象等
1
2
3
4
5
6
7
// 输出变量
export const a = 100;
const b = 200;
export { b };

// 输出函数及类class
export function c() {}
  • 通常export输出变量名称即其原本名称,但可以使用as关键字重命名
1
2
const d = 300;
export { d as D };
  • export指令规定的对外接口,必须与模块内部变量建立一一对应的关系

  • export输出的接口,与其对应的值是动态绑定关系,可以实时获取模块内部的值

import命令

加载模块

  • import命令接受一对大括号,内部指定从其他模块导入的变量名(该变量名必须与对外接口名称相同)
1
import { a, b } from "module.js";
  • 可以使用as关键字对输入变量重命名
1
import { c as C } from "module.js";
  • import输入的接口是只读的,无法对其重新赋值(若其为对象,可改变其的内容)

  • import 后的 from 规定模块文件的位置,相对路径与绝对路径均可,单独的模块名需要相应的配置文件给出模块的位置

1
import jQuery from "jquery";
  • import命令具有提升效果,会把变量提升到模块头部首先执行(因为 import 命令是编译阶段执行的,在代码运行之前)

  • import命令无法使用表达式与变量,也无法在 if、for 等块状作用域内执行

  • import 语句会执行所加载的模块

1
2
// 执行一次lodash模块,但不会导入任何值
import "lodash";

模块整体加载

使用*as实现

1
2
import * as Z from "module.js";
Z.c();

export default指令

为模块指定默认输出,用户可以自行定义任意的名称

1
2
3
4
5
// export.js
function foo() {}
export default foo;
// import.js
import FOO from "export.js";

该命令只能使用一次,因为一个模块只能有一个默认输出

本质上,export default就是输出一个叫做default的变量或方法,并且系统允许为它取任意名字

2022.04.17
No.22

exportimport的复用

1
export { foo } from "module.js";

相当于导入了模块并作为对外接口发放出去(但实际上并没有导入当前模块,不能直接使用)

1
2
3
4
5
6
7
// 其他一些用法
export { foo as FOO } from 'module';
export * from 'module';
export * as BAR from 'module';
export { default } from 'module';
export { foo as default } from 'module';
export { default as foo } from 'module';

模块的继承

1
2
3
4
5
6
7
8
export * from "circle";
export var e = 2.71828182846;
export default function (x) {
return Math.exp(x);
}

import * as math from "circleplus";
import exp from "circleplus";

跨模块常量

将 const 常量作为对外接口导出

1
2
export const A = 1;
import { A } from "./constants";

动态导入

使用import(specifier)动态导入模块,接受一个参数代表模块的位置,返回一个 Promise 实例(它是一个异步加载方法)

1
2
3
4
5
6
7
import(`./section-modules/${someVariable}.js`)
.then((module) => {
module.doSomeThing();
})
.catch((err) => {
console.error(err);
});
  • 按需加载
    在需要时再加载模块
  • 条件加载
    在if代码块内根据需要加载不同的模块
  • 动态模板路径加载
    动态生成希望加载的模块路径

可以结合 async - await 使用

1
2
3
4
5
6
7
8
9
async function main() {
const myModule = await import('./myModule.js');
const { export1, export2 } = await import('./myModule.js');
const [ module1, module2, module3 ] = await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}

2022.04.18
No.23

Proxy 代理器

Proxy

Proxy 用于修改某些操作的默认行为,相当于在目标对象前添加一层拦截,可以对外界的访问进行过滤与改写

使用如下的方法生成一个对象的拦截器
const proxy = new Proxy(target, handler);
其中target代表将拦截的目标对象
handler代表拦截行为,是一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** define */
var obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});

/** test */
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2

要使得Proxy起效,需要直接对Proxy实例进行操作而不是对目标对象进行操作
显而易见Proxy实例可以做其他对象的原型对象
同一个拦截器对象可以一次性拦截多个操作
可以把拦截器设置到proxy属性上var object = { proxy: new Proxy(target, handler) };因而可以在object对象上调用

Proxy 实例方法

  • get(target, propKey, receiver)

    1. 简介
      拦截对象属性的读取
      标对象某属性不可写且不可配置,则对该属性的get方法会报错
    2. 参数
      • target 目标对象
      • propKey 属性名
      • receiver 操作行为所针对的proxy实例(可选)
    3. 目标
      • proxy.foo
      • proxy['foo']
    4. 作用
      • 实现链式调用
  • set(target, propKey, value, receiver)

    1. 简介
      拦截对象属性的设置
      应返回一个布尔值(严格模式下返回 false 或 undefined 均会报错)
      目标对象的不可写属性,则对该属性的set方法不起效
    2. 参数
      • target 目标对象
      • propKey 属性名
      • value 属性值
      • receiver 操作行为所针对的proxy实例(可选)
    3. 目标
      • proxy.foo = v
      • proxy['foo'] = v
    4. 作用
      • 阻止内部属性的点运算符与中括号运算符访问,实现私有属性的效果
  • has(target, propKey)

    1. 简介
      拦截propKey in proxy的操作
      目标对象不可配置或不可扩展时使用has方法会报错
      返回一个布尔值
      特别的其对 for - in 循环不起效
    2. 参数
      • target 目标对象
      • key 查询的属性名
    3. 目标
      • foo in proxy
    4. 作用
      • 阻止对内部属性的in运算符访问,实现私有属性的效果
  • deleteProperty(target, propKey)

    1. 简介
      拦截delete操作
      返回一个布尔值
      目标对象的不可扩展属性无法被删除,否则会报错
    2. 参数
      • target 目标对象
      • propKey 删除的属性名
    3. 目标
      • delete proxy[propKey]
    4. 作用
      • 阻止对内部属性的in运算符访问,实现私有属性的效果
  • ownKeys(target)

    1. 简介
      拦截读取对象自身属性操作
      返回一个数组(数组成员只能是字符串或Symbol值,其他值出现会报错)
      会自动过滤不存在的属性、不可遍历的属性,Symbol值属性
      不可配置的属性必须被返回,否则报错
      不可扩展的目标对象必须包含所有属性,否则会报错
    2. 参数
      • target 目标对象
    3. 目标
      • Object.getOwnPropertyNames(proxy)
      • Object.getOwnPropertySymbols(proxy)
      • Object.keys(proxy)
      • for...in循环
  • getOwnPropertyDescriptor(target, propKey)

    1. 简介
      拦截Object的获取属性描述对象方法
      返回属性的描述对象或undefined
    2. 参数
      • target 目标对象
      • propKey 希望获取属性描述对象的属性名
    3. 目标
      • Object.getOwnPropertyDescriptor(proxy, propKey)
  • defineProperty(target, propKey, propDesc)

    1. 简介
      拦截Object的定义属性方法
      返回一个布尔值
      不可扩展的目标对象无法被删除,否则会报错
      目标对象的不可写属性或者不可配置属性无法被改变
    2. 参数
      • target 目标对象
      • propKey 删除的属性名
      • propDesc 删除的属性描述对象
    3. 目标
      • Object.defineProperty(proxy, propKey, propDesc)
      • Object.defineProperties(proxy, propDescs)
  • preventExtensions(target)

    1. 简介
      拦截Object的标记对象不可扩展方法
      必须返回一个布尔值
      仅目标对象不可扩展时才能返回true,否则会报错(因此通常内部需要调用一次Object.preventExtensions(proxy)方法)
    2. 参数
      • target 目标对象
    3. 目标
      • Object.preventExtensions(proxy)
  • getPrototypeOf(target)

    1. 简介
      拦截获取原型对象操作
      返回一个对象或null
      不可扩展的目标对象,必须返回目标对象的原型对象
    2. 参数
      • target 目标对象
    3. 目标
      • Object.prototype.__proto__
      • Object.prototype.isPrototypeOf()
      • Object.getPrototypeOf()
      • Reflect.getPrototypeOf()
      • instanceof
  • isExtensible(target)

    1. 简介
      拦截判断对象可扩展性操作
      返回一个布尔值
      其返回值必须与目标对象的isExtensible属性一致
    2. 参数
      • target 目标对象
    3. 目标
      • Object.isExtensible(proxy)
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截

    1. 简介
      拦截设置原型对象方法
      必须返回一个布尔值
      不可扩展的目标对象不得改变其原型对象
    2. 参数
      • target 目标对象
      • proto 将设置的原型对象
    3. 目标
      • Object.setPrototypeOf(proxy, proto)
  • apply(target, object, args)

    1. 简介
      拦截 Proxy 实例作为函数调用的操作
    2. 参数
      • target 目标对象
      • ctx 目标对象的上下文对象this
      • args 目标对象的参数数组
    3. 目标
      • proxy(...args)
      • proxy.call(object, ...args)
      • proxy.apply(...)
  • construct(target, args)

    1. 简介
      拦截 Proxy 实例作为构造函数调用的操作
      返回必须为一个对象
    2. 参数
      • target 目标对象(必须是函数)
      • args 构造函数的参数数组
      • newTarget new命令作用的构造函数
    3. 目标
      • new proxy(...args)
    4. 作用
      • 阻止对内部属性的in运算符访问,实现私有属性的效果

Proxy 静态方法

  • Proxy.revocable()
    返回一个可取消的 Proxy 实例
    可以应用于:若目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let target = {};
    let handler = {};

    let {proxy, revoke} = Proxy.revocable(target, handler);

    proxy.foo = 123;
    proxy.foo // 123

    revoke();
    proxy.foo // TypeError: Revoked

issue of this on Proxy

Proxy 代理情况下,目标对象内部的this会指向 Proxy 实例
此时需要使用bind、apply等方法绑定this的指向

2022.04.19
No.24

Reflect

Reflect 对象是 Object 对象的更新

svg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<svg width="500" height="500" style="background-color: #eee;">

<!-- 矩形 rect -->
<rect width="100" height="100" x="100" y="100" rx="10" ry="10"></rect>
<!-- 圆形 circle -->
<circle cx="250" cy="250" r="100"></circle>
<!-- 椭圆 ellipse-->
<ellipse cx="475" cy="450" rx="25" ry="50"></ellipse>
<!-- 直线 line -->
<line x1="0" y1="0" x2="500" y2="500" stroke="green"></line>
<!-- 折线 polyline -->
<polyline points="500 0, 100 100, 0 500" stroke="blue" fill="none"></polyline>
<!-- 多边形 polygon -->
<polygon points="500 0, 400 400, 0 500" stroke="none" fill="green"></polygon>
<!-- 直线路径 path -->
<path d="M 0 0 L 50 50 L 50 100 L 100 400 L 400 400 L 400 100 L 50 50 Z" stroke="orange" fill="none" ></path><path d="M 0 0 l 250 150 l 100 0 l 0 -100 l 50 50 l 50 0 l 0 350 l -350 0 l 0 -50 l -50 -50 l 100 0 l 0 -100 Z" stroke="red" fill="none" stroke-width="5" stroke-dasharray="25 5 10 5" stroke-dashoffset="5" stroke-linecap="round" stroke-linejoin="round"></path>
<!--
属性样式 直接设置在元素属性上
内联样式 设置在元素 style 属性内
内部样式 写在 style 标签内
外部样式 写在独立的 css 文件中
-->
<!--
svg 常见属性
fill 填充颜色
stroke 描边颜色
fill-opacity 填充颜色的不透明度
stroke-opacity 描边颜色的不透明度
stroke-width 描边宽度
stroke-dasharray 描边样式 - 可以用于设置虚线
stroke-dashoffset 设置偏移量
stroke-linecap 线帽样式
butt 平头 | 默认
round 圆头
square 方头
stroke-linejoin 拐角样式
miter 尖角 | 默认
round 圆角
bevel 平角
shape-rendering 消除锯齿
crispEdges 关闭反锯齿功能
geometricPrecision 开启反锯齿功能
-->
<!--
svg 支持颜色
颜色关键字
十六进制
RGB 和 RGBA
HSL 和 HSLA
-->

<!-- 文本元素 text -->
<text x="250" y="250" fill="pink" font-size="50" font-weight="bold" text-decoration="underline" text-anchor="middle" dominant-baseline="middle">SVG</text>
<!-- 多行文本 tspan -->
<text font-size="25">
<tspan x="400" y="380">S</tspan>
<tspan x="400" y="400">V</tspan>
<tspan x="400" y="420">G</tspan>
</text>
<!--
文本元素属性
font-size 字号
font-weight 粗体
text-decoration 装饰线
text-anchor 水平对齐方式
dominant-baseline 垂直对齐方式
writing-mode 文字方向
-->

<!-- 超链接 a -->
<a xlink:href="https://developer.mozilla.org/zh-CN/docs/Web/SVG" xlink:title="svg" target="_blank">
<text x="50" y="50" font-size="25">SVG</text>
</a>

<!-- 图片 image -->
<image xlink:href="https://img.zcool.cn/community/0167b95fc9ea7a11013ee04dc55982.jpg@1280w_1l_2o_100sh.jpg" width="50" height="50" x="100" y="100"></image>

</svg>
webpack

webpack

前端工程化

  • 模块化
    • js模块化
    • css模块化
    • 资源模块化
  • 组件化
    • 复用UI结构、样式、行为
  • 规范化
    • 目录结构划分
    • 编码规范化
    • 接口规范化
    • 文档规范化
    • git分支管理
  • 自动化
    • 自动化构建
    • 自动部署
    • 自动化测试

标准化企业级项目开发过程:工具、技术、流程、经验

grunt gulp => webpack(主) parcel => vite

webpack

前端项目工程化

  • 代码压缩混淆
  • 处理JavaScript兼容性
  • 性能优化

安装webpack

npm i webpack webpack-cli -D

配置webpack

在webpack.config.js文件内
保存webpack的打包设定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 导入 path 包
const path = require('path');

module.exports = {
/**
* mode 标记构建模式
* development 开发模式 不会代码压缩或性能优化 打包速度快
* production 发布模式 会代码压缩与性能优化 打包速度慢
* none 无模式
*/
mode: 'development',
/**
* entry 输入文件路径,开始打包的文件路径
*
* __dirname:当前文件绝对路径
*/
entry: path.join(__dirname, './src/index.js'),
/**
* output 输出设定
*/
output: {
/**
* path 输出文件路径
*/
path: path.join(__dirname, './dist'),
/**
* filename 输出文件名称
*/
filename: 'main.js',
},
}

在package.json文件内

1
2
3
"scripts": {
"dev": "webpack"
}

使用webpack

npm run dev


webpack-dev-server

实时更新修改文件,从而能够随时观察编辑效果
实际搭建一个本地服务器,把实时改变的文件放在内存内,以加快访问速度

  1. 安装 npm i webpack-dev-server -D

  2. 修改 package.json 文件如下:

    1
    2
    3
    "scripts": {
    "dev": "webpack serve"
    }
  3. 可通过 npm run dev 编译

  4. 访问 https://localhost:8080

配置 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
devServer: {
/**
* 初次打包完成后自动打开浏览器
*/
open: true,
/**
* 指定host名
*/
// host: '127.0.0.1',
/**
* 指定端口
*/
// port: 8080,
},
}

html-webpack-plugin

编译时实时移动 html 文件至项目根目录
自动注入项目打包后的 js 文件和其他文件

  1. 安装 npm i html-webpack-plugin -D
  2. 配置 webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
// 导入 HtmlPlugin 包
const HtmlPlugin = require('html-webpack-plugin');

// 添加 plugins 设置
{
plugins: [
new HtmlPlugin({
template: './src/index.html',
filename: './index.html',
}),
],
}

loader

由于 webpack 仅可处理 js 及 json 文件
加载器:协助 webpack 处理特定的文件模块

  • css-loader 处理 css 文件
  • less-loader 处理 less 文件
  • babel-loader 处理高级 js 语法

CSS-loader

协助处理 css 文件

  1. 安装 npm i style-loader css-loader -D

  2. 配置 webpack.config.js 内的 module 选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    // 第三方模块的匹配规则
    module: {
    // 规则数组
    rules: [
    {
    // 正则表达式匹配的文件类型
    test: /\.css$/,
    // 使用的 loader
    // 特别的: loader 中的顺序不可交换,会按从后往前的顺序调用
    use: [
    'style-loader',
    'css-loader',
    ],
    }
    ],
    },
    }
  3. 在 index.js 中导入 css 文件
    import './css/index.css';

less-loader

协助处理 less 文件

  1. 安装 npm i less less-loader -D

  2. 配置 webpack.config.js 内的 module 选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    module: {
    rules: [
    {
    test: /\.less$/,
    use: [
    'style-loader',
    'css-loader',
    'less-loader',
    ],
    },
    ],
    },
    }
  3. 在 index.js 中导入 less 文件
    import './css/index.less';

url-loader&file-loader

协助处理图片
会把给定限制大小下的图片转为base64编码,否则仍使用url导入的方式

  1. 安装 npm i url-loader file-loader -D

  2. 配置 webpack.config.js 内的 module 选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    module: {
    rules: [
    {
    test: /\.jpg|png|gif$/,
    // limit 指定转换图片的阈值
    use: 'url-loader?limit=8192',
    },
    ],
    },
    }
  3. 在 index.js 中导入图片
    import logo from './images/icon.png';

  4. 给img标签赋值
    $('#img').attr('src', logo);

babel-loader

  1. 安装 npm i babel-loader @babel/core @babel/plugin-proposal-decorators -D

  2. 配置 webpack.config.js 内的 module 选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    module: {
    rules: [
    {
    test: /\.js$/,
    use: 'babel-loader',
    exclude: /node_modules/,
    },
    ],
    },
    }
  3. 配置 babel.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    module.exports = {
    plugins: [
    [
    '@babel/plugin-proposal-decorators',
    {
    legacy: true,
    },
    ],
    ],
    }

打包发布

package.json 配置

1
2
3
4
"scripts": {
// --mode production 指定发布时模式为 production
"build": "webpack --mode production"
}

优化js路径

1
2
3
4
output: {
path: path.join(__dirname, './dist'),
filename: 'js/main.js',
}

优化img路径

1
2
3
4
5
6
7
8
9
10
{
test: /\.jpg|png|gif$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
outputPath: 'images',
},
},
},

配置清理 dist 目录 clean-webpack-plugin

  1. 安装 npm i clean-webpack-plugin -D

  2. 导入与配置 webpack.config.js

    const {CleanWebpackPlugin} = require('clean-webpack-plugin');

    1
    2
    3
    4
    5
    6
    7
    plugins: [
    new HtmlPlugin({
    template: './src/index.html',
    filename: './index.html',
    }),
    new CleanWebpackPlugin(),
    ],

Source Map

信息文件,保存打包前代码的详细位置信息

webpack.config.js 添加 devTools: 'eval-source-map' 选项

建议发布时去除 source map 选项

只定位行号,不暴露源码:nosources-source-map 设定
定位行号且暴露源码:source-map 会生成一个独立文件

使用@导入文件

  • 配置
1
2
3
4
5
resolve: {
alias: {
'@': path.join(__dirname, './src/'),
},
},
  • 使用
    import msg from '@/msg.js';
react

react

React

React 概述

React 概念

React 用于构建用户界面的 JavaScript 库

MVC 中的 V {视图层}

React 特点

声明式
基于组件
应用范围广

react 基本使用

react 安装

npm i react react-dom

react 使用

1
2
<script type="text/javascript" src="./node_modules/react/umd/react.development.js"></script>
<script type="text/javascript" src="./node_modules/react-dom/umd/react-dom.development.js"></script>
1
2
const title = React.createElement('h1', null, 'Hello React');
ReactDOM.render(title, document.getElementById('root'));

React.createElement
返回 React 元素
第一个参数为元素名
第二个参数为元素属性
第三个及之后的参数为元素的子节点

ReactDOM.render
第一个参数为要渲染的 react 元素
第二个参数为将挂载的 DOM 对象

React 脚手架使用

npx create-react-app <my-app>

npx 在不安装脚手架包情况下使用脚手架包提供的命令

npm init react-app <my-app>
yarn create react-app <my-app>

导入 React

创建 React 元素

渲染 React 元素

JSX 概述

JSX 基本使用

比 createElement 简洁

在 JS 中书写 HTML 格式代码

1
2
3
const title = <h1>Hello JSX</h1>;
const root = document.getElementById(root);
ReactDOM.render(title, root);

JSX 注意点

React 元素属性名使用驼峰命名法

class 属性需替换为 className 属性
for 属性需替换为 htmlFor 属性

无子节点的元素可以以单标签形式写

建议使用小括号包裹 JSX

JSX 嵌入 JS 表达式

单大括号

1
2
3
const title = <h1>Hello JSX</h1>;
const root = document.getElementById(root);
ReactDOM.render(title, root);

允许使用任意的 JS 表达式
但使用对象可能会出现 bug
不能使用任意的 JS 语句

条件渲染

函数 - ifelse
函数 - 三元表达式
函数 - 逻辑与运算符

列表渲染

使用数组 map 方法实现

1
2
3
4
5
6
const array = [1, 2, 3, 4, 5]
const list = (
<ul>
{array.map(v => <li key={v}>{v}</li>)}
</ul>
)

样式处理

行内样式

<h1 style={{ color: red; }}></h1>

类名 className

React 组件介绍

使用 React 组件介绍
从而实现完整的页面功能

函数组件

使用函数创建组件

  • 函数名称必须以大写字母开头
  • 函数组件必须有返回值,表示组件内容 | 返回值为 null,为无内容

渲染组件,直接使用函数名为组件标签名

类组件

使用 class 类创建组件

  • 类名称必须以大写字母开头
  • 类组件需继承 React.component 父类,以使用父类提供的方法
  • 类组件需提供 render 方法,且应有返回值,表示组件将渲染的内容

独立文件

单独组件放在单个JS文件内

事件绑定

示例如 onClick={ () => {} }
采用驼峰命名法

事件对象

合成事件(包含原生事件)
兼容性好

组件分类

函数组件 - 无状态组件
类组件 - 有状态组件

状态 - 数据

组件状态是私有的

受控组件 - value 绑定 state 变量,监听 change 事件实时改变 state 变量

非受控组件 - |

组件高级

组件通讯

组件 props | 父组件=>子组件

可以传递各种数据,包括对象、函数乃至JSX等
props 只读

  • 父->子 props属性传数据
  • 子->父 props属性传回调方法
  • 兄弟<->兄弟 状态共享至公共父组件,由公共父组件负责管理

跨多个组件的通讯 context
Provider 传送数据
Customer 接纳数据

props.children 组件标签的子节点

props 校验

使用 prop-types

设置propTypes
支持 array bool func number object string bigint symbol
element node elementType
instanceOf
oneOf oneOfType
arrayOf objectOf
required
shape
设置defaultProps

组件生命周期

仅类组件有生命周期

  • 创建时
    • constructor 初始化state 绑定this
    • render 渲染UI
    • componentDidMount 发送网络请求 DOM操作
  • 更新时
    • render
    • componentDidUpdate
  • 卸载时
    • componentWillUnmount

组件复用

  • render props 模式

    将单独的state及state方法封装为单独的组件
    利用 prop 传递参数
    传递函数以函数返回值作为将渲染的 UI 内容(或使用 children)

  • 高阶组件 HOC

    函数
    接受需包装的组件,返回增强的组件
    (名称约定以 with 开头)
    组件配置 displayName 作为组件名称

路由

SPA 单页面应用,单HTML页面的web应用程序

路由 - 配对路径何组件

  1. 安装 react-router-dom
  2. 导入组件 import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
  3. Router 组件包裹整个应用
  4. Link 作为路由入口
  5. Route 配置路由规则和需要展示的组件|路由出口

Router 组件包裹整个应用,仅使用一次
包括 BrowserRouter 和 HashRouter

Link 组件作为导航链接
Route 组件指定路由展示组件相关信息

编程式导航
this.props.history.push
this.props.history.go

默认路由
path为 / 的路由

默认 route 遵从模糊匹配
设置 extra 遵从精确匹配

vue

vue

vue

一套构建用户界面的前端框架

  • 数据驱动视图
  • 双向数据绑定

MVVM:Model-View-ViewModel

Model 数据源

View DOM 结构

ViewModal Vue 实例

创建 Vue 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const app = new Vue({
// Vue实例挂载对象,接收一个选择器
// 若匹配多个DOM对象,取匹配到的第一个DOM对象
el: "#app",
// 普通属性
data: {
msg: "",
},
// 计算属性:带返回值的方法
computed: {},
// 监听器
watch: {},
// 普通方法
methods: {},
// 过滤器
filters: {},
});

指令结构

指令:参数:修饰符

v-on:submit.prevent=""

文本

“Mustache”语法 {{ }} v-text

<span>{{msg}}</span> 只能用于内容节点

<span v-text="msg"></span> 会覆盖内部文本

v-once 单次渲染

原始 HTML

  • v-html

<span v-html="msg"></span> 会覆盖内部内容

避免使用,可能会发生 XSS 攻击存在安全风险

绑定 attr

  • v-bind

<a v-bind:href='url'></a>

简写 <a :href='url'></a>

计算属性

computed 对象

属性需为一个函数,并返回相应的值

使用时同正常的 data 属性即可

1
2
3
4
5
computed: {
val1: function () {
return `the number is ${this.val0} in the input box`;
}
}

其是依赖于响应式依赖进行缓存的,仅在其相应式依赖改变时才重新计算,因而可以提高性能

可以添加 setter 属性,默认只针对 getter 属性,这样可以同时更新 data 中数据

1
2
3
4
5
6
7
8
9
10
computed: {
val1: {
get: function () {
return `the number is ${this.val0} in the input box`;
},
set: function (val_1) {
this.val0 = val_1;
}
}
}

侦听属性

watch 对象

1
2
3
4
5
6
7
8
9
10
11
watch: {
// 方法形式
val0: function (newData, oldData) {
},
// 对象形式
val1: {
handler (newVal, oldVal) {},
immediate: true,
},
deep: true,
},

实质是一个函数,函数名是监听的变量名

  • immediate 选项

立即以表达式当前值触发回调

  • deep 选项

用于监听对象内部值的变化

若侦听特殊格式变量,可包裹单引号 如’info.id’ () {}

计算属性与侦听属性区别,计算属性根据其响应式属性的改变而改变,侦听属性根据其监视的属性的改变而修改相应的属性

绑定事件

  • v-on

<a v-on:click='handle_click'></a>

简写 <a @click='handle_click'></a>

  • 动态事件

<a @[event]='handle_click'></a>

  • 传参

<a @click='handle_click(1)'></a>

  • 传原生 DOM 事件
    $event

<a @click='handle_click($event)'></a>

实际方法无参时可以不用该方法,但有参数时必须使用该方法

  • 事件修饰符

  • prevent 阻止事件默认行为

  • stop 阻止事件传播

  • once 使事件仅触发一次

  • self 仅在触发事件的元素为绑定的元素本身时起效

  • capture 捕获事件模式:比内部元素绑定事件处理方法优先处理

  • passive 预先完成事件的默认行为而不是等待事件处理方法完成

修饰符可以串联使用

鼠标修饰符

  • left
  • right
  • middle

分别仅在点击鼠标相应侧按键时起效

按键修饰符

  • 可以指定为相应的键码才起效

.enter .tab .delete .esc .space .up .down .left .right

系统修饰符

  • exact 控制仅为给定的系统修饰符触发的事件
  • ctrl alt shift meta 控制需按下对应的按键才可触发事件

绑定表单

v-model

表单 <input><textarea><select> 元素有效

会忽略所有表单元素的 valuecheckedselected attribute 的初始值,故需在 data 中声明数据的初始值

  • text 和 textarea 元素使用 value property 和 input 事件
  • checkbox 和 radio 使用 checked property 和 change 事件
  • select 字段将 value 作为 prop 并将 change 作为事件

修饰符

  • number 自动转为数值类型
  • trim 自动过滤字符串两端空格符
  • lazy 改为在 change 事件后同步而不是默认的 input 事件后同步,即输入时失去或获得焦点才会触发事件

条件绑定

  • v-if truthy 值显示,falsy 值不显示

实质惰性渲染

  • v-show truthy 值显示,falsy 值不显示

实质仍渲染,通过 display 属性控制

若经常切换显示与否用 v-show,否则用 v-if;可以大幅提高性能

1
2
3
<div v-if="val === 0">0</div>
<div v-else-if="val === 1">1</div>
<div v-else>other</div>
  • :key 可以用于强制区别元素对象

循环绑定

  • v-for

  • :key 建议绑定 key 属性,并使用独一无二的值作为 key 的值 一般需为字符串或数字类型

1
<div v-for="(item, index) in array">{{index + ' : ' + item}}</div>

过滤器

filters

常用于实现文本格式化

可以用于插值表达式或 v-bind 属性

  • 语法

{{ message | capitalize }}

v-bind:id="rawId | formatId"

前一个是定义的变量,后一个是定义的过滤器方法,接收唯一一个参数为变量

  • 定义过滤器方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 局部过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}

// 全局过滤器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
  • 其他玩法

过滤器串联 {{ message | filterA | filterB }}

过滤器接收参数 {{ message | filterA('arg1', arg2) }}

默认 message 是第一个参数,’arg1’是第二个参数,arg2 是第三个参数 da

SPA 单页面应用程序 & vue-cli

vue-cli 用于简化使用 webpack 创建 vue 项目的过程

  • 创建 vue 项目
    vue create PROJECT_NAME

  • vue 项目目录

  1. src:
    1. main.js:项目入口脚本文件
    2. App.vue:项目根组件
    3. assets:项目静态资源 如 css 样式表、图片资源
    4. components:组件
  2. public:
    1. favicon.ico:项目网页图标
    2. index.html:项目页面文件
  • vue 项目运行流程

通过 main.js 把 App.vue 渲染到 index.html 的指定位置

导入 vue
import Vue from 'vue';

导入 app 模板
import App from './App.vue';

1
2
3
4
5
// 添加 Vue 实例
new Vue({
// 渲染 render 函数指定的组件
render: (h) => h(App),
});

使用 $mount() 代替 el 属性绑定 DOM 对象

组件

使用 vue 后缀名文件定义独立组件

  1. 注册组件

    全局:建议注册于入口文件 main.js

1
2
3
Vue.component("my-component-name", {
// ... options ...
});

局部:注册于预期使用的区域

1
2
3
4
5
6
7
new Vue({
components: {
"com-1": {
/* options */
},
},
});
  1. 独立组件文件

    • template 组件模板结构
    • script 组件 js 行为
    • style 组件样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div></div>
</template>
<script>
export default = {
name: 'my-component-name',
data: function () {
return {};
},
watch: {},
computed: {},
filters: {},
// props: ['my-prop-name'],
props: {
'my-prop-name' : {
default: 'default-value',
},
},
};
</script>
<style></style>
  1. 注意点

    • 组件应拥有一个根元素
    • 组件内部 data 必须是返回包含数据的对象的函数
    • 指定 style 的类型 <style lang="less">
    • 组件封装后是相互独立的,使用时具有父子关系
  2. 导入 vue 文件方法

1
2
3
4
5
6
7
8
import Test from "./components/test.vue";
export default {
name: "App",
// 声明使用Test组件
components: {
Test,
},
};
  1. 组件属性 props

    大多数属性同 Vue 实例对象

    • name: String 组件名称
    • props: Array | Object 组件自定义属性
      可以直接在方法中使用,只读,可以绑定 v-bind 以动态修改自定义属性的值
      • default 规定属性的默认值
      • type 规定属性的类型
        String Number Boolean Array Object Date Function Symbol 一个自定义的构造函数
      • required 规定属性是否为必须的
  2. 样式冲突问题

    组件内的样式全局生效,可能会影响到整个 index 页面
    利用 scoped 设定 <style scoped>
    原理是通过自定义 HTML 属性结合 CSS 属性选择器实现
    /deep/ div 仅修改子组件的样式

组件生命周期

  • 生命周期:组件的运行阶段
  • 生命周期函数:Vue 的内置函数,按组件生命周期自动按次序执行
  1. 组件创建状态

    • new Vue()
    • init events & lifecycle:初始化事件与生命周期函数
    • beforeCreate:尚未初始化数据方法
    • init injections & reactivity:初始化 props、data、methods
    • created:尚未形成实例模板结构【常用】
    • 基于数据和模板,在内存中编译生成 HTML 结构
    • beforeMount:即将编译好的 HTML 结构渲染至浏览器中
    • 用内存中编译完成的 HTML 结构替换掉 el 属性指定的 DOM 元素
    • mounted:已将编译好的 HTML 结构渲染至浏览器中,DOM 树已存在当前组件【常用】
  2. 组件运行状态

    • beforeUpdate:即将根据变化后最新的数据重新渲染组件的模板结构
    • 根据变化后最新的数据重新渲染组件的模板结构
    • updated:已将根据变化后最新的数据重新渲染组件的模板结构【常用】
  3. 组件销毁阶段

    • beforeDestroy:即将销毁组件
    • destroyed:已将组件销毁

生命周期

组件数据共享

  • 父->子 自定义属性 props

  • 子->父 自定义事件 $emit()

  • 兄弟<->兄弟 EventBus

利用 vue 实例中转,使用 bus.$emit() 触发自定义事件,使用bus.$on() 注册自定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// son
export default {
data() {
return {
i = 0;
};
},
methods: {
a() {
this.i++;
this.$emit('e', this.i);
},
},
};
// fa
// <Son @e='f'></Son>
export default {
data() {
return {
j = 0;
};
},
methods: {
f(v) {
this.j = v;
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// send data
import bus from './eventBus.js';
export default {
methods: {
send_msg() {
bus.$emit('share', /* data */);
},
},
}
// receive data
import bus from './eventBus.js';
export default {
created() {
bus.$on('share', (/* data */) => {
/* do something with data */
});
},
}
// eventBus.js
import Vue from 'vue';
export default new Vue();

操作 DOM 元素

ref 引用
默认 vue 组件实例包含 $refs 属性,包含组件内所有的设置 ref 属性的 DOM 元素引用,其为一对象,键名是 ref 的值,键值即为相应的 DOM 对象

1
2
3
4
/* <component ref="name"></component> */
fun() {
// using this.$refs.name
}

this.$nextTick() 将回调函数推迟至下一个 DOM 更新周期后执行

动态组件

利用<component></component>实现动态组件渲染

1
<component is="Left"></component> <component :is="comType"></component>
  • is 属性指定预期渲染的组件的名称,可以绑定变量实现动态切换使用的组件
  • 可以用<keep-alive>包裹组件,使之能在失活时缓存,从而可以高效重复使用组件
    • mount:
      • activated() 钩子 组件被激活
      • deactivated() 钩子 组件被缓存
    • props:
      • include 仅名称匹配的组件被缓存
      • exclude 任意名称匹配的组件不被缓存
      • 注意 include 与 exclude 不应同时使用

插槽

普通插槽

将组件内部在使用组件时插入内容时提供占位符

1
2
3
4
5
6
7
8
<!-- 组件 -->
<template>
<div>
<slot></slot>
</div>
</template>
<!-- 文件 -->
<Com> g </Com>

使用 <slot></slot> 声明一个插槽区域

内容使用 v-slot: 指令,且应用在 template 标签上,内部放置填充的内容
v-slot:default 可简写为 #default

编译时,插槽内使用的数据,父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的

后备内容

可以在组件内的插槽的 slot 标签内放入内容,指定若未给出应插入内容时的渲染内容

1
2
3
4
5
6
7
8
<!-- 组件 -->
<template>
<div>
<slot>default value</slot>
</div>
</template>
<!-- 文件 -->
<Com> given value </Com>

具名插槽

具有多个插槽的组件,需要指定插槽的名称,才可以正确地插入部件

通过指定 name 属性实现对插槽进行区别, name 属性默认值为 default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 组件 -->
<template>
<div>
<p>t</p>
<slot name="s1"></slot>
<slot name="s2"></slot>
</div>
</template>
<!-- 文件 -->
<Com>
<template v-slot:s1>
<p>s1</p>
</template>
<template v-slot:s2>
<p>s2</p>
</template>
</Com>

可以在组件内指定插槽的默认内容

作用域插槽

通过插槽自子元素组件向父元素传参

1
2
3
4
5
6
7
8
9
10
<!-- 子组件 -->
<span>
<slot v-bind:user="user"> {{ user.lastName }} </slot>
</span>
<!-- 父组件 -->
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>

自定义指令

  • 私有自定义指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 使用
<div v-color="color">color</div>;
export default {
// 私有自定义指令声明在该节点之下np
directives: {
color: {
// 首次绑定时调用
bind(el, binding) {
el.style.color = binding.value;
},
// DOM 更新时调用
update(el, binding) {
el.style.color = binding.value;
},
},
/**
* 若binding与update相同时可简写为
* color(el, binding) {},
*/
},
};
  • 全局自定义指令
1
2
3
4
5
Vue.directive("colo", {
bind() {},
update() {},
});
Vue.directive("colo", function () {});
  • 钩子函数

    • bind 指令绑定到元素时调用
    • inserted 被绑定元素被插入父节点时调用
    • update 组件的 VNode 更新时调用
    • componentUpdated 组件的 VNode 及其子 VNode 全部更新时调用
    • unbind 指令自元素解绑时调用
  • 钩子函数参数

    • el 指令绑定的元素
    • binding 指令的一些属性

路由

hash 地址与组件之间的对应关系

初始化配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建实例对象
// @/src/router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const router = new VueRouter();
export default router;
// 导入 App.vue
// @/App.vue
import router from "@/router"; // 相当于 import router from '@/router/index.js';
new Vue({
router,
/* ... */
}).$mount("#app");

模块化导入,给定文件夹,默认导入文件夹下的 index.js 文件

基本路由

规定占位符与切换组件

  • <router-view></router-view> 占位符,显示组件
  • <router-link to="/about">关于</router-link>路由的切换

定义路由关系

1
2
3
4
5
6
7
8
9
10
11
12
// 导入组件
import Home from "@/components/Home.vue";
// 声明路由关系
const router = new VueRouter({
routes: [
{
path: "/home",
component: Home,
},
// ...
],
});

路由重定向

1
2
3
4
5
6
7
8
9
const router = new VueRouter({
routes: [
{
path: "/",
redirect: "/home",
},
// ...
],
});

动态路由

使用动态参数实现相同组件不同路由

<router-link to="/about/${id}">关于</router-link>

1
2
3
4
5
6
7
8
9
10
const router = new VueRouter({
routes: [
{
path: "/movie/:id",
component: Movie,
props: true, // 配置利用 props 获取参
},
// ...
],
});

组件内部获取参数 {} = this.$route.params

props 方式:直接在 Vue 实例中的 props 中获取参数

  • 路径参数 this.$route.params
  • 查询参数 this.$route.query

path:仅路径部分 & fullPath:完全参数

嵌套路由

路由内部的路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const router = new VueRouter({
routes: [
{
path: "/about",
component: Home,
/**
* ! 重定向
* redirect: '/about/tab1',
*/
children: [
{
path: "tab1",
component: Tab1,
},
{
/**
* ! 默认子路由
*/
path: "",
component: Tab1,
},
],
// ...
},
],
});

声明式导航 & 编程式导航

  • 声明式:点击链接
    • <a> <route-link>
  • 编程式:调用 API 跳转
    • location
    • this.$route.push(${url}) 跳转至指定的地址,并增加一条历史记录
    • this.$route.replace(${url}) 跳转至指定的地址,并替换当前的历史记录
    • this.$route.go(${number}) 在浏览器记录中进行前进 or 后退
    • this.$route.push()
    • this.$route.back()

导航守卫

控制路由的访问权限

全局前置守卫

在所有路由触发时起效

1
router.beforeEach((to, from, next) => {});

接收一个回调函数参数
回调函数第一个参数代表将访问的对象,第二个参数代表将离开的对象,第三个代表调用允许的路由导航

调用 next()方法

  • next() 允许跳转
  • next(url) 强制跳转至指定的页面
  • next(false) 不允许跳转

eslint

检查代码的风格的一款插件

.eslintrc.js eslint的配置文件

  • quotes
  • key-spacing
  • comma-dangle
  • no-multiple-empty-lines
  • no-trailing-spaces
  • eol-last
  • spaced-comment
  • indent
  • space-before-function-paren

Vue3

typescript

typescript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

/** TS变量类型 */
let an: any;
let str: string = '12';
let num: number = 20;
let flag: boolean = true;

let arr: number[] = [1];
let ar: Array<number> = [2];
let tuple: [string, number] = ['test', 10];

enum Color {
Red,
Green,
Blue,
};
let co: Color = Color.Red;

function hello(): void {
console.log('hello');
}

let nu: null;
let un: undefined;
let ne: never;

/** 类型断言 */
var num0: number = <number> <any> str;

/** 类型推断 */

/**
* 变量作用域
* - 全局作用域
* - 类作用域
* - 局部作用域
*/

/** 运算符 */

/** 条件 */

/** 循环 */

/** 函数 */
// 可选参数 默认参数 剩余参数
function add(x: number = 0, y: number = 0, z?: number/* 可选参数: 必须在参数列表最后 */, ...other: number[]): number {
return x + y + (z ?? 0) + add(...other);
}
const sub = (x: number, y: number): number => x - y
const add_plus: (x: number, y:number) => number = (x, y) => x + y;

// 匿名函数 自动执行函数 递归函数 箭头函数
var res = function(a: number, b: number) {
return a * b;
};
(() => console.log('Hello!'))();

// 函数重载

/** 字面量类型 */
// 使用具体值作为类型
let fu: '0' | '1' | '2' | '3' = '1';
const fv = 1;

/** Number String Boolean 包装类型 */

/** Array 数组 元组 Map */
// 数组
let arr1: number[] = [1, 2];
let arr2: Array<number> = [3, 4];
// 元组 Tuple
let position: [number, string, boolean] = [1, '2', true];
// Map
const m: Map<number, number> = new Map();

/** 联合类型 */
var union: number | number[];
union = 12;
union = [12, 34];

/** 枚举类型 */
enum Direction {
Up,
Down,
Left,
Right,
};
// 枚举成员值默认是自第一个值(默认为0)开始的数值,即默认为数字枚举
var dir: Direction = Direction.Up;
enum Direction1 {
Left = 10,
Right,
};
// 字符串枚举必须有初始值
enum Direction2 {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right',
};

/** typeof */
var cc: typeof position;

/** 接口 interface */
// 描述一个对象类型
// 当然也可以使用 type 关键字声明
interface Person {
name: string,
age: number,
birth?: Date,
sayHi: string | string[] | (() => string),
}
var csy: Person = {
name: 'CSY',
age: 20,
birth: new Date(),
sayHi: (): string => 'Hi',
}
const ccc: {
name: string,
sex?: boolean,
} = {
name: 'ccc',
};
// 接口的继承
interface Human extends Person {
father: Person,
mother: Person
}

/** 类型推论 */
// 自动推断变量类型
// 1. 声明变量并初始化
// 2. 决定函数返回值
let c = 20;
function f(a: number, b:number) {
return a + b;
}
/** 类型断言 */
// (可类型推论变量类型)自行指定变量的类型
const alink1 = document.getElementById('link') as HTMLAnchorElement;
const alink2 = <HTMLAnchorElement>document.getElementById('link');

/**
* 类 对象
* - 构造函数
* - 实例属性及实例方法
* - 访问控制修饰符 public protected private
* - 继承 extends 类 | implements 接口
* - 只读 readonly (仅适用方法)
*/
abstract class Animal {}
class Human extends Animal implements Person {
public a: string;
protected b: number;
private c: boolean;

name = 'CSY';
age = 40;
birth = new Date();
sayHi = () => 'Hi';

readonly d: String;

constructor (v: boolean) {
super();
this.c = v;
};

static isHuman = (o: any) => typeof o === 'object' && o instanceof Human;

get e () {
return this.a + this.a
}
set e (s) {
this.a = s.toLowerCase();
}
}
const son: Human = new Human(true);

/**
* 类型兼容 不同名称相同结构的类型是等价的
*
* - 类 | 若A类型内容包含B类型内容(非严格包含),则A类型变量可赋值给B类型变量
* - 接口 | 若A类型内容包含B类型内容(非严格包含),则A类型变量可赋值给B类型变量
* 类与接口亦可相互兼容
* - 函数 | 若B函数参数表包含A函数参数表(非严格包含),则A类型函数可赋值给B类型函数;相同位置参数需相同或兼容(对象多数服从少数);返回值需相同或兼容(对象少数服从多数)
*/

/**
* 交叉类型(类似接口继承)
* 将多个类型组合为同一个类型
* 重复的属性会合并为联合类型,相当于重载
*/
interface Co1 {
a: number,
}
interface Co2 {
b: string,
}
type Co = Co1 & Co2;
const co0: Co = {
a: 12,
b: '',
};

/** 泛型 */
// 泛型方法
function print <T> (v: T): void {
console.log(v);
}
// T 相当于类型变量
// 具体类型需用户使用时指定
print<number>(10)
print<string | boolean>('')
// 某些情况下可自动类型推定
print(1)
// 类型约束 结合interface使用 extends
function print0 <T extends Array<string> | string[]> (v: T): void {
console.log(v);
}
function print1 <T, K extends keyof T> (v: T, k: K): void {
console.log(v[k]);
}
// keyof 关键字接受对象类型并生成键名称(字符串和数字)的联合类型

// 泛型接口
interface PrintInterface <T> {
do: (v: T) => void
}

// 泛型类
class PrintClass <T> {
value: T;
}

// 泛型工具类
// Partial<T> 创建一个类型且T中所有属性均可选
type partial = Partial<Person>
// Readonly<T> 创建一个类型且T中所有属性均只读
type readonly = Readonly<Person>
// Pick<T, K extends keyof T> 创建一个类型并从给定类型中选出一组属性
type pick = Pick<Person, 'name' | 'age'>
// Record<K extends keyof any, T> 构造一个对象类型,属性键为keys,属性类型为Type
type record = Record<'a' | 'b', string>

// 索引签名类型
interface Obj {
[K: string]: number,
}
// [K: string] 表示任意string类型属性名称均可作为对象出现,且属性值为number类型变量

// 映射类型 in 关键字和 keyof 关键字
type p = {
[K in 'x' | 'y' | 'z']: number
}
type q = {
[K in keyof Person]: string
}

// 索引查询类型
type props = { a: number };
type typeA = props['a'];

/** 命名空间(可嵌套) */
namespace n {
export interface Person {};

namespace nn {}
}
var d: n.Person = {};

// 单独引用ts文件
/// <reference path="SomeFileName.ts" />

/** 模块 */

/** 声明 */
declare var jQuery: (selector: string) => any;

markdown

markdown

标题

1
2
3
4
5
6
7
8
9
10
11
12
13

# 一级标题

## 二级标题

### 三级标题

#### 四级标题

##### 五级标题

###### 六级标题

使用 # 号可表示 1-6 级标题,一级标题对应一个 # 号,二级标题对应两个 # 号,以此类推

段落

Markdown 段落没有特殊的格式,直接编写文字就好,段落的换行是使用两个以上空格加上回车

当然也可以在段落后面使用一个空行来表示重新开始一个段落

粗体斜体

1
2
3
4
5
6
7
8
9
10
11
12
13

*斜体*

_斜体_

**粗体**

__粗体__

***斜粗体***

___斜粗体___

分割线删除线

1
2
3
4
5
6
7
8
9
10
11
12
13

---------------------------------------------------------

---

-----

___

---

---

你可以在一行中用三个以上的星号、减号、底线来建立一个分隔线,行内不能有其他东西

1
2
3

~~删除线~~

如果段落上的文字要添加删除线,只需要在文字的两端加上两个波浪线~~即可

1
2
3

<u>下划线</u>

下划线可以通过 HTML 的 <u> 标签来实现

脚注

1
2
3

脚注[^脚注类似这样]

脚注是对文本的补充说明

1
2
3

[^脚注类似这样]: 对对对

列表

1
2
3
4

* 无序列表
* and

或者

1
2
3
4

+ s
+ d

或者

1
2
3
4

- s
- d

无序列表使用星号(*)、加号(+)或是减号(-)作为列表标记,这些标记后面要添加一个空格,然后再填写内容

1
2
3
4
5

1. d
2. e
3. r

有序列表使用数字并加上 . 号来表示

列表嵌套只需在子列表中的选项前面添加四个空格即可

1
2
3
4
5
6
7

1. 1
- ss
- ss
- e
2. w

引用

1
2
3
4
5
6
7
8
9
10
11
12
13

> def
>
> end
>
> > d
> >
> > e
> >
> > > get
> > >
> > > end

Markdown 区块引用是在段落开头使用 > 符号 ,然后后面紧跟一个空格符号

区块是可以嵌套的,一个 > 符号是最外层,两个 > 符号是第一层嵌套,以此类推

区块中使用列表,正常使用即可

列表中使用区块,需要在 > 前添加四个空格的缩进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


* 第一项

> d
> 菜鸟教程
> 学的不仅是技术更是梦想

* 第二项

* >


* > d
>
> dd

* d

代码

段落上的一个函数或片段的代码可以用反引号把它包起来

1
2
3

`printf()`

代码区块使用 ``` 包裹一段代码,并指定一种语言(也可以不指定)符

1
2
3

```javascript
document.getElementByTagName("video")[0].click()
1
2
3
4
5
6
7
8
9
10
11

# 链接

```markdown

[链接](https://www.runoob.com)

<https://www.runoob.com>

[1]:https://www.runoob.com

可以通过变量来设置一个链接,变量赋值在文档末尾进行[IT][1]

图片

1
2
3

![Oh](https://pics6.baidu.com/feed/72f082025aafa40f99cb5d63e6222f4679f019ca.jpeg?token=6d0ab25f666cf492c9ff83d037cb74e0)

表格

1
2
3
4
5
6
7
8
9

| b1 | b2 | b3 | b4 |
| ---- | ---- | ---- | ---- |
| d1 | d2 | d3 | d4 |

| b1 | b2 | b3 | b4 |
| :---- | ----: | :----: | ---- |
| d1 | d2 | d3 | d4 |

  
network

network

计算机网络

通信:互联网

  • 主体:服务器、客户端浏览器
  • 内容:内容
  • 方式:响应方式

通信协议:通信遵循的规则约定|通信格式

超文本:网页内容

超文本传输协议:网页内容传输协议 HTTP协议客户端与服务器间进行网页内容传输时需遵守的传输格式 提交&响应

HTTP 请求

HTTP协议模型:请求/响应 交互模型

HTTP 请求消息

HTTP 请求报文

  • 请求行:以空格分隔

    • 请求方式:GET、POST等
    • URL:域名之后的url部分
    • 协议版本
  • 请求头部:以回车符&换行符分隔,以键值对组成,键值对以冒号分隔

    • Host:服务器域名

    • User-Agent:说明客户端的浏览器类型

    • Connection:客户端与服务器的连接方式

    • Content-Type:发送到服务器的数据格式

    • Content-Length:请求体大小

    • Accept:客户端可接受的返回数据类型

    • Accept-Language:客户端可接收的语言文本类型

    • Accept-Encoding:客户端可接受的内容压缩编码形式

  • 空行:回车符or换行符

  • 请求体:POST有、GET无

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.81 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: BIDUPSID=028E86627DA4BB24A78F13047AE65439; PSTM=1628179856; BD_UPN=12314753; __yjs_duid=1_0275df3f717d1e5149c8d0bc2305f82d1628213742697; BAIDUID=D5D732CC5CBECD9277FA4AF82CF82B37:FG=1; BDUSS=JzeWVqZmpKdC1kRmFDN2VKRDl4R3d5YUR5WnJzVjV6dWRsdE5tSlotUXdNZGxoRVFBQUFBJCQAAAAAAQAAAAEAAACSPpQhzOy~1bXE1MYxMDAwMDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCksWEwpLFhQ3; BDUSS_BFESS=JzeWVqZmpKdC1kRmFDN2VKRDl4R3d5YUR5WnJzVjV6dWRsdE5tSlotUXdNZGxoRVFBQUFBJCQAAAAAAQAAAAEAAACSPpQhzOy~1bXE1MYxMDAwMDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCksWEwpLFhQ3; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BAIDUID_BFESS=D5D732CC5CBECD9277FA4AF82CF82B37:FG=1; BD_HOME=1; H_PS_PSSID=35834_35106_31253_35765_35489_34584_35491_35871_35246_35905_35796_35316_26350_35884_35724_35879_35746; sug=3; sugstore=0; ORIGIN=0; bdime=0; BA_HECTOR=8004al2520258h0lb41h0s1c90q

HTTP 响应消息:HTTP 响应报文

  • 状态行
    • 协议版本
    • 状态码:200等
    • 状态码描述
  • 响应头部
  • 空行
  • 响应体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HTTP/1.1 200 OK
Bdpagetype: 2
Bdqid: 0xbde875de000166a1
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Thu, 17 Feb 2022 08:52:39 GMT
Expires: Thu, 17 Feb 2022 08:52:39 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=302; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=35834_35106_31253_35765_35489_34584_35491_35871_35246_35905_35796_35316_26350_35884_35724_35879_35746; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1645087959064662324213684317064108533409
X-Frame-Options: sameorigin
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

请求方法

  • GET 查询 - 获取服务器资源

请求数据放在数据头内

  • POST 新增 - 提交资源

请求数据放在请求体内

  • PUT 修改 - 提交新资源并替换
  • DELETE 删除 - 移除资源

请求删除指定的资源

响应状态码

用于标识响应状态

三十进制:字,first类型,then细分

  • 1** 信息:收到请并需继续操作(少见)
  • 2** 成功:请求成功接收并处理
    • 200 OK 请求成功
    • 201 Created 已创建
  • 3** 重定向:需进一步操作以完成请求
    • 301 Moved Permanently 永久移动 使用新URL
    • 302 Found 临时移动 仍使用原有URL
    • 304 Not Modified 未修改 会直接访问缓存内资源
  • 4** 客户端错误:请求包含语法错误或无法完成请求
    • 400 Bad Request 语义有误or请求参数有误
    • 401 Unauthorized 需用户验证
    • 403 Forbidden 拒绝执行请求
    • 404 Not Found 无法找到资源
    • 408 Request Timeout 请求超时
  • 5** 服务器错误:服务器处理请求出错
    • 500 Internal Server Error 服务器内部错误,无法完成请求
    • 501 Not Implemented 不支持请求方法
    • 503 Service Unavailable 服务器暂时无法处理:超载or系统维护

跨域与 JSONP

同源策略

同源:两页面协议、域名、端口相同

浏览器网页默认端口 80

同源策略:浏览器安全功能-限制来源同一个加载文档脚本与其他源资源的交互

  • 阻止读取非同源网页的 cookie、localstorage 与 indexedDB
  • 阻止接触非同源网页的 DOM
  • 阻止向非同源地址发送 Ajax 请求

跨域:非同源

浏览器会拦截跨域请求回来的数据

JSONP

JSON 的一种使用模式,实现跨域数据访问

由于 script 标签允许请求非同源的 js 脚本

利用 script 标签请求,并传递一个回调函数名作为参数;服务器端把数据作为回调函数参数包装后返回;浏览器自动执行回调函数代码,即实现数据渲染

JSONP:兼容性好、只支持 GET 请求

CORS:支持 GET 与 POST,不兼容低版本浏览器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="divCustomers"></div>
<script type="text/javascript">
function callbackFunction(result, methodName) {
var html = "<ul>";
for (var i = 0; i < result.length; i++) {
html += "<li>" + result[i] + "</li>";
}
html += "</ul>";
document.getElementById("divCustomers").innerHTML = html;
}
</script>
<script
type="text/javascript"
src="https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction"
></script>

jQuery 实现用户端

采用动态创建与移除 script 标签:向 head 内添加 script 标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div id="divCustomers"></div>
<script>
$.getJSON(
"https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=?",
function (data) {
var html = "<ul>";
for (var i = 0; i < data.length; i++) {
html += "<li>" + data[i] + "</li>";
}
html += "</ul>";

$("#divCustomers").html(html);
}
);
</script>

<script>
$.ajax({
url: 'https:www.sky.icu',
dataType: 'jsonp', // 必需
success: (res) => {
console.log(e);
},
jsonp: 'callback' // 发送至服务器参数名称:可选
jsonpCallback: 'jQueryxxx', // 自定义回调函数名称:可选
})
</script>
<!-- 会自动带一个jQueryXXXX的回调函数名称 -->

服务器端

1
2
3
4
5
6
7
8
9
<?php
header('Content-type: application/json');
//获取回调函数名
$jsoncallback = htmlspecialchars($_REQUEST ['jsoncallback']);
//json数据
$json_data = '["customername1","customername2"]';
//输出jsonp格式的数据
echo $jsoncallback . "(" . $json_data . ")";
?>

节流

节流,就是指连续触发事件但是在 n 秒中只执行一次函数

减少事件触发频率

  • 使得鼠标事件仅在单位时间内触发一次
  • 降低监测滚动条位置的频率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 时间戳
function throttle(func, delay) {
let prev = 0; // 上次触发时间
return function (...args) {
const now = Date.now();
if (now - prev > delay) {
last = now;
func.apply(this, args);
}
};
}

// 定时器
function throttle(func, delay) {
let timeout = null;
return function (...args) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(this, args);
});
}
};
}

防抖

防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

常用于搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 非立即执行
// 触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
function debounce(func, delay) {
let timer = null; // 上次调用指向的计时器
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}

// 立即执行
// 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果
function debounce(func, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeOut(timer);
const cal = !timer;
timer = setTimeOut(() => {
timer = null;
}, delay);
if (cal) func.apply(this, args);
};
}
  
ajax

ajax

AJAX

AJAX

Ajax 简介

AJAX = Asynchronous JavaScript And XML

  1. 网页中发生一个事件(页面加载、按钮点击)
  2. 由 JavaScript 创建 XMLHttpRequest 对象
  3. XMLHttpRequest 对象向 web 服务器发送请求
  4. 服务器处理该请求
  5. 服务器将响应发送回网页
  6. 由 JavaScript 读取响应
  7. 由 JavaScript 执行正确的动作(比如更新页面)

Ajax 使用

XMLHttpRequest 对象用于同后台服务器交换数据

1
2
3
4
5
6
7
8
9
10
11
12
13
let request = new XMLHttpRequest();

request.open('POST', 'https://www.baidu.com', true);

request.setRequestHeader('Content-Type', 'application/json');

request.onreadystatechange = function() {
if(this.readyState === 4 && this.status === 200) {
console.log(this.responseText)
}
}

request.send({});
  • readyState 请求状态
    • 0:已创建对象未调用open方法
    • 1:已调用open方法
    • 2:已调用send方法已接收响应头
    • 3:数据接收中
    • 4:请求完成,数据接收完成或失败
  • status 服务器响应状态
  • responseText 请求返回的数据

请求数据类型 Content-Type

  • application/x-www-form-urlencoded

url 末尾加, ? 接 = 连接的键值对, 以 & 分隔多个参数

https://www.baidu.com?id=1&name=Lily

中文字符等会进行 URL 编码

使用 decodeURL() 编码,encodeURL() 解码

Ajax 默认请求数据类型

  • application/json

json 数据类型

  • multipart/form-data

常用于上传文件

Ajax 新特性

  • 设置请求时限
1
2
3
4
5
6
// 请求时限
request.timeout = 3000;
// 超时回调函数
request.ontimeout = (e) => {
console.log(e);
}
  • 使用 FormData 对象管理表单
1
2
let data = new FormData();
data.append('key', value);
  • 上传文件
1
2
3
4
5
6
7
8
// 获取文件
let files = document.querySelector('input[type=file]').files;
// 检测文件是否已选中
if(files.length <= 0)
return alert('ERROR');
// 创建 FormData 实例
let data = new FormData();
data.append('file', files[0]);
  • 获取数据传输进度信息
1
2
3
4
5
6
7
8
request.upload.onprogress = function (e) {
// lengthComputable 表示上传的资源是否具有可计算的长度
if(e.lengthComputable) {
// loaded 已传输的子节
// total 需传输的总子节
let percentComplete = Math.ceil((e.loaded / e.total) * 100);
}
}

jQuery 的 Ajax

  • $.ajax() 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$('#button').on('click', function () {

const files = $('#file')[0].files;
if(files.length <= 0) {
return;
}

const data = new formData();
data.append('file', files[0]);

$.ajax({
method: 'POST',
url: 'https://www.baidu.com',
data: data,
// 内容编码类型
// 默认值: "application/x-www-form-urlencoded"
contentType: false,
// 是否进行url编码
// 默认值: true
processData: false,
success: function (res) {
console.log(res);
},
});

});
  • $(document).ajaxStart() 方法

在 Ajax 请求发送前执行函数

1
2
3
$(document).ajaxStart(function () {
$('#loading').show();
});
  • $(document).ajaxStop() 方法

在 Ajax 请求结束执行函数

axios

专注于网络数据请求的库

目前最主流的

官方网站

  • axios.get & axios.post
1
2
3
4
5
6
7
8
9
10
11
12
axios.get(url, params)
.then(function (res) {
// 处理成功情况
console.log(res);
})
.catch(function (err) {
// 处理错误情况
console.log(err);
})
.then(function () {
// 总是会执行
});

axios.get(url[, config])

axios.post(url[, data[, config]])

  • axios({})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// promise 语法
axios({
url: '',
method: '',
params: {}, // GET 数据:url参数
data: {}, // POST 数据:默认json参数对象
}).then(res => {
// do something with res.data
});

// async-await 语法
const {data} = await axios({
url: '',
method: '',
params: {},
data: {},
});
// do something with data

:D 一言句子获取中...