面试宝典

手写promise的实现

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和强大

Promise包含三种状态:

  1. pending 过渡态

  2. fulfilled 完成态

  3. rejected 失败态

Promise使用方法:

1
2
3
4
5
6
7
8
9
let promise = new Promise((resolve, reject) => {
//这里放入我们要执行的函数,可能是同步,也可能是异步, 这里我们就来写一个异步的执行
setTimeout(() => {
resolve('hello');
})
})
promise.then(data => {
console.log(data);
}, err => {console.log(err)})

由上可见:

当我们在new Promise的时候传入了一个函数,这个函数在规范中叫执行器(exector),因此,我们可以构建自己的一个构造函数

1
2
3
function Promise(exector){

}

第一步已经完成,接着分析原生promise,在执行器中传入了两个参数,第一个参数是让promise状态变为resolve,即为成功,第二个参数则让promise变为reject,失败,并且这两个形参都可以传入参数

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
function Promise(exector){
this.state = "pending";
this.result = null;
// 以下两行为了解决异步的Promise
this.onFulfilledFunc = [];//保存成功回调
this.onRejectedFunc = [];//保存失败回调

var _this = this;
function resolve(value){
_this.state = 'FULFILLED';
_this.result = value;
// 同步数组长度为0,异步数组有长度,执行顺序不一样
_this.onFulfilledFunc.forEach(function(callback){
callback(value)
})
}
function reject(err){
// 异步执行
_this.state = 'REJECTED';
_this.result = err;
_this.onRejectedFunc.forEach(function(callback){
callback(err)
})
}
this.then = function(onFulfilled,onRejected){
if(this.state === "FULFILLED"){
onFulfilled(this.result);
}else if(this.state === "REJECTED"){
onRejected(this.result);
}else{
// 异步执行 存在回调里 resolve还没执行完 然后走这里
if(typeof onFulfilled === "function"){
this.onFulfilledFunc.push(onFulfilled);//保存回调
}
if(typeof onRejected === "function"){
this.onRejectedFunc.push(onFulfilled);//保存回调
}
}
}
exector(resolve,reject);
}

var p = new Promise(resolve => {
setTimeout(()=>resolve('Hello'),1000)
});
p.then((res)=>console.log(res));

JavaScript垃圾回收机制

为什么需要垃圾回收?

因为字符串、对象、数组没有固定的大小,所以当他们大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、对象、数组时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统可用的内存,从而造成奔溃

  1. 标记清除法

    这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

    垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

  2. 引用计数法

    引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

闭包为什么造成内存泄漏

内存泄漏是指我们已经无法再通过js代码来引用到某个对象,但垃圾回收器却认为这个对象还在被引用,因此在回收的时候不会释放它

页面输入到加载成功发生了什么

  1. 浏览器的地址栏输入URL并按下回车。

  2. 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。、

  3. DNS解析URL对应的IP。

  4. 根据IP建立TCP连接(三次握手)。

    第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;

  第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

  1. HTTP发起请求。

  2. 服务器处理请求,浏览器接收HTTP响应。

  3. 渲染页面,构建DOM树。

  4. 关闭TCP连接(四次挥手)。

    ​ 第一次挥手是浏览器发完数据后,发送FIN请求断开连接。

      第二次挥手是服务器发送ACK表示同意,如果在这一次服务器也发送FIN请求断开连接似乎也没有不妥,但考虑到服务器可能还有数据要发送,所以服务器发送FIN应该放在第三次挥手中。

      这样浏览器需要返回ACK表示同意,也就是第四次挥手。

    至此从浏览器地址栏输入URL到页面呈现到你面前的整个过程就分析完了,上面内容如有错误欢迎留言交流。

重绘和重排

​ Reflow,也称作Layout,中文叫回流,一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树,这个过程称为Reflow。

  Repaint,中文重绘,意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就OK了,这个过程称为Repaint。

  所以说Reflow的成本比Repaint的成本高得多的多。DOM树里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。

  下面这些动作有很大可能会是成本比较高的:

  • 增加、删除、修改DOM结点时,会导致Reflow或Repaint
  • 移动DOM的位置,或是搞个动画的时候
  • 内容发生变化
  • 修改CSS样式的时候
  • Resize窗口的时候(移动端没有这个问题),或是滚动的时候
  • 修改网页的默认字体时

  基本上来说,reflow有如下的几个原因:

  • Initial,网页初始化的时候
  • Incremental,一些js在操作DOM树时
  • Resize,其些元件的尺寸变了
  • StyleChange,如果CSS的属性发生变化了
  • Dirty,几个Incremental的reflow发生在同一个frame的子树上

https相关原理

HTTPS是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL

SSL用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之 传输过程中不会被截取及窃听。

SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。

SSL协议可分为两层:

  1. SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。

  2. SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

SSL协议提供的服务主要有:

1)认证用户和服务器,确保数据发送到正确的客户机和服务器;
2)加密数据以防止数据中途被窃取;
3)维护数据的完整性,确保数据在传输过程中不被改变。

cookie与session原理,还有token相关的一些内容

cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。

cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。

session 从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。

session 也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。

服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。

在Web领域基于Token的身份验证随处可见。在大多数使用Web API的互联网公司中,tokens 是多用户下处理认证的最佳方式。

以下几点特性会让你在程序中使用基于Token的身份验证

  1. 无状态、可扩展

  2. 支持移动设备

  3. 跨程序调用

  4. 安全

token,服务器存储在redis,可以减轻服务器压力

这个文档讲的很好

深拷贝

深拷贝

算法学习

算法学习

react-router的内部原理解释

NodeJs的事件循环怎么理解?事件循环里各个阶段的认识

说一下对bind,call,apply三个函数的认识,自己实现一下bind方法

前端的requestAnimationFrame了解吗?有使用过吗?说一下使用场景

nodejs的异步IO原理

TCP三次握手四次挥手的具体细节

React的Dom的diff算法描述一下

跨域分哪几种类型,如何解决各个跨域的问题

前端模块化,使用过的打包工具有哪些,打包原理

XSS,CSRF攻击过程,前端怎么去防止这类攻击