js随笔

血红的双手。 提交于 2019-11-27 04:49:12

参考链接:https://juejin.im/post/5d23e750f265da1b855c7bbe
https://github.com/ly2011/blog/issues/153

7 ES5/ES6 的继承除了写法以外还有什么区别

class Super {}
class Sub extends Super {}

const sub = new Sub();

Sub.__proto__ === Super;
子类可以直接通过 proto 寻址到父类。

function Super() {}
function Sub() {}

Sub.prototype = new Super();
Sub.prototype.constructor = Sub;

var sub = new Sub();

Sub.__proto__ === Function.prototype;
而通过 ES5 的方式,Sub.__proto__ === Function.prototype

相关问题 : js的继承问题

// 寄生组合式继承
// 通过借用构造函数来继承属性, 通过原型链来继承方法
// 不必为了指定子类型的原型而调用父类型的构造函数,我们只需要父类型的一个副本而已
// 本质上就是使用寄生式继承来继承超类型的原型, 然后再讲结果指定给子类型的原型
function object(o){ // ===Object.create()
  function F(){};
  F.prototype = o;
  return new F();
}
function c1(name) {
  this.name = name;
  this.color = ['red', 'green'];
}
c1.prototype.sayName = function () {
  console.log(this.name);
}
function c2(name, age) {
  c1.call(this, name)
  this.age = age
}
// 第一步:创建父类型原型的一个副本
// 第二步:为创建的副本添加 constructor 属性, 从而弥补因重写原型而失去的默认的 constructor 属性
// 第三步:将新创建的对象(即副本)赋值给子类型的原型
function inheritPrototype(superType, subType) {
  const prototype = object(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype;
}

inheritPrototype(c1, c2);
// c2的方法必须放在寄生继承之后
c2.prototype.sayAge = function () {
  console.log(this.age);
}

更直接的babel转义之后的class长什么样看看。

11 将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组

主要是数组的api了

Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b})

不用原生的避免不了循环判断怎么都浪费性能

12 JS 异步解决方案的发展历程以及优缺点

1、回调,不够简洁,逻辑混乱
2、promise 逻辑清晰 异常捕获是个问题,只能 reject中捕获 无法取消,这是个问题一执行必须有结果。
3、yeild Generator 可以配合co来使用,特点就是可控制可
4、 async 和await 代码清晰,缺点 如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

await 就是 generator 加上 Promise的语法糖,且内部实现了自动执行 generator

13 Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

构造函数是同步执行的,then方法是异步的,即微任务。及时resolve同步调用。

14 实现一个new

知道原理不用死记硬背
new即实例化一个对象,最终结果是一个构造函数。
目的是该对象获得 示例和原型属性。实例属性就是this 指向,原型通过原型来实现。

function new_1 (fn){
    var obj = Object.create(fn.prototype)
    var res = fn.call(obj)
    // 少的这一步在于new 是这样的构造函数执行之后 如果显式return 对象 则是该对象,否则就是obj
    return res instanceof fn ? res : obj
}

这里使用var obj = Object.create(fn.prototype) 而非 var obj = Object.create(fn.prototype)

就是直接使用了Object.create的特性,基于当前对象new一个实例。

function create(obj){
    // 实现了该对象的一个示例
    var fn= function (){}
    fn.prototype = obj
    return new fn()
}

15 http2 多路复用

有个概念就行,同一个http请求,可以进行多个task,而不用重新建立连接
比http1 的keep-alive更优,即原生支持

16 TCP三次握手和四次挥手的理解

TCP 提供一种面向连接的、可靠的字节流服务
在一个 TCP 连接中,仅有两方进行彼此通信。广播和多播不能用于 TCP
TCP 使用校验和,确认和重传机制来保证可靠传输
所以每次都需要 校验 确认 这两步确认连接

三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。

三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手

第一次握手(SYN=1, seq=x):

客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。

发送完毕后,客户端进入 SYN_SEND 状态。

第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):

服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。

第三次握手(ACK=1,ACKnum=y+1)

客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1

发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。

总结起来就是
1、客户端携带标识位,指明我是谁,要与哪个端口建连接了
2、服务端收到响应,确认客户端身份,表明自己身份和接收数据能力,返回客户端
3、客户端接收之后,确认身份,并且可以接受,再表明自己可以接收数据,发给服务端

