HTTP/2的多路复用实现原理:
http/2多路复用的机制是基于“帧”和“流”的概念提出的;http/2以前的传输是基于文本分割形式的消息,以换行符进行键值对的分割,服务端接收时无法预估其内存需求,在解析速度和传输效率上都有影响;http/2提出的帧对信息进行了封装,内部包含了帧标识和对应的流ID(流是由一个或多个帧组成的数据流);HTTP/2中的信息是以数据流的方式中进行传输的;这样,在一个TCP连接中可以直接发送多个流,通过帧中的流ID可以具体区分属于哪个请求;
ES7 async/await实现跑马灯
- 基础版本
const sleep = (delay) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, delay) }) } async function marquee(initialState=0) { while (true) { await sleep(3000); if (initialState === 0) { initialState = 1; } else { initialState = 0; } console.log('state: ', initialState) } } marquee()
类数组对象判断
类数组对象指的是:结构看起来像数组(具有length
属性,可以通过索引访问属性),但是不具有数组的方法;常见的有arguments
对象、HTMLCollection
、string
等;
类数组对象可以通过Array.from()
方法转换为数组对象:Array.from(arrayLike[, mapFn[, thisArg]])
// 来源于《JavaScript权威指南》
function isArrayLike(o) {
if(o && // o不是null、undefined等
typeof o === 'object' && // o是对象
isFinite(o.length) && // o.length是有限数值
o.length >= 0 && // o.length为非负值
o.length === Math.floor(o.length) && // o.length是整数
o.length < 4294967296) // o.length < 2^32
return true
else
return false
}
Redux引入中间件的意义
参见https://medium.com/@meagle/understanding-87566abcfb7a 和https://github.com/pshrmn/notes/blob/master/redux/redux-middleware.md
根据Redux middleware
介绍:
中间件提供了一个用于与那些已经被
dispatch
到store
但是没有到达reducer
的action
的方法;中间件的应用实例包括日志记录、错误上报、异步请求、dipatch
新的action
等;
默认情况下,redux
只支持同步数据流;redux
中间件的引入主要目的是为了集中处理异步action
;这样既不破坏redux原本的三大原则,又能将异步action
相对统一地管理起来;
当然,Redux Middleware
的实质还是对store.dispatch
进行重写,然后使用修改后的dispatch
去派发一个action
;
React super的两种用法
详情见https://blog.csdn.net/luofeng457/article/details/101864677
- 函数调用
super()
,通常用于构造函数中 - 访问父类原型链属性或方法:
super.prop
或super[prop]
React Higher Order Components in depth
React Context
-
特点:主要用于多个不同层级的子组件共享相同状态,不需要像
props
一样逐层传递,不便于管理和维护;Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言
-
API:
React.createContext
、Context.Provider
、Context.Consumer
、Context.displayName
、class.contextType
-
React.createContext(defaultValue)
const MyContext = React.createContext(defaultValue); MyContext.displayName = 'customizedDisplayName`; <MyContext.Provider value={/*xxx*/} /> <MyContext.Consumer> { value => <div>xxx<div>} </MyContext.Consumer>
-
context.Provider
可以进行嵌套,内层属性的优先级更高,一个context.Provider
内层可以对应多个context.Consumer
;当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
-
class.contextType
class Test extends React.Component { componentDidMount() { let value = this.context; } componentDidUpdate() { let value = this.context; } render() { let value = this.context; /* 基于Mycontext的共享状态渲染组件 */ } } Test.contenxtType = MyContext; // 指定类的contextType建立联系
-
context.displayName
:控制react devTools
中查看时显示的context名称,相当于MyContext的别名;
-
前端埋点
小组内活动营销页面的投递:
定义了action
和ttype
:前者用于表明页面行为,后者进行类型区分;
action
取值:view
(页面初始化加载)、stay
(页面停留)、show
(弹窗展现)、click
(点击事件)、request
(接口请求)
初始化公共投递数据(终端类型、网站类型、活动id、用户ip、uid等);
缺点:目前的投递都是依赖于开发手动添加,而投递js文件仅暴露出对应的方法在自己的事件处理函数中调用,与业务严重耦合:比如点击投递,投递js并不会去监听,而是需要业务中去手动添加监听事件,然后手动调用投递js暴露出的与click相关的投递方法;
待优化。。。
JSBridge
https://segmentfault.com/a/1190000010356403
https://juejin.im/post/5abca877f265da238155b6bc
JSBridge相当于一个桥接器,构建了一个可以用于Native
和JavaScript
相互通信的全双工通道;主要包括了:
- H5调用Native:调用Native的原生功能、通知Native当前JS的状态;包括
注入API
和URL Scheme
两种方式; - Native调用H5:回溯调用结果、通知JS当前Native的状态、消息推送等;
Native 调用 JavaScript,其实就是执行拼接 JavaScript 字符串,从外部调用 JavaScript 中的方法,因此 JavaScript 的方法必须在全局的 window 上
CSRF
CSRF(Cross-Origin Request Forgery),即跨站请求伪造——表示攻击者假借用户的身份伪造用户请求,并以用户的身份去执行某些操作,比如发送email、银行转账等,造成用户隐私泄露和财产损失。
CSRF曾被列为互联网20大安全隐患之一,2007年Gmail也因CSRF攻击使用户遭受巨大损失。
更多信息参考https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/index.html
-
CSRF 攻击实例
CSRF 攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作。比如说,受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外
-
CSRF攻击对象
在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。
-
防御
- 尽量使用
POST
请求(攻击者仍可以通过form表单等形式伪造请求) - 加入验证码
- 验证
HTTP Referer
字段(部分浏览器支持修改Referer) - Anti CSRF Token
- 加入自定义的Header(加自定义头,值为随机token)
- 尽量使用
求多维数组深度
function getArrayDepth(arr, count=0, len=[]) {
for (let i in arr) {
if (Array.isArray(arr[i])) {
count++;
getArrayDepth(arr[i], count, len);
} else {
len.push(count);
}
}
return Math.max.apply(null, len) + 1;
}
flex实现换行
描述: 使用flex实现一个类似于br
元素换行效果的元素;
解析: flex-wrap
可以让容器内的项目在必要的时候进行换行;flex-basis
指定了元素在主轴方向上的初始大小;因此可以结合这两个属性实现
demo如下,如果不设置flex-wrap
,那么默认不会换行,div.box
的项目大小会被缩放,剩余空间被div.br
占据;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.root {
display: flex;
width: 100%;
border: 1px solid greenyellow;
flex-wrap: wrap;
}
.box {
width: 100px;
height: 100px;
border: 1px solid orange;
}
.br {
flex-basis: 100%;
}
</style>
</head>
<body>
<div class="root">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="br"></div>
<div class="box"></div>
<div class="box"></div>
<div class="br"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</body>
</html>
HTTPS
-
https的通信过程:
使用无SSL/TLS的HTTP协议进行通信时,消息相当于在裸奔,存在被窃取或者篡改的可能性;SSL/TLS的提出就是为了解决http通信的安全问题;SSL(Secure Socket Layer)即安全套接层,最早由
Netscape
提出,主要用于数据安全问题,后期逐渐演化为一种标准;TLS(Transport Layer Security)即传输层安全协议,是SSL的继任者,主要的功能包括:数据加密、身份认证、消息完备性校验;是位于传输层和应用层之间的一种协议;参考阮一峰文章,
SSL/TLS
的握手过程大概如下:Client
发送SSL/TLS
协议的版本号、一个Client
生成的随机数及客户端支持的加密算法;Server
选择用于通信的加密算法,然后返回数字证书及一个Server
生成的随机数;Client
确认证书有效性,然后发送一个由数字证书公钥加密的随机数到Server
;Server
使用数字证书私钥解密该随机数;- 此时,
Client
和Server
都拥有三个相同的随机数;双方按照之前约定的加密算法, 使用这三个随机数生成对话密匙,用于加密后续的数据通信过程;
(三个随机数是为了保证对称密匙的可靠性)
加密分为对称加密和非对称加密,对称加密使用同样的秘钥进行加密和解密,非对称加密使用一对秘钥——公钥和私钥,使用其中一个加密,另一个解密;非对称加密更加安全,但由于算法复杂度等因素导致其性能开销较大;对称加密传输速度快,但是存在一定的安全风险;
为了在性能和安全性方面
make a trade off
,通常会在对话密匙生成阶段使用非对称加密,在数据传输时使用对话密钥进行对称加密; -
证书认证过程:
前面提到,
Client
与Server
间的内容传输采用的是对称加密的方式,使用的是由三个随机数加密生成的密匙;Client
向Server
发送的第二个随机数是由数字证书公钥加密的;为了保证数据通信的可靠性,必须要对证书进行认证;服务器返回的站点证书一般包含了证书的有效期、服务器名称、服务器公钥、发证机构名称及发证机构对证书的签名、数字摘要计算方法以及证书对应的域名;发证机构会对证书信息hash得到证书的数字摘要,然后使用发证机构的私钥加密证书摘要;客户端拿到证书后,可以使用CA的私钥进行解密得到证书的信息摘要;同时使用数字摘要的方法对证书信息进行计算同样得到一份信息摘要;如果两者一致,则表示证书未被篡改;
一般可以通过发证机构来保证服务器站点的可靠性,而服务器站点的可靠性则通过上一级的中间证书商进行认证;这样逐级回溯,最终可以追溯到根证书机构;而根证书机构一般是固定的,并且是被浏览器识别的,是可信的;因此证书认证是通过证书信任链确保服务器站点的可靠性;
另外,
证书颁发者一般提供两种方式来验证证书的有效性: CRL 和 OCSP
其中
CRL(Certificate Revocation List)
即证书吊销名单,表示发证机构维护的一份失效证书名单,客户端缓存在本地并定期更新,并作为证书有效性查询参考;OCSP(Online Certificate Status Protocol)
即在线证书状态协议,是由发证机构提供的一份实时查询某个证书有效性的接口,延时较大;
下图作为参考,图片来源未考证,如有侵权,请联系本人~
CSS之line-height
line-height
取值:
normal
:与客户端浏览器相关,一般浏览器取值为1.2,具体还与font-size
有关;number
:这是推荐使用的line-height
设置方式,实际值是作用的目标元素自身的font-size
与number
的乘积;发生line-height
继承时,继承的是line-height: number
而不是计算后的实际值;percentage
:该方式可能会导致出现意料之外的值;它的计算是基于设置了line-height
的元素的font-size
与百分比的乘积;发生继承时,继承的是计算后的实际值;length
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.wrapper {
width: 100%;
height: 100px;
font-size: 20px;
line-height: 1.5;
}
.box-1 {
font-size: 16px;
}
.container {
width: 100%;
height: 100px;
font-size: 20px;
line-height: 150%;
}
.box-2 {
font-size: 16px;
}
</style>
</head>
<body>
<div class="wrapper"> // font-size: 20px; line-height: 30px;
<div class="box-1"></div> // font-size: 16px; line-height: 24px;
</div>
<div class="container"> // font-size: 20px; line-height: 30px;
<div class="box-2"></div> // font-size: 16px; line-height: 30px;
</div>
</body>
</html>
求两个数组的交集
**问题描述:**给定两个数组,写一个函数用于计算它们的交集
**提示:**返回结果中的交集元素数要与在原数组中出现的次数相同;
解法一:
很容易想到的就是hashmap
;该解法是利用空间复杂度换取事件复杂度,空间复杂度和时间复杂度都是O(n)
;
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersect = function(nums1, nums2) {
const map = {};
const res = [];
for (let i=0, len=nums1.length; i<len; i++) {
if (!map[nums1[i]]) {
map[nums1[i]] = 1;
} else {
map[nums1[i]]++;
}
}
for (let i=0, len=nums2.length; i<len; i++) {
if (map[nums2[i]]) {
res.push(nums2[i]);
map[nums2[i]]--;
}
}
return res;
};
解法二:
使用数组自身的方法,
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersect = function(nums1, nums2) {
const res = [];
nums1.map(ele => {
let idx = nums2.indexOf(ele);
if (idx > -1) {
res.push(ele);
nums2.splice(idx, 1);
}
})
return res;
};
React生命周期函数
- React生命周期函数
- demo及测试
// App.js
import React from 'react';
import Child from './child.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
console.log('constructor---App');
}
componentWillMount() {
console.log('componentWillMount---App');
}
componentDidMount() {
console.log('componentDidMount---App');
}
componentWillReceiveProps() {
console.log('componentWillReceiveProps---App');
}
shouldComponentUpdate() {
console.log('shouldComponntUpdate---App');
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate--App');
}
componentDidUpdate() {
console.log('componentDidUpdate---App');
}
handleClick() {
this.setState((state,props) => {
return {count: state.count + 1}
})
}
render() {
console.log('render---App');
return (
<div>
<button onClick={this.handleClick.bind(this)}>Count</button>
<Child count={this.state.count} />
</div>
)
}
}
export default App;
// Child.js
import React from 'react';
import Min from './Min.js';
class Child extends React.Component {
constructor() {
super();
console.log('constructor---Child');
}
componentWillMount() {
console.log('componentWillMount---Child');
}
componentDidMount() {
console.log('componentDidMount---Child');
}
componentWillReceiveProps() {
console.log('componentWillReceiveProps---Child');
}
shouldComponentUpdate() {
console.log('shouldComponntUpdate---Child');
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate--Child');
}
componentDidUpdate() {
console.log('componentDidUpdate---Child');
}
render() {
console.log('render---Child');
return (
<div>
<Min count={this.props.count} />
</div>
)
}
}
export default Child;
// Min.js
import React from 'react';
class Min extends React.Component {
constructor() {
super();
console.log('constructor---Min');
}
componentWillMount() {
console.log('componentWillMount---Min');
}
componentDidMount() {
console.log('componentDidMount---Min');
}
componentWillReceiveProps() {
console.log('componentWillReceiveProps---Min');
}
shouldComponentUpdate() {
console.log('shouldComponntUpdate---Min');
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate--Min');
}
componentDidUpdate() {
console.log('componentDidUpdate---Min');
}
render() {
console.log('Min');
return (
<div>
{this.props.count}
</div>
)
}
}
export default Min;
首次加载时:
Reac更新时