• 首页

  • 归档

  • 关于我
H i , e v e r b r e z
Hi, everbrez

If I could be the hero

02月
17
security

web security

发表于 2019-02-17 • 字数统计 2120

XSS攻击

XSS(cross site scripting),跨站脚本攻击 ,是一种代码注入攻击。攻击者通过在目标网站注入恶意脚本,使之可以在用户浏览器上运行。攻击者可以获取用户的敏感信息如cookie、sessionID等,进而危害数据安全。

本质:恶意代码没有过滤,与网站正常代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。(HTML注入)

在处理输入的时候,以下内容都不可信:

  • 来自用户的UGC信息
  • 来自第三方的连接
  • URL参数
  • POST参数
  • Referer
  • Cookie

XSS攻击分为两种类型:

  1. 存储型,恶意代码存在于数据库
  2. 反射性,恶意代码存在于URL中
  3. DOM型,恶意代码存在于URL或者输入框,前端JavaScript取出执行,属于前端JavaScript的安全漏洞(关闭属性或者关闭标签)

XSS注入的方法:

  1. HTML中内嵌文本,恶意内容以script标签形成注入
  2. 内联JavaScript中,拼接的数据突破了原本的限制
  3. 标签属性中,恶意内容突破属性值的限制,注入其他属性的标签
  4. 在标签的href、src属性中,包含JavaScript:等可执行代码
  5. 在onload、onerror、onclick等事件中,注入不受控制代码
  6. 在style属性和标签中,包含类似url(JavaScript:)的代码,新版浏览器已经可以防范
  7. 在style属性和标签中,包含类似expression()的CSS表达式代码,新版浏览器已经可以防范

预防

  1. 输入检查,查找敏感信息
    1. 前端过滤:攻击者可以伪造请求
    2. 后端在写在数据库前过滤:不确定内容要输出到哪里。(在前端中,不同位置所需要的编码不同,如html与JavaScript)
    3. 对于明确的过滤类型:如电话,邮箱等提前过滤
  2. 预防存储行和反射性XSS攻击,输出检查
    1. 对HTML对充分的转义,利用成熟的转义库,利用模板引擎
    2. 避免内联事件
    3. 改成纯前端渲染,把代码和数据分隔开(JavaScript来调用Ajax来渲染的数据)
    4. 在HTML中输出,在CSS中输出,在JavaScript中输出,在URL等属性中输出,在内联属性中输出。
      1. 其中URL中有可能是其他协议的(伪协议)
    5. 处理富文本:使用白名单
  3. 预防DOM型XSS攻击,使用.innerHTML、.outerHTML、document.write()的时候小心,不要把不可信的数据作为HTML插到页面上,a标签的href属性,JavaScript中的eval、setTimeout、setIntercal都能够将字符串当成代码运行。
  4. CSP
  5. Cookie: HTTP only
  6. 验证码
  7. 不信任的输入限定长度(增强攻击难度)
  8. www-x-frame2

XSS构造技巧

  • 利用location.hash,然后构造onload事件,绕过length限制
  • 利用base标签劫持链接
  • window.name利用
  • flash,

持久型

持久型就是攻击的代码被服务端写进了数据库,这种攻击危害性很大

非持久型

非持久型一般通过修改URL参数的方式加入攻击代码,诱导用访问链接从而进行攻击

转义

  • 对引号、尖括号、斜杠等进行转义
  • 采用白名单过滤

CSP

CSP本质上就是建立白名单,明确告诉浏览器哪些外部资源可以加载和执行
通过以下两种方式来开启CSP

  1. 设置HTTP Header中的 Content-Security-Policy
  2. 设置标签<meta http-equiv="Content-Security-Policy">

作用:

  1. 禁止加载外域代码,防止复杂的攻击逻辑
  2. 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域
  3. 禁止内联脚本执行
  4. 禁止未授权脚本执行(未授权)
  5. 合理使用上报可以即使发现XSS
1
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

CSRF

Cross-site Request Forgery 跨站请求伪造
Cookie分为两种:
Third-party Cookie:只有过了expire时间后失效
Session Cookie:临时Cookie,连接断开就失效了

本质:重要操作的所有参数都是可以被攻击者猜测到的。

防御:

  1. Get请求不对数据进行修改
  2. 不让第三方网站访问到用户cookie
  3. 阻止第三方网站请求接口
  4. 请求时附带验证信息,验证码或者Token,这个是最有效的方式
  5. Referer Check(但是浏览器会限制referer的发送。比如https跳到http)
    1. H5中a和area标签可以设置一个新的link types:noreferrer,即不再发送referrer(用户隐私)
  6. 将参数加密,或者使用一些随机数,让攻击者无法猜测到。

Cookie设置 sameSite属性,cookie不随着跨域请求发送

验证Referer:验证Referer判断是否为第三方网站发起的请求

Token:服务器随机生成一个Token,每次请求的时候将Token带上,服务器验证是否有效
多页面共存情况,保密性

点击劫持

点击劫持是一种视觉欺骗手段,攻击者将需要攻击的网站通过iframe嵌套的方式嵌入自己的网页中,并将iframe设置为透明,在页面中透出一个按钮诱导用户点击

由于手机上的浏览器隐藏了地址栏,所以手机上的点击劫持更容易实现

防御:

  1. 设置X-FRAME-OPTION,为了防御iframe嵌套的点击劫持攻击

有三个值可选:

  • DENY,表示页面不允许通过iframe方式展示
  • SAMEORIGIN 表示页面可以在相同域名下通过iframe的方式展示
  • ALLOW-FROM,表示页面可以在指定来源的iframe中展示
  1. 通过js代码检查自己网页是否为top

H5中为iframe添加了sandbox功能,能够通过参数来支持更精确的控制:

  • allow-same-origin 允许同源访问
  • allow-top-navigation 允许访问顶层窗口
  • allow-forms 允许提交表单
  • allow-scripts 允许执行脚本

中间人攻击

中间人攻击时攻击方同时与服务端和客户端建立起连接,并让对方认为连接是安全的。但实际上整个通信过程都被攻击者控制了,攻击者不仅能够狗获得双方的通信信息,还能修改通信信息

防御:

  1. HTTPS可以防御中间人攻击

DDoS 分布式拒绝服务

Distribute Denied of Server
在短时间内发起大量请求,耗尽服务器的资源,无法相应正常的访问,造成网站实质下线。

  • TCP全连接攻击:通过大量僵尸主机不断与服务器建立大量TCP服务,耗尽服务器资源。
    • 优点:能够绕过防火墙,缺点:需要找到大量的僵尸主机,如果僵尸主机的IP暴露容易被驱逐
    • 防御:限制SYN流量,定期扫描,骨干节点配置防火墙,用足够的机器承受住黑客的攻击,过滤不必要的服务和端口(在路由器上过滤假IP)
  • SYN Flood 攻击
    • 发送大量伪造原IP地址的攻击报文,而真实IP不作回应
    • 服务端在重试和等待SYN Timeout的过程中维持着一个非常大的半连接队列而消耗大量的CPU和内存
    • 解决:使用硬件防火墙(由防火墙代理该连接,验证有效后才向内部服务器发起SYN请求,最后才建立连接。防火墙要对序列的序列号进行修改),硬件防火墙是指将防火墙程序做到芯片里面,由硬件执行里面的功能,能够减少CPU的负担,使路由更稳定。
    • 使用SYN Cache技术。收到SYN的时候不急着去分配系统资源,先回应ACK报文,并在专用的HASH表中保存这种半连接报文,直到收到正确的ACK后再分配资源。