服务端收到之后,握手结束。

关闭挥手呢就是四次

也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。

第一次挥手(FIN=1,seq=x)

假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。

发送完毕后,客户端进入 FIN_WAIT_1 状态。

第二次挥手(ACK=1,ACKnum=x+1)

服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。

发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。

第三次挥手(FIN=1,seq=y)

服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。

发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。

第四次挥手(ACK=1,ACKnum=y+1)

客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。

服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。

客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。

1、第一次客户端挥手,表示自己要关闭,发出关闭请求
2、服务端收到信息,挥手表示已知晓,但可能有任务要执行,不能立即结束
3、服务端发出关闭消息,表示进入ready状态,可以结束
4、客户端等待客户端关闭消息,回复确认包,我已知晓可以关闭。 服务端收到之后就关闭。客户端等待一端时间,确认无消息,则关闭。

概述就是:
三次握手:客户端发起,目的是鉴权和验证通讯能力
1、客户端携带身份标识,告诉客户端身份和要建立链接的操作
2、服务端收到之后,同样返回身份和 标识,带上自己身份和数传输能力
3、客户端收到服务端鉴权信息和数据,之后确认是对的人和时间。把服务端返回的数据发过去,再证明一下可以通信。 双方进入可通信状态,握手结束。

挥手:
1、客户端 发出断开请求
2、服务端接受 返回信息标识收到,但还没结束任务
3、结束当前任务,返回信息,表明进入可结束状态
4、客户端收到消息,确认一下,表明服务端可以关闭,服务端就关闭,客户端等一会也关闭。

TCP KeepAlive
TCP 的连接,实际上是一种纯软件层面的概念,在物理层面并没有“连接”这种概念。TCP 通信双方建立交互的连接,但是并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会。在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启等各种意外,当这些意外发生之后,这些 TCP 连接并未来得及正常释放,在软件层面上,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,为了解决这个问题,在传输层可以利用 TCP 的 KeepAlive 机制实现来实现。主流的操作系统基本都在内核里支持了这个特性。

TCP KeepAlive 的基本原理是,隔一段时间给连接对端发送一个探测包,如果收到对方回应的 ACK,则认为连接还是存活的,在超过一定重试次数之后还是没有收到对方的回应,则丢弃该 TCP 连接。

TCP-Keepalive-HOWTO 有对 TCP KeepAlive 特性的详细介绍,有兴趣的同学可以参考。这里主要说一下,TCP KeepAlive 的局限。首先 TCP KeepAlive 监测的方式是发送一个 probe 包,会给网络带来额外的流量,另外 TCP KeepAlive 只能在内核层级监测连接的存活与否,而连接的存活不一定代表服务的可用。例如当一个服务器 CPU 进程服务器占用达到 100%,已经卡死不能响应请求了,此时 TCP KeepAlive 依然会认为连接是存活的。因此 TCP KeepAlive 对于应用层程序的价值是相对较小的。需要做连接保活的应用层程序,例如 QQ,往往会在应用层实现自己的心跳功能。

心跳的功能除了这里还有就是容灾降级

18 React 中 setState 什么时候是同步的,什么时候是异步

因为React的合成事件和声明周期中的行为都会有事务机制,异步操作state,以便于对于互斥操作的处理,
所以setState会加入队列中,等待执行。
而原生事件和settimeout等事件,则isBatchingUpdates为fasle 可以同步执行

例子就是:

componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log 0

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log 0

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log 2

      this.setState({val: this.state.val + 1});## 有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣
      console.log(this.state.val);  // 第 4 次 log 3
    }, 0);
  }

20 介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?

参考链接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/22

21 有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣

Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()

1、toString 兼容性较好
2、 instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype

但 instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。,如果被修改了就有问题了

3、 Array.isArray 兼容性有问题

instanceof不能检测来自iframe的数组。就是官方文档上,window.frames[xx].Array构造出来的数组。

这里牵扯到安全相关的问题,xss和csrf 攻击的区别
cookie在于网站会自动获取 并提交。
csrf 跨站请求伪造这种是攻击者盗用了当前网站的cookie。

主要发生在正规网站未推出,同时浏览其他网站,导致cookie被利用。其他网站此时请求正规信息相当于正常用户

