赞
踩
基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
1. 长连接(Persistent Connection)
HTTP1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启长连接keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。HTTP1.0需要使用keep-alive参数来告知服务器端要建立一个长连接。
2. 节约带宽
HTTP1.0中存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能。HTTP1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,客户端接收到100才开始把请求body发送到服务器;如果返回401,客户端就可以不用发送请求body了节约了带宽。
3. HOST域
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname),HTTP1.0没有host域。随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request)。
4. 缓存处理
在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
5. 错误通知的管理
在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
虽然说非对称加密安全,但是由于和对称加密比较来说,速度较慢(指的是加密和解密的速度而言),故我们https采用了将对称加密和非对称加密结合的方式,即我们将对称加密的秘钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用非对称的私钥进行解密得到对称加密的秘钥,此时双方就可以使用对称加密的秘钥进行通信沟通了,这样既保证了安全性,又增加了通信的速度,即加密和解密的速度.
对称加密:快速简单,加密和解密都使用同样的秘钥。
非对称加密:安全,使用一对密钥,公钥/私钥。私钥由一方安全保管,公钥可以发给任何请求他的人,发送请求时通过公钥对消息进行加密,只有拥有私钥的人才能对你的消息进行解密,安全性大大提高。
SSL加密过程:
1.客户端向服务端发送请求
2.服务端返回给客户端公钥
3.客户端使用公钥对信息加密后在想服务端发送请求
4.服务端通过私钥对信息解密
RSA加密算法:
一种公钥密码体制,公钥公开,私钥保密,它的加密解密算法是公开的。 RSA的这一对公钥、私钥都可以用来加密和解密,并且一方加密的内容可以由并且只能由对方进行解密。
为了防止证书颁发过程中被人修改,出现了数字签名。
CA机构将证书内容用hash算法生成hash字符串1,并用CA私钥加密发送给服务端
服务端用CA公钥解密得到hash字符串1,同时也将证书内容用hash算法生成hash字符串2,如果hash字符串1与hash字符串2相同,说明证书没被修改过
1.多路复用
HTTP2.0使用了多路复用的技术,做到**同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。**HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
2. 头部数据压缩
HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
3. 服务器推送
服务端推送是一种在客户端请求之前发送数据的机制。网页使用了许多资源:HTML、样式表、脚本、图片等等。在HTTP1.1中这些资源每一个都必须明确地请求。这是一个很慢的过程。浏览器从获取HTML开始,然后在它解析和评估页面的时候,增量地获取更多的资源。因为服务器必须等待浏览器做每一个请求,网络经常是空闲的和未充分使用的.
为了改善延迟,HTTP2.0引入了server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前,免得客户端再次创建连接发送请求到服务器端获取。这样客户端可以直接从本地加载这些资源,不用再通过网络。
下面这张图是由TMX总结 :
超文本传输协议,基于TCP/IP通信协议来传递数据
HTTP/0.9是第一个版本的HTTP协议,已过时。它的组成极其简单,只允许客户端发送GET这一种请求,且不支持请求头。由于没有协议头,造成了HTTP/0.9协议只支持一种内容,即纯文本。
HTTP/0.9具有典型的无状态性,每个事务独立进行处理,事务结束时就释放这个连接。由此可见,HTTP协议的无状态特点在其第一个版本0.9中已经成型。一次HTTP/0.9的传输首先要建立一个由客户端到Web服务器的TCP连接,由客户端发起一个请求,然后由Web服务器返回页面内容,然后连接会关闭。如果请求的页面不存在,也不会返回任何错误码。
相对于HTTP/0.9增加了如下主要特性:
是目前使用最广泛的协议版本。相对于HTTP/1.0新增了以下内容:
HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection:keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
提供了范围请求功能,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。这是支持文件断点续传的基础。
提供了虚拟主机的功能,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
HTTP/1.1在1.0的基础上加入了一些cache的新特性,引入了实体标签,一般被称为e-tags,新增更为强大的Cache-Control头。
在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
相对于HTTP/1.1新增了以下内容:
HTTP 2.0 的所有帧都采用二进制编码
- 帧:客户端与服务器通过交换帧来通信,帧是基于这个新协议通信的最小单位。
- 消息:是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成。
- 流:流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2 … N);
多路复用允许同时通过单一的HTTP/2.0 连接发起多重的请求-响应消息。有了新的分帧机制后,HTTP/2.0不再依赖多个TCP 连接去处理更多并发的请求。每个数据流都拆分成很多互不依赖的帧,而这些帧可以交错(乱序发送),还可以分优先级。最后再在另一端根据每个帧首部的流标识符把它们重新组合起来。HTTP 2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接(每个域名一个连接)即可。
HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。HTTP/2.0 要求通讯双方各自缓存一份首部字段表,从而避免了重复传输。
浏览器可以在发现资源时立即分派请求,指定每个流的优先级,**让服务器决定最优的响应次序。**这样请求就不必排队了,既节省了时间,也最大限度地利用了每个连接。
把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。
快速UDP互联网连接
2个主要特征:
基于TCP的HTTP/2,尽管从逻辑上来说,不同的流之间相互独立,不会相互影响,但在实际传输方面,数据还是要一帧一帧的发送和接收**,一旦某一个流的数据有丢包,则同样会阻塞在它之后传输的流数据传输**。而基于UDP的QUIC协议则可以更为彻底地解决这样的问题,让不同的流之间真正的实现相互独立传输,互不干扰。
当前移动端的应用环境,用户的网络可能会经常切换,比如从办公室或家里出门,WiFi断开,网络切换为3G或4G。基于TCP的协议,由于切换网络之后,IP会改变,因而之前的连接不可能继续保持。而基于UDP的QUIC协议,则可以内建与TCP中不同的连接标识方法,从而在网络完成切换之后,恢复之前与服务器的连接。
GET和POST本质上没有区别
HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。不同的浏览器(发起http请求)和服务器(接受http请求)就是不同的运输公司,他们对get/post请求的数据处理不同。
GET和POST还有一个重大区别
GET产生一个TCP数据包;POST产生两个TCP数据包。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
状态码 | 类别 | 原因短语 |
---|---|---|
1XX | Informational(信息性状态码,服务器给客户端的信息) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法进行处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
状态码 | 描述 |
---|---|
200 OK | 请求已正常处理 |
301 Moved Permanently | 永久性重定向 : 请求的资源已经被分配了新的URI,以后应使用资源现在所指的URI |
302 Found | 临时性重定向 : 和301相似,但302代表的资源不是永久性移动,只是临时性性质的。换句话说,已移动的资源对应的URI将来还有可能发生改变 |
303 See Other | 303状态码和302状态码有着相同的功能,但303状态码明确表示客户端应当采用GET方法获取资源,这点与302状态码有区别 |
304 Not Modified | 该状态码表示客户端发送附带条件的请求时(采用GET方法的请求报文中包含If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任一首部)服务端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回304.。 |
400 Bad Request | 服务器端无法理解客户端发送的请求,请求报文中可能存在语法错误((前端提交到后台的数据应该是json字符串类型,但是前端没有将对象JSON.stringify转化成字符串)) |
401 Unauthorized | 该状态码表示发送的请求需要有通过HTTP认证(BASIC认证,DIGEST认证)的认证信息 |
403 Forbidden | 不允许访问那个资源。该状态码表明对请求资源的访问被服务器拒绝了。(权限,未授权IP等) |
404 Not Found | 服务器上没有请求的资源。路径错误等 |
500 Internal Server Error | 该状态码表明服务器端在执行请求时发生了错误。也有可能是web应用存在bug或某些临时故障 |
503 Service Unavailable | 服务器暂时处于超负载或正在停机维护,现在无法处理请求 |
502 | 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 |
504 | 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 |
如果在cookie中设置了HttpOnly,那么通过程序(js脚本)将无法读取到cookie信息,这样能有效防止XSS攻击
表示来源,正确英语拼法是Referrer,为了向后兼容,将错就错
在www.google.com里有一个
www.baidu.com
链接,那么点击这个www.baidu.com
,它的header信息里就有:Referer=http://www.google.com,可以利用这个来防止盗链了,比如我只允许我自己的网站访问我自己的图片服务器,那我的域名是www.google.com
,那么图片服务器每次取到Referer来判断一下是不是我自己的域名www.google.com
,如果是就继续访问,不是就拦截。直接在地址栏中输入URL的地址是不会包含referer字段的,因为他不是藏一个地方链接过去的
允许 Referer 为空,意味着你允许比如浏览器直接访问,就是空
是一个计算机通信协议,该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程.它是建立在Socket之上
RPC实现远程调用的策略:
(1) 采用何种网络通讯协议?
比较流行的RPC框架,采用的都是TCP作为底层传输协议
(2)数据传输的格式是怎么样的?
不同于HTTP,RPC远程过程调用需要让调用者和被调用者采用相同的数据传输格式,就好比两个人使用同一种语言进行交流沟通一样.
下面是RPC的调用流程图:
HTTP称为超文本传输协议,是一种应用层的协议,规定了网络传输的请求格式、响应格式、资源定位和操作的方式等。但是底层采用什么网络传输协议,并没有规定,不过现在都是采用TCP协议作为底层传输协议.
客户端通过HTTP向服务器端发送请求,并接受响应的流程:
常用的RPC框架: webservie(cxf)、dubbo
HTTP客户端工具: HTTPClient
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。建立网络通信连接至少要一对端口号(Socket)。Socket本质是编程接口(API).
当然我们也可以理解为: Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
在高并发短连接的server端,当server处理完client的请求后立刻close socket此时会出现time_wait状态,然后如果client再并发2000个连接,此时部分连接就连接不上了,用linger强制关闭可以解决此问题,但是linger会导致数据丢失,linger值为0时是强制关闭,无论并发多少多能正常连接上,如果非0会发生部分连接不上的情况!(可调用setsockopt设置套接字的linger延时标志,同时将延时时间设置为0。)
TIME_WAIT是TCP连接断开时必定会出现的状态。是无法避免掉的,这是TCP协议实现的一部分。
在WINDOWS下,可以修改注册表让这个时间变短一些,time_wait的时间为2msl,默认为4min.你可以通过改变TcpTimedWaitDelay的量,把它缩短到30s.TCP要保证在所有可能的情况下使得所有的数据都能够被投递。当你关闭一个socket时,主动关闭一端的socket将进入TIME_WAIT状态,而被动关闭一方则转入CLOSED状态,这的确能够保证所有的数据都被传输。但是这样也会给我们带来两个问题 :
(1) 我们没有任何机制保证最后的一个ACK能够正常传输
(2) 网络上仍然有可能有残余的数据包(wandering duplicates),我们也必须能够正常处理
由于TIME_WAIT状态所带来的相关问题,我们可以通过设置SO_LINGER标志来避免socket进入TIME_WAIT状态,这可以通过发送RST而取代正常的TCP四次握手的终止方式。但这并不是一个很好的主意,TIME_WAIT对于我们来说往往是有利的,下面我们来谈谈TIME_WAIT的作用
1. 可靠地实现TCP全双工连接的终止;
如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。
2. 允许老的重复分节在网络中消逝。
如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝。
ACK包到达服务器需要MSL时间,服务器重传 FIN 包也需要MSL时间,2MSL 是数据包往返的最大时间,通常为2min,如果2MSL后还未收到服务器重传的FIN 包,就说明服务器已经收到了ACK包,此时由TIME_WAIT状态转变为CLOSED状态.
如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被丢弃。建立第二个连接的时候,不会混淆。
每个应用程序都是一个进程,每个进程都有自己独立的一块内存空间,一个进程可以有多个线程。
进程的三种状态:就绪态(以获得除cpu外的所有资源),阻塞态,执行态
进程中的一个执行任务(控制单元)。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可以共享数据。
1.1匿名管道(半双工,数据只能单向移动,面向字节流,允许具有血缘关系的进程通信)
两个文件描述符指向管道的两端
1.2有名管道(半双工,允许无亲缘关系的进程通信)
(发送进程添加消息到队列的末尾,接收进程在队列头部接收消息,消息一旦被接收就会从队列中删除,类似FIFO)
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
信号量工作原理 :
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)
共享内存就是允许两个或多个进程共享一定的存储区。共享内存没有任何的同步与互斥机制,所以要使用信号量来实现对共享内存的存取的同步
可用于不同机器间的进程通信
如上图所示,在浏览器的地址栏中输入url : 此时执行过程简单地介绍如下所示
第一步 : 我们的DNS域名解析器会对url进行域名解析,找到对应的IP节点进行返回
第二步 : 拿到我们访问的IP,我们就要与对应的服务器建立TCP连接(三次握手)
第三步 : TCP连接建立成功,我们就可以向服务器发送HTTP请求了,当服务器获取我们的请求,进行处理,然后将请求的响应返回给我们
第四步 : 关闭TCP连接(四次挥手)
DNS是进行域名和与之相对应的IP地址转换的服务器
浏览器会首先搜索浏览器自身的DNS缓存,如果浏览器自身的缓存里面没有找到对应的条目,
那么浏览器会搜索操作系统自身的DNS缓存,操作系统域名解析的过程是读取hosts文件(位于C:\Windows\System32\drivers\etc),如果在hosts
文件中也没有找到对应的条目,
浏览器就会找TCP/IP参数中设置的首选DNS服务器(本地DNS服务器)发起一个DNS的系统调用(运营商dns-->
根(13台)域名服务器-->
顶级域名服务器-->
域名注册商服务器)
从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询。
将URL对应的各种资源,通过浏览器渲染引擎,输出可视化的图像
渲染引擎主要包括:html解析器,css解析器,布局,js引擎
html引擎:将html文本解释成DOM树(文档对象模型)
css解释器:为dom中的各个元素对象加上样式信息(CSS对象模型)
布局:将dom和css样式信息结合起来,计算他们的大小及位置信息,构建(渲染树)
js引擎:解释js代码并把代码逻辑对应的dom和css改动信息应用到布局中去
输入一个网址以后,首先会进行DNS域名解析,找到目标的IP地址,然后和目标地址建立TCP连接,连接成功以后,发送HTTP请求,目标服务器处理完请求以后,返回响应结果,最后关闭TCP连接
主机A首先查看自己的ARP缓存,检查是否含有主机B的IP到MAC地址的映射,如果存在映射,则构造报文,目的IP为主机B的IP,源IP为主机A的IP,目的MAC为主机B的MAC,源MAC为主机A的MAC,将报文发送给交换机C.交换机C进行MAC地址表学习,将主机A和MAC和报文入端口号记录下来,然后交换机C查看自己的MAC转发表,检查是否含有主机B的MAC到端口的映射,如果有对应的端口,则获取对应的端口,将报文从此端口转发出去,报文到达主机B.如果交换机C没有主机B的MAC转发映射表,则采用洪泛的形式广播报文,主机B收到报文后向主机A进行回复,交换机C进行MAC表学习,将主机B的MAC和报文入端口号记录下来
如果主机A没有主机B的ARP映射,主机A需要发送ARP请求,以获取主机B的MAC,将报文发往交换机C,交换机C采用洪泛的形式广播报文,主机B收到广播报文后,在自己的ARP缓存表中写入主机A的IP到MAC的映射,将自己的MAC封装到ARP回复报文中,单播给主机A,主机A获取到主机B的MAC后,在自己的ARP缓存表中写入主机B的IP到MAC的映射,构造报文发送给主机B,过程同上。
主机B向主机A回复报文的过程类似。
主机A查看自己的ARP缓存表,检查是否有路由器E的IP到MAC的映射,如果有映射,获取路由器E的MAC,构造报文,目的IP为主机B的IP,源IP为主机A的IP,目的MAC为路由器E的MAC,源MAC为主机A的MAC,将报文通过交换机C发往路由器E,过程同上。 如果主机A没有路由器E的IP到MAC的映射,需要发送ARP请求,获取路由器E的MAC,过程同上。路由器E收到主机A的报文后,剥离报文的MAC帧头,查询路由表,发现目标主机B所在的网络是直连的,查看自己的ARP缓存表,如果有主机B的IP到MAC的映射关系,获取主机B的MAC,封装报文MAC帧头,目的MAC为主机B的MAC,源MAC为路由器E的MAC,将报文通过交换机D发往主机B,如果路由器E没有主机B的IP到MAC的映射关系,需要发送ARP请求,获取主机B的MAC,过程同上。
主机B向主机A回复报文的过程类似。
注意 :
ARP协议指的是地址解析协议(Address Resolution Protocol),是根据**IP地址获取物理地址(MAC地址)**的一个TCP/IP协议.
在计算机间通信的时候,计算机要知道目的计算机是谁(就像我们人交流一样,要知道对方是谁),这中间需要涉及到MAC地址,而MAC是真正的电脑的唯一标识符。
我们通过两个主机之间的ping连接,来看一下简单的ARP请求应答
PC1依据OSI模型 :
① 依次从上至下对数据进行封装,包括对ICMP Date加IP包头的封装,但是到了封装MAC地址的时候
② PC1首先查询自己的ARP缓存表,发现没有IP2和他的MAC地址的映射,这个时候MAC数据帧封装失败。我们使用ping命令的时候,是指定PC2的IP2的,计算机是知道目的主机的IP地址,能够完成网络层的数据封装,因为设备通信还需要对方的MAC地址,但是PC1的缓存表里没有,所以在MAC封装的时候填入不了目的MAC地址。
那么PC1为了获取PC2的MAC地址 :
③ PC1要发送询问信息,询问PC2的MAC地址,询问信息包括PC1的IP和MAC地址、PC2的IP地址,这里我们想到一个问题,即使是询问信息,也是需要进行MAC数据帧的封装,那这个询问信息的目的MAC地址填什么呢,规定当目的MAC地址为ff-ff-ff-ff-ff-ff时,就代表这是一个询问信息,也即使后面我要说的广播。
PC2收到这个询问信息后,将这里面的IP1和MAC1(PC1的IP和MAC)添加到本地的ARP缓存表中,然后 :
④ PC2发送应答信息,对数据进行IP和MAC的封装,发送给PC1,因为缓存表里已经有PC1的IP和MAC的映射了呢。这个应答信息包含PC2的IP2和MAC2。PC1收到这个应答信息,理所应当的就获取了PC2的MAC地址,并添加到自己的缓存表中。
经过这样交互式的一问一答,PC1和PC2都获得了对方的MAC地址,值得注意的是,目的主机先完成ARP缓存,然后才是源主机完成ARP缓存。之后PC1和PC2就可以真正交流了。
对于交换机而言,它也具有记忆功能,会基于源MAC地址建立一个CAM缓存表(记录MAC对应接口的信息),理解为当PC1发送消息至交换机的Port1时,交换机会把源MAC(也就是MAC1)记录下来,添加一条MAC1和Port1的映射,之后交换机可以根据MAC帧的目的MAC进行端口转发
它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
(1)外形上:
交换机通常端口比较多看起来比较笨重,而路由器的端口就少得多体积也小得多
(2)工作层次不同:
最初的交换机工作在数据链路层,而路由器则工作网络层
(3)数据的转发对象不同:
交换机是根据MAC地址转发数据帧,而路由器则是根据IP地址来转发IP数据。
(4)”分工“不同
交换机主要是用于组建局域网,而路由器则是负责让主机连接外网。
(5)冲突域和广播域
交换机分割冲突域,但是不分割广播域,而路由器分割广播域。
(6)工作原理
交换机的原理比较简单,一般都是采用硬件电路实现数据帧的转发
路由器工作在网络层,肩负着网络互联的重任,要实现更加复杂的协议,具有更加智能的转发决策功能,一般都会在在路由器中跑操作系统,实现复杂的路由算法,更偏向于软件实现其功能。
(7)安全性能
路由器一般有防火墙的功能,能够对一些网络数据包选择性过滤。现在的一些路由器都具备交换机的功能,一些交换机具备路由器的功能,被称为3层交换机,广泛使用。相比较而言,路由器的功能较交换机要强大,但是速度也较慢,价格昂贵,三层交换机既有交换机的线性转发报文的能力,又有路由器的良好的路由功能因此得到广泛的使用
由于我们使用本地域名解析器进行解析,获取源服务器IP导致每一次获取数据,都要去源服务器进行访问获取,造成性能的损失的降低,故我们在网络各处部署节点服务器,构建CDN系统,当我们使用本地DNS域名系统进行解析的时候,DNS系统会将域名解析交给CNAME指向的专用的DNS服务器,通过CDN的DNS服务器获取到一台合适的缓存服务器IP给我们,这里的合适性指的是(服务器距离我们的距离位置,该服务器上是否存在我们需要的内容等等…),此时我们就与该缓存服务器建立TCP连接,已经后续的HTTP请求获取数据,得到响应等过程,如果该节点服务器不能满足我们的需求,我们会层层向它的上级服务器进行访问,直到源服务器为止.
(Content Delivery Network)
就近访问原理:采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。cdn就是用来加速的,他能让用户就近访问数据,这样就更更快的获取到需要的数据。
举个例子,现在服务器在北京,深圳的用户想要获取服务器上的数据就需要跨越一个很远的距离,这显然就比北京的用户访问北京的服务器速度要慢。但是现在我们在深圳建立一个cdn服务器,上面缓存住一些数据,深圳用户访问时先访问这个cdn服务器,如果服务器上有用户请求的数据就可以直接返回,这样速度就大大的提升了。
代售点优势:大部分请求在CDN边缘完成,起到了分流的作用,减轻源站的负载。
我们怎么知道用户的所在位置从而给他分配最佳的cdn节点呢。这就需要dns服务来进行定位了。
当我们在浏览器中输入一个域名时,首先需要将域名转换为ip地址,再将ip地址转换为mac地址,这样才能在网络上找到该服务器。当我们向dns服务器发起解析域名的请求时,dns服务器首先会查询自己的缓存中有没有该域名,如果缓存中存在该域名,则可以直接返回ip地址。如果缓存中没有,服务器则会以递归的方式层层访问。例如,我们要访问www.baidu.com,首先我们会先向全球13个根服务器发起请求,询问com域名的地址,然后再向负责com域名的名称服务器发送请求,找到baidu.com,这样层层递归,最终找到我们需要的ip地址。
DNS劫持又称为域名劫持,是一种恶意攻击,黑客或其他方通过使用流氓DNS服务器或其他策略来重定向用户,该策略更改了Internet用户被重定向到的IP地址。
DNS污染最常见的就是篡改host主机文件,通过篡改DNS主机文件ip地址和网址将不再一一对应,打开的网站将不是最初想要访问的网站。
DNS区域传输的时候使用TCP协议:
辅域名服务器会定时向主域名服务器进行查询以便了解数据是否变动。如有变动,会执行一次区域传送,进行数据同步。
域名解析时使用UDP协议:
客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过三次握手,这样DNS服务器负载更低,响应更快。
cdn中缓存了服务器上的部分资源。
当我们与DNS域名解析器解析的IP服务器建立TCP连接以后,此时向其发送请求时,我们可以在其中建立Nginx反向代理,可以实现
负载均衡
,屏蔽真实地址,安全管理
,解决跨域问题
,节约有效的IP资源
,减少web服务器压力,提高响应速度
Nginx是一个轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载均衡
.
企业内部所有的网站共享一个在Internet中注册的IP地址,这些服务器分配私有地址,采用虚拟主机的方式对外提供服务。
反向代理就是通常所说的web服务器加速,它是一种通过在繁忙的web服务器和外部网络之间增加一个高速的web缓冲服务器来降低实际的web服务器的负载的一种技术。反向代理服务器会强制将外部网络对要代理的服务器的访问经过它,它会将从源服务器上获取到的静态内容缓存到本地,以便日后再收到同样的信息请求时,直接将本地缓存的内容发给客户端,减少后端web服务器的压力,提高响应速度。
请求的统一控制,包括设置权限,过滤规则等。
区分动态和静态可缓存内容。
实现负载均衡,内部可以采用多台服务器来组成服务器集群,外部还是可以采用一个地址访问。
解决ajax跨域问题。
作为真实服务器的缓存,解决瞬间负载量大的问题。
配置简单
占内存小,可实现高并发连接,处理响应快
处理动态页面很慢
因为他的事件处理机制:异步非阻塞事件处理机制(运用了epoll模型,提供了一个队列,排队解决)
nginx接收一个请求后,首先由listen和server_name指令匹配server模块,再匹配server模块里的location,location就是实际地址
server { # 第一个Server区块开始,表示一个独立的虚拟主机站点
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / { # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
} # 第一个location区块结果
比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了
2.反向代理(Reverse Proxy)对外就表现为一个服务器。(反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了)
实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器
[root@localhost ~]# tree /usr/local/nginx /usr/local/nginx ├── client_body_temp ├── conf # Nginx所有配置文件的目录 │ ├── fastcgi.conf # fastcgi相关参数的配置文件 │ ├── fastcgi.conf.default # fastcgi.conf的原始备份文件 │ ├── fastcgi_params # fastcgi的参数文件 │ ├── fastcgi_params.default │ ├── koi-utf │ ├── koi-win │ ├── mime.types # 媒体类型 │ ├── mime.types.default │ ├── nginx.conf # Nginx主配置文件,很重要!!! │ ├── nginx.conf.default │ ├── scgi_params # scgi相关参数文件 │ ├── scgi_params.default │ ├── uwsgi_params # uwsgi相关参数文件 │ ├── uwsgi_params.default │ └── win-utf ├── fastcgi_temp # fastcgi临时数据目录 ├── html # Nginx默认站点目录 │ ├── 50x.html # 错误页面优雅替代显示文件,例如当出现502错误时会调用此页面 │ └── index.html # 默认的首页文件 ├── logs # Nginx日志目录 │ ├── access.log # 访问日志文件 │ ├── error.log # 错误日志文件 │ └── nginx.pid # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件 ├── proxy_temp # 临时目录 ├── sbin # Nginx命令目录 │ └── nginx # Nginx的启动命令 ├── scgi_temp # 临时目录 └── uwsgi_temp # 临时目录
答: 首先当前我们的网络存在很多的恶意攻击,例如XSS,CSRF等等,我们客户端给服务器发送请求,访问服务器时,服务器需要对我们的请求进行相应的认证,同时对于我们客户端授予不同的权限,拥有相应的权限才能访问服务器,发送相应的请求信息。
jwt(Json web token) : 是为了在网络应用环境中传递声明而执行的一种基于JSON的开放标准(RFC 7519)
其中对应生成的token被设计成紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token可直接被用于认证,也可被加密。
JWT生成编码以后是以下的样子:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
JWT由三个部分组成:
头部(header):
HMAC算法介绍:
完整的头部head信息就像下面的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
然后我们会对头部信息进行加密,构成第一部分:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
载荷(payload):
载荷是存放有效信息的地方,有效信息包括三个部分:
其中,标准中注册的声明(建议但是不强制使用) :
公共的声明:
私有声明:
我们这里定义一个payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
进行加密处理以后,构成第二部分:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
签证(signature):
jwt的第三部分是一个签证信息,一般由三个部分组成:
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
其中密钥是保存在服务端的,服务端会根据这个密钥生成token和验证,所以必须要保存好!!!
最后一步签名的过程,实际上是对头部以及载荷内容进行签名。一般而言,加密算法对于不同的输入产生的输出总是不一样的。对于两个不同的输入,产生同样的输出的概率极其地小.
所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的.
服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名。那么服务器应用是怎么知道我们用的是哪一种算法呢?别忘了,我们在JWT的头部中已经用alg字段指明了我们的加密算法了。
如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。
注意:
一般会在请求头中加入Authoriation,并加入Bearer标注:
fetch('api/user/1', {
headers: {
'Authorization': 'Bearer ' + token
}
})
服务端会验证token,如果验证通过就会返回相应的资源!
我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
它的流程如下所示:
这个token值必须在每次请求时传递给服务端,它应该保存在请求头中,另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了(Access-Control-Allow-Origin)
方案1 : 适当减短token的有效期,让token尽快失效
方案2 : 使用redis (即使用JWT还要使用redis的场景)
(1) 我们在用户登录时,生成JWT
(2) 把JWT的id存储到redis中,只有redis数据库中有id的JWT,才是有效的JWT
(3) 退出登录时,将对应的id从redis中删除,即间接解决了token的注销问题
答:JWT设计为了实现无状态的登录,因此token无法修改,难以实现异地登录的判断,或者强制让登录token失效。
因此如果有类似需求, 就不应选择JWT作为登录方案,而是使用传统session登录方案。
但是,如果一定要用JWT实现类似要过,就需要在Redis中记录登录用户的JWT的token信息,这样就成了有状态的登录,但是这样还不如一开始就选择Session方案.
我们一般选择在网关中实现认证和登录.
我们的微服务隐藏在网关的后面,而且整个服务被Nginx反向代理,用户只能看到nginx的地址,微服务暴露的可能性很低。
然后,即便真的暴露了,我们的微服务都做了严格的服务间鉴权处理,任何对微服务的访问都会被验证是否有授权,如果没有则会被拦截。具体实现:
当一个用户打开一个浏览器或者访问一个网站时,只要不关闭这个浏览器,不管该用户点击多少个超链接,访问多少个资源,直到该用户关闭浏览器或者服务器关闭.这样一个过程称为一个会话.
那么在会话过程中需要解决一些问题 : 主要就是保存用户在访问过程中产生的数据
注意:
session的生命周期
session:
关闭浏览器,只会使浏览器中的sessionid消失,但不会使服务端的session对象消失。因此服务端需要设置一个过期时间,当距离客户上一次使用session的时间超过了这个失效时间时就认为客户端已经停止了活动,从而删除session。session.setMaxInactiveInterval(3600);
cookie:
Cookies.set('name', 'value', {
expires: 7, //设置失效时间为7天
});
var millisecond = new Date().getTime();
var expiresTime = new Date(millisecond + 60 \* 1000 \* 15); //设置过期时间为15分钟后
Cookies.set('name', 'value', {
expires: expiresTime, //如果设置一个过去的时间点会直接删除
});
sessionStorage和localStorage均用于客户端本地存储数据
不能设置过期时间。生命周期是永久性的,即使关闭浏览器也不会让数据消失,除非主动去删除数据。
if(window.localStroage){ //判断浏览器是否支持localstroage
window.localStroage.setItem('name','zs') //存储数据
window.localStroage.getItem('name') //获取数据
window.localStroage.removeItem('name') //移除数据
}
生命周期是在浏览器关闭前,在浏览器关闭前,其数据一直存在。使用方法与localStroage一致。
当变量进入环境时,例如:在一个函数中声明一个变量,就将这个变量标记为“进入环境”;当变量离开时,则将其标记为离开环境。
(从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能还会用到它们)
跟踪记录每个值被引用的次数。
当申明了一个变量并将一个引用类型值A赋给该变量时,则这个引用类型值A的引用次数就是1。如果同一个引用类型值A被赋给另一个变量,则这个引用类型值A的引用次数+1;相反,如果包含这个引用类型值A的变量又取得了另外一个引用类型值B,则这个引用类型值A的引用次数-1;当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。
循环引用
function circularReference() {
let obj1 = {
};
let obj2 = {
b: obj1
};
obj1.a = obj2;
}
//当obj1这个变量指向obj1这个对象时,obj1这个对象的引用计数为1
//当obj2这个变量指向obj2这个对象时,obj2这个对象的医用计数为1;obj2中的b属性指向obj1,obj1这个对象的引用计数变为2
//obj1的a属性指向obj2,obj2这个对象的引用计数变为2
//当代码执行完毕,会将obj1和obj2赋值为null,但此时obj1对象和obj2对象的引用计数仍为1,不为0,所以不会进行垃圾回收,造成垃圾泄露。
//这两个对象已经没什么作用了,在函数外部也访问不到他们。
解决问题的关键也就是可以将这些引用存储起来并在发现引用时返回被引用过的对象,从而结束递归的调用。
原理:
利用uniqueList存储拷贝过的对象,如果之前拷贝过就把他的拷贝结果返回,否则遍历该对象的各个属性,是对象的话继续深拷贝,不是对象就直接赋值
//arr中存储了多个对象 //[{source:xxx, target:xxx},{source:xxx, target:xxx},] function find(arr,item){ for(var i=0; i<arr.length; i++){ if(arr[i].source === item){ return arr[i] //返回的是这整个对象{source:xxx, target:xxx} } } return null; } function isObject(obj) { return typeof obj === 'object' && obj != null; } function deepClone(source,uniqueList){ //被拷贝的source必须是对象 if(!isObject(source)) return source; if(!uniqueList) uniqueList = []; //初始化数据 //存储结果 var target = Array.isArray(source) ? [] : {}; var uniqueData = find(uniqueList,source); //在初始化数据中查看是否存在source if(uniqueData) return uniqueData.target; uniqueList.push({ source:source, //拷贝源 target:target //拷贝结果 }); for(var key in source){ if(Object.prototype.hasOwnProperty.call(source,key)){ if(isObject(source[key])){ target[key] = deepClone(source[key], uniqueList) // 传入数组 }else{ target[key] = source[key]; } } } return target; } var a = { name:"key1", eat:[ "苹果", "香蕉" ] } a.eat[2] = "桃"; a.d = a; console.log(a); //{name: "key1", eat: Array(3), d: { // {name: "key1", eat: Array(3), d: {…}} //}} b = deepClone(a); console.log(b); //{name: "key1", eat: Array(3), d: { // {name: "key1", eat: Array(3), d: {…}} //}}
同源策略是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。
同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
缺点:只能实现get一种请求
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
</script>
服务端返回如下(返回时即执行全局函数):
handleCallback({"status": true, "user": "admin"})
后端node.js代码示例:
var querystring = require('querystring'); var http = require('http'); var server = http.createServer(); server.on('request', function(req, res) { var params = qs.parse(req.url.split('?')[1]); var fn = params.callback; // jsonp返回设置 res.writeHead(200, { 'Content-Type': 'text/javascript' }); res.write(fn + '(' + JSON.stringify(params) + ')'); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...');
主要是后端设置,如果前端发送请求需要携带cookie,前端才需要设置
前端设置:
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
后端设置
/\*
\* 导入包:import javax.servlet.http.HttpServletResponse;
\* 接口参数中定义:HttpServletResponse response
\*/
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'\*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true");
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
前端请求:
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
代理服务器处理请求:
#proxy服务器 server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名 index index.html index.htm; # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用 add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为\* add_header Access-Control-Allow-Credentials true; } }
后台:
var http = require('http'); var server = http.createServer(); var qs = require('querystring'); server.on('request', function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前台写cookie res.writeHead(200, { 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取 }); ### 最后 基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。 **[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。