Land攻击

这种攻击方式采用了特别构造的TCP SYN数据包(通常用于开启一个新的连接),使目标机器开启一个源地址与目标地址均为自身IP地址的空连接,持续地自我应答,消耗系统资源直至崩溃。

阅读全文 »
02月
17
JavaScript

webpack optimize

发表于 2019-02-17 • 字数统计 462

Webpck 性能优化

减少Webpack打包时间

  1. 优化loader
    对于Babel,Babel会将代码从字符串转化成AST,然后从AST再转化成新的代码。所以项目越大,转换代码越多,效率就越低。
    • 优化文件搜索范围(可以不用编译node_modules)中的代码,因为里面的代码都是编译过的。
    • 可以设置缓存,将Babel编译过的代码缓存起来,下次只需要编译更改过的代码即可。
  2. 使用HappyPack
    由于Node是单线程运行的,所以webpack在打包的过程中也是单线程的,特别是在执行loader的时候,长时间编译的任务很多,这样就会导致等待的情况。
    HappyPack可以将loader从同步执行转换成并行的。充分利用系统资源加快打包效率
  3. DllPlugin
    DllPlugin可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数。主要当类库更新版本才有需要重新打包
  4. 代码压缩
    在webpack4中,只要将mode设置为production就可以默认开启代码压缩功能(还可以配置删除console类代码)
  5. resolve.extensions,尽可能减少后缀列表长度,将出现频率高的后缀排在前面
  6. resolve.alias:设置别名的方式映射一个路径,让Webpack更快找到路径
  7. module.noParse:如果你确定一个文件下没有其他依赖,可以让webpack不扫描该文件。

减少Webpack打包体积

  1. 按需加载,为每一个路由页面单独打包成一个文件
  2. Scope Hoisting,代码合并到一个函数。
    webpack4可以设置optimization.concatenateModules
  3. TreeShaking 可以删除项目中未被引用的代码
    Webpack4自动启动这个优化功能
阅读全文 »
02月
17
JavaScript

webpack principle

发表于 2019-02-17 • 字数统计 93
  1. 使用Babel转换代码,分析文件中的dependencies,转换代码code,返回对象。(parseCode)
  2. 利用parseCode处理入口文件,然后根据dependencies中的依赖,寻找出所有依赖之后返回一个依赖数组(处理依赖的时候处理依赖的路径)
  3. 打包功能:将文件利用CommonJS的语法进行打包
阅读全文 »
02月
17
algorithm

algorithm fundamental

发表于 2019-02-17 • 字数统计 190

位运算

十进制转二进制:
如33:

1
2
3
4
5
6
33 / 2 = 16 + 1
16 / 2 = 8 + 0
8 / 2 = 4 + 0
4 / 2 = 2 + 0
2 / 2 = 1 + 0
1 / 2 = 0 + 1

所以二进制为:100001(按照从下到上的顺序)

小数转二进制
如0.1

1
2
3
4
5
6
0.1 * 2 = 0 + 0.2
0.2 * 2 = 0 + 0.4
0.4 * 2 = 0 + 0.8
0.8 * 2 = 1 + 0.6
0.6 * 2 = 1 + 0.2
...

所以二进制为0.0(0011)(按照从上到下的顺序)

左移 <<

可以看成 a * (2 ** b)

右移 >>

可以看成 a / (2 ** b)

右移可以在二分法中取中间值

按位与 &

8 & 7 => 0

按位或 |

8 | 7 => 15

按位异或 ^

每一位不同才会为1

两个数不通过四则运算得出和:

阅读全文 »
02月
17
JavaScript

design pattern

发表于 2019-02-17 • 字数统计 1412

设计模式

工厂模式

单例模式

单例模式的核心是确保只有一个实例,并提供全局访问,减少了内存开支(在频繁需要创建和销毁时)
惰性单例
创建对象的职责和管理单例的职责可以分布在两个不同的方法。

1
2
3
4
5
6
7
8
9
10
function getSingle(fn) {
let instance = null
return function(...args) {
if (!instance) {
if (new.target) instance = new fn()
else instance = fn.apply(this, fn)
}
return instance
}
}

上面的方法不能保证instanceof 能够正常工作

享元模式

如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就很有用。
尽量减少共享对象的数量。
使用享元模式的关键是怎样区分内部状态和外部状态。可以被对象共享的状态通常被划分为内部状态

对象池的实现

策略模式

定义一系列的算法,把他们一个个封装起来。(在JavaScript中可以直接作为一个对象的方法)
应用场景:缓动动画、表单校验
方便后期的扩展和更改

适配器模式

适配器使原来接口不兼容的两个软件实体可以正常工作
装饰者的作用是给对象增加功能,而适配器是解决两个接口不兼容的问题

代理模式

代理模式是为对象提供一个代用品和占位符,以便控制对它的访问
代理可以帮助target过滤一些请求(这种代理叫做保护代理),虚拟代理则是把一些开销很大的对象延迟到真正需要它的时候采取创建。缓存代理,惰性加载

代理和本体接口的一致性:代理和本体可以替换使用,这意味着当不需要代理功能的时候,可以直接访问本体。

单一职责,一个类而言,应该仅有一个引起它变化的原因。

应用:虚拟代理图片预加载

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 createImg() {
const img = new Image
img.src = 'loading.gif'
document.body.append(img)
return {
setSrc(src) {
img.src = src
}
}
}

const myImage = createImg()

function createProxy(img) {
return {
setSrc(src) {
const image = new Image
image.src = src
image.onload = function(){img.setSrc(src)}
}
}
}

const myImageProxy = createProxy(myImage)

发布-订阅模式

发布订阅模式又称观察者模式,定义了对象当中的一对多的关系。
解耦,(网站登陆的例子),全局Event对象(如同中介),(先订阅再发布?)

弱化对象之间的联系,解耦时间和空间,消耗时间和空间

命令模式

有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。
将对象封装成命令对象,提供execute方法

  • 撤销和重做
  • 宏命令(命令模式和组合模式)

职责链模式

可以解耦大量的if/else模式,解耦了请求发送者和N个接收者之间的复杂关系(比如订单问题)

需要在链尾添加一个保底的接收者节点来处理这种即将离开链尾的请求。

AOP实现职责链

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

class Task {
constructor(fn) {
this.fn = fn
this.next = null
}

after(next) {
return this.next = next
}

passRequest(...args) {
const res = this.fn(...args)
if (res === false && this.next) {
return this.next.passRequest(...args)
}
return res
}
}

let console1 = new Task(function(a){if(a > 500) return '500+'; return false})
let console300 = new Task(function(a){if(a > 300) return '300+'; return false})
let console2 = new Task(function(a){if (a > 200) return '200+';return false})
let console3 = new Task(function(a){if (a > 100) return '100+';return false})
let normal = new Task(function(a){return 'normal'})

console1.after(console300).after(console2).after(console3).after(normal)

优缺点:解耦,只需要将请求传递给第一个节点即可
灵活地拆分重组
但是不能保证一定会被处理,

应用:优先度(比如选用下载工具。。。)

中介者模式