1、尽量使用POST,限制GET
2、将cookie设置为HttpOnly
3、增加token,脱离cookie额外进行一层验证
4、 通过Referer识别 来源

参考链接:https://www.jianshu.com/p/67408d73c66d

xss则是利用脚本代码提交到服务端,再次解析之后进行攻击,此类为反射型。

另一种是存储性,将脚本落在数据库中。
例如,张三发了一篇帖子,李四进行回复:但内容却是一段js脚本,这篇帖子被他人浏览的时候就会中招,例子中的只是一个alert(),但脚本可以写的比较复杂一点盗用用户cookie等等操作。

别人浏览时,js脚本可以获取当前cookie等信息,发送到目标服务器上。

所以应对就是:
1、入参过滤
2、出参编码
3、httponly也是有些作用的。
参考链接:https://www.cnblogs.com/mao2080/p/9460397.html

http://blog.nsfocus.net/xss-advance/

30 请把两个数组 ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'] 和 ['A', 'B', 'C', 'D'],合并为 ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']。

function concat(arr1 = [],arr2=[]){
    // 复杂度
    let len2 = arr2.length,
        i=0
    for(i;i<len2;i++){
        let item = arr2[i],
            index = 3*i+2
        arr1.splice(index,0,item)
    } 
    return arr1
}

31 改造下面的代码,使之输出0 - 9,写出你能想到的所有解法

for (var i = 0; i< 10; i++){
    setTimeout(() => {
        console.log(i);
    }, 1000)
}

原因在于循环结束之后 执行定时器任务,此时变量为全局作用域,共享,隔离开即可

这个题比较常见了,就是从闭包到let到promise都可以泳道

闭包

for (var i = 0; i < 10; i++) {
    (function(i){
        setTimeout(() => {
            console.log(i);
        }, 1000)
    })(i)
}

let 变量

for (let i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i);
    }, 1000)
}

说白了就是不让公用当前变量,基本类型形参是值传递
跟闭包类似

for (var i = 0; i < 10; i++) {
    function run(index){
        setTimeout(() => {
            console.log(index);
        }, 1000)
    }
    run(i)
}

try-catch 有点秀

for(var i = 0; i < 10; i++){ 
  try{
     throw i;
  }catch(i){
     setTimeout(() => { console.log(i); },1000)    
  }
} 

32 Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的想法

是否快,跟实现算法有关。
在js中 操作dom其实是瓶颈,虚拟dom是将多个操作操作合并为一次。

33 下面的代码打印什么内容,为什么? C

var b = 10;
(function b(){
    b = 20;
    console.log(b); 
})();

这个是想问变量提升,不过IFF函数 中。 不过函数名是b有点蛋疼了,执行时,向上查找显然先当前区间应该是function

这个说的有道理

var b = 10;
(function b() {
   // 内部作用域,会先去查找是有已有变量b的声明,有就直接赋值20,确实有了呀。发现了具名函数 function b(){},拿此b做赋值;
   // IIFE的函数无法进行赋值(内部机制,类似const定义的常量),所以无效。
  // (这里说的“内部机制”,想搞清楚,需要去查阅一些资料,弄明白IIFE在JS引擎的工作方式,堆栈存储IIFE的方式等)
    b = 20;
    console.log(b); // [Function b]
    console.log(window.b); // 10,不是20
})();

几个例子:

var b = 10;
(function b() {
   // 内部作用域,会先去查找是有已有变量b的声明,有就直接赋值20,确实有了呀。发现了具名函数 function b(){},拿此b做赋值;
   // IIFE的函数无法进行赋值(内部机制,类似const定义的常量),所以无效。
  // (这里说的“内部机制”,想搞清楚,需要去查阅一些资料,弄明白IIFE在JS引擎的工作方式,堆栈存储IIFE的方式等)
    b = 20;
    console.log(b); // [Function b]
    console.log(window.b); // 10,不是20
})();

所以严格模式下能看到错误:Uncaught TypeError: Assignment to constant variable

var b = 10;
(function b() {
  'use strict'
  b = 20;
  console.log(b)
})() // "Uncaught TypeError: Assignment to constant variable."

其他情况例子:

有window:

var b = 10;
(function b() {
    window.b = 20; 
    console.log(b); // [Function b]
    console.log(window.b); // 20是必然的
})();

有var:

