Skip to content

Commit

Permalink
-
Browse files Browse the repository at this point in the history
  • Loading branch information
tingyuan committed May 15, 2024
1 parent 9181fe2 commit d5f7e54
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 95 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ test.js

dev-dist
hint-report

a.js
2 changes: 1 addition & 1 deletion blogs/algorithm/二分查找.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

但是如果增加一些限制,比如有重复元素,目标值不一定存在而是寻找上界或者下界,此时虽然还是二分查找的框架,但是一些细节的条件判断上是有不同的

有的题目并不会直接给出有序的序列,需要你自己排序或者去寻找跟解集相关的单调性,例如如果某个问题的解具有一定的范围并且具备二分性并且每个解都比较容易验证,那么就可以考虑使用二分法进行查找,例如 [最长重复子串](https://leetcode.cn/problems/longest-duplicate-substring/)[分割数组的最大值](https://leetcode.cn/problems/split-array-largest-sum/)[在D天内送达包裹的能力](https://leetcode.cn/problems/capacity-to-ship-packages-within-d-days/)
有的题目并不会直接给出有序的序列,需要你自己排序或者去寻找跟解集相关的单调性,例如如果某个问题的解具有一定的范围并且具备二分性并且每个解都比较容易验证,那么就可以考虑使用二分法进行查找,例如 [最长重复子串](https://leetcode.cn/problems/longest-duplicate-substring/)[分割数组的最大值](https://leetcode.cn/problems/split-array-largest-sum/)[在D天内送达包裹的能力](https://leetcode.cn/problems/capacity-to-ship-packages-within-d-days/)[分割数组的最大值](https://leetcode.cn/problems/split-array-largest-sum/)

**值得注意的一点是,取中点时,这个中点不管是向上取整还是向下取整,它都是有可能会跟left或者right重复的,而这一点会有可能造成死循环,也就是必须保证每次二分,left或者right必须有变化**
25 changes: 25 additions & 0 deletions blogs/algorithm/二叉树的迭代遍历.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,28 @@ function postorderTraversal(root) {
```

类似的,你也可以从上面的遍历方法推导出另一种二叉树的后序遍历方法

```js
function postorderTraversal(root) {
if (!root) {
return []
}

const stack = [root]
const result = []

while (stack.length > 0) {
const node = stack.pop()
result.unshift(node.val) // 跟前序不同,这里要逆序加入解
// 并且这里要顺序遍历子节点
if (node.left) {
stack.push(node.left)
}
if (node.right) {
stack.push(node.right)
}
}

return result
}
```
90 changes: 61 additions & 29 deletions blogs/algorithm/千分位分隔符.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
### 千分位分隔符

一串数字,使用千分位分隔符分隔开
如 100000 -> 100,000

10000000 -> 10,000,000

最简单的方式是使用正则表达式来实现,从末尾开始向前,每三个数字就插入一个分隔符

`/(\d{3})+$/`

这样会匹配末尾之前三的整数倍的数字

> 关于正则量词可以参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Quantifiers
但实际我们要匹配的是三个数字之前的那个空白位置,分隔符需要被插入到该位置。这时需要用到前向断言`x(?=y)`,这个正则会匹配 x 但是要求 x 后面必须跟着 y,`(?=(\d{3}))`这样会匹配一个空白位置,后面跟着三个数字,这样正则变成了 `(?=(\d{3})+$)`

> 关于正则断言可以参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Assertions
接下来需要处理开头的特殊情况,不处理的话如果数字恰好是三的整数倍,那么开头也会被插入分隔符
我们只需要不匹配开头即可,`/(?=(\B\d{3})+$)/g`,这里的元字符`\B`表示非单词边界,宽度为 0

> 关于正则元字符可以参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
```js
var str = '10000000000',
reg = /(?=(\B\d{3})+$)/g
console.log(str.replace(reg, ',')) // 10,000,000,000
```
### 千分位分隔符

一串数字,使用千分位分隔符分隔开
如 100000 -> 100,000

10000000 -> 10,000,000

最简单的方式是使用正则表达式来实现,从末尾开始向前,每三个数字就插入一个分隔符

`/(\d{3})+$/`

这样会匹配末尾之前三的整数倍的数字

> 关于正则量词可以参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Quantifiers
但实际我们要匹配的是三个数字之前的那个空白位置,分隔符需要被插入到该位置。这时需要用到前向断言`x(?=y)`,这个正则会匹配 x 但是要求 x 后面必须跟着 y,`(?=(\d{3}))`这样会匹配一个空白位置,后面跟着三个数字,这样正则变成了 `(?=(\d{3})+$)`

> 关于正则断言可以参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Assertions
接下来需要处理开头的特殊情况,不处理的话如果数字恰好是三的整数倍,那么开头也会被插入分隔符
我们只需要不匹配开头即可,`/(?=(\B\d{3})+$)/g`,这里的元字符`\B`表示非单词边界,宽度为 0

> 关于正则元字符可以参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
```js
var str = '10000000000',
reg = /(?=(\B\d{3})+$)/g
console.log(str.replace(reg, ',')) // 10,000,000,000
```

规则 描述
\ 转义
^ 匹配输入的开始
$ 匹配输入的结束

- 匹配前一个表达式 0 次或多次

* 匹配前面一个表达式 1 次或者多次。等价于 {1,}
? 匹配前面一个表达式 0 次或者 1 次。等价于{0,1}
. 默认匹配除换行符之外的任何单个字符
x(?=y) 匹配'x'仅仅当'x'后面跟着'y'。这种叫做先行断言
(?<=y)x 匹配'x'仅当'x'前面是'y'.这种叫做后行断言
x(?!y) 仅仅当'x'后面不跟着'y'时匹配'x',这被称为正向否定查找
(?<!y)x 仅仅当'x'前面不是'y'时匹配'x',这被称为反向否定查找
x|y 匹配‘x’或者‘y’
{n} n 是一个正整数,匹配了前面一个字符刚好出现了 n 次
{n,} n是一个正整数,匹配前一个字符至少出现了n次
{n,m} n 和 m 都是整数。匹配前面的字符至少n次,最多m次
[xyz] 一个字符集合。匹配方括号中的任意字符
[^xyz] 匹配任何没有包含在方括号中的字符
\b 匹配一个词的边界,例如在字母和空格之间
\B 匹配一个非单词边界
\d 匹配一个数字
\D 匹配一个非数字字符
\f 匹配一个换页符
\n 匹配一个换行符
\r 匹配一个回车符
\s 匹配一个空白字符,包括空格、制表符、换页符和换行符
\S 匹配一个非空白字符
\w 匹配一个单字字符(字母、数字或者下划线)
\W 匹配一个非单字字符
2 changes: 2 additions & 0 deletions blogs/computer/浮点数精度问题.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ JavaScript 中的数字类型只有 Number 一种,Number 类型采用 IEEE754
- 1bit 符号
- 11bit 指数部分
- 52bit 尾数

https://www.cnblogs.com/god-of-death/p/16705565.html
29 changes: 29 additions & 0 deletions blogs/http/cookie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## cookie

### 格式

Set-Cookie: username=jimu; Domain=jimu.com; Path=/blog; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Max-Age=347824; Secure; HttpOnly; SameSite=Lax; Partitioned

Domain 和 Path 标识定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前主机,不包含子域名。如果指定了Domain,则一般包含子域名。例如,如果设置 `Domain=mozilla.org`,则 Cookie 也包含在子域名中(如`developer.mozilla.org`)。
Path则限制了cookie在有效域名下面可以生效的地址,例如Path=/docs,则以下地址都会匹配:

- /docs
- /docs/Web/
- /docs/Web/HTTP

Expires表示cookie失效的时间,Max-Age表示cookie的生效时长(优先级更高),如果不设置Expires或者Max-Age那么这个cookie就是会话cookie,只在页面生命周期内有效

Secure表示限制访问协议必须是https,否则不生效

HttpOnly表示限制cookie无法通过JavaScript获取

SameSite表示限制第三方cookie的发送,它可以有效防止用户追踪以及降低CSRF的风险。它有三个值Strict, Lax, None。同站设置的cookie就是第一方cookie,跨站设置的cookie就属于第三方cookie(是否跨站会根据协议和域名公共后缀是否相同来判断)。现在SameSite的默认值是Lax,表示在点击链接,预加载请求,GET 表单这三类请求时会发送三方cookie,其余情况不会发送;Strict是任何情况都不会发送;None则是会发送(需要启用Secure属性才有效)。

[Partitioned](https://developer.mozilla.org/zh-CN/docs/Web/Privacy/Privacy_sandbox/Partitioned_cookies)表示分区cookie

### 限制

1. 设置cookie时,Domain只能指定为当前域名或者其父域名,否则cookie会被拒绝设置
2. 指定了Domain的cookie在其及其子域名下面都有效,例如 foo=a; Domain=example.com; foo在example.com及其子域名下都是生效的
139 changes: 74 additions & 65 deletions blogs/nodejs/Koa-Express中间件.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,74 @@
[meta]: nodejs "title: 'Koa和express的中间件', keywords: 'middleware', date: '2020-8-22'"

## Koa 和 Express 的中间件机制

### Koa

```javascript
const createServer = (req, res) => {
const ctx = createContext(req, res)
ctx.res.statusCode = 404
const fnMiddleware = compose(middlewares)
return fnMiddleware(ctx)
.then(() => finalRespond(ctx))
.catch((err) => ctx.onerror(err))
}
// 洋葱模型
function compose(middlewares) {
return function (context, next) {
let index = -1 // last called middleware #
return dispatch(0)
function dispatch(i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middlewares[i]
// 当i等于中间件队列长度时表示最后一个中间件正在调用next
if (i === middlewares.length) fn = next
// 这里next是空的就直接resolve返回即可,之后就是从洋葱芯回到表皮的过程
if (!fn) return Promise.resolve()
try {
// 这里可以看到,调用next方法其实就是调用下一个中间件
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}
```

### Express

Express 中间件其实和 Koa 类似,也是从上到下的执行流程,当调用`next`的时候,express 会执行下一个中间件,如果没调用`next`并且也没有返回响应,那么 express 会挂起,前端就一直等待,而 Koa 则不同,不调用`next`只是不会调用剩余的中间件,但最终还是会返回响应;另外 express 的中间件是同步调用的,意味着如果中间件有异步操作,`next`也不会等待异步完成,而就只是线性的调用下一个中间件

```javascript
// 伪代码
function dispatch(req, res, done) {
var idx = 0
var stack = this.stack
if (stack.length === 0) return done()
next()
function next(err) {
var layer = stack[idx++] // 因为express注册中间件途径多种多样,每个中间件都会被抽象成layer
if (err === 'route' || err === 'router' || !layer) return done(err)
if (err) {
layer.handle_error(err, req, res, next)
} else {
layer.handle_request(req, res, next)
}
}
}
```

---

总结来讲,Koa 和 Express 的中间件区别主要在于,express 是依靠主动调用`res.end`来响应请求,koa 则只是向 ctx 挂载数据,中间件执行完再返回响应;二者都是通过`next`来调用下一个中间件,区别在于 express 不会等待异步的中间件执行完,而 koa 会强制让你`await next()`才可以
[meta]: nodejs "title: 'Koa和express的中间件', keywords: 'middleware', date: '2020-8-22'"

## Koa 和 Express 的中间件机制

### Koa

```js
app.use(async (ctx, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})
```

```javascript
const createServer = (req, res) => {
const ctx = createContext(req, res)
ctx.res.statusCode = 404
const fnMiddleware = compose(middlewares)
return fnMiddleware(ctx)
.then(() => finalRespond(ctx))
.catch((err) => ctx.onerror(err))
}
// 洋葱模型
function compose(middlewares) {
return function (context, next) {
let index = -1 // last called middleware #
return dispatch(0)
function dispatch(i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middlewares[i]
// 当i等于中间件队列长度时表示最后一个中间件正在调用next
if (i === middlewares.length) fn = next
// 这里next是空的就直接resolve返回即可,之后就是从洋葱芯回到表皮的过程
if (!fn) return Promise.resolve()
try {
// 这里可以看到,调用next方法其实就是调用下一个中间件
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}
```

### Express

Express 中间件其实和 Koa 类似,也是从上到下的执行流程,当调用`next`的时候,express 会执行下一个中间件,如果没调用`next`并且也没有返回响应,那么 express 会挂起,前端就一直等待,而 Koa 则不同,不调用`next`只是不会调用剩余的中间件,但最终还是会返回响应;另外 express 的中间件是同步调用的,意味着如果中间件有异步操作,`next`也不会等待异步完成,而就只是线性的调用下一个中间件

```javascript
// 伪代码
function dispatch(req, res, done) {
var idx = 0
var stack = this.stack
if (stack.length === 0) return done()
next()
function next(err) {
var layer = stack[idx++] // 因为express注册中间件途径多种多样,每个中间件都会被抽象成layer
if (err === 'route' || err === 'router' || !layer) return done(err)
if (err) {
layer.handle_error(err, req, res, next)
} else {
layer.handle_request(req, res, next)
}
}
}
```

---

总结来讲,Koa 和 Express 的中间件区别主要在于,express 是依靠主动调用`res.end`来响应请求,koa 则只是向 ctx 挂载数据,中间件执行完再返回响应;二者都是通过`next`来调用下一个中间件,区别在于 express 不会等待异步的中间件执行完,而 koa 会强制让你`await next()`才可以

0 comments on commit d5f7e54

Please sign in to comment.