利用发布/订阅设计模式
最少知识法则
在中介者模式中,他们只能通过中介者对象来互相影响对方,使对象之间得以解耦

装饰者模式

decorator
装饰者模式可以动态地给某个对象添加一些额外的职责,从而不影响这个从这个类中派生的其他对象。
在函数执行前后添加操作

可以处理数据上报、统计函数的执行时间、动态改变函数参数以及插件式的表单验证

作为一个框架,提供一下稳定以及方便移植的功能,使用装饰者模式动态装饰上去

状态模式

原则

  1. SRP原则(单一职责原则):引起变化的原因,如果有两个动机去写一个方法,那么这个方法就有两个职责。体现在一个对象只做一件事(单例模式、装饰着模式、代理模式)。降低了单个类或者对象的复杂度,利于单元测试,但是增大了对象之间的联系。
  2. 最少知识原则:一个实体应该尽量少地与其他实体发生作用(中介者模式、)
  3. 开放-封闭原则:当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以增加代码的方式,但是不允许改动程序的源代码。发布订阅方法,策略模式,代理模式,职责链模式
阅读全文 »
02月
17
NetWork

HTTP

发表于 2019-02-17 • 字数统计 1354

GET 与 POST

GET与HEAD被RFC认为是一种安全方法,即不会再服务器上产生结果

  1. GET将参数包含在URL中,POST通过request body传递参数
  2. 参数长度限制(http没有定义),浏览器会对其进行限制
  3. GET提交数据没有POST安全,因为GET在地址栏上,其数据可以出现在URL上,因为GET数据可以缓存,有人查看浏览器历史记录就可能拿到
  4. GET请求是幂等的,而POST请求是不幂等的
  5. GET请求能缓存,POST不能
  6. POST支持更多的编码类型且不对数据类型限制

幂等性是指一次或者多次请求某一个资源都应该具有同一个副作用。对同一个URL应该返回同样的结果

(下面的不一定正确,浏览器不同而不同)
GET产生一个TCP数据包;POST产生两个TCP数据包

  1. 对于GET请求,浏览器会把http header 和 data 一起发送出去,服务器相应200
  2. 对于POST,浏览器先发送header,服务器相应100 continue,浏览器再发送data,服务器相应200ok

HTTP/2

多路复用技术,只通过一个TCP连接就可以传输所有的请求数据,很好地解决了浏览器限制同一域名下的请求数量问题,同时更容易实现全速传输(新开TCP连接需要慢慢提升传输速度的)

二进制传输

在之前的HTTP版本都是通过文本的方式传输数据的,在HTTP/2中引入了新的编码机制,所有传输的数据都会被分隔,并采用二进制格式编码

多路复用

在HTTP/2中,有两个非常重要的概念,分别是帧(frame)和流(stream)
帧代表着最小的数据单位,流就是多个帧组成的数据流,帧中包含着流的标识

多路复用就是一个TCP连接中可以存在多条流。换句话说,就是可以发送多个请求,对端可以通过识别帧中流的标识就可以知道是哪个请求了。通过这个技术可以结局HTTP中的队头阻塞问题。

Header压缩

使用HPACK压缩格式对传输的header进行编码,同时在两端维护索引表,用于记录出现过的header。后面在传输过程中就可以传输已经记录过的键名了。(通过键名找到对应的值)

服务端PUSH

服务端可以在客户端某个请求之后,主动推送其他资源。

HTTP/3

因为HTTP/2使用了多路复用,但是如果底层的TCP出现了丢包的情况,就会导致HTTP/2的表现情况反而不如HTTP/1了。

因为在丢包的情况下,整个TCP都要开始等待重传,导致了后面的数据被阻塞了。在HTTP/1中,开启了多个TCP连接,出现这种情况法尔只会影响到一个连接,剩余的TCP连接还是可以正常传输的。

基于这个原因,Google就基于UDP协议推出了一个QUIC协议,使用在了HTTP/3上。

QUIC

多路复用

QUIC原生实现了多路复用功能,并且传输的单个数据流可以有序交付且不会影响其他的数据流。
QUIC在移动端表现优于TCP,因为TCP是基于IP和端口去识别连接的。这种方式在多变的移动端网络是脆弱的。但是QUIC通过ID的方式识别一个连接,不管网络环境如何变化,只要ID不变就可以迅速重连上。

0-RTT

通过使用类似TCP的快速打开技术,缓存当前会话上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证就可以进行传输了。

纠错机制

如果要发出三个包,那么协议会算出这三个包的异或值并且单独发出一个校验包,总共发出4个包
当出现非校验包丢包的情况下,可以通过另外三个包计算出丢失的数据包内容。(只能用于丢失一个包的情况下)

HTTP报文结构

请求行或状态行CRLF
首部行CRLF
空行(CRLF)
实体

状态码和相应的短语

  • 1XX 信息性状态码,接收的请求正在处理
    • 100 Continue 等待状态码
  • 2XX 成功状态码,请求正常处理完毕
    • 200 OK
    • 204 No Content
    • 206 Partial Content
  • 3XX 重定向状态码,需要附加操作以完成请求
    • 301 Move Permanently 禁止从POST变成GET,但浏览器,原URL已经移除
    • 302 Found 禁止从POST变成GET,但浏览器
    • 303 See Other 从POST变成GET
    • 304 Not Modified
    • 307 Temporary Redirect 不会从POST变成GET
  • 4XX 客户端错误状态码,服务器无法处理请求
    • 400 Bad Request 存在语法错误
    • 401 Unauthorized 需要认证,如果之前已经进行一次请求,表示认证失败
    • 403 Forbidden
    • 404 Not Found
    • 412 Precondition Fail
    • 417 Expectation Fail
  • 5XX 服务器错误状态码,服务器处理请求错误
    • 500 Inernal Server Error 执行请求出现错误
    • 503 Service Unavailable 超负荷,停机维护
    • 504 Gateway Timeout 网关等待服务器相应超时

头部字段

阅读全文 »
02月
17
JavaScript

监控

发表于 2019-02-17 • 字数统计 219

监控

前端监控主要分为三种:

  1. 页面埋点
  2. 性能监控
  3. 异常监控

页面埋点

页面埋点一般监控以下几个数据

  • PV/UB
  • 停留时长
  • 流量来源
  • 用户交互

实现的思路主要分为手写埋点和无埋点的方式

性能监控

  1. 可以使用浏览器自带的Performance API来实现这个功能
    performance.getEntriesByType('navigation')

异常监控

  • 代码报错
  • 接口异常上报

代码报错:
一般可以通过拦截window.onerror来获得大部分详细的报错信息
对于跨域的代码运行错误会显示Script error,这种情况需要给script标签添加crossorigin属性

对于异步代码,可以通过catch的方式捕获错误

对于捕获的错误,可以通过一个img标签src简单发起一个请求

阅读全文 »
02月
17
JavaScript

输入URL到页面渲染的整个流程

发表于 2019-02-17 • 字数统计 690

overview

  1. 解析URL
  2. 输入的是URL还是搜索关键字
  3. 转换非ASCII字符
  4. 检查HSTS(HTTP严格传输安全)列表,包含了那些请求浏览器只使用HTTPS进行连接的网站Reason
  5. DNS查询
    1. 浏览器缓存
    2. 调用系统库方法gethostbyname
    3. 检查host文件
    4. 检查缓存
    5. 发起请求
  6. Http请求(调用系统库函数socket,请求一个TCP流套接字)TCP/TLS
  7. 解析、渲染