var b = 10;
(function b() {
    var b = 20; // IIFE内部变量
    console.log(b); // 20
   console.log(window.b); // 10 
})();

34 简单改造下面的代码,使之分别打印 10 和 20

var b = 10;
(function b(){
    b = 20;
    console.log(b); 
})();
var b = 10;
(function b(b){
    console.log(b)
    b = 20;
    console.log(b); 
})(b);

35 浏览器缓存读取规则

Service Worker、Memory Cache、Disk Cache 和 Push Cache,那请求的时候 from memory cache 和 from disk cache 的依据是什么,哪些数据什么时候存放在 Memory Cache 和 Disk Cache中?

这个还是有研究的

不易变的css之类from disk hash字符串的js 来自 disk cache

参考这个:https://www.jianshu.com/p/54cc04190252

36 使用迭代的方式实现 flatten 函数。

flatten 函数就是把数组拍平,实现也比较粗暴
判断元素是否为数组,是则继续遍历。
另一种比较取巧,转成字符串,然后再截断,即可。

let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]]
// 迭代实现
const flatten = function (arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }
    return arr
}

console.log(flatten(arr)
// 递归
const flatten = array => array.reduce((acc, cur) => (Array.isArray(cur) ? [...acc, ...flatten(cur)] : [...acc, cur]), [])
// 字符串
arr.join(',').split(',').map(item => Number(item)))

37 为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作?

vue用的不是很多,所以不是很清楚mutation里面为什么不能有异步操作,下面解释一下为什么Redux的reducer里不能有异步操作。

先从Redux的设计层面来解释为什么Reducer必须是纯函数
如果你经常用React+Redux开发,那么就应该了解Redux的设计初衷。Redux的设计参考了Flux的模式,作者希望以此来实现时间旅行,保存应用的历史状态,实现应用状态的可预测。所以整个Redux都是函数式编程的范式,要求reducer是纯函数也是自然而然的事情,使用纯函数才能保证相同的输入得到相同的输入,保证状态的可预测。所以Redux有三大原则:

单一数据源,也就是state
state 是只读,Redux并没有暴露出直接修改state的接口,必须通过action来触发修改
使用纯函数来修改state,reducer必须是纯函数
下面在从代码层面来解释为什么reducer必须是纯函数
那么reducer到底干了件什么事,在Redux的源码中只用了一行来表示:

currentState = currentReducer(currentState, action)
这一行简单粗暴的在代码层面解释了为什么currentReducer必须是纯函数。currentReducer就是我们在createStore中传入的reducer(至于为什么会加个current有兴趣的可以自己去看源码),reducer是用来计算state的,所以它的返回值必须是state,也就是我们整个应用的状态,而不能是promise之类的。

要在reducer中加入异步的操作,如果你只是单纯想执行异步操作,不会等待异步的返回,那么在reducer中执行的意义是什么。如果想把异步操作的结果反应在state中,首先整个应用的状态将变的不可预测,违背Redux的设计原则,其次,此时的currentState将会是promise之类而不是我们想要的应用状态,根本是行不通的。

其实这个问题应该是Redux中为什么不能有副作用的操作更合适。

38 下面代码中 a 在什么情况下会打印 1?

var a = ?;
if(a == 1 && a == 2 && a == 3){
    console.log(1);
}

什么情况下,这也就用了隐式类型转换的内容了,
a 要满足下面是三个 == 所以要自定义valueOf了,每次执行一次 value要更新:

var a = {
  val:1,
  valueOf:function(){
    return this.val++
  }
}

另一种数组:

var a = [1,2,3];
a.join = a.shift;
if(a == 1 && a == 2 && a == 3) {
  console.log('1');
}

es6 Symbol

let a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};
if(a == 1 && a == 2 && a == 3) {
  console.log('1');
}

另外还有generate函数
每次执行的结果不同:

let a = {
    gn: (function* () {
        yield 1;
        yield 2;
        yield 3;
    })(),
    valueOf() {
        return this.gn.next().value;
    }
};

defineProperty getter

Object.defineProperty(window, 'a', {
    get: function() {
          return this.value = this.value ? (this.value += 1) : 1;
    }
});

总得来说就是在get值的时候 同时执行函数,更新值

39 BFC 及其应用

