文章目录
- 网络,浏览器篇
-
- dns解析
- TCP协议、IP协议、HTTP协议分别在哪一层吗?
- tcp/ip协议有几层
- 网络七层协议内容
- 浏览器进程和线程
- 谷歌浏览器为什么使用多进程
- 进程通信的方式
- https加密过程
- url到页面过程
- http2和http1的区别
- 如何实现有状态的http协议
- 强缓存和协商缓存(浏览器缓存机制)
- 跨域请求
- 跨域方式
- TCP三次握手为什么不能两次
- Tcp为什么是4次挥手
- javascript内存机制
- DOM 树 和 渲染树 的区别
- 怎么避免js,css等资源阻塞
- XSS 和 CSRF
- get和post
- http请求头中Referer的含义和作用
- HTTPS和HTTP的主要区别
- cookie、sessionStorage和localStorage的区别
- TCP和UDP的区别
- js原理篇
-
- prototype 和 __proto__ 的关系是什么
- 手写jsonp
- js继承
- 预编译
- 长列表优化
- 深浅拷贝
- object.create和object.assign
- 尾递归(避免栈溢出的方法)
- typeof
- js原型和原型链
- jsv8引擎原理
- js执行机制
- js为什么设计成单线程语言
- 手写节流,防抖
- 页面性能优化
- 手写new
- 手写event类
- ES6中class是语法还是语法糖
- 原型的终点
- 手写实现promise,promise. all,promise. race
- 闭包优缺点
- function和object的关系
- 手写call
- 异步执行顺序(易错)
- this指向
- this绑定优先级
- “硬绑定” bind为什么在new之后
- 事件委托
- regexp正则,讲讲贪婪模式
- css篇
- 框架篇
- 算法
网络,浏览器篇
dns解析
-
客户端通过浏览器访问域名为 www.baidu.com (http://www.baidu.com) 的网站,发起查询该域名的 IP 地址的 DNS 请求。该请求发送到了本地 DNS 服务器上。本地 DNS 服务器会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果。如果没有,本地 DNS 服务器还要向 DNS 根服务器进行查询。
-
本地 DNS 服务器向根服务器发送 DNS 请求,请求域名为 www.baidu.com (http://www.baidu.com) 的 IP 地址。
-
根服务器经过查询,没有记录该域名及 IP 地址的对应关系。但是会告诉本地 DNS 服务器,可以到域名服务器上继续查询,并给出域名服务器的地址(.com 服务器)。
-
本地 DNS 服务器向 .com 服务器发送 DNS 请求,请求域名 www.baidu.com (http://www.baidu.com) 的 IP 地址。
-
.com 服务器收到请求后,不会直接返回域名和 IP 地址的对应关系,而是告诉本地 DNS 服务器,该域名可以在 baidu.com 域名服务器上进行解析获取 IP 地址,并告诉 baidu.com 域名服务器的地址。
-
本地 DNS 服务器向 baidu.com 域名服务器发送 DNS 请求,请求域名 www.baidu.com (http://www.baidu.com) 的 IP 地址。
-
baidu.com 服务器收到请求后,在自己的缓存表中发现了该域名和 IP 地址的对应关系,并将IP地址返回给本地 DNS 服务器。
-
本地 DNS 服务器将获取到与域名对应的 IP 地址返回给客户端,并且将域名和 IP 地址的对应关系保存在缓存中,以备下次别的用户查询时使用。
TCP协议、IP协议、HTTP协议分别在哪一层吗?
运输层,网络层和应用层
tcp/ip协议有几层
tcp/ip协议包含4层,分别是:1、应用层,为应用进程提供服务的;2、运输层;3、网络层,可以进行网络连接的建立和终止;4、网络接口层,是传输数据的物理媒介。
网络七层协议内容
OSI的7层从上到下分别是 7 应用层 6 表示层 5 会话层 4 传输层 3 网络层 2 数据链路层 1 物理层
浏览器进程和线程
线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
进程是系统中正在运行的一个程序,程序一旦运行就是进程。进程可以看成程序执行的一个实例,可以视作为浏览器打开了多个tab页,每个tab页相当于独立的进程。进程是系统资源分配的独立实体,每个进程都拥有独立的地址空间。一个进程无法访问另一个进程的变量和数据结构,如果想让一个进程访问另一个进程的资源,需要使用进程间通信,比如管道,文件,套接字等。
一个进程可以拥有多个线程,每个线程使用其所属进程的栈空间。线程与进程的一个主要区别是,统一进程内的一个主要区别是,同一进程内的多个线程会共享部分状态,多个线程可以读写同一块内存(一个进程无法直接访问另一进程的内存)。同时,每个线程还拥有自己的寄存器和栈,其他线程可以读写这些栈内存。
谷歌浏览器为什么使用多进程
传统的浏览器被设计为显示网页,而Chrome的设计目标是支撑“Web App”(当时的js和相关技术已经相当发达了,Gmail等服务也很成功)。这就要求Chrome提供一个类似于“操作系统”感觉的架构,支持App的运行。而App会变得相当的复杂,这就难以避免出现bug,然后crash。同时浏览器也要面临可能运行“恶意代码”。流览器不能决定上面的js怎么写,会不会以某种形式有意无意的攻击浏览器的渲染引擎。如果将所有这些App和浏览器实现在一个进程里,一旦挂,就全挂。因此Chrome一开始就设计为把隔离性作为基本的设计原则,用进程的隔离性来实现对App的隔离。这样用户就不用担心:
- 一个Web App挂掉造成其他所有的Web App全部挂掉(稳定性)
- 一个Web App可以以某种形式访问其他App的数据(安全性)
进程通信的方式
程间通信的方式——信号、管道、消息队列、共享内存
https加密过程
验证证书 ,创建密钥,传上去验证
url到页面过程
域名解析(递归域名),三次握手,服务器端返回请求数据,渲染页面,四次挥手
http2和http1的区别
1、HTTP2使用的是二进制传送,HTTP1.X是文本(字符串)传送。二进制传送的单位是帧和流。帧组成了流,同时流还有流ID标示
2、HTTP2支持多路复用。因为有流ID,所以通过同一个http请求实现多个http请求传输变成了可能,可以通过流ID来标示究竟是哪个流从而定位到是哪个http请求
3、HTTP2头部压缩。HTTP2通过gzip和compress压缩头部然后再发送,同时客户端和服务器端同时维护一张头信息表,所有字段都记录在这张表中,这样后面每次传输只需要传输表里面的索引Id就行,通过索引ID查询表头的值
4、HTTP2支持服务器推送。HTTP2支持在未经客户端许可的情况下,主动向客户端推送内容
如何实现有状态的http协议
HTTP是一种无状态协议,即服务器不保留与客户交易时的任何状态。
用户登录后,切换到其他界面,进行操作,服务器端是无法判断是哪个用户登录的。 每次进行页面跳转的时候,得重新登录。使用Cookie session来实现有状态的http协议
强缓存和协商缓存(浏览器缓存机制)
强缓存
Expires 是以前用来控制缓存的http头,Cache-Control是新版的API。
现在首选 Cache-Control。
如果在Cache-Control响应头设置了 “max-age” 或者 “s-max-age” 指令,那么 Expires 头会被忽略。
响应头设置方式:
Expires: Wed, 21 Oct 2015 07:28:00 GMT
Expires 响应头包含日期/时间, 即在此时候之后,响应过期。
注意: 因为过期标准的时间用的是本地时间,所以不靠谱,所以要使用Cache-Control代替Expires
-
cache-control: max-age=xxxx,public
客户端和代理服务器都可以缓存该资源;
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,如果用户做了刷新操作,就向服务器发起http请求 -
cache-control: max-age=xxxx,private
只让客户端可以缓存该资源;代理服务器不缓存
客户端在xxx秒内直接读取缓存,statu code:200 -
cache-control: max-age=xxxx,immutable
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,即使用户做了刷新操作,也不向服务器发起http请求 -
cache-control: no-cache
跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。 -
cache-control: no-store
不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了
协商缓存
-
etag:每个文件有一个,改动文件了就变了,就是个文件hash,每个文件唯一。
-
last-modified:文件的修改时间,精确到秒
ETag和Last-Modified的作用和用法,他们的区别:
1.Etag要优于Last-Modified。Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;
2.在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值;
3.在优先级上,服务器校验优先考虑Etag。
浏览器缓存过程
1.浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上请求下载下来,并把response header及该请求的返回时间一并缓存;
2.下一次加载资源时,先比较当前时间和上一次返回200时的时间差,如果没有超过cache-control设置的max-age,则没有过期,命中强缓存,不发请求直接从本地缓存读取该文件(如果浏览器不支持HTTP1.1,则用expires判断是否过期);如果时间过期,则向服务器发送header带有If-None-Match和If-Modified-Since的请求
3.服务器收到请求后,优先根据Etag的值判断被请求的文件有没有做修改,Etag值一致则没有修改,命中协商缓存,返回304;如果不一致则有改动,直接返回新的资源文件带上新的Etag值并返回200;;
4.如果服务器收到的请求没有Etag值,则将If-Modified-Since和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回304;不一致则返回新的last-modified和文件并返回200;
跨域请求
协议域名ip。属于浏览器的同源策略,服务器可以接受到,同源策略判断的条件是协议,域名和端口号
跨域方式
- document.domain + iframe (只有在主域相同的时候才能使用该方法)
- window.name + iframe (name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。)
- JSONP(只支持get,用一个动态script标签直接进行get请求,因为同源策略对script无效)
- web sockets(web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。)
TCP三次握手为什么不能两次
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认
Tcp为什么是4次挥手
这个因为第一次挥手表示客户端发送了一个fin的包,表示客户端已发送数据完毕,但是服务端这个时候可能还有数据没有发送完成,先发送给客户端一个ask的包,等待自己的数据发送完成才能向客户端发送一个 fin的包,表示自己的数据也已发送完成。这样中间就必须为两次来发送ask和fin。
javascript内存机制
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。
其中栈存放基本数据类型,堆存放引用类型,池存放常量。
DOM 树 和 渲染树 的区别
DOM 树与 HTML 标签一一对应,包括 head 和隐藏元素
渲染树不包括 head 和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的 css 属性
怎么避免js,css等资源阻塞
- 内联javascript: 如果页面的初始渲染的确依赖于page.js,我们可以考虑使用内联JavaScript。
- 推迟加载:如果页面的初始渲染并不依赖于page.js,我们可以考虑推迟加载page.js,让其在页面初始内容渲染完成后再加载。
- 异步加载:HTML5允许我们给 script 标签添加属性: “async” 来告诉浏览器不必停下来等待该脚本执行,什么时候下载完什么时候执行该脚本就可以了。这样的话浏览器会边下载page.js边渲染后面的内容。
XSS 和 CSRF
XSS是注入攻击的一种,攻击者通过将代码注入被攻击者的网站中,用户一旦访问访问网页便会执行被注入的恶意脚本。反射型XSS的脚本被解析的地方是浏览器,而存储型XSS的脚本被解析的地方是服务器
CSRF全程 Cross Site Request Forgery, 跨站域请求伪造,伪造表单诱骗用户点击
XSS防范方法
1.代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
2.避免直接在cookie 中泄露用户隐私,例如email、密码等等。
3.通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
4.尽量采用POST 而非GET 提交表单
CSRF防范方法
- referer 验证:根据HTTP协议,在http请求头中包含一个referer的字段,这个字段记录了该http请求的原地址.通常情况下,执行转账操作的post请求www.bank.com/transfer.php应该是点击www.bank.com网页的按钮来触发的操作,这个时候转账请求的referer应该是www.bank.com.而如果黑客要进行csrf攻击,只能在自己的网站www.hacker.com上伪造请求.伪造请求的referer是www.hacker.com.所以我们通过对比post请求的referer是不是www.bank.com就可以判断请求是否合法.这种方式验证比较简单,网站开发者只要在post请求之前检查referer就可以,但是由于referer是由浏览器提供的.虽然http协议有要求不能篡改referer的值.但是一个网站的安全性绝对不能交由其他人员来保证.
- token 验证:从上面的样式可以发现,攻击者伪造了转账的表单,那么网站可以在表单中加入了一个随机的token来验证.token随着其他请求数据一起被提交到服务器.服务器通过验证token的值来判断post请求是否合法.由于攻击者没有办法获取到页面信息,所以它没有办法知道token的值.那么伪造的表单中就没有该token值.服务器就可以判断出这个请求是伪造的.
get和post
get是获取数据的,而post是提交数据的, get参数置于url,post参数置于报文
http请求头中Referer的含义和作用
表示页面的来源页面
HTTPS和HTTP的主要区别
https协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl/tls加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的;HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
cookie、sessionStorage和localStorage的区别
TCP和UDP的区别
TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。 一个TCP连接必须要经过三次“对话”才能建立起来
UDP是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。
TCP 是面向连接的,UDP 是面向无连接的
UDP程序结构较简单
TCP 是面向字节流的,UDP 是基于数据报的
TCP 保证数据正确性,UDP 可能丢包
TCP 保证数据顺序,UDP 不保证
js原理篇
prototype 和 proto 的关系是什么
proto 属性指向其构造函数的 prototype 属性
prototype 是函数对象(包括 Function 以及 Object)的专有属性;
proto 存在于所有对象属性中,是原型链遍历中实际访问的属性。
手写jsonp
/**
* jsonp获取请求数据
* @param {string}url
* @param {object}params
* @param {function}callback
*/
function jsonp({
url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script');
params = JSON.parse(JSON.stringify(params));
let arrs = [];
for (let key in params) {
arrs.push(`${
key}=${
params[key]}`);
}
arrs.push(`callback=${
callback}`);
script.src = `${
url}?${
arrs.join('&')}`;
document.body.appendChild(script);
window[callback] = function (data) {
resolve(data);
document.body.removeChild(script);
}
})
}
js继承
https://blog.csdn.net/Yuki_yuhan/article/details/108248086
预编译
- JavaScript 脚本的运行由两个阶段组成:预编译阶段 和 执行阶段,先进行预编译,再执行语句;
- 预编译,简单理解,就是在内存中开辟一块空间,用来存放变量和函数。在 JavaScript 中,JavaScript 会在内存中为使用
var
关键字声明的变量和使用function
关键字声明的函数开辟一块空间,用来存放使用这两者声明的变量和函数; - 在预编译时,
function
的优先级比var
高 - 在预编译阶段,只进行 变量/函数声明,不会进行变量的初始化(即变量赋值,所有变量的值都是
undefined
);变量赋值 是在执行阶段才进行的。
长列表优化
setTimeout => requestAnimationFrame按照帧数回调 => DocumentFragment避免新建dom回流
DocumentFragment,文档片段接口,表示一个没有父级文件的最小文档对象。它被作为一个轻量版的Document使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为DocumentFragment不是真实DOM树的一部分,它的变化不会触发DOM树的(重新渲染) ,且不会导致性能等问题。
可以使用document.createDocumentFragment方法或者构造函数来创建一个空的DocumentFragment
深浅拷贝
浅拷贝
function clone(target) {
// 如果不是数组或者对象
if (target && typeof target !== 'object') {
// 直接复制
var clone = target
console.log(clone)
}
// 如果是数组或对象
else if (target && typeof target === 'object') {
// 先判断是数组还是对象
var clone = Array.isArray(target) ? [] : {
}
// 遍历目标
for (var key in target) {
clone[key] = target[key]
}
}
return clone
}
深拷贝
function deepClone(target) {
let clone;
if (target && typeof target !== 'object') {
clone = target;
} else if (target && typeof target === 'object') {
clone = Array.isArray(target) ? [] : {
};
for (let key in