DNS查询

Domain Name System

在TCP握手之前就已经进行DNS查询了,由操作系统来查询,当要访问www.google.com时候,会进行这样操作:

  1. 检查hosts文件
  2. 操作系统会首先在本地的缓存中查询IP
  3. 如果没有的话就去系统配置的DNS服务器中查询
  4. 如果这时候还没有,会直接去DNS根服务器查询(根据是否启用转发模式),这一步会找出负责com这个一级域名的服务器
  5. 然后去该服务器查询google这个二级域名
  6. 接下来三级域名的查询其实我们配置的,可以给www这个域名配置一个IP,还可以给别的三级域名配置一个IP

以上是DNS递归查询,还有一种是迭代查询:本地服务器将请求转发到根服务器

当本地服务器代替客户端向其他服务器查询的时候,客户端完全处于等待状态
DNS查询DNS是应用层协议,是通过UDP进行的查询。DNS采用分布式集群的工作方式(单点故障,通信容量,远距离时间延迟,维护开销大)

网址结构(从右往左)

  • 根域
  • 顶级域(国家或者组织)
  • 第二层域
  • 子域
  • 主机名

使用UDP的原因:一次UDP名字交换可以短到两个包:一个查询宝和一个响应包。但是使用TCP的话,就需要9个包(其中3个握手4个挥手),开销大,所以前者(UDP)的效率很高。

DNS服务器一般分三种:根DNS服务器,顶级DNS服务器,权威DNS服务器
一般只有13个根服务器:
这是因为UDP数据包中512字节限制了信息量(又因为UDP报文中包含所有跟服务器的信息),所以只能有13个。

其实512是为了让UDP报文中不会因为size > MTU而导致IP分片。IP分片发生后,只有一片有端口号,其他分片没有端口号,能否通过就取决于防火墙了,这对于是否通信成功是一个未知数。(以太网MTU=1500)

DNS负载均衡

阅读全文 »
02月
17
NetWork

TCP/UDP

发表于 2019-02-17 • 字数统计 5196

在TCP和UDP中,采用5个信息来识别一个通信:源IP,目标IP,源端口,目标端口,协议只要某一样不同就会被认为是其他通信。

端口号在传输层相当于地址,通过端口号来识别应用程序。

端口的确定:标准既定端口号(如http80,https443,ftp20,21,SMTP25,DNS53),以及动态分配法(由操作系统分配4w9到6w5)

不同的传输协议可以通过相同的端口号传输

二者区别:

  1. 面向无连接/有连接(需要建立连接、无连接的可以不需,可以用于多播,广播等)
  2. 可靠性(UDP不关心是否收到数据,TCP采用超时重发、分片、流量控制,拥塞管理等保证可靠性)
  3. 高效性(UDP高效,报文段字节少,不需要建立连接),如DNS

UDP

user datagram protocol
UDP协议是面向无连接的,也就是说不需要再正式传递数据之前连接双方,不保证数据的完整性和有序性
没有任何控制流量的算法,比TCP更加轻便

面向无连接

UDP不需要想TCP一样需要三次握手建立连接

  • 在发送端,应用层将数据传递给传输层UDP协议,UDP只会给数据增加一个UDP头识别,然后传递给网络层
  • 在接收端,网络层将数据传递给传输层,UDP只取出IP报文头就传递给应用层,不会任何拼接操作(不会分段)

不可靠性

发送数据不会关心对方是否已经正确接收到数据了。适用于某一些实时性要求高的场所

高效

UDP头部开销小,只有8byte,相比TCP20byte要小,传输报文的时候很高效

原因:

  1. 无需建立连接
  2. 不需要确认收到数据
  3. 没有超时重发机制
  4. 没有流量控制和拥塞控制
  5. 报文段小

UDP头部包含以下数据:

  • 两个16位的端口号,分别为源端口和目标端口
  • 整个报文的长度Length
  • 整个数据报文的校验和(IPv4可选字段),该字段用于发现头部信息和数据的错误

传输方式

UDP不止支持一对一的传输方式,同时支持一对多,多对多,多对一的方式。(单播,多播,广播)

TCP

TCP建立连接和断开连接都需要进行握手,在传输数据的过程中,通过各种算法保证数据的可靠性。相比UDP不那么高效

TCP是面向连接的,可靠的流协议。

可靠性

  1. 通过三次握手来建立连接
  2. 将数据截断为合理的长度(按照字节编号,合理分片),使数据包保持不变
  3. 超时重发机制(定时器超时前收不到母的段的确认报文段将重发)
  4. 对于收到的请求,给出确认相应(会推迟几分之一秒,用包的校验)
  5. 校验出包出错则丢弃报文段,不响应,让对方超时重发
  6. 丢弃重复的数据
  7. 进行流量控制
  8. 拥塞控制

头部

  • 2字节 * 2 源端口以及目标端口
  • 4byte Sequence number 这个序号保证TCP传输的有序性,表明该报文携带数据第一个字符的序号(如果数据大小为100字节,那么下一次的序号为这次序号加上100)不会从0开始,而是建立连接时由计算机生成随机数通过SYN包发送到端主机。
  • 4byte Acknowledgment number(if ACK set) 这个序号表示数据接收端期望接受的下一个字节的编号是多少,同时表示上一个序号为止的数据已经收到
  • 2 byte 数据偏移位 + 保留位 + 标识符,其中每个标识符占1bit
    • 数据偏移位:长4bit,单位为4字节,表示从哪个位开始是数据
    • 保留位:6/4
    • 控制位:6/8
  • 2 byte window size 窗口大小,表示还能接受多少字节的数据,用于流量控制
  • 2 byte checksum 校验和(区别与UDP,TCP的校验和无法关闭)
    • 计算过程:将所有16bit字节,反码相加,若大于16位则高位叠加,得到检验和。接收端采用同样的方法,如果得到1111则校验成功
  • 2 byte Urgent pointer(if URG set),紧急指针,说明紧急数据具有多少个字节
  • 选项字段(长度可变),数据字段的最长度MSS
  • 填充

标识符*(控制位):

  1. URG=1,本数据包部分包含紧急信息,是一个高优先级的数据报文。紧急数据一定位于当前数据包数据部分的最前面,紧急指针表明了紧急数据的尾部
  2. ACK=1 表示确认好字段有效,TCP规定在连接建立之后传送的所有报文段都必须把ACK置为1
  3. PSH=1 该字段表示接收端应该立即将数据push给应用层,而不是等到缓冲区满了之后再提交
  4. RST=1 表示当前TCP连接出现严重问题,可能需要重新建立TCP连接,也可以拒绝非法报文段和拒绝连接请求
  5. SYN=1 SYN=1,ACK=0表示这是一个连接请求报文;当SYN=1,ACK=1表示这个一个同意连接的应答报文
  6. FIN=1 表示这是一个释放连接的请求报文
  7. CWR=1 且ECE=1表示拥塞窗口已经缩小
  8. ECE=1 表示当前网络用拥塞

状态机

建立链接的三次握手

客户端 发送 SYN

服务端 接收SYN,并返回 SYN + ACK,如果服务器无法立刻建立连接,返回RST报文报文表示重置