BFC 块级格式上线文
BFC 就是块级格式上下文,是页面盒模型布局中的一种 CSS 渲染模式,相当于一个独立的容器,里面的元素和外部的元素相互不影响。创建 BFC 的方式有:

html 根元素
float 浮动
绝对定位
overflow 不为 visiable
display 为表格布局或者弹性布局
BFC 主要的作用是:

清除浮动
防止同一 BFC 容器中的相邻元素间的外边距重叠问题

参考链接:https://github.com/glitchboyl/blog/issues/6

40 在 Vue 中,子组件为何不可以修改父组件传递的 Prop

会导致父组件的数据不一致问题,单向数据流向。

41 下面代码输出什么 C

var a = 10;
(function () {
    console.log(a) //undefined
    a = 5
    console.log(window.a) // 10
    var a = 20;
    console.log(a) // 20
})()

想的太复杂,掌握的不是太清楚
这里就是IIFE 函数作用域是与全局作用域隔离的,
同样具有变量提升的效果,所以第一和第三输出就一样了
二次的输出,就是外层的a了,不相互影响。

其实作用域和正常的函数没什么差别

var a = 10;
function b() {
    console.log(a)
    a = 5
    console.log(window.a)
    var a = 20;
    console.log(a)
}
b()

具名函数也是一样的输出

IIFE 参考链接:https://zhuanlan.zhihu.com/p/22465092

IFEE 相当于创建一个匿名函数并执行,当函数被理解成表达式时就会执行,如+(function(){})(),,作用是避免全局污染

42 实现一个 sleep 函数

等待执行,就是延时,promise 、Generator、Async/Await 等方式可以实现。

43 使用 sort() 对数组 [3, 15, 8, 29, 102, 22] 进行排序,输出结果

function _sort (arr = []){
    var a = arr.sort && arr.sort((prev,next)=>{
        return prev- next
    })
    console.log(a)
}

44 介绍 HTTPS 握手过程 c https相关内容需要复习

参考 https://developers.weixin.qq.com/community/develop/article/doc/000046a5fdc7802a15f7508b556413
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
https://zhuanlan.zhihu.com/p/25587986

SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。
即非对称加密,但有两个问题:
1、如何保证公钥不被篡改?
解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。

2、公钥加密计算量太大,如何减少耗用的时间
每一次对话(session),客户端和服务器端都生成一个"对话密钥"(session key),用它来加密信息。由于"对话密钥"是对称加密,所以运算速度非常快,而服务器公钥只用于加密"对话密钥"本身,这样就减少了加密运算的消耗时间。

公钥只是用以握手阶段验证信息。

数字证书用以确认服务端身份

有了证书之后,当你的浏览器在访问某个 HTTPS 网站时,会验证该站点上的 CA 证书(类似于验证介绍信的公章)。如果浏览器发现该证书没有问题(证书被某个根证书信任、证书上绑定的域名和该网站的域名一致、证书没有过期),那么页面就直接打开;否则的话,浏览器会给出一个警告,告诉你该网站的证书存在某某问题,是否继续访问该站点

数字证书有点反过来:用私钥进行加密,用公钥进行解密。CA用自己的私钥对服务端的信息(包含服务端公钥)进行加密,由于客户端无条件信任CA,所以已经提前知道CA的公钥,当她收到服务端证书的时候,只要用CA的公钥对Bob证书内容进行解密,发现能否成功解开(还需要校验完整性)

这就是验证服务端身份的方式。

"握手阶段"涉及四次通信,我们一个个来看。需要注意的是,"握手阶段"的所有通信都是明文的。
整个通信过程可以概括为:
(1) 客户端向服务器端索要并验证公钥。

(2) 双方协商生成"对话密钥"。

(3) 双方采用"对话密钥"进行加密通信。


1、客户端发出请求ClientHello
客户端发出请求,表示要进行加密通信,即索要服务器的公钥,内容包括:
  • 支持的协议版本
  • 随机数,用于生成对话秘钥
  • 支持的加密方式
    2、服务器回应(SeverHello)
    针对客户端的请求,一是返回证书,二是确认加密协议,三是生成随机数

  • 确认使用的加密通信协议版本,如果后续不一致则服务器关闭
  • 服务器证书
  • 服务器生成的随机数,用于生成对话秘钥
  • 确认使用的加密方法

