前端面试速记
操作系统
进程与线程
- 进程是具有一定功能的程序,是系统进行资源分配调度的一个独立单位,每个进程拥有自己的资源。
- 线程是进程的执行单元,是操作系统能够进行运算调度的最小单位。
- 一个程序至少有一个进程,一个进程至少有一个线程,资源分配给进程,同一个进程下所有线程共享该进程的资源。
- 线程共享的资源:堆、全局变量、静态变量、文件等共用资源
- 线程自己的资源:栈、程序寄存器
- 在你的文本编辑器进程中,可能会有几个线程:一个线程负责显示文本,另一个线程在后台进行拼写检查,还有一个线程负责定期自动保存文件。这些线程都是同一个进程(文本编辑器)的一部分,它们共享相同的资源(比如内存),但它们各自独立运行,完成不同的任务。(便于理解 不用背
进程的三种状态
- 就绪(Ready):进程已分配到除CPU以外的所有必要资源,只要获得处理机便可立即执行。
- 执行(Running):进程已获得处理机,其程序正在处理机上执行。
- 阻塞(Blocked):正在执行的程序,由于等待某个事件发生而无法执行时,便放弃处理机进入阻塞状态。阻塞原因可能是等待I/O完成、申请缓冲区不能满足、等待信号等。
- 为什么就绪到阻塞和阻塞到执行不能转换?:就绪状态进程没有占用处理机,不会被阻塞;阻塞状态进程唤醒后先要进入就绪队列,才会被调度程序选中,进入执行状态。
进程调度任务
- 保存处理机的现场信息,当操作系统决定从一个进程切换到另一个进程时,必须保存当前进程的状态,包括程序计数器、寄存器内容等,这样进程在重新获得CPU时能够继续执行。
- 通过一个调度算法选择下一个要执行的进程。
- 把处理器分配给进程,将进程的现场信息,如寄存器值等,装入处理器的寄存器中,并将控制权交给该进程。
进程调度机制
- 排队器:将所有就绪的进程按照一定的策略排成一个或多个队列
- 分派器:取出进程调度程序选中的进程,进行进程间上下文切换、分配处理器
- 上下文切换器:保存当前进程的上下文信息,并装入下一个被调度进程的上下文信息。
进程调度方式
- 非抢占式:一旦处理机被分配给某个进程,就一直运行直到完成或阻塞。
- 抢占式:允许调度程序根据某种规则,暂停某个正在执行的进程。
进程调度算法
- FCFS(先来先服务):非抢占式,按次序调度,可能会导致短作业等待时间过长。
- SJF(短作业优先):非抢占式,选择预计运行时间最短的进程,可能导致长作业饥饿;未考虑作业的优先紧迫程度。
- HPF(优先级调度):非抢占式,按优先级顺序调度
- HRRF(高响应比优先):非抢占式,计算后备作业队列中每个作业的响应比((等待时间+处理时间)/ 处理时间),适用于批处理系统;计算响应比开销大
- RR(时间片轮转):抢占式,适用于分时系统,给每个进程固定时间的执行机会,在进程完成或时间片用完时切换进程。平均等待时间较长,上下文切换较费时。
- 多级反馈队列:抢占式,设置多个就绪队列,每个队列有不同的优先级;每个队列内采用FCFS算法。队列按优先级调度,仅当前一队列空闲时才调用下一队列。兼顾长短作业,有较好的响应时间,可行性强。
进程间通信方式
- 无名管道:半双工通信方式,数据只能单向流动且只能在有亲缘关系的进程间使用
- 有名管道:半双工通信方式,允许在非亲缘关系的进程间使用
- 信号:通知接收进程某个事件已发生
- 消息队列:传递消息的链表,存放在内核中。克服了信号传输信息少,管道只能传输无格式字节流以及缓冲区大小受限的缺点.
- 信号量:一个计数器,用来控制多个进程对共享资源的访问。常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源
- 共享内存:映射一份能被其他进程所访问的内存,这份内存由一个进程创建但其他进程可以访问(缺点:共享内存不提供同步机制,在使用共享内存进行通信时需要借助其他手段来进行进程间的同步工作,常与信号量一起使用实现同步对共享内存的访问。)
- 套接字:不同机器之间的进程通信
并发与并行
- 并发:逻辑上的同时发生,强调有处理多个任务的能力但不一定要同时
- 并行:物理上的同时发生,强调有同时处理多个任务的能力
- (并发是两条队列交替使用一台咖啡机,并行是两个队列同时使用两台咖啡机。)
- 并发与顺序:顺序是上一个开始执行的任务完成后,当前任务才能开始执行;并发是无论上一个开始执行的任务是否完成,当前任务都可以开始执行。
- 并行与串行:串行是有一个任务执行单元,从物理上就只能一个任务一个任务执行;并行是有多个任务执行单元,从物理上就可以多个任务一起执行。
死锁
- 定义:死锁是指两个或两个以上进程在执行过程中,因为资源竞争或彼此通信而相互等待,导致每个进程都无法继续执行的现象。
- 原因:系统资源不足;进程运行推进顺序不当;资源分配不当
- 四个必要条件:
互斥条件:一个资源每次只能被一个进程使用
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:进程已获得的资源在未使用完之前不能强行剥夺
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系 - 解决死锁的方法:死锁的预防(静态策略)、死锁的避免(动态策略)、死锁的检查与恢复
- 死锁的预防:要求进程申请资源时遵循某种协议,从而打破产生死锁的四个必要条件中的一个或几个。
打破请求与保持条件:预先分配资源,系统能够满足当前进程的全部资源时,一次性将所申请的资源全部分配给该进程。但是进程在执行之前不可能知道它所需的全部资源。
打破循环等待条件:资源有序分配,事先将资源分类编号,所有进程对资源的请求必须严格按照序号递增的顺序提出。但是为了遵循顺序,进程需要提前申请暂不使用的资源,增加了进程对资源的占用时间。 - 死锁的避免:动态检查进程所发出的每一个申请资源命令,并根据检查结果决定是否进行资源分配。(银行家算法)
- 死锁的检查与恢复:当系统没有采取死锁预防或避免措施时,需要能够检测死锁并从中恢复。
- 检测:(进程-资源分配图):
每种资源类中仅有一个资源的死锁检测:资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。
从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。
每种资源类中有多个资源的死锁检测:简化分配图,看能否消去图中所有边,使所有进程成为孤立点
每种资源类中有多个资源的死锁检测:银行家算法,结束时检查是否有没被标记的进程 - 恢复:重启系统、资源剥夺法(剥夺陷于死锁的进程所占用的资源,但并不撤销此进程)、进程回退法(根据系统保存的检查点让所有进程回退)、撤销进程法(撤销陷入死锁的所有进程,或按顺序逐个撤销陷入死锁的进程)
- 银行家算法:一个银行家拥有一定数量的资金,有若干个客户想要贷款,每个客户必须在一开始就声明他所需贷款的总额。若该客户贷款总额不超过银行家的资金总数,银行家可以满足客户的要求。客户不一定能一次性拿到自己所需的全部贷款,他在借满全部款额之前可能会等待,但银行家必须保证这种等待是有限的并且等待是有有结果的。
从当前状态出发,寻找一个进程其需求不超过当前可用资源,若找到,假设它运行完成并释放资源,加入安全序列。
缺点:算法要求客户数固定不变;不能满足实时客户要求快速响应的需求。
计算机网络
OSI七层协议:应用表示会传输(TCP/UDP)网络(IP)数据给物理
TCP/IP四层模型:应用(包括OSI模型的会话层、表示层和应用层)、传输层、网际层(OSI网络层)、网络接口层(OSI 模型的物理层和数据链路层)
五层模型:应用(包括OSI模型的会话层、表示层和应用层)、传输、网络、数据链路、物理
应用层的网络协议:
- 基于TCP:FTP:文本传输协议;SMTP:简单邮件传输协议;TELNET:Internet远程登录协议;HTTP
- 基于UDP:DNS;TFTP;SNMP:简单网络管理协议;NFS:网络文件系统
地址:
- MAC地址:数据链路中的地址,用来识别同一链路中不同的计算机
- IP地址:IP中的地址,用来识别连接到网络中的主机和路由器
- 程序地址(端口号):识别同一台计算机中进行通信的不同应用程序
TCP三次握手:客户端和服务器都需要知道双方可收发/æk/
- 首先客户端向服务器发送SYN报文,包含一个随机的序列号并进入SYN_SENT状态(seq=x:顺序码,自己发送报文的上一个seq+1)。
- 服务器确认客户端的SYN,同时自己也发送一个SYN报文,包含一个随机序列号,将状态设置为SYN_RECEIVED(seq=y,ack=x+1:对方发送报文的上一个seq+1)
- 客户端发送一个ACK报文作为回应,将状态设置为ESTABLISHED;服务器收到这个ACK报文后,也将状态设置为ESTABLISHED。(seq=x+1,ack=y+1
- SYN:告诉对方自己已经准备好连接,FIN:告诉对方自己已经准备好关闭连接,ACK表示响应
- 防止已经失效的连接请求报文突然又传送到服务器,所以客户端需要再次确认
TCP四次挥手
- 客户端向服务器发送一个FIN(结束)标志位的包,进入FIN_WAIT_1(seq=x
- 服务器发送ACK包作为回应,并进入CLOSE_WAIT状态(seq=y,ack=x+1;客户端收到ACK后,进入FIN_WAIT_2状态
- 服务器会继续发送数据直到所有数据发送完毕,服务器发送自己的FIN包给客户端,进入LAST_ACK状态(seq=z,ack=x+1
- 客户端收到服务器的FIN包后,会进入TIME_WAIT状态,并发送一个ACK给服务器(seq=x+1,ack=z+1;为了确保服务器接收到ACK包,客户端通常会等待2MSL的时间
- 为什么建立连接是三次握手,关闭连接是四次挥手:因为关闭连接时,服务器先确认客户端的关闭报文,然后需要发送完全部数据才能向客户端发送关闭报文。而建立连接时,服务器将SYN和ACK放在一个报文中发送给客户端。
- 如果已经建立了连接,但客户端突然出现故障怎么办?
TCP设有一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器。如果一定时间之内没有收到客户端的数据,服务器会向客户端连续发送探测报文,如果客户端一直没有回应,服务器关闭连接
TCP和UDP
- TCP:面向连接的可靠性传输,发送的数据被视为一个连续的字节流,只能一对一传输,提供流量控制和拥塞控制。
- UDP:尽最大努力交付的传输,无连接不可靠,发送的数据被封装为报文,可以一对一或一对多传输。UDP用于高速传输和对实时性有较高要求的通信。
- 流量控制:TCP使用滑动窗口进行流量控制,确保发送的数据不会溢出接收方的缓冲区,窗口大小由接收方的接收缓冲区大小决定。
- 拥塞控制:慢启动(逐渐增加传输数据量,直到确定最大网络带宽,并在网络负载较高时减少传输数据量)+拥塞避免;快速重传+快速恢复
拥塞窗口用于实现拥塞控制,窗口大小由网络的当前拥塞状况决定,由发送方动态调整。
HTTP/HTTPS/浏览器
HTTP
- 状态码:1表示信息,2表示成功,3表示重定向,4表示客户端错误,5表示服务器错误
- 200 OK:请求成功
- 301 Moved Permanently:永久移动到新地址/302 Found:临时移动/304 Not Modified:所请求的资源未修改
- 400 Bad Request:请求错误/401 Unauthorized:需要认证/403 Forbidden:服务器拒绝请求/404 Not Found:服务器找不到资源
- 500 Internal Server Error:服务器内部错误 502 Bad Gateway:作为网关或代理的服务器,从上游服务器收到了无效的响应。
HTTP首部:
- 通用首部:既可以用在请求头中,也可以用在响应头中,例如date
- 请求首部:用于HTTP请求,例如Accept、Host、User-Agent、Cookie
- 响应首部:用于HTTP响应,例如Server、Set-Cookie
- 实体首部:描述实体部分,如消息内容的长度(Content-Length)和类型(Content-Type)等信息。
HTTP方法:
- GET:请求指定的页面信息并返回响应主体
- POST:向指定资源提交数据,请求服务器处理
- HEAD:获取服务器的响应头信息
- options:请求服务器返回该资源所支持的所有HTTP请求方法
- put:向指定资源位置上传其最新内容
- delete:请求服务器删除请求URI所标识的资源
- connect:建立一个到由目标资源标识的服务器的隧道,可以发送加密的SSL/TLS流量
- trace:请求服务器回显其收到的请求信息
get和post有什么区别?
- GET请求通常用于请求服务器发送资源,POST请求通常用于向服务器提交数据进行处理
- GET请求数据通常附加在URL后面,POST请求的数据通常在请求体中发送
- GET请求可以被缓存和在浏览器历史记录保留,POST请求不会
- GET请求在URL中传递的参数有长度限制,POST请求没有
http2.0的新特点
- 基于HTTPS,具有更好的安全性
- 二进制分帧层:将通信分解为不同类型的帧,使用二进制进行数据编码和传输。多个请求或响应的帧可以交错发送,不会互相阻塞。
- 多路复用:在同一个TCP连接上可以同时发送多个请求和响应,提高了连接的利用率,减少了因多个 HTTP 请求造成的延迟。
- 服务器推送:允许服务器主动向客户端发送将会用到的资源,而不是等待客户端的请求。
- 头部压缩:通过压缩请求和响应的头部信息,减少传输的数据量,提高性能。
HTTPS
HTTPS工作原理:使用非对称加密传输一个对称密钥,服务器和客户端使用这个对称密钥加密和解密数据
- 1 客户端通过URL发起HTTPS请求,要求服务器建立SSL链接
2 服务器收到客户端的请求后,返回公钥证书
3 客户端验证公钥证书是否有效,验证不通过显示警告信息;验证通过利用伪随机数生成器生成会话密钥,用公钥加密并发送给服务器
4 服务器通过私钥解密会话密钥。
5 此后,服务器和客户端之间的通信通过会话密钥加密 - SSL/TLS:TLS是SSL的后续版本,更加安全,目前所说的SSL实际都是指TLS。
- SSL/TLS证书的主要内容:证书持有者的信息(CN, Comman Name)、公钥、CA信息(CN)、CA数字签名、证书有效期等
HTTPS优点和缺点
- 优点:能够进行信息加密、完整性校验和身份验证,很大程度上避免了HTTP协议容易发生信息窃听、篡改、和劫持的风险。
- 缺点:建立连接时SSL/TLS握手会增加初始加载时间;功能强大的SSL证书需要费用;HTTPS缓存不如HTTP高效,会增加数据开销
HTTPS和HTTP的区别
- HTTPS使用SSL加密传输协议;HTTP是明文传输
- HTTPS端口号443;HTTP端口号80
- HTTPS基于传输层;HTTP基于应用层
- HTTPS需要使用SSL证书
HTTPS中间人攻击及其防范
- 中间人攻击:攻击者与通讯的两端分别建立独立的联系,通讯的两端认为他们正在与对方直接对话,但整个会话被攻击者完全控制
- 防范:中间人攻击会在会话建立阶段替换公钥,所以需要保证公钥的正确性。因此在提供公钥的同时需要提供数字证书,数字证书由CA签发,信任链一直延续到根证书。为了保证根证书不被泄露,一般通过设备内置操作系统分发,如果设备不被恶意修改,整个过程就是安全的。
浏览器
URL
- URL指的是统一资源定位符,即特定资源在Web上的地址
- 协议://域名:端口/资源路径?查询字符串#指向特定部分的锚点
BOM
- BOM是浏览器对象,开发者可以通过BOM与浏览器交互
- location对象:获取或设置当前窗口的URL
- history对象:记录用户曾经浏览过的页面,进行后退或前进操作
- navigator对象:返回浏览器的信息
Cookie:
- 浏览器首次访问某网站时,服务器可以通过发送一个 Set-Cookie 响应头来创建一个 Cookie。浏览器会根据服务器提供的信息在设备上存储cookie。当用户再次向同一服务器发送请求时,浏览器会自动在请求头中包含 Cookie 头,将之前存储的 Cookie 信息发送给服务器。
- 作用:保存用户登录状态、跟踪用户行为
- 参数:name(名称),value(值),expires:到期日期,path:接收cookie的文件路径,domain:接收cookie的域,secure:cookie 仅通过 HTTPS 发送,HttpOnly:只能由 Web 服务器访问
Cookie和Session的共同点和区别
- 共同点:都用来维护用户状态,可以存储用户信息和个性化用户体验
- cookie存储在用户的浏览器,有泄漏风险
- session保存在服务器,安全性较高,但可能影响服务器性能
cookie,localStorage,sessionStorage的共同点和区别
- 共同点:都是前端本地存储的方式,保存在浏览器端且受到同源策略限制
- cookie存储容量一般只有4KB,localStorage和sessionStorage可以保存5MB的信息
- cookie可以设置有效期,默认在关闭浏览器后失效;sessionStorage仅在当前网页会话下有效,关闭页面或浏览器后失效;localStorage会永久存储直到被手动清除
- cookie会发送到服务器,localStorage和sessionStorage仅在本地保存
- cookie和localStorage在所有同源窗口共享,sessionStorage不在不同窗口中共享
CSRF和XSS攻击
CSRF(跨站请求伪造):
- 攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,冒充用户攻击网站。攻击者并不需要获取cookie信息。
- 防御:1. 在转账等敏感操作中使用验证码;2. 使用token验证用户身份(服务端生成一个token,加密后传递给用户,用户在提交请求时,需要携带这个token;例如,服务器生成随机数放入表单隐藏字段,用户在提交表单时将随机数一同提交);3. 服务器处理请求时,验证origin标头是否为允许的来源;4. 限制身份验证的有效期。
XSS(跨站脚本攻击):
- 攻击者在目标网站上注入恶意脚本,当受害者访问网站时就会执行这些恶意脚本
- 根本原因:服务器方面:恶意代码未经过滤,与网站正常的代码混在一起;客户端方面:浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
- 反射型XSS攻击:攻击者构造包含恶意代码的URL,用户点击后网站服务端将恶意代码拼接在 HTML 中返回给浏览器,浏览器执行恶意代码。
- DOM型XSS攻击:恶意脚本是由浏览器端的脚本逻辑(如JavaScript)处理并执行的,而不是来自服务器的直接响应。
- 存储型XSS:恶意脚本永久存储在目标服务器上。当其他用户访问存储了恶意脚本的数据的页面时,脚本会在他们的浏览器中执行。
- 防御:对url的查询参数、输入内容、数据库接收数据等进行转义/过滤;在数据输出到HTML页面前,对数据进行编码处理。
- 其他防范手段:设置内容安全策略(CSP):仅执行来自白名单源的脚本,忽略内联脚本、html中的事件处理属性等;设置Secure和HttpOnly属性的Cookie防止脚本访问。
在地址栏里输入一个URL,到这个页面呈现出来,中间会发生什么?
- 浏览器根据URL域名寻找服务器ip,首先在缓存中查找,找不到则查询DNS服务器;
- 浏览器与服务器建立TCP连接;
- 浏览器向web服务器发送http请求
- 服务器响应请求,并发送对应的数据到浏览器;
- 浏览器下载数据,解析数据,渲染页面;
- 关闭TCP连接;
浏览器在生成页面时会生成哪两棵树?
- 浏览器遍历HTML文档的结点,生成DOM树;解析CSS文件,生成CSSOM规则树
- 解析:浏览器解析HTML文件,构建DOM树,如果解析过程中遇到link或者style标签,会同时开始解析CSS,并行构建CSSOM树。如果遇到了script标签,默认情况下会阻塞DOM构建。
- 渲染:解析中创建的 CSSOM 树和 DOM 树组合成一个渲染树,包含所有可见节点的内容和计算样式。根据渲染树,浏览器开始布局,确定每个对象的确切大小和位置。最后,浏览器将各个节点绘制到屏幕上。
- 为了防止脚本解析阻塞进程,可以为script标签添加async或defer属性。
- async属性:脚本的下载和DOM构建并行进行,并且在下载完成后立即执行。
- defer属性:脚本的下载和DOM构建并行进行,在 DOM 完全构建完成后执行脚本(DOMContentLoaded事件之前)
前端优化/Web性能优化
- 加载优化:减少HTTP请求、使用按需加载(用户触发动作时才加载对应的功能)、预加载(提前加载图片,当用户需要时直接从本地缓存中渲染),缓存资源
- 执行优化:使用外部JS和CSS、将CSS样式表放在顶部,JS脚本放在底部,避免重置图像大小
- 渲染优化:减少DOM节点,设置viewport
强缓存和弱缓存
- 强缓存:浏览器不向服务器发送请求,直接从本地缓存中读取文件并返回
- 弱缓存:如果强缓存失效,浏览器向服务器发送请求,服务器告知资源是否更新(ETag唯一标识资源)
- Last-Modified:服务器返回资源时,响应头中添加Last-Modified(最后修改时间);浏览器再次请求时,请求头中添加If-Modified-Since等同于最后修改时间,服务器判断资源是否是最新的。
- ETag:服务器响应请求时,返回当前资源文件的ETag;浏览器再次请求时,将ETag放入请求头中的If-None-Match
重排/回流(reflow)和重绘(repaint)
- 重排:DOM的变化影响到元素的几何信息(例如宽度、高度、位置等),浏览器需要重新计算元素的位置
- 重绘:元素的外观被改变,但没有改变布局,例如改变颜色、背景图片等
引起重排重绘的原因
- 浏览器初始化窗口
- 添加或删除可见的DOM元素
- 移动或者给页面中的DOM节点添加动画
- 添加一个样式表,调整样式属性
- 用户行为,如调整窗口大小、改变字号、滚动等
减少重排重绘的方法
- 批量改变和表现DOM:使用documentFragment对象,在内存中组装DOM,然后一次性添加到文档中;通过display:none属性隐藏元素,进行多此修改,然后再显示(只触发两次重排重绘)
- 通过更改class或使用cssText属性(js中DOM元素的属性,可以一次性设置多个内联样式),批量修改样式。
- 对于多次重排的元素,比如动画,使用绝对定位使其脱离文档流,不影响其他元素。
CSS
盒模型
- 外边距margin,边界border,内边距padding,内容
- 标准盒子模型:width=content 盒子宽度=除了外边距
- 低版本IE盒模型:width = content+border+padding
- 背景颜色会填充content、padding和border区域(除了外边距)
选择器
- id选择器>类选择器>标签选择器、伪类选择器、伪元素选择器、通用选择器
- :伪类表元素的不同状态(:hover,:nth-child),::伪元素表示元素的特定部分,(::before ::after)
- 样式表来源优先级:内联>内部样式>外部样式>浏览器用户自定义样式>浏览器默认样式
@import和link标签
- 引入外部css样式,link属于HTML标签,与页面同时加载,权重更高;@import属于CSS,在页面加载完成后加载
CSS3新增内容
- 边框:border-radius、box-shadow、border-image圆角阴影图像
- 背景:background-size、background-origin(规定定位区域
- 文字效果:text-shadow、word-wrap(对长单词进行拆分并换行)
- 2D转换:translate()、rotate()、scale()移动旋转缩放
- 用户界面:resize(允许用户调整大小)、box-sizing(控制盒模型解析模式,选择应用高度和宽度的区域 context-box/border-box)
隐藏页面中某个元素的方法
- opacity=0:元素透明度为0
- visibility=hidden:元素不可见但仍然存在
- display:none 元素从页面中被删除
定位属性
- fixed:元素位置相对于浏览器窗口固定,脱离普通文档流
- absolute:元素位置相对于最近的已定位父元素,脱离普通文档流
- relative:通过属性相对于文档流中的初始位置移动
inline-block、inline和block
- block:块级元素,前后换行符,可设置宽高,内外边距有效
- inline:内联元素,前后无换行符,不可设置宽高,垂直方向内外边距无效
文档流
- 普通流:定位为relative和static
- 定位流:fixed或absolute
- 浮动流:元素浮动到父元素左上或右上
浮动溢出
- 如果父元素的高度被设置为auto,无法被撑开,使得内容溢出到容器外面而影响布局。
- 清除浮动:最后一个浮动元素后加空div标签 并添加样式clear:both;
给浮动元素的容器添加overflow:hidden属性;给浮动元素的容器添加看不见的:after伪元素,并设置clear: both
布局方式
- 弹性布局:需要调整宽度的元素尺寸采用rem/em做单位(em相对其父元素,rem始终相对html大小)
- 响应式布局:搭配媒体查询技术,为不同的屏幕分辨率定义布局;但媒体查询是有限的,只能适应主流媒体的宽高
弹性布局
- em是相对其父元素,而rem是始终相对于html大小
- 采用flex布局的元素称为容器,子元素称为项目,容器存在水平的主轴和垂直的交叉轴
- 对于容器,可以设置沿主轴排列的方向flex-direction,换行方式flex-wrap,水平对齐方式justify-content,垂直对齐方式align-items,多条轴线对齐方式align-content
- 对于项目,可以设置排列顺序order,放大缩小比例flex-grow/flex-shrink,占据空间flex-basis,对齐方式align-self
垂直居中
- 不知道子元素的宽度和高度:
容器display:flex; justify-content:center; align-items:center; /əˈlaɪn/
弹性布局,设置水平和垂直居中 - 知道子元素的宽度和高度:
容器position:relative;
子元素position:absolute; top:50%; left:50%; transform: translateX(-50%) translateY(-50%);
将子元素的左上角移动到容器的中心,然后向上和向左移动自身宽度和高度的50%
容器position:relative;
子元素position: absolute;margin:auto; top:0; right:0; bottom:0; left:0;
top、right、bottom、left均设置为0,元素触及容器边界,设置自动外边距使外边距平均分配
BFC
什么是BFC
- BFC是块级格式化上下文,是一个独立的区域,区域里的子元素不会影响到外面的布局。
哪些元素会生成BFC
- 根元素,即HTML标签
- 浮动元素:float值为left或right
- 定位元素:position为fixed或absolute的元素
- overflow值为auto、scroll、hidden的元素
- display值为inline-block、table-cell、flex、inline-flex的元素
约束规则
- 内部的box会在垂直方向上一个接一个放置,垂直方向的距离由margin决定
- 属于同一个BFC的两个相邻的标签外边距会合并
- 计算BFC高度时,浮动元素也参与计算
- BFC的区域不会与float的元素区域重叠
作用
- 为浮动元素容器触发BFC,防止高度塌陷
- 防止相邻元素margin合并
- 阻止元素被浮动元素覆盖
- 阻止因浏览器四舍五入造成的多列布局换行
HTML
HTML5新增内容
- 语义化标签:header,nav,main,artical,section,aside,footer
有利于代码可读性,帮助搜索引擎优化,样式丢失时也能保持清晰结构 - 新增input类型:url、email、date、search等
- 本地存储 localStorage 和 sessionStorage
- 音频元素audio、视频元素vedio
- 地理位置、canvas画布、拖放API、多线程编程的webworker、全双工通信协议websocket
<!DOCTYPE> 声明:
什么是iframe?有什么缺点?
- iframe会创建包含另一文档的内联框架
- 缺点:会阻塞主页面的onload事件;搜索引擎无法解读这种页面;iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
什么是WebSocket?有什么特点?
- WebSocket是支持浏览器与服务器进行全双工通讯的应用层协议。
- 服务器可以主动向客户端发送信息,客户端也可以主动向服务器发送信息
- 建立:客户端通过http请求与WebSocket服务端协商升级协议,协议升级完成后,后续的数据交换遵照WebSocket的协议。
什么是Web Worker
- 为JS创建多线程环境,允许主线程创建Worker线程,将一些任务分配给线程运行。
- Workers 和主代码运行在完全分离的环境中,只有通过相互发送消息来进行交互。Worker不能访问DOM。
拖放API
- HTML5实现了原生拖放功能
- dragstart,drag,dragend:开始拖放、正在拖放、结束拖放事件
- dragenter,dragover,dragleave:被拖放元素进入、在其中移动、离开目标元素事件
- drop:被拖放元素在目标元素上释放
JS
JS三大组成部分
- ①ECMAScript;②文档对象模型(DOM);③浏览器对象模型(BOM)。
ES6新特性
- 新增let、const声明变量,实现了块级作用域
- 新增箭头函数
- 引入promise、await、async/eɪˈsɪk/解决异步回调问题
- 引入Class作为对象的模板
- 引入新的数据类型symbol(生成一个全局唯一的值),新的数据结构set和map
- 引入模块方便模块化编程
变量
- var:存在变量提升,允许重复声明,作用域为全局或函数
- let:没有变量提升和重复声明,作用域为块级作用域
- const:没有变量提升和重复声明,需要初始值并且不能修改,但如果初始值为数组和对象,可以修改数组元素或对象属性
数据类型
- 基本数据类型:null、undefined、symbol、boolean、string、number
- 引用数据类型:Object、Array、Date、Function等
- 引用数据类型比较的是内存地址是否相同
- 基本数据类型存放在栈区;引用数据类型的指针存储在栈中,该指针指向堆中该实体的起始地址。
arguments
- arguments对象包含传递给函数的每个参数,是一个类数组数据
- 可以读取函数接收到的所有参数,而不管在定义函数时是否声明了形参。
js的new操作符做了哪些事情
- 创建一个类的实例:创建一个空对象obj,将obj.proto设置为构造函数的prototype
- 初始化实例:构造函数被传入参数并调用,this指针被设定为指向该实例obj
- 返回实例obj
Set、Map、WeakSet 和 WeakMap 的区别
- Set 和 Map 允许任何类型的键,而 WeakSet 和 WeakMap 只接受对象作为键。
- Set和WeakSet元素不可以重复。
- Set 和 Map可以遍历。
- WeakSet 和 WeakMap 都是弱引用,不会阻止垃圾回收器回收它们所引用的对象
内存泄露
- 意外的全局变量,声明变量时忘记使用 var, let 或 const
- 闭包:在一个函数内部创建另一个函数,内部函数被返回时仍然能够访问外部函数作用域中的变量,这些变量不会被回收;闭包中的变量并不保存在栈内存中,而是保存在堆内存中,所以函数调用之后闭包还能引用到函数内的变量
- setInterval 创建定时器未被清除
- 已经从文档中移除的DOM元素的引用或事件监听
垃圾回收的两种方法
- 标记清除:从根部(全局对象)出发定时扫描内存中的对象,删除不能从根部到达的对象
- 引用计数:统计引用类型变量声明后被引用次数,当次数为0时该变量被回收。
json和xml
- json轻量级传递速度更快,占用带宽更少,与javascript的交互更加方便,但对数据的描述性较差
判断变量类型
- typeof A区分原始数据类型
- A instanceof B判断对象是否为特定类型
- Object.prototype.toString.call(A)精确获取对象的类:toString是Object.prototype 的内置函数,默认检索调用它的对象的 [[Class]] 内部属性,.call()将函数的this绑定到目标对象上。
Math.min()/.max()
- Math.min()没有参数时返回无穷大,.max()没有参数时返回负无穷大
原型链
- 每个对象(object)都有一个私有属性(proto)指向另一个原型(prototype)对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。
- 构造函数的prototype属性指向调用该构造函数而创建的实例的原型。
- 每个原型都有一个 constructor 属性指向关联的构造函数。
- 通过原型链,新对象可以访问原型对象的属性和方法,除非这些属性或方法在新对象上被覆盖。
- 当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。

同源策略
- 限制一个源的文档或者脚本与另一个源的资源进行交互,例如跨源iframe与父文档之间的互相访问与DOM操作
- 只要协议、域名、端口有任何一个不同,都被当作是不同的源
- 同源策略可以被继承:一些嵌入元素(例如 script、img)会继承其父元素的源。这意味着,如果一个script标签被插入到一个来自 “https://example.com” 的页面中,那么这个script标签的源就是 “https://example.com”,无论这个标签是从哪里加载的。但是frame和 iframe元素的源是由它们的 src 属性决定的。
- 如果cookie没有设置HttpOnly,即便规定了path,也可能会被js绕过。
跨域访问方法:
- CORS:当一个网页尝试进行跨域 HTTP 请求时,在请求头中加入一系列信息,服务器会在响应头中显式表明请求是否被允许,浏览器解析响应头决定是否处理响应。CORS 请求分为简单请求和预检请求。
- document.domain(不安全):允许具有相同顶级域名的页面交互,在页面中将 document.domain 设置为相同的父域。
- JSONP(不安全):通过修改script标签的src属性,从一个域名下加载并执行另一个域名下的 JavaScript,返回JSON数据对象。
- Fragment Id(location.hash):将接收方嵌入发送方的iframe,发送方改变 iframe 的 src 属性的 fragment id,接收方从url中解析数据,并且页面不重新加载。
- otherWindow.postMessage():发送方获取接收方窗口的引用,使用postMessage(message, targetOrigin)方法;接收方监听message事件来验证来源并处理数据。
箭头函数与普通函数的区别
- 箭头函数没有this,如果箭头函数被非箭头函数包含,this绑定最近一层非箭头函数的this(永远指向上一层作用域的this)
- 箭头函数没有自己的arguments对象
- 不能通过new关键字调用,无原型
什么是事件委托/事件代理?
- 在事件发生元素的父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发
- 新增子对象时无需再次对其绑定事件
- 举例:购物网站页面中有多个产品,监听加入购物车按钮的点击事件时,不是分别监听所有产品按钮的点击事件,而是监听document对象的点击事件,检查触发事件的目标元素是否包含addToCart类
事件模型
- 事件传播分为三个阶段:捕获阶段→处理阶段→冒泡阶段;捕获阶段事件从文档开始向下传递到目标节点,冒泡阶段则相反
- type获取事件类型,target获取事件的目标节点,stopPropagation()阻止当前事件的传播,preventDefault()阻止事件默认行为
高频度触发事件的优化方案
- 防抖:抖动结束的时间超过指定间隔时才执行任务:输入验证和搜索框提示
function debounce(func, delay) {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);/清除之前设置的定时器。只有最后一次调用的delay 毫秒后才会执行函数 func。
timer = setTimeout(() => func.apply(context, args), delay);
};
}
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
}
}
JS类有哪几种继承方式?各有什么特点?
- 原型链继承:将父类实例作为子类原型,无法实现多继承
- 构造继承:使用父类的构造函数增强子类实例,可以实现多继承,但只能继承父类实例的属性和方法
- 组合继承:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。
- 寄生组合继承:
C++,Java,JavaScript的区别
- C++、Java属于静态类型语言,编译的时候就能够知道每个变量的类型,编程时需给定类型;js属于动态类型语言,运行的时候才知道每个变量的类型
- C++是编译型语言,需要编译器编译成本地可执行程序后才能运行;js是解释型语言,用户直接使用脚本解释器将脚本文件解释执行;Java首先经过编译器编译生成字节码,然后由Java虚拟机运行字节码,使用解释器执行
this
- 执行上下文:JavaScript引擎在执行一段代码之前将代码内部会用到的一些变量、函数、this提前声明然后保存在变量对象中。
- this只在函数调用阶段确定
- 默认绑定:全局环境中this默认绑定到window
- 隐式绑定:函数作为对象的方法被调用时,函数中的this 隐式地绑定到该对象上。
- 隐式丢失:被隐式绑定的函数丢失绑定对象时,会默认绑定到window
- 显示绑定:通过call()、apply()、bind()方法把对象绑定到this上
- new绑定:当一个函数通过 new 关键字被调用作为构造函数时,this 被绑定到新创建的对象上。
call()、apply()、bind()
- 改变函数中this的指向
- function.call():第一个参数为要改变指向的对象,之后的参数为arg1,arg2…,函数立即执行
- function.apply():第一个参数为要改变指向的对象,之后的参数为一个数组arguments,函数立即执行
- function.bind():返回一个新的函数,函数不会立即执行,参数与call()相同
解析查询字符串
function getQueryParams(url) {
let params = new URLSearchParams(url.split('?')[1]);
let result = {};
for (let [key, value] of params) {
result[key] = value;
}
return result;
}
let url = 'http://example.com/?a=1&b=2';
console.log(getQueryParams(url));
虚拟滚动
- 虚拟滚动计算可视区域内可以显示的列表项的数量,以及用户滚动的位置,仅渲染应该在用户视窗中的列表项。
- 实现:设置一个固定高度的容器作为滚动的视窗。监听滚动事件的deltaY属性,当用户滚动时,计算容器内可视的列表项,清理旧的列表项,更新需要渲染的列表项。
- 为了优化性能可以设置缓存区,预先在可视区域前后渲染一部分列表元素,这样在缓存区滚动时只需要改变列表的translateY样式,减少重新渲染的次数。
立即执行函数(IIFE)
- 立即执行函数中的代码,不会在内存中留下对该函数的引用;函数内部的所有变量都会被立即销毁。在ES6之前可以实现块级作用域。
(function (参数) {
})(参数);
webpack
- webpack是js应用程序的静态模块打包器。当用webpack处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包。
获取页面元素位置与宽高
- getBoundingClientRect 方法:返回一个对象,包括元素相对于视口左上角的left、top、right、bottom位置信息和元素的width、height属性
- offset 系列:offsetWidth/Height:元素不包括外边距的宽高;offsetTop/Left:元素相对于其设置position属性的父容器的位置
- client 系列:clientWidth/Height:元素不包括边框和外边距的宽高;clientTop/Left:元素边框的厚度
- scroll 系列:scrollWidth/Height:包括溢出部分的元素完整内容宽度和高度;scrollTop/Left:元素滚动条垂直和水平滚动的距离。
- 只能获取不能赋值!
requestAnimationFrame
- window.requestAnimationFrame()方法告知浏览器希望执行一个动画,并且在下次重绘之前调用指定的回调函数来更新动画,可以确保回调函数与浏览器屏幕刷新次数同步执行,减少不必要的重绘和回流。当浏览器标签不可见或最小化时,requestAnimationFrame()将暂停调用。
(如果使用 setTimeout 或 setInterval,你的回调函数可能会在屏幕刷新之间的任意时间点执行,这可能会导致两个连续的屏幕刷新间发生多次重绘和回流。)
浏览器中的线程
- 主线程(UI线程):处理用户界面,执行 JavaScript 代码;处理鼠标、键盘等事件;解析 HTML 和 CSS,构建 DOM 和渲染树,进行页面布局和重绘。
- 定时器触发线程:管理定时器(setTimeout 和 setInterval)的调用。
- HTTP请求线程:管理网络请求,如Fetch API。
- 事件触发线程:管理异步事件,将事件反馈给主线程执行(如处理用户输入、文件读写操作等)。
- 渲染线程:将主线程计算好的布局信息转化为像素数据,并输出到屏幕。
Vue
为什么要用Vue?
- Vue 是一个MVVM(Model-View-ViewModel)框架,双向数据绑定把view和model层连接起来,通过对数据的操作自动更新DOM的状态,完成对页面视图的渲染,而不是进行繁琐的DOM操作。
- MVVM模型是在前端页面中,把Model用纯JavaScript对象表示(对应data中的数据),View负责显示(对应DOM模板),ViewModel把Model和View关联起来(对应Vue对象)。
v-for中的key的理解?
- 需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。主要是为了高效的更新虚拟DOM。
data为什么是函数
- 在Vue中,组件的data必须被定义为一个函数,这样每个实例可以维护一份被返回对象的独立的拷贝。 如果data是一个对象,则所有的组件实例将共享这个对象,导致一个实例的状态变化可能会影响到所有其他实例。
computed和watch的区别
- computed:计算属性,当依赖的数据变化时,会计算新的值。计算属性是基于依赖进行缓存的,只要依赖属性还没变,多次访问计算属性会立即返回之前的计算结果。watch属性也可以做到相同的事,但是实现更加复杂。计算属性不支持异步。
- watch:监听某一个值,当被监听的值发生变化时,执行相关操作,更适合做一些数据变化后的异步操作或者较大开销操作。
- 所有不被Vue管理的函数(定时器、ajax的回调函数等),最好写成箭头函数,this才能指向vue对象
双向绑定
- Vue 2.0:数据代理,通过Object.defineProperty()将data中的所有属性转换为 getter/setter。当渲染函数(或计算属性等)被首次执行时,会访问与其相关的数据属性,这时数据属性的 getter收集依赖,使每个数据属性知道被那些组件依赖。
- 当数据发生变化时,调用数据属性的setter,通知所有依赖于该数据属性的 watcher 对象数据已经改变。watcher 对象收到变化通知后,会触发其回调函数,更新视图。
- Object.defineProperty(对象,属性,配置项): 为对象添加属性,通过这种方法添加的属性默认不可枚举、不可修改、不可删除(基本配置项:value,enumerable,writable,configurable)
- get/set为defineProperty的配置项,属性值被读取时,会调用get函数返回属性值,属性值被修改时,set函数会被调用,收到修改的值(然后在函数体中修改被代理属性的值)。
obj2代理obj.x属性的读写:
let obj = {x:100}
let obj2 = {y:100}
Object.defineProperty(obj2,'x',{
get(){return obj.x},
set(value){obj.x = value}
})
- watcher:每个组件实例都对应一个 watcher 实例,可以订阅并收到每个属性的变动通知,执行指令绑定的相应回调函数,从而更新视图。渲染 watcher :组件渲染时观察该组件所依赖的所有数据;计算属性 watcher:计算属性被访问时响应数据变化;侦听器 watcher:使用组件的 watch 对数据进行侦听时,为每个侦听器创建watcher
- 发布-订阅模式:消息的发送者不会直接将消息发送给接收者。发布的消息被分类后放入一个共享的消息队列或通道中,订阅者可以订阅这些通道来接收感兴趣的消息。
- 缺点:当创建一个Vue实例时,Vue将遍历所有DOM对象,并为每个数据属性添加getter 和 setter,但如果在 Vue 实例创建之后,添加或删除一个新的属性,Vue 将无法追踪这个新属性的变化。所以在 Vue 实例创建之后,应该使用Vue.set 和 Vue.delete向响应式对象添加或删除属性。
- Vue3通过Proxy实现数据双向绑定, Proxy 可以对外界对对象的访问进行过滤和改写,可以拦截对象的任何操作,包括属性的添加和删除。
生命周期
- Vue提供了许多生命周期钩子,可以在生命周期的不同阶段调用指定函数
- 生命周期函数中的this指向vm或组件实例对象
- 生命周期分为8个阶段:创建前/后、挂载前/后、更新前/后、销毁前/后
钩子函数 | 状态 | 用处 |
---|
beforeCreate | 实例初始化完成,数据代理未开始 | |
| 初始化数据监测、数据代理 | |
created | 实例创建完成,属性已绑定,可以访问数据和方法 | |
| 解析模板,生成虚拟DOM | |
beforeMount | 页面呈现未经编译的DOM结构,但随后会被虚拟DOM覆盖,无法进行对DOM的操作 | |
| 将虚拟DOM转为真实DOM | |
mounted | vue实例挂载完成,对DOM的操作有效 | 可以进行开启定时器,发送请求等初始化操作 |
beforeUpdate | 页面尚未和数据保持同步 | |
| 根据新数据生成虚拟DOM,与真实DOM比较最终完成页面更新 | |
updated | 页面已经和数据保持同步 | |
| 调用vm.$destoy()销毁组件或组件在 v-if 指令的条件下从真变为假 | |
beforeDestory | 组件销毁之前,可以访问数据和方法,但是数据修改不会触发更新 | 清除定时器,解绑自定义事件等 |
| 调用vm.$destoy()销毁组件,解绑全部指令和自定义事件,但是会保留原生事件回调 | |
destroyed | 组件销毁之后,DOM结构依然存在 | |
虚拟DOM
- 虚拟DOM是以js 对象形式存在的DOM树结构。当状态变更时,渲染器得到新的虚拟DOM,对比新旧虚拟DOM的差异(Diff算法),只重新渲染变化的部分,减少多次重排和重绘,提高渲染效率。
- 虚拟DOM对象中有三个属性:
tagName:元素的标签名
props:元素包含的属性
children:元素的子节点
Diff算法
- 对新旧两棵树进行深度优先遍历,标记每个节点;每遍历到一个节点就把该节点和新的树进行对比
- 同层比较:diff 算法只会在同一层级的节点之间进行比较,如果一个节点在旧树中存在,而在新树中不存在,会直接移除该节点及其子节点
- 节点比较:如果新旧节点的tagName不同,新节点替换旧节点;如果节点相同,会递归比较子节点;如果节点属性、文本内容发生变化,使用patch对象记录。
- 子节点比较:Vue建议对列表元素使用key属性唯一标识,识别相同的列表元素,防止遍历时子节点的顺序发生变化。
- 对于子节点列表,采用双端对比算法,从列表两端开始向中间对比元素的key值,如果相同则复用旧节点,如果不相同,尝试对比对边节点元素的key值是否相同,相同则将节点移动到对边。如果都不相同,尝试通过 key 值查找对应节点进行对比。直到新列表或旧列表长度为0,插入新元素或删除旧元素。双端对比算法