客户端 接收SYN + ACK,返回ACK

初期上方都处于CLOSED状态。在通信开始之前,双方会创建TCB。服务器创建完TCB之后就会进入LISTEN状态,单带客户端发送数据

第一次握手:

客户端发送一个SYN,然后进入SYN-SENT状态

第二次握手

服务端接收到SYN之后,返回SYN + ACK(包含数据初始序号)表示同意连接,发送之后进入SYN-RECEIVED状态

第三次握手

客户端接收到SYN + ACK之后,向服务器发送一个确认报文ACK,发送之后进入ESTABLISHED,服务端接收到这个应答之后也进入ESTABLISHED状态

第三次握手可以包含数据

需要第三次握手的原因是因为防止失效的连接请求报文段被服务端接收的情况:

假设客户端发送一个A请求,因为网络延迟,导致其又发了第二个请求B,请求B与服务端正确建立连接,通信结束后,如果请求A到达服务端,服务端可能会认为这是另一个通信请求,如果两步握手,服务端发送SYN+ACK之后进入ESTABLISHED状态,就需要一直等待客户端,浪费资源

MSS(Max Segment Size)是最大消息长度,理想状态下,最大消息长度正好是IP中不会被分片处理的最大数据长度

在握手的时候,两端的主机可以计算出这个数值,在TCP首部写入自己接口能够适应的MSS的大小。

断开连接的四次挥手

发送端:发送FIN请求释放连接,进入FIN_WAIT_1状态

接收端:接收到FIN请求,告诉应用层释放TCP连接。然后发送ACK包,表示可以继续发送数据,同时进入CLOSE_WAIT状态

发送端:发送端接收到ACK包之后,进入FIN_WAIT_2状态

接收端:当确认数据已经发送完毕了之后,发送FIN释放请求,然后进入LAST_ACK状态

发送端:收到FIN释放请求,发送ACK应答,然后进入TIME_WAIT状态,持续2MSL(最大段生存期),如果此时没有重发请求时,便进入CLOSED状态。

接收端:接收端接收到ACK应答,也进入CLOASED状态

TCP

重发超时

重发超时使指在重发数据之前,等待确认应答到来的那个特定的时间间隔。如果超过了这个时间仍没有收到确认应答,发送端将数据进行重发。

重发的时间长度使通过每次发包的时候都计算一下往返需要的时间以及偏差,将这个时间和偏差加起来,重发超时的时间就是比这个时间大一点。(因为有一些包是通过不同路径来的,所以偏差可能会大,也可能会小,为了使一些偏差大的也可以到达,不浪费网络流量)

如果数据重发了之后还是没有应答,则继续发送,等待时间将会以2倍,4倍等指数函数延长。但是数据包不会无限重发,如果重发超过了一定次数之后就会判断网络或对端主机出现了异常,强制关闭连接。并且通知应用程序强行终止。

流量控制

采用连续ARQ协议

滑动窗口

在TCP中,两端其实都维护着窗口:发送端窗口和接收端窗口

动态调整窗口:

发送端窗口包含着已经发送但未收到应答的数据和可以发送但是未发送的数据,其中发送窗口是根据接收窗口的剩余大小决定的

接收方会把当前接收窗口的剩余带下写进应答报文,发送端会根据这个来调整发送窗口的值。

当发送端接收到应答之后,会将发送窗口进行滑动(它帮助TCP实现了流量控制功能,接收方通过告知发送方还可以发送多少数据来保证接收方能够来得及接收数据,防止出现接收方宽带已满,但是发送方还一直发送数据的情况)

Zero 窗口

在发送报文的时候,可能会出现零窗口的情况,在这种情况下,发送端会停止发送数据,并启动persistent timer,该定时器会定时发送请求到对端,让其告知窗口大小。在重试超过一定次数之后,可能会终端TCP连接。

在窗口在一定程度上大的时候,即使少部分确认应答丢失也不会重发,可以通过确认下一个应答来确认。

如果连续3次收到同一个应答,就会对对应的数据进行重发。(快速重传,见下方)

死锁

当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。

为了避免流量控制引发的死锁,TCP使用了持续计时器。每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。

传输效率(提高网络利用率)

  1. Nagle算法:

发送端即使还有应该发送的数据,但是如果这部分数据很少的话,那么进行延迟发送的一种的处理机制。仅在下面两个条件都不满足的时候,暂时等待一段时间再发送。

  • 已发送的数据都已经确认应答时
  • 可以发送最大段(MSS)数据时
  • 紧急数据发送
  • 等待超过一段时间

缺点:造成了延迟,所以一些机械控制领域中使用TCP时,往往会关闭这个算法。
优点:减少大量的小包在网络上造成拥塞

  1. 延迟确认应答

  2. 削带应答
    计算机既要应答,也要发送数据的情况

拥塞处理

拥塞处理和流量控制不同,后者作用于接收方,保证接收方来得及接收数据。前者作用于网络,防止过多的数据拥塞网络,避免出现网络负载过大的情况。

算法:

  • 慢启动算法
  • 拥塞避免
  • 快速重传
  • 快速恢复

慢开始算法

在传输开始的将发送窗口慢慢指数扩大,从而避免一开始传输大量数据导致网络拥塞。

  1. 连接初期设置拥塞窗口(Congestion Window)为1 MSS(一个分段的最大数据量)
  2. 每过一个RTT就将窗口大小乘2
  3. 当窗口大小大于一个阈值时就会启动拥塞避免算法
    (TCP在通信开始的时候没有设置阈值,只有在第一次重传超时的时候才会设置为当前窗口的一半)

拥塞避免算法

拥塞避免算法每过一个RTT窗口大小只加1,这样能够避免指数级增长导致网络拥塞,慢慢将大小调整到最佳值

在传输过程中可能定时器超时的情况,这时候TCP认为网络拥塞了,会马上进行以下步骤:

  1. 将阈值设为当前拥塞窗口的一半
  2. 将拥塞窗口设为1 MSS
  3. 启动慢开始算法

快速重传

快速重传一般和快速恢复一起出现。
如果接收端收到的报文出现失序的情况,接收端只会回复最后一个顺序正确的报文序号。如果发送端收到三个重复的ACK,无需等待定时器超时而是直接启动快速重传算法。

TCP Reno算法:

  • 拥塞窗口减半
  • 将阈值设置为当前拥塞窗口
  • 进入快恢复阶段(一旦收到一个新的ACK答复就退出该阶段)
  • 使用拥塞避免算法

TCP New Ren 改进后的快恢复:

TCP发送端会记下三个重复ACK的分段的最大序号

假设有一个1 - 10十个序号的报文,如果丢失了3 和 7的报文,那么该分段最大序号就是10。

这时候重发序号为3的报文,接收方接收后发送7的应答,此时TCP继续发送序号为7的报文,接收方接收后发送ACK序号为11的应答,此时发送端可以认为这个分段接收端已经顺利接收,接下来退出快恢复阶段。

快恢复阶段阶段不执行慢启动算法。因为同时收到3个包,说明了当前网络不处于拥塞环境,直接使用拥塞避免算法。

建立连接后保持为连接

  1. TCP协议成keep alive机制

连接闲置后一段时间,发送一个keepalive探测包

对方收到之后会回复ACK

如果出现错误就恢复一个RST

如果没有多次没有回复则视为断开

  1. 应用层实现心跳包
  • 客户端定时发送一个心跳包,告诉自己仍然在线