3、客户端回应
收到服务器的证书之后,客户端先验证证书,证书合格则取出服务器公钥,根据该公钥加密一个随机数,用于后面生成key,即三个随机数

如何验证证书。证书是CA发布的,去CA验证即可。

证书里是服务器的公钥和身份信息,这些是经过证书颁发机构即CA公证过的,客户端拿到后去CA验证,看这个公钥是不是服务器的,若不是说明证书是伪造的,当然如果证书经过数字签名证书就不可能被伪造了

  • 一个随机数(pre-master key)。该随机数用服务器公钥加密,防止被窃听
  • 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
  • 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。

pre-master key 用服务器的公钥加密,可以防止第三方冒充服务器,因为任何人都可以获取已经公开的服务器公钥与客户段进行通信。想要获得 pre-master key 明文只有利用服务器的私钥进行解密,所以这里进行了服务器的身份验证,也防止了中间人攻击。

也就是第三个随机数是需要服务器用私钥解密的,所以没有私钥,第三方无法冒充服务端,当然私钥泄露,那就没办法了。

如果需要验证客户端身份:
客户端在发送 pre-master key 之前需要发送客户端公钥到服务器,可以选择对 pre-master key 用客户端的私钥签名然后再用服务器公钥加密,则服务器收到 pre-master key 同时对客户端进行了身份验证。

4、服务器的最后回应
服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的"会话密钥"。然后,向客户端最后发送下面信息。

  • 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送
  • 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。

服务器此时也得到了三个随机数,

这里主要是没有解释清楚三个随机数如何利用起来的,理解下来就是双方都保存三个随机数+

hash内容的用处在于确认完整性

单向Hash函数可以把输入变成一个定长的输出串,其特点就是无法从这个输出还原回输入内容,并且不同的输入几乎不可能产生相同的输出,即便你要特意去找也非常难找到这样的输入(抗碰撞性),
因此客户端只要将明文内容做一个Hash运算得到一个Hash值,并一起加密传递过去给服务端。
Hacker即便篡改了内容,服务端解密之后发现拿到的内容以及对应计算出来的Hash值与传递过来的不一致,说明这个包的完整性被破坏了。

非对称加密的优势
任何人都可以通过拿到Bob公开的公钥对内容进行加密,然后只有Bob自己私有的钥匙才能解密还原出原来内容。
hacker无法对内容解密,所以第三个随机数就无法获知。

45 HTTPS 握手过程中,客户端如何验证证书的合法性

刚看完就可以随手写一下了,
服务端的数字证书由CA发布,当服务端返回数据证书时,客户单可以使用CA的公钥对其进行解密得到相关内容。内容包括服务器相关的整数时间,公钥等。

46 对obj push item

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)

自己先猜想,增加了数组的能力,那么元素应该也被当成不可用对象了,想arguments一样了。
以为会保留 34 没想到是这个:
Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]

push 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时,可应用在类似数组的对象上。push 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。
唯一的原生类数组(array-like)对象是 Strings,尽管如此,它们并不适用该方法,因为字符串是不可改变的。

这里length为2,所以从第三位开始插入,此时2,3对应的value 就被替换掉了。

47 双向绑定和 vuex 是否冲突

不发表评论暂时

48 call和apply的差别,性能 c

区别在于参数传递形式,call为参数,apply为数组。
下意识还是 apply更好一些。
也对实际上call就是函数调用的常规形式,apply还要转化一下。

看结论呢:
早期call确实比apply快那么一丢丢,但是现在两者没什么差距,即使有也微乎其微。

49 为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片?

这个没注意过,不过还是体积小、兼容性好
1 1 体积小和性能
gif的体积显然不如png,透明是减少重绘吗?

1、使用图片是支持跨域
2、图片请求无阻塞
3、GIF的最低合法体积最小(最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节)
无需相应
目前比较合适的做法是服务器发送"204 No Content",即“服务器成功处理了请求,但不需要返回任何实体内容”。

50 (百度)实现 (5).add(3).minus(2) 功能。

这是调用链的实现
5 + 3 - 2,结果为 6
原来这个用法没看明白: (5) 等于直接使用 5 其实
那就是Number原型链加方法

Number.prototype.add=function(num){
    return this.valueOf() + num
}
Number.prototype.minus=function(num){
    return this.valueOf()- num
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!