TCP异常

  1. 与一个不存在的端口建立连接

    • 服务器端口没有监听这个端口,那么客户端发送的请求(SYN)就会在服务器的系统内出发RST分节的条件,表示出错。客户端的TCP接收到这个RST之后就会放弃连接,并且返回应用程序一个错误。
  2. 与不存在主机上的端口建立连接

    • TCP没有任何相应,6s后会继续发送一个SYN,如果还是没有反应,24S后再发送一个,总共等待75s。如果还没有收到相应就会返回ETIMEOUT错误
  3. Server进程被塞满

    • 这时候连接可以正常建立(因为建立连接的进程对于应用程序来说是不可见的),客户端可以发数据给服务端,同时服务端TCP会应答ACK表示已经收到分节(数据在内核缓冲区,但是因为应用程序进程被阻塞,所以不能将数据从内核缓冲区复制到应用层序的缓冲区)
  4. kill Server

    • 在程序正常退出的时候会自动调用close函数关闭它打开的文件描述符,相当于服务器主动关闭连接(发送一个FIN给客户端)
      客户端需要配合对端关闭连接
  5. Server所在主机关机

    • 系统关闭时,init进程会给所有进程发送SIGTERM信号,等待一段时间(5-20s),再给所有仍在运行的进程发送SIGKILL信号。当服务器进程死掉时,会关闭所有的文件描述符。
  6. Server进程所在的主机宕机

    • 客户端持续重传分节,试图从服务器中接受到一个ACK,重传数次之后,大约4-10分钟停止,返回一个ETIMEOUT错误
    • TCP提供一个SO_KEEPALVE的socket选项,应用程序每隔一段时间发送对方一个心跳包,当对方没有相应的时候会以更短的时间间隔发送,一段时间之后仍无反应就断开这个连接。
    • 如果服务器接收到报文段,那么就会回复一个RST表示出错
  7. 模糊窗口综合征

    • 发送端产生数据慢,一产生就发送; 接收端消费数据慢,每次发送确认报文设置窗口大小为1
      导致了大量小包发送
    • 解决:
      • nagle算法,延迟发送
      • 延迟应答
      • 或者收到数据宣布窗口为0,待缓冲区有足够空间的时候
  8. 黏包
    UDP不会发生黏包,因为有明确的边界

原因:

  1. Nagle算法造成的发送端黏包
  2. 接收端接收不及时,倒是TCP缓冲区存放几段数据

解决:

  1. 关闭Nagle算法
  2. 接收端尽可块从缓冲区读取数据
  3. 发送数据在开头和结尾标记

TCP提供可靠的数据传输

实行顺序控制或者重发控制机制,还具备流量控制,拥塞控制,提高网络利用率等众多功能

ARQ 协议

(Automatic Repeat-reQuest) 自动重传请求
通过确认和超时两个机制,实现可靠的信息传输

  • 停止等待ARQ协议
  • 连续ARQ协议

停止等待ARQ协议

正常传输过程

只要A向B发送一段报文,都要停止发送并启动一个计时器,等待对端回应,在定时器时间内接收到对端应答就取消定时器并发送下一段报文。

报文丢失或出错

在报文传输阶段可能出现丢包,这时候定时器设定的时间就会再次发送丢失的数据知道对端的相应,所以需要每次都备份发送的数据。。

即使报文正常传输到了对端,可能出现传输过程中报文出错的问题。这时候会抛弃该报文并等待A段重传

ACK 超时或者丢失

对端传输的应答也有可能丢失导致超时,这样A端会重传报文,如果B端收到了相同序号的报文会丢弃该报文并重传应答,直到A端发送下一个序号的报文

连续ARQ协议

在连续ARQ中,发送端拥有一个发送窗口,可以在没有收到应答的情况下持续发送窗口内的数据,这样相比停止等待ARQ减少了等待时间,提高了效率。

累计确认

接收端会通过累计确认,在收到多个报文以后统一回复一个应答报文,报文中的ACK标识可以告诉发送端这个序号之前的数据已经全部接收到了,下次请发送这个序号后的数据。

弊端:可能会造成发送端重复发送数据的情况

阅读全文 »
02月
17
JavaScript

JavaScript 易错

发表于 2019-02-17 • 字数统计 4087

字符串方法

字符方法

  1. 字符方法
    • charAt
    • charCodeAt
    • codePointAt
  2. 拼接方法
    • concat
    • slice 如果start大于end,则返回空字符串
    • substr
    • substring 若start大于end,那么二者会交换,任何大于length都会被视为length

      上面三者都接受两个参数,其中slice和substring两个接受起始位置和终止位置,但是substr接受一个起始位置和返回的字符串长度
      如果输入负数,slice会加上数组长度,substring则会将所有负数转化为0,substr第一个负的参数加上字符串长度,第二个转化为0
      如果输入NaN,视为0
      如果开始和结束相等,返回空字符串

  3. 索引方法
    • indexOf
    • lastIndexOf
    • startsWith
    • endsWith
    • includes
    • repeat
  4. trim方法
    • trim 删除所有前置和后置空格
  5. 大小写方法
    • toUpperCase
    • toLowerCase
    • toLocaleLowerCase
    • toLocaleUpperCase
  6. 匹配方法
    • match 返回一个符合正则表达式的数组,而exec方法则会返回一个符合正则表达式的数组外,还有捕获组以及index和input的属性
    • replace 第一个参数为字符串或者正则,如果没有设定g flag,那么指挥替换第一个
    • search 从头往后查找模式,如果存在则返回索引,如果没有就返回-1
  7. 比较
    • localeCompare 如果字符串在字母表中中应该排在字符串参数之前,返回一个负数,如果字符串等于字符串参数,返回0,如果应该排在字符串参数之后,返回1
  8. String方法
    • String.fromCharCode
    • String.fromCodePoint

严格模式

  • 转换错误
    • 给未声明的变量赋值会报错 (不可以意外地创建全局变量)
    • 分配无效报错(比如writable设置为false的量,undefined和NaN赋值)TypeError,在正常模式下不会报错
    • 尝试delete一个无法delete的变量时候出错TypeError
    • 函数参数的名称的唯一的
    • 严格禁止8进制,如果有前导0会报错
    • 在原始值上设置属性,抛出TypeError
  • 简化变量的使用
    • 禁止with,否则不知道在内部定义的变量是全局的还是obj的(编译器可以很好地优化代码)
    • eval变量不影响外部作用于
    • 禁止删除普通名称(不是对象的属性)
  • argument
    • argument不跟踪相应的命名参数的值
    • argumen.callee不再支持
  • this
    • this将不会自动转化成对象(如apply等需要自己显式传this对象)
    • 指向window的this将会变成undefined、
  • 新增保留关键字

call、apply和bind的区别

  • 其中bind返回一个函数,call和apply直接执行
  • call接受一个this参数以及…args(多个argument参数)
  • apply接受一个this参数以及args数组或者类数组(NodeList以及HTMLCollection,argument之类的)
  • bind接受一个this参数以及若干参数,柯里化
  • 实现bind要点
    注意验证this是否函数
    验证是否new调用(使用new.target判断)
    将参数封装
    (A.apply(Object.create(A.prototype)),此时instanceOf就会误判)

ajax相关

fetch
用法:

  • const xhr = new XMLHttpRequest()
  • xhr.open(method, url, ?async, username, psw) 启动一个请求以备发送
  • xhr.send(data)主体,如果不需要通过主体发送数据,则必须传入null(某些浏览器必须的)
  • 请求完成后,属性会填充再xhr对象里面:
    • responseText
    • responseType
    • responseURL
    • responseXML
    • response
    • statusText
    • status
      方法:
  • setRequestHeader(),必须在open之后,send之前
  • send()
  • getResponseHeader()
  • getAllResponseHeaders() 由CRLF分隔的所有响应头,如字符串,或者null(没有收到响应)
  • abort() 如果请求已经发送,则终止请求。然后将状态设置为UNSENT
  • onprogress()
  • overrideMimeType 可以设置自定义类型,上传时候可以使用
  • .timeout 设置超时

状态

xhr.readyState,返回请求的状态。
xhr.onreadystatechange

readystate:

  • 0 UNSENT 对象创建,未open
  • 1 OPENED 已经open,未send
  • 2 HEADERS_RECEIVED 响应头和相应状态已经返回
  • 3 LOADING 响应体下载中
  • 4 DONE 请求完成

事件:(同下)

XMLHttpRequest.upload

该方法返回一个XMLHttpRequestUpload对象,可以用来监视上传的进度
事件:

  • loadstart 上传开始
  • progress 上传进度
  • abort 上传终止
  • error 错误,上传失败
  • load 上传成功
  • timeout 上传超时
  • loadend 上传结束,无论是load还是timeout还是error,都会触发这个事件

如果拔出网线,则会:

  1. 如果上传结束,那么触发xhr.onerror
  2. 如果上传未完成,那么触发xhr.onerror以及触发xhr.upload.onerror

兼容性问题

  1. IE11不支持responseType为json
  2. 某些低版本firefox和chrome不支持.timeout

XMLHttpRequest Level 2

  1. uploading progress events
  2. uploading/doloading binary data.

Blob Binary Large Object, 二进制大对象。Blob对象是二进制数据,类似于文件对象的二进制对象。File继承自Blob
Blob(blobParts [, option]) 第一个数组类型,如果数组内是对象,则会调用toString()方法
blob url 可以通过URL.createBlobURL()创建。(前端代码生成,供浏览器下载)
DATA URL 一般不是所有浏览器都支持通过XMLHttpRequest获取资源的,但是blob URL可以通过这个获取资源。bolb url一般比data url 要短
form data

跨域

页面间通讯

  1. 通过window.open打开,获得window对象,然后通过操作或者通过postMessage来进行通讯,缺点是只能与自己打开的页面进行通讯,应用面窄。但是在跨域中依旧可以进行通讯
  2. localStorage
    设置共享区域的storage,storage会触发storage事件
    window.onStorage
    写入操作的页面下不会触发事件
    重复设置相同值不会触发

由于sessionStorage只存在一个tab,没出现一个新的tab,都会产生一个新的sessionStorage

promise的实现

  • then返回一个promise

数组

  • 浅拷贝 Array.from(),解构(迭代器)
  • 深拷贝
    • 注意环、对象数组
    • 特殊类型的值
    • 方案:递归、JSON(JSON遇到环会报错,无法解析函数,对象忽略undefined,symbol,数组将NaN、null、undefined、infinity,symbol变成null)
  • 判断是否数组方法:
    • Array.isArray()
    • Object.prototype.toString.call()
    • instanceOf 这个方法不准确

parseInt

第二个参数代表进制

数组转字符串

  • join(‘’)
  • 直接调用 + ,自动转换
  • 迭代
  • reduce

字符串转数组

  • split()
  • 迭代

字符串转整数

  1. Number/ + / 等操作符
  2. parseInt
  3. parseFloat

事件机制

事件流

事件流描述的是从页面中接收事件的顺序。
IE的事件流是事件冒泡流,网景的是事件捕获流

DOM2规定的时间流包括三个阶段:事件捕获阶段、处于目标阶段以及事件冒泡阶段。

规范要求,捕获阶段不会涉及事件目标

Event对象上的3-5个属性或者方法

  1. event.target
  2. event.stopPropagation
  3. event.preventDefault
  4. event.currentTarget
  5. 各种按键以及相对位置

阻止冒泡

  • event.stopPropagation()
  • window.event.cancelBubble = true //ie

取消默认行为

  • event.preventDefault()
  • window.event.returnValue = false // ie

兼容写法

ie9兼容addEventListener写法

  1. addEventListener(‘’, fn, false) attachEvent(‘on..’, fn)
  2. removeEventListener(‘’, fn, false) detachEvent(‘on..’, fn)=43

React的事件处理机制

React事件机制
Source源码

  1. 几乎所有的事件代理(delegate)到document,达到性能优化的目的
  2. 对于每种类型的事件,拥有统一分发的函数dispatchEvent
  3. 事件对象event是合成对象(SyntheticEvent),不是原生事件

原理

  1. 事件注册:
    • React在组件加载和更新时候,ReactDOMComponent会对事件属性进行处理,对相关事件进行注册和存储。document中注册的事件不处理具体事件,而是对事件进行分发。ReactBrowserEventEmitter.listenTo作为事件注册的入口,担负着事件注册和事件触发。注册事件的回调函数由EventPluginHub来统一管理(采用listenerBank来进行处理),根据事件的类型(type)和组件标识(_rootNodeID)为key唯一标识事件并进行存储。
  2. 事件执行:
    • 事件执行的时候,document上绑定事件ReactEventListener.dispatchEvent会对事件进行分发,先获取原生对象的target,然后找到组件实例。循环将所有父组件获取,保存在数组中。
      ReactEventEmitter利用EventPluginHub中注入的plugins将原生事件转化为合成事件,然后批量执行存储的回调函数。
      回调函数执行的时候分为两步,第一步将所有合成事件放到事件队列里面,第二部是逐个执行。浏览器原生会为每一个事件的每个listener创建一个事件对象,但是这会造成高额内存分配,所以React在启动的时候就为每种对象分配内存池,用到某一个事件对象的时候可以从内存池中复用,节省内存。(对应享元模式)
  3. 无法使用event.stopPropagation()停止事件传播,需要使用React定义的event.preventDefault
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
/*
* - Top-level delegation is used to trap most native browser events. This
* may only occur in the main thread and is the responsibility of
* ReactDOMEventListener, which is injected and can therefore support
* pluggable event sources. This is the only work that occurs in the main
* thread.
*
* - We normalize and de-duplicate events to account for browser quirks. This
* may be done in the worker thread.
*
* - Forward these native events (with the associated top-level type used to
* trap it) to `EventPluginHub`, which in turn will ask plugins if they want
* to extract any synthetic events.
*
* - The `EventPluginHub` will then process each event by annotating them with
* "dispatches", a sequence of listeners and IDs that care about that event.
*
* - The `EventPluginHub` then dispatches the events.
* Overview of React and the event system:
*
* +------------+ .
* | DOM | .
* +------------+ .
* | .
* v .
* +------------+ .
* | ReactEvent | .
* | Listener | .
* +------------+ . +-----------+
* | . +--------+|SimpleEvent|
* | . | |Plugin |
* +-----|------+ . v +-----------+
* | | | . +--------------+ +------------+
* | +-----------.--->|EventPluginHub| | Event |
* | | . | | +-----------+ | Propagators|
* | ReactEvent | . | | |TapEvent | |------------|
* | Emitter | . | |<---+|Plugin | |other plugin|
* | | . | | +-----------+ | utilities |
* | +-----------.--->| | +------------+
* | | | . +--------------+
* +-----|------+ . ^ +-----------+
* | . | |Enter/Leave|
* + . +-------+|Plugin |
* +-------------+ . +-----------+
* | application | .
* |-------------| .
* | | .
* | | .
* +-------------+ .
* .
* React Core . General Purpose Event Plugin System
*/

JS上下文

作用域(scope)与上下文(context)是不同的。

作用域

作用域包括:全局作用域、局部作用域(函数作用域)、块作用域(es6)

上下文

上下文是指定代码特定部分中的this值
全局作用域中this为window,函数中的this取决于调用时环境,箭头函数取决于词法作用域(声明时绑定,不可更改绑定)
函数中this还会受到这些影响:bind、call、new、apply

严格模式下,函数上下文默认为undefined
You-Dont-Know-JS

继承 & 原型链

原型链

使用new操作符的时候发生了什么

  1. 创建一个对象
  2. 将这个对象的原型指向构造函数的原型
  3. 将this指向这个对象
  4. 执行代码
  5. 判断返回是否为一个对象,如果是,则返回结果,如果不是,则返回这个对象

实现继承的方法

  1. Object.create
  2. extends
    与组合继承相比:
    • 能够继承静态方法
    • extends继承与call不同,call是一开始就指定了this,导致无法调用基类的某些功能,但是extends是访问基类功能之后才修改this
  3. 组合继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function SuperType(name) {
this.name = name
this.colors = ['red', 'blue']
}

SuperType.prototype.sayName = function() {console.log(this.name)}

function SubType(name, age) {
SuperType.call(this, name) //借用构造函数
this.age = age
}

SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function(){console.log(this.age)}
  1. 借用构造函数

创建对象的方法

  • 工厂模式,方法无法共用,无法通过constructor识别对象
  • 构造函数方式,可以通过constructor识别对象,instanceof可以识别对象,通过new来创建实例,方法无法共用
  • 原型模式,对于引用类型会出现共用的情况
  • 构造函数和原型组合模式,解决了上面的缺点:引用类型,共享方法(一般采用的方法)
  • 动态原型方法(完美方案)添加一个条件判断,如果原型上由该方法就不再创建
  • 稳妥构造
  • 寄生构造

this

作用:

  • 作为对象使用
  • 作为函数调用
  • 作为构造函数调用
  • bind/apply/call

闭包

作用域链中包含了上一层作用域,所以即使上一层的作用域所在的函数执行完毕,该函数还是保留着作用域的引用,即可以访问到作用域的变量
应用:

  1. 模拟私有变量,例如debounce
  2. 单例模式
  3. 柯里化

异步流程控制方法

  • setTimeout\setInterval
  • requestAnimationFrame/ requestIdleCallback
  • Promise
  • async/await
  • xhr…
  • worker

promise 实现

要点:

  1. resolve, reject, then, catch,
  2. resolve 中 setTimeout 延时
  3. 判断状态,then方法中状态如果为fulfilled就直接调用
  4. 链式调用,then方法以及catch方法会返回一个promise

async/await实现

ES2016

  • Array.prototype.includes
  • ** 幂运算符

ES2017

  • Object.values() // 跟Object.keys()类似
  • Object.entries() // 可以直接将Object转化为Map
  • string.padStart(num, str) & padEnd() 在字符串首尾添加字符(指定数量和字符)
  • Object.getOwnPropertyDescriptor
  • Async/Await

asyn/await处理错误的方法

由于await返回一个promise,所以我们可以在await后面直接执行catch进行处理错误

1
2
3
async function test() {
const a = await someAction().catch(e => console.error(e))
}

还可以对整个函数catch:

1
test().catch(e => console.error(e))

通用方法:try-catch块

ES2018

  • promise.prototype.finally()
  • 异步循环 for-await-of
1
2
3
4
5
function test2() {
for await (const obj of promises) {
console.log(obj) // 输出1, 2, 3
}
}
  • 正则:unicode转义、lookbehind断言即(?<=)之类的、使用命名组Named Group、点.匹配所有字符(空白字符)(s flag)
  • 对象的...操作
  • 带标签的模板字面量限制被移除
  • 共享内存和原子性,由之前JS引擎管理内存变成自己管理内存。(SharedArrayBuffer)这个区域可以被主线程和web-worker共享,即多线程共用,引入原子性的全局对象提供多种方法保证正在被某个县城访问你的内存被锁住

HTMLEncode通常在哪个阶段做

  • 将变量输出到HTML的时候做,如渲染模板的时候
  • 可以破解:

0.1 + 0.2 !== 0.3

JavaScript’s Number type

执行上下文

  • 变量对象
  • 作用域链
  • this

变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。

全局上下文

全局上下文中的变量对象就是全局对象

函数上下文

在函数上下文中,用活动对象(activation object,AO)来表示变量对象

只有当进入一个执行上下文中,这个执行上下文的变量对象才被激活,只有被激活的活动对象,也就是活动对象上各种属性才能被访问。

活动对象是进入函数上下文时刻被创建,它通过函数的argument属性初始化。arguments属性值是arguments对象

执行过程

执行上下文的代码分成两个阶段执行:

  1. 进入执行上下文
  2. 代码执行

进入执行上下文

当进入执行上下文的时候,此时还没有执行代码
变量对象包括:

  1. 函数所有形参,如果没有,属性值设置为undefined
  2. 函数声明,由名称和对应值组成一个变量对象的属性被创建,如果变量对象已经存在相同名称的属性,则完成替换
  3. 变量声明,如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性

前端路由

移动端

URL到展现

  1. 加载慢怎么优化

React出现的意义

  1. 自由

React的优化(空间大)

  • redux
  • shouldComponentUpdate

安全

resful API

  • 动词 + 宾语(HTTP),如果服务器只接受两种方法(GET与POST),必须使用X-HTTP-Override: Method来指定
  • 宾语是名词,即URL下的目录一般都要求是名词
  • 避免多级URL,这种URL不利于拓展,一般除了第一级其他都使用查询字符串表示
  • 状态码必须精确,发生错误的时候不要返回200
  • 服务器回应要设为JSON格式,不要纯文本。(客户端请求时也要将ACCEPT设置为JSON)

静态资源放在另一个服务器上

  1. 不携带cookie,节省流量
  2. 浏览器对一个域名有访问上限
  3. CDN

canvas优化性能的方法

  1. 预渲染
  2. 所有点一起绘制
  3. 重绘不需要清除整个画布
  4. 多个画布一起工作
  5. requestAnimationFrame
  6. GPU

webpack底层实现原理

判断图片是否加载完成:onload & onerror

阅读全文 »
1…34567

everbrez

你能抓到我么?

everbrez
最喜欢的作品
最喜欢的女孩子
最喜欢的游戏
路人女主的养成方法、我的青春恋爱物语果然有问题
加藤惠
Minecraft

博客已萌萌哒运行(●'◡'●)ノ♥

© 2021 Hi, everbrez. 由 Hexo 强力驱动. Theme By Sagiri v0.0.74. 站点地图.

Made with by everbrez.