前端面经总结

HTML及浏览器


对栅栏布局的理解

栅格化布局中的元素:column列,row行,gutter列之间的距离,container容器
栅格是否可以嵌套

canvas和svg的区别

SVG:
SVG 是一种使用 XML 描述 2D 图形的语言。
在 SVG 中,每个被绘制的图形均被视为对象。

Canvas:
Canvas 相当一个画布,通过 JavaScript 来绘制 2D 图形。
Canvas 是逐像素进行渲染的。
可以在canvas中引入jpg或png这类格式的图片,在实际开发中,大型的网络游戏都是用canvas画布做出来的,并且canvas的技术现在已经相当的成熟。百度地图就是用canvas技术做出来的。
另外从技术发面来讲canvas里面绘制的图形不能被引擎抓取

input与textarea区别

input是单行框,html标签不闭合

而textarea是多行文本输入框,html标签闭合

若要实现高度随内容自适应可以用 contenteditable=“true”

HTML5新增一些标签

语义化标签:nav; header; content; footer; article;video

input增加了许多表单输入类型。比如说email,url。
增加许多表单元素。比如datalist,output

增加了画布Canvas和地理定位

meta标签作用

meta有name属性用于描述网页,比如网页的关键词,叙述等。与之对应的属性值为content,content中的内容是对name填入类型的具体描述,便于搜索引擎抓取。

meta设置utf-8

meta移动端适配的viewport

HTML5新增api

  1. 监听网络状态

     window.addEventListener('online',function(){
     	//连接
     })
     window.addEventListener('offline',function(){
     	//断开
     })
    
  2. 获取定位

     navigator.geolocation//判断是否可定位
     navigator.geolocation.getCurrentPosition()
    

input中的一些事件

  1. onfocus
    并没有什么特别的,就是当焦点转移到(点击,tab切换) input 框上边的时候触发;
  2. onkeydown
    键盘按下的时候触发,但是此时按下的值并没有被输入到 input ,所以,此时的 value 没有值,或者说它的值 只能是之前的旧值
  3. onkeypress
    按键在按下之后,并且是按键松开之前触发的;和 keydown 一样不能获取新的到 value
  4. oninput
    每次输入框的值变化时候出发的,可以拿到value值
  5. onkeyup
    按键在松开之后触发的; 能获取新的到 value,keycode;
  6. onchange
    在失去焦点之后触发的,明明是 onchange 为什么是在失去焦点后触发的,还偏偏比 onblur 快;
  7. onblur
    失去焦点时候触发,但是还是比 onchange 慢了;

input标签中type属性

button 定义可点击按钮(多数情况下,用于通过 JavaScript 启动脚本)。
submit 定义提交按钮。提交按钮会把表单数据发送到服务器。
radio 定义单选按钮。
checkbox 定义复选框。
file 定义输入字段和 "浏览"按钮,供文件上传。
hidden 定义隐藏的输入字段。
password 定义密码字段。该字段中的字符被掩码。
reset 定义重置按钮。重置按钮会清除表单中的所有数据。
text 定义单行的输入字段,用户可在其中输入文本。默认宽度为 20 个字符。

inline-block的产生间隙问题

inline-block元素间有空格或是换行,因此产生了间隙。

父级设置
.box {
    font-size: 0;
    letter-spacing: -3px;
}
//font-size:0
//letter-spacing 属性明确了文字的间距行为。 

web性能优化

代码层面:css解析从右往左,合理设置选择器。css放上面,script放下面
缓存利用:使用CDN加速,http缓存头等
请求数量:合并css样式和js脚本,使用css图片精灵,图片懒加载
请求带宽:压缩文件,开启GZIP

  1. 减少HTTP请求。 图片Base64编码,只能用在大小小的图片。
  2. 图片懒加载
  3. 压缩图片资源文件,//可以用雪碧图
  4. CDN加速:分布在不同位置的服务器,存放静态资源,以便用户访问最近的节点资源。

CDN加速:当用户发起访问时,他的访问请求被智能DNS定位到离他较近的缓存服务器。
当服务器没有缓存数据,服务器就去请求离他最近的服务器中的数据。天猫超市就是这样用的。

预加载

图片等静态资源在使用前提前请求。 比如通过点击等事件才会用到的图片。
最常见的就是预加载图片新建一个image,添加src路径,当浏览器渲染时就会使用预加载的图片

页面白屏,判断问题

  1. 如果页面完全一片空白。极有可能是后端出现问题,后端配置无错误输出,并且服务状态500,这个时候页面就是一片空白。
  2. 如果是前端导致的,那么极有可能是单页应用异常,比如到了一个没有设置的新的页面。

1、打开能看到源码和request、response的浏览器,如chrome,查看源码输出
2、如果是后端问题,那么后端查看accesslog、程序日志,看看是否有问题
3、如果是前端问题,那么根据给出的js异常之类的排查

html5移动端项目优化

大部分的PC端的优化其实都适用移动端。
这里说一下移动端需要的。
移动端:

  1. 点透现象。
  2. 首屏页面加载时间。
    减少http请求:合并资源,图片懒加载,css精灵图
    加快请求速度:用CDN加速
  3. 缓存优化
    使用http缓存
    使用LocalStorage缓存HTML文档,页面大部分的逻辑都会由前端JS去执行,JS执行完才会生成完整的HTML文档,而JS执行的成本是非常大的,将HTML文档缓存到LocalStorage里面。

CRP关键渲染路径

收到一个HTML页面的请求时,到屏幕上渲染出来要经过很多个步骤。

构建DOM树
树建CSSOM树
运行JavaScript
创建Render树
生成布局
绘制(Painting)

优化方法:
首屏内联CSS
使用媒体查询优化CSS
JS脚本async异步
减小CSS和JS的体积
脚本放到最底部

一个网站很卡找原因

首先用3g网络测试一下:如果卡就是http请求数据可能过大。
可以合并JS脚本和CSS文件,css精灵图,对HTTP传输进行gzip压缩。
css放顶部,javascript放底部。

可能服务端出问题:
比如用户访问量大,并发量大。
mysql没有优化好,造成死锁。

可以用CDN加速把数据放在离用户更近的位置。

http的报文的主要组成

请求报文:
1,请求行。 该行包含了一个方法和一个请求的URL,还包含HTTP 的版本。
2,请求头
3,空行
4,请求体。

响应报文:
1,状态行。 该行包含了请求资源的状况。
2,响应头
3,空行
4,响应体。

get和post请求

  1. get在浏览器回退时是无害的,而post会再次请求
  2. get请求会被浏览器主动缓存,而post不会,除非手动设置
  3. get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留
  4. get 请求在url中传送的参数有长度限制,而post没有
  5. get参数通过url传递,poet放在request body中

http请求的方法

  1. option:检查服务器支持哪些请求,跨域时用到
  2. head:与GET请求相一致的响应,只不过响应体将不会被返回。
  3. get:请求
  4. post:
  5. put :向指定资源上传最新内容
  6. delete:请求服务器删除Request-URL所标识的资源
  7. connect:将连接改为管道方式

DOCTYPE标签的作用

  • 在标准模式中,浏览器以其支持的最高标准呈现页面。
  • 在混杂模式中,浏览器以一种较宽松的方式向后兼容浏览器。

ContentType类型:

  • application/x-www-form-urlencoded:提交post请求时会使用这种方式。浏览器提交原生form表单,不设置enctype时,会默认是这样的方式。
  • multipart/form-data:用于上传文件。
  • application/json:同样是请求时使用的,指定即可。
  • text/html:

移动端适配

<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=可视区域的宽度
inital-scale=页面首次显示可视区域的缩放级别
user-scalable=no 用户不可以手动缩放

上传文件

前端用html5的input的type:file。FormData二进制流传输。
服务器获取后可以写到本地文件中。

移动端点透

移动端当弹窗点击关闭时可能触发底层元素的click事件
原因:当一个用户在点击屏幕的时候,系统会触发touch事件和click事件,touch事件优先处理,touch事件经过 捕获,处理, 冒泡 一系列流程处理完成后, 才回去触发click事件。 而移动端的click事件会延时300ms执行,所以触发底层元素。
解决:

  • 引入github的fastclick.js文件
  • click事件全换为touch事件

HTTP压缩

首先,浏览器发送一个请求(request)给web服务器,支持一个压缩格式如(gzip),服务端会将原来的源码压缩之后,通过http响应(response)信息返回给web浏览器,浏览器接收之后,显示出来。

LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。

HTTP缓存又叫浏览器缓存

HTTP主要控制缓存的字段有

  • Expires: 表示资源过期的日期
  • Cache-control:max-age: 指的是从文档被访问后的存活时间,这个时间是个相对值(比如:3600s)
  • Last-Modified: 表示最后一次更新资源的时间

ETAG在HTTP协议中的定义是资源实体的标记(entity tag),强标识一个资源。是缓存过期的一种代替方案
客户端首次访问资源,服务器返回资源实体内容和在头区中返回ETAG值,客户端保存实体内容和ETAG值。
客户端再次访问资源的时候,在头域(header)中加入“If-match:etag值”指令。
服务器接受到请求后,检查资源的ETAG值是否与请求的If-match指定的etag值相同(强匹配),如果匹配则响应304 Not Modified。

ETag比Last-Modified和 Expires 的优势
Last-Modified和Expires都是时间作为判断资源是否改变的标志存在一些隐患。

  1. 一些文件也许会周期性的更改,修改时间改变,但是他的内容并不改变,
  2. 某些文件修改非常频繁,比如在一秒以下的时间内进行修改,last-modified能检查到的最小就是秒级的。
  3. 某些服务器不能精确的得到文件的最后修改时间

HTTP缓存分为强制缓存和协商缓存

  • 对于强制缓存,服务器返回一个过期时间,过期时间内,下次请求,直接用缓存,过期后执行协商缓存
  • 带上缓存的头部字段,服务器验证资源是否改变,返回304,代表资源未改变。返回200,就重新请求资源。

强制缓存内容放在memory cache

浏览器的渲染过程

  1. 解析HTML生成DOM树
  2. 解析CSS生成CSS规则树
  3. 将DOM树和CSS规则树合并生成渲染树
  4. 遍历渲染树开始布局,计算每一个节点的位置信息,将渲染树中每个节点绘制到屏幕上。

回流与重绘

回流: 当渲染的一部分元素更改自己的宽高等,导致重新构建布局,就产生了回流。
重绘: 当一个元素自身的高度没改变,只改变了背景颜色的,发生重绘。
回流必定重绘,当重绘不一定回流。

减少reflow/repaint的措施:

  • 不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。
  • 建立图层,为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
  • 千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。

域名收敛与域名发散

域名收敛: 就是将静态资源放在一个域名下。减少DNS解析的开销。
域名发散:是将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。
域名发散是pc端为了利用浏览器的多线程并行下载能力。而域名收敛多用与移动端,提高性能,因为dns解析是是从后向前迭代解析,如果域名过多性能会下降,增加DNS的解析开销。

DNS解析过程

  1. 浏览器根据地址去本身缓存中查找dns解析记录,如果有,则直接返回IP地址,否则浏览器会查找操作系统中(hosts文件)是否有该域名的dns解析记录,如果有则返回。
  2. 如果浏览器缓存和操作系统hosts中均无该域名的dns解析记录,或者已经过期,此时就会向域名服务器发起请求来解析这个域名。
  3. 请求会先到LDNS(本地域名服务器),让它来尝试解析这个域名,如果LDNS也解析不了,则直接到根域名解析器请求解析。
  4. 根域名解析器通过迭代查询和递归查询结合的方式来查找解析,最后返回相应的IP地址。

DNS劫持

  1. 用户计算机感染病毒,病毒篡改HOSTS文件,添加虚假的DNS解析记录。
  2. 使用假的运营商

DNS缓存

从输入url到显示页面都发生了什么

  1. 输入url,常见的http协议的端口号是80,https是443
  2. 查看浏览器是否有缓存,其中分为强制缓存和相对缓存
  3. dns查询,用迭代查询和递归查询结合的方式查询
  4. TCP三次握手建立连接
  5. 浏览器向服务器发送HTTP请求
  6. 浏览器接收响应 服务器在收到浏览器发送的HTTP请求之后,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头,响应报文三个部分。
  7. 页面渲染,涉及浏览器的渲染过程和回流,重绘

http:80和https:443区别

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

https的原理

  1. 客户端发送请求到服务端
  2. 服务端返回公钥和证书
  3. 客户端验证证书合法性,客户端生成一个随机数密钥,通过证书中的公钥加密并发送给服务端。
  4. 服务端使用私钥解密,获取随机数密钥后,然后把内容通过该密钥进行对称加密。发送给客户端。
  5. 因为客户端也知道这个密钥,所以客户端可以还原信息。

可能会给中间人攻击,解决方法可以用第三方安全证书来认证。

https性能优化

可以优化算法,比如RSA消耗很多时间,现在都在用ECC椭圆曲线加密了。
证书优化。
正常优化http都可以

https双向认证和单向认证

双向认证:
一个是服务端证书,另一个或多个是客户端证书。
在证书验证成功的情况下即可完成请求响应。
双向认证一般企业应用对接。

单向认证:
客户端保存着服务端的证书并信任该证书即可。
https一般是单向认证,这样可以让绝大部分人都可以访问你的站点。

httpDNS

阿里云服务器就有。
HTTPDNS使用HTTP协议进行域名解析,代替现有基于UDP的DNS协议,域名解析请求直接发送到阿里云的HTTPDNS服务器,从而绕过运营商的Local DNS,能够避免Local DNS造成的域名劫持问题和调度不精准问题。

Web安全方面

JsonP跨域可能造成的安全危害:

  1. JSON 劫持
    当网站用 JSONP 的方式来传递用户认证后的敏感信息时,比如用户名,我可以构造恶意的 JSONP 调用页面,我可以获取服务器返回的json数据。

解决方法:利用了

  1. Callback可自定义导致的XSS安全漏洞
    假如callback的参数是一个script标签,就可能执行script标签内容这就会造成xss攻击。
  • 跨站脚本xss(cross site script)

XSS是指恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web页面中去。使别的用户访问都会执行相应的嵌入代码。源于过于信任客户端提交的数据,对提交的数据进行过滤,去掉script等关键字,或者拼接html时注意规则。

1、持续型XSS攻击:恶意脚本来源于网站的数据库。
攻击者通过评论表单提交将script>alert(‘aaa’)</script提交到网站。
网站后端对提交的评论数据不做任何操作,直接存储到数据库中。
其他用户访问正常访问网站,并且需要请求网站的评论数据。
网站后端会从数据库中取出数据,直接返回给用户
用户得到页面后,直接运行攻击者提交的代码,所有用户都会在网页中弹出aaa的弹窗

2,反射型XSS攻击:恶意脚本来源于受害者的请求url。
在一个反射型XSS攻击中,恶意文本属于受害者发送给网站的请求中的一部分。随后网站又把恶意文本包含进用于响应用户的返回页面中,发还给用户。
比如url中的参数带着script标签可能就执行

过滤可以采用字符转义

  • 跨站请求伪造csrf(cross-site request forgery)

攻击盗用你的身份,发送恶意请求。
登陆可信网站A,在本地生成cookie
在不登出A的情况下,访问危险网站B
这时恶意站点 B的某个页面向站点A服务器发起请求,而这个请求会带上浏览器端所保存的站点A的cookie;因为同源政策所以会自动加上所请求的cookie

B可以有img标签去请求A的服务器
防御方法:

(1) 加入验证码
每次要提交数据时都要提交一组随机生成的验证码。
(2) token验证
1,用户验证成功登陆后,服务端会签发一个 Token,再把这个 Token 发送给客户端
2,客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
3,客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
4,服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

  • SQL注入

原理利用程序对用户输入的检查不足,轻则可以获取敏感信息,重则可以控制服务器。
最常见的是用户名和密码,在传给服务器时,它会拼接到sql语句中,并存入服务器。假如我们在密码中加入一个#号,#号在mysql中是注释的意思,这样后面的语句就不会执行了。

  • 重定向中间人攻击
    比如http百度,会重定向到https,但是中间人攻击数据劫持。
    中间返回一个恶意的报文。
    预防方法使用HSTS的方法,配合307,它会强制使用https

  • 文件上传安全

文件上传用户上传的可执行脚本文件,获得执行服务端的功能。
解决:上传时就过滤文件。

session,cookie,sessionStorage,localStorage

cookie的字段:
name字段 :一个cookie的名称。

value字段 :一个cookie的值。

domain字段 :可以访问此cookie的域名

path字段:可以访问此cookie的页面路径。

expires:过期时间

  • cookie: 网上商城中的购物车、保存用户登录信息、将某些数据放入session中,供同一用户的不同页面使用、防止用户非法登录,cookie 只能存储 String 类型的对象,且长度最大只有4KB
  • session:Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。主要用于:网上商城中的购物车、保存用户登录信息、将某些数据放入session中,供同一用户的不同页面使用、防止用户非法登录。session 能够存储任意的 java 对象。
    session生命周期:
    (1)在用户第一次访问服务器时创建
    (2) 服务器会把长时间没有活动的session从服务器清除,Tomcat默认失效20分钟。

Session分布式集群:
多台服务器的时候就会遇到Session共享的问题。
Session Sticky:
让负载均衡器能够根据每次的请求的会话标识来进行请求的转发,这样就能保证每次都能落到同一台服务器上面,这种方式称为Session Sticky方式。

  • localStorage:localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据
  • sessionStorage:的生命周期是在仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。sessionStorage:敏感账号一次性登录

原生ajax请求带cookie,设置
当我们手动在 ajax 请求中把 cookie 添加到头信息中的时候

xhr.withCredentials = true; //支持跨域发送cookies

然后呢,我们需要在我们的 Server 返回的时候加上一条头信息

response.setHeader("Access-Control-Allow-Credentials", true);

为什么用cookie存用户信息,而不用localStorage。

  1. cookie有httponly可以预防xss攻击,而localStorage不行
  2. localStorage没过期时间

navigator.cookieEnabled判断支不支持cookie

cookie的编码,我认为应该是使用url编码吧,因为要编码分号,逗号和空格。
encodeURIComponent(URL)可把字符串作为 URI 组件进行编码。
decodeURIComponent(URL)函数可把字符串作为 URI 组件进行解码。
uel编码不可以用中文。

cookie使用中文
可以转为utf-8形式的字符串编码,读时再解码回来。
URLEncoder.encode(“虎嗅”,“utf-8”);
URLDecoder.decode(cookie.getValue(),“utf-8”);

cookie编码

1.前台编码

encodeURIComponent(str)

2.后台解码

原因是有关中文编码的问题,中文采用的是unicode编码,而英文采用的是ASCII编码

Javascript设置cookie

document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";

css、js、dom的渲染关系

  1. css不会阻碍dom的解析,但会阻碍dom渲染,很好理解。
  2. js会阻碍dom解析。
  3. 遇到js时,如果没有async或defer标签,会触发页面渲染。

defer和async都是在html解析(parase)的时候下载js文件,不同的是,async下载完后,会立即执行,而defer会等待html解析完成完后才执行。如果该脚本依赖于另一个脚本或被另一个脚本依赖,则使用defer。

http1.X和2.0的区别

  1. 新的二进制模式,http1解析基于文本,2.0基于二进制格式,使它更健壮性。
  2. 多路复用:即连接共享,一个连接有多个request和respone,并发多个请求和多个响应。
  3. header压缩,减少请求header的大量信息

几种常见的状态码

1xx:指示信息–表示请求已接收.
2xx:成功-请求已接受并返回响应头
200:请求已成功,返回响应头。
3xx:重定向–要完成请求必须进行更进一步的操作。
301:请求的资源已被永久移到新位置。
302:临时重定向
304:缓存的内容并未改变。
4xx:客户端错误–请求有语法错误或请求无法实现。
400:请求无效,前端提交数据的字段名称或者是字段类型和后台的实体类不一致。
401:请求要身份验证。
404:找不到页面。
5xx:服务器端错误–服务器未能实现合法的请求。
500:服务器遇到未知状况,无法处理。
503:服务器过载或维护。

//301例如:http百度网页永久定位到https百度上
//302例如:用户中心的页面前端没认证,则跳到登陆页面,临时重定向。

浏览器兼容的问题

样式兼容性(css),交互兼容性(javascript),浏览器 hack 三个方面。

样式兼容性(css)方面

  1. 不同的浏览器样式存在差异,可以reset.css去重制全局样式
  2. 针对浏览器的内核,加上浏览器前缀。
  3. ie9一下不能使用opactiy, filter: alpha(opacity = 50); //IE6-IE8我们习惯使用filter滤镜属性来进行实现

交互兼容性(javascript)

  1. ie低版本不支持addEventListener(),使用attachEvent()来绑定事件监听函数。
  2. new Date()中,‘2018-07-05’是无法被各个浏览器中,使用new Date(str)来正确生成日期对象的。 正确的用法是’2018/07/05’.
  3. scrollTop 通过 document.documentElement.scrollTop 兼容非chrome浏览器

浏览器 CSS hack
由于不同的浏览器对CSS的解析认识不完全一样,因此会导致生成的页面效果不一样。
我们就需要针对不同的浏览器去写不同的CSS,让它能够同时兼容不同的浏览器。

option请求

option测试服务器支持的请求类型。
cors跨域时对非简单请求时,发送一个option测一测是否允许跨域。

移动端问题

移动端1px 像素问题

首先明白:物理像素:移动设备出厂时,不同设备自带的不同像素,也称硬件像素;
逻辑像素: 即css中记录的像素。
window.devicePixelRatio是代表物理像素与css逻辑像素的比值。
iPhone的 devicePixelRatio==2,所以你设置1px的边框,实际显示物理像素2px显示,在iphone中会比较粗。

解决方法:

  1. 用小数写,但在ios和安卓低版本不适用
  2. 使用淘宝的flexible.js去适配。
    核心好像是利用devicePixelRatio即物理像素与css逻辑像素的比值,假如是2,我就设置viewport中的scale为0.5,使它的物理像素等于css写的1px像素。

js操作dom

获取html中所有dom元素

用docment.getElementByTagName()获取标签名的元素集合,可以设置为*号,这样就可以获取全部。

JAVASCRIPT

javascript 有三部分构成,ECMAScript,DOM和BOM,根据宿主(浏览器)的不同,具体的表现形式也不尽相同,ie和其他的浏览器风格迥异。
BOM:是指浏览器对象模型,它使JavaScript可以和浏览器进行交互。包含:window。用来获取或设置浏览器的属性、行为,例如:新建窗口、获取屏幕分辨率、浏览器版本号等。
DOM是文档对象模型,用来获取或设置文档中标签的属性,例如获取或者设置input表单的value值。

js的数组和字符串的一些函数

js的数组:push(), pop(),unshift(),shift(),indexOf(),toString(),join(),concat(),reverse(),slice(),splice(),sort()

js的字符串: indexOf(),lastIndexOf(),slice(),split(),replace(),match()

js数据类型

  1. 基本数据类型:Boolean、Undefined、Null、Number、String
  2. 引用数据类型:Array, Boolean, Date, Error, Function, Math, Object, RegExp

基本数据类型存储在栈内存,引用数据类型存储在堆内存

NaN

NaN不是数字,和任何数都不相等。
typeof NaN == number。 //true
用isNaN()来判断

函数声明

// 函数表达式(function expression)
var h = function() {
// h
}

// 函数声明(function declaration)
function h() {
// h
}

先说两者的显著区别:

第一种声明方式也就是var声明方式, 函数只有在var语句声明之后才能被调用

第二种生命方式也就是function声明方式, 函数可以在function声明之前被调用

js获取变量类型的几种方法

  1. typeof:可以判断基本类型,但无法判断对象具体类型;且当判断基本包装类型创建的实例如 new String()时和array数组会判断成Object。
    typeof原理:js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息。
  2. instanceof:变量 instanceof 对象类型,返回Boolean值,且如new Array() instanceof Array或Object都返回true。当instanceof undefined和null时会报错。
  3. Object.prototype.toString.call():可以判断具体对象类型,可以把new Array()、new Reg()分别判断成数组和正则表达式

js遍历对象方法

第一种: for…in

1)、Object.keys(obj)
2)、Object.values(obj)

	const obj = {
	    id:1,
	    name:'zhangsan',
	    age:18
	}
	
	 console.log(Object.keys(obj))
	
	console.log(Object.values(obj))

第三种:使用Object.getOwnPropertyNames(obj)

	const obj = {
	            id:1,
	            name:'zhangsan',
	            age:18
	    }
	    Object.getOwnPropertyNames(obj).forEach(function(key){
	        console.log(key+ '---'+obj[key])
	    })

冻结对象

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;

冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

js获取当前浏览器

console.log(navigator.appName) //浏览器名字
console.log(navigator.appVersion)   //浏览器版本

js原型链的理解

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,如果还没有找到就会再的__proto__中查找,这样一层一层向上查找最后到object就会形成一个链式结构,我们称为原型链。

每一个构造函数都有一个prototype指针,他指向它的原型对象。
每一个原型对象都有一个constructor指针,它指向它的构造函数。
每一个对象都有一个__proto__指针,它指向它的原型对象。
原型对象的__proto__指针一般指向object,因为它是基础object的。
这样链式的连接集合就构成了原型链。

js作用域链的理解

作用域链是保证对执行环境有权访问的所有变量和函数的有序访问。
变量对象:环境定义的所有变量和函数,对象的集合。

每一个函数都有一个scope属性。是所有父级变量对象的层级链。我是把它理解为父级的作用域链的。

比如一个全局函数,函数定义时,他的scope属性中只包含一个全局对象GO(global object)。
当执行这个函数时,它会创建唯一的执行上下文。 首先会创建一个它自己的活动对象【Activation Object】(即当前函数定义的变量和方法的集合)和当前执行环境下的作用域链,把这个函数的[scope]属性按顺序复制到作用域中,再在顶部加入这个执行环境的活动对象。这样就行成了作用域链。
函数执行完,执行环境就会销毁。

作用域链就是一个有序的栈,保证对执行环境有权访问的所有变量和函数的有序访问。

js的eval函数和with

eval()参数是一个字符串,能像js一样执行语句。
with()是对原来的属速写,如果没有的话会声明为全局变量。

var obj = {
a: 1,
b: 2,
c: 3
};
with (obj) {
    a = 3;
    b = 4;
    c = 5;
}

js深克隆

var cloneObj = function(arr) {
    var obj = arr.constructor == Array ?[]:{};
    for(var item in arr) {
        if(typeof arr[item] === 'object') {
            obj[item] = cloneObj(arr[item]);
        }
        else {
            obj[item] = arr[item];
        }
    }
    return obj;
}

函数节流

用在连续点击div时,
是为了限制函数一段时间内只能执行一次,在延时的时间内,方法若被触发,则直接退出方法。

//节流
let throttle = (func, wait) => {
    let timer;
    return function() {
        if(timer) {
            return ;
        }
        var self = this;
        var args = arguments;
        timer = setTimeout(function() {
            func.apply(self, args);
            timer = null;
        }, wait)
    } 
}

函数防抖

用在搜索输入时,
函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务未执行完,则 clear 掉定时任务,重新定时。

//防抖
let debounce = function(func,wait) {
    let timer;
    return function(){
        let args = arguments;
        var self = this;
        clearTimeout(timer);
        timer = setTimeout(function(){
            func.apply(self,args);
        }, wait);
    }
}

实现bind函数

 Function.prototype.bind = function(obj){
     var self = this;
     var arg1 = Array.prototype.slice.call(arguments,1);
     return function(){
         self.apply(obj,arg1.concat(Array.prototype.slice.call(arguments)));
     } 
 }

JS垃圾回收机制

新生代垃圾回收:用于存储存活时间短的对象
由于新生代中的垃圾回收非常频繁,因此回收算法必须足够快。
新生代被划分为两个大小相等的子区:to区及from区。绝大多数对象的内存分配都发生在to区。当to区被填满后,交换to区和from区,现在所有对象都存储在from区,然后将活着的对象从from区拷贝到to区或者将它们晋升至老生代中,拷贝过后,释放from区的内存。

在新生代的垃圾回收周期中,始终有一半的内存时空的,由于新生代很小,因此浪费的空间并不大.

老生代垃圾回收:用于存储存活时间长的对象

  • 大多数浏览器都是标记清除
    当变量进入执行环境时,就标记为“进入环境”,当变量离开时标记“离开环境”。
    垃圾回收器运行时,会在内存中标记所有变量,然后去除环境中的变量以及被环境中变量所引用的变量(闭包),之后标记的变量就是要清除的。

  • IE是用引用计数的方式清除

垃圾回收器是周期性运行的,按照固定的时间执行。
IE6是按照内存分配量运行的。

js取消事件冒泡

取消事件冒泡
w3c标准:
event.stopPropagation();
ie:
event.cancelBubble=true;

取消默认:
e.preventDefault();
ie:
window.event.returnValue = false;

即取消默认行为也不冒泡,就直接return false

不支持冒泡事件

blur、focus

onorientationchange

监听窗口变化onorientationchange

JS事件模型

DOM2事件模型
a. 事件捕获阶段
b. 事件处理阶段
c. 事件冒泡阶段

dom.addEventListener(‘click’,function(){},true)
第一个参数侦听的事件,第二个参数为触发事件执行的方法,第三个true捕获阶段执行,false冒泡阶段执行

IE事件流:叫做事件冒泡。从点击的元素向上传播。用ele.attachEvent(‘onclick’, function(){ }); 只支持冒泡阶段

DOM0级事件处理程序:
就是用onclick绑定事件

事件委托又叫事件代理

利用事件冒泡,子级触发冒泡,委托他们的父级代为处理事件。
常见的如ul中包含li

JS闭包

闭包指有权限访问另一个函数的变量的函数。

深入理解的话:
当函数执行时,会创建一个称为执行期上下文。

函数还会获得它所在作用域的作用域链,是存储函数能够访问的所有变量对象的集合,即这个函数中能够访问到的东西都是沿着作用域链向上查找直到全局作用域。

函数每次执行时对应的执行环境都是唯一的,当函数执行完毕,函数都会失去对这个作用域链的引用,JS的垃圾回收机制就回收。

但是,当闭包存在时,即内部函数保留了对外部变量的引用时,这个作用域链就不会被销毁,此时内部函数依旧可以访问其所在的外部函数的变量,这就是闭包。

  • 优点:
    1:防止函数执行完后,变量被销毁,使其保存在内存中。
    2:通过闭包和立即执行函数来封装函数, 全局变量可能会造成命名冲突,使用闭包不用担心这个问题,因为它是私有化,加强了封装性,这样保护变量的安全。

  • 缺点:
    由于它是驻留在内存中,会增大内存使用量,使用不当很容易造成内存泄露。

JS内存泄漏的一些方式

内存泄漏:指一块被分配的内存既不使用,也不能回收

  1. 意外的全局变量引起的,在function中声明变量时未用var ,会变成全局变量不可以回收。

  2. IE浏览器中引用计数的回收方式中,用循环引用,a对象中属性值为b,b对象中属性值为a。即a.pro=b;b.pro=a;

  3. 闭包所引起的内存泄漏。假设一个函数中要给一个元素绑定事件,改变当前元素的文字。使用到的dom除了绑定事件使用,剩下永远不会再使用,当闭包不可以删除内存。

     function bindClick(){
     var dom =document.getElementById('id');
     dom.onclick = function() {
     	dom.innerText = '123';
     }
     }
    

JS事件循环机制(Event Loop)

  1. 所有同步任务都在主线程上执行,形成一个执行栈。
  2. 只要异步任务有结果,就在事件队列中加入一个事件。
  3. 一旦执行栈中所有同步任务执行完后,系统就会去取事件队列中的事件执行。

主线程从“事件队列”中读取事件,这个过程是循环的,较事件循环。

Web worker(worker线程)

worker是一个线程,通过构造函数Worker创建,参数是js文件路径;文件中的js文件会运行再主线程之外的worker线程。

worker线程不能访问window对象和dom对象,一般只用来计算数据。
通过PostMessage()发送消息,onMessage()监听事件接受消息。来进行交流

  1. 专用的 worker,只能被创建它的 JS 访问,创建它的页面关闭,它的生命周期就结束了。

  2. 共享的 worker,可以被同一域名下的 JS 访问,关联的页面都关闭时,它的生命周期就结束

  3. ServiceWorker:ServiceWorker的主要能力集中在网络代理和离线缓存上。具体的实现上,可以理解为ServiceWorker是一个能在网页关闭时仍然运行的WebWorker。

说到Service worker不得不说一下PWA即渐进式WEB应用。就是让web网页更像原生手机app程序。
丰富的离线体验:通过Cache Api离线缓存,让用户能够完整地打开整个页面,比如页面的白屏时间过长,网络不稳定造成的加载中断导致页面不可用。 Https环境部署,可以把 App Icon 入口添加到桌面。
拦截网络请求,消息推送通知等功能。关于离线环境的支持我们就需要仰赖ServiceWorker。

同源政策

同协议,域名,端口才能访问。
不同ip地址要跨域
CDN不同ip地址要跨域

为什么要同源:不同域下DOM任意操作,ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据。

跨域

  1. jsonp:动态创建script标签,利用script标签不受同源政策约束来跨域获取数据(只能用get请求);
  2. cors跨域:前端正常请求附带Origin字段附带网址,服务端接收到后,更据自己的跨域规则,如果允许访问,响应头设置Access-Control-Allow-Origin字段。
  3. html5的postMessage方法跨域:不能和服务端交换数据,只能在两个窗口(iframe)之间交换数据。A页面有B页面的引用。A用postMessage方法发送消息,B页面通过message事件监听并接受消息:

要设置cookie:

  1. cors的话设置xhr.withCredentials = true; // 设置跨域 Cookie
    同时服务端的响应中必须携带 Access-Control-Allow-Credentials: true

cors简单请求:HEAD,GET,POST
简单请求:请求头只包含

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type

new()操作到底做了什么

  1. 创建一个新的空对象;

  2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;

  3. 执行构造函数中的代码(为这个新对象添加属性) ;

  4. 返回新对象。

     var obj = new Base();
     // 等价于
     var obj  = {};
     obj.__proto__ = Base.prototype;
     Base.call(obj);
     //随后通过Base.prototype.xxx = () => {}为Base添加方法时,obj也同时拥有此方法
    

js的继承

(1)原型链继承:将父类实例作为子类的原型对象
缺点:所有子类的实例的原型都共享同一个父类实例的属性和方法。

function Parent () {
  this.names = ['kevin', 'daisy'];
}
function Child () {
 
}
 
Child.prototype = new Parent();
 
var child1 = new Child();
 
child1.names.push('yayu');
 
console.log(child1.names); // ["kevin", "daisy", "yayu"]
 
var child2 = new Child();
 
console.log(child2.names); // ["kevin", "daisy", "yayu"]

(2)构造函数继承:在在函数中运行父级构造函数
缺点:父类函数没有共享,浪费内存。
无法继承原型链上的属性和方法

// 子类
function Sub(){
  Super.call(this)
  this.property = 'Sub Property'
}

(3)组合继承:原型继承和构造函数继承的组合。
缺点:父级构造函数被调用两次,子类实例也有两份,浪费内存
实际上子类上会拥有超类的两份属性,只是子类的属性覆盖了原型对象上的属性

// 子类
function Sub(){
  Super.call(this)
  this.property = 'Sub Property'
}
Sub.prototype = new Super()
// 注意这里new Super()生成的超类对象并没有constructor属性,故需添加上
Sub.prototype.constructor = Sub

(4)原型式继承:传入参数obj,生成一个继承obj对象的对象
缺点:不是类式继承,而是原型式基础,缺少了类的概念

function objectCreate(obj){
  function F(){}
  F.prototype = obj
  return new F()
}
Object.create(obj)

(5)寄生式继承(和原型式继承差不多):原型式继承套个壳子,增加你要传入的参数。
缺点:依旧没有类的概念

   	function objectCreate(obj){
  function F(){}
  F.prototype = obj
  return new F()
}
function createSubObj(superInstance){
  var clone = objectCreate(superInstance)
  clone.property = 'Sub Property'
  return clone
}

(5)寄生组合式继承:在子构造函数中执行父级函数,并创建Object.create(父级的原型对象)复值给obj,再修改obj的constructor的指针指向子构造函数,最后obj作为子构造函数的原型对象。

function Sub(){
    Super.call(this);
}
var proto = Object.create(Super.prototype);
proto.constructor = Sub;
Sub.prototype = proto ;

Js一些常用的方法

var obj = {
    one:{
        two:[1,2,3]
    }
}
Object.assign(obj); //创建一个新对象,第一级式深拷贝,二级以下是浅拷贝
Object.create(obj); //创建一个新对象,新对象的__proto__指针指向obj
var arr = [1,2,3,4];
arr.splice(1,2); //截取从位置1开始后的两位返回[2,3],对原数组有影响
arr.slice(1,3); //返回从位置1到位置3-1的数组[2,3],对原数组无影响
var str = "  123 456";
str.split(' ');  //会返回空格分开两边到数组上,[ '', '', '123', '456' ]
arr.join('+');   //返回数组用+连接起来 

var x = JSON.stringify({abc:'123'});
console.log(x);  //对象转化为json字符串{"abc":"123"}
var y = JSON.parse(x);
console.log(y);  //将json字符串转化为对象{ abc: '123' }
JSON.stringify(xiaoming, ['name', 'skills'], '  ');
//第二个参数用于控制如何筛选对象的键值,如果我们只想输出指定的属性,可以传入Array:

this指向问题

this指向是包含它的函数作为方法被调用时所属的对象。
es6中箭头函数的this指针是定义函数时就绑定了的。就是this是继承自父执行上下文。

隐式转换一些规则

对象当转number时调用valueOf(),当转字符串时toString()

1,如果有一个操作数是布尔值,则比较相等之前将其转化为数值,true=1,false=0
2,如果有一个操作数是对象,调用valueOf方法,得到基本数据类型。

常见的比较:
console.log(null == undefined); //true
console.log([] == false); //true
console.log([] == ![]); //true
 if([]) //true

js严格模式

使用"use strict"即可。

  1. 不可使用未声明变量
  2. 不允许删除变量和函数
  3. 不允许八进制

作用:
1,消除代码不安全之处
2,提高编译效率

JS预编译

(1)函数声明提前
(2)变量声明提前undefined

正则表达式

var reg = new RegExp('abc', 'g');
var reg2 = /abc/gim;
//i表示忽略大小写,g表示全局匹配,m表示多行匹配
//^为开头, $为结尾
reg.test('abcde'); //返回true
reg.exec('abc1abc2abc') //返回一个数组,一般循环调用

console.log('abc2abc'.match(reg)); //返回一个数组,匹配到的字符串[ 'abc', 'abc' ]

字母转换相关

stringObject.toLowerCase()//转小写
stringObject.toUpperCase()//转大写
var str1 = 'a';
str1.charCodeAt();  // 97

将数字化为千位一个逗号的样子

//金钱符号转换
function formatNumber(str) {
    return str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  }
  
  console.log(formatNumber("1234.5629")) // 1,234,567,890

  var reg = /\B(?=(\d{3})+(?!\d))/g;
  console.log('1234567'.replace(reg,','))

exec可以使reg对象的.lastIndex属性增加

首屏渲染优化

腾讯前端团队alloyteam之前发布的手Q首屏渲染
第一,首屏数据拉取逻辑置于顶部,是为了数据能够第一时间返回,相比起将数据拉取逻辑放在外部资源会少了一个JS资源加载的往返时间。

第二,首屏渲染css及js逻辑优先内联HTML,这是为了当HTML文档返回时CSS和JS能够立即执行。

第三,次屏逻辑延后处理和执行,各种数据上报最好是延时上报,这样可以减少阻塞。

前端如何监控错误

即时运行错误的捕获方式:

1、try…catch

2、window.onerror:只能捕获即时运行错误,不能捕获资源加载错误(原理:资源加载错误,并不会向上冒泡,object.onerror捕获后就会终止,所以window.onerror并不能捕获资源加载错误);

资源加载错误的捕获方式:

1、object.onerror:img标签、script标签都可以添加onerror事件,用来捕获资源加载错误;

看控制台报错

target和currentTarget的区别

<body>  
    <ul id="fa" onclick="getEventTrigger(event)">  
        <li id="son">点我试试</li>  
    </ul>  
</body>  
    <script type="text/javascript">  
        function getEventTrigger(event)  
        {  
            x=event.currentTarget;  
            y=event.target;  
            alert("currentTarget 指向: " + x.id + ", target指向:" + y.id);  
        }  
    </script>  

其实就是target指向触发事件的元素
currentTarget指向的是绑定的标签元素

js如何定义私有字段

es5中在构造函数中var 一个变量。之后封装get和set函数来操作这个私有变量。

function Person(firstName,lastName){
    var name=firstName+" "+lastName;
    this.getFullName =function(){
        return name;
    }

es6中可以在constructor构造函数中var 变量,之后this.get方法来操作私有变量

class Example {
  constructor() {
    var _private = '';
    _private = 'private';
    this.getName = function() {return _private}
  }
}

js如何准确获取当前页面url网址信息

1、window.location.href(设置或获取整个 URL 为字符串)

var test = window.location.href;
alert(test);
返回:http://i.cnblogs.com/EditPosts.aspx?opt=1

2、window.location.protocol(设置或获取 URL 的协议部分)

var test = window.location.protocol;
alert(test);
返回:http:

3、window.location.host(设置或获取 URL 的主机部分)

var test = window.location.host;
alert(test);
返回:i.cnblogs.com

4、window.location.port(设置或获取与 URL 关联的端口号码)

var test = window.location.port;
alert(test);
返回:空字符(如果采用默认的80端口(update:即使添加了:80),那么返回值并不是默认的80而是空字符)

5、window.location.pathname(设置或获取与 URL 的路径部分(就是文件地址))
var test = window.location.pathname;
alert(test);
返回:/EditPosts.aspx

6、window.location.search(设置或获取 href 属性中跟在问号后面的部分)

var test = window.location.search;
alert(test);
返回:?opt=1

PS:获得查询(参数)部分,除了给动态语言赋值以外,我们同样可以给静态页面,并使用javascript来获得相信应的参数值。

7、window.location.hash(设置或获取 href 属性中在井号“#”后面的分段)

var test = window.location.hash;
alert(test);
返回:空字符(因为url中没有)

es6


symbol数据类型

  1. 数据类型的特点又唯一性,常作为对象的属性名
  2. symbol另一特性隐藏性,所以for…in是访问不到的。

let const var区别

let:
1,不能重复声明一个变量
2,了他拥有块级作用域
3,let声明不提前

4,总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

const:
1,不能重复声明,要初始化

箭头函数

  1. 箭头函数不可以做构造函数
  2. 箭头函数中的this指向它父级的执行上下文里的this。
  3. 箭头函数不绑定arguments

promise

1,promise原理:
promise使用观察者模式,promise内部管理pending(等待),fulfilled(成功),rejected(失败)的状态改变,通过触发构造中的resolve()和reject()来改变状态,进而触发.then()中的回调函数,回调函数中返回promise对象,方便链式调用。

2,promise优点:异步编程,解决回调地狱。

3,promise缺点:promise一旦执行就不能停止
如果想停止1,不设置resolve()和reject()这样就一直是pending状态了
2,直接抛出错误catch接受

promise函数

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
//全部成功才会执行回调函数

Promise.race([promise1, promise2]).then(function(value) {
  console.log(value);
  // Both resolve, but promise2 is faster
});
//返回先完成的那个

promise函数的then的第二个参数和catch的区别

Promise.prototype.catch方法是.then(null,rejeaction)的别名,用于指定发生错误时的回调函数。
主要区别就是,如果在 then 的回调函数里抛出了异常,后面的 catch 能捕获到。

async函数和await

async function 声明用于定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。
返回一个promise对象。

async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待 Promise 的结果出来,然后恢复async函数的执行并返回解析值(resolved)。
注意:await只能在async中使用。

function f1() {
    await f2();
}
则会回两次主线程,第一次是等待f2()返回promise,当promise返回后会执行并返回解析值(resolved)
这个时候执行resolved又要再等待,异步执行回调函数,这个时候又回主线程,
当主线程执行完就执行resolve触发的回调函数

以下代码就是经典await回两次主线程

async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}

async function async2() {
console.log('async2');

} 

console.log('script start');
setTimeout(function() {
 
console.log('setTimeout');

}, 0); 
async1();

new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});

console.log('script end');

es6数组操作的一些函数

var arr = [1,2,3];
arr.forEach(function(value, index) {
    //要执行的操作
},this);
arr.filter(function(value,idnex){
    return true;//要返回的过滤
},this);
arr.map(function(value,index){
    return value;//要返回的数字
},this);
var b = arr.reduce(function(pre,current,index){
    return pre + arr[index] + "+";
},'');
console.log(b);  //1+2+3+

ajax和fetch区别

fetch 是全局量 window 的一个方法,它的主要特点有。

  1. 使用了 JavaScript Promises 来处理结果/回调:
  2. 从 fetch()返回的 Promise 将不会拒绝HTTP错误状态, 即使响应是一个 HTTP 404 或 500。
  3. 在默认情况下 fetch不会接受或者发送cookies,要发cookie设置 credentials: ’same-origin’

fetch添加超时时间

Promise.race()方法,它只会执行先完成的。

	Promise.race([
	    fetch(URL),
	    new Promise(function(resolve,reject){
	        setTimeout(()=> reject(new Error('request timeout')),2000)
	    })])
	    .then((data)=>{
	        //请求成功
	    }).catch(()=>{
	        //请求失败
	});

原生ajax请求步骤

var xhr = new XMLHttpRequest();
xhr.open('post','url',true);
xhr.setRequestHeader("content-Type", "x-www-form-urlencoded");
xhr.onreadystatechange(function() {
    if(xhr.readyState == 4&&xhr.status==200) {
        //执行
    }
})
xhr.send();

CSS


CSS中 link 和@import 的区别是?

(1) link属于HTML标签,而@import是CSS提供的;
(2) 页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;
(3) import只在IE5以上才能识别,而link是HTML标签,无兼容问题;
(4) link方式的样式的权重 高于@import的权重.

css解析

从右往左解析

常见css的操作

1,css画一个三角形

.triangle_border_up{
    width:0;
    height:0;
    border-width: 30px; /*上右下左 上为0为上三角形*/
    border-style:solid;
    border-color:transparent transparent transparent #333;/*透明 透明 透明  灰*/
}

2,文本溢出时候省略号(css3)

text-overflow: ellipsis;
overflow: hidden;
white-space:nowrap;

多行文本溢出
overflow: hidden;
text-overflow:ellipsis;//文本溢出显示省略号
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;

3,css五星好评思路
需要两张图片暗色的星星,亮色的星星。首先设置div宽度为5个星星宽度,用repeat-x平铺重复。
之后5个a标签分别占1/5的宽度,如果鼠标放上去,就触发伪类:hover,将left边定义到最左边,right定位到第i个位置,再background背景图片并repeat-x平铺i个亮的星星。

4,设置input中的两端对齐

    span{
        width: 100px;
        text-align: justify;
        float: left;
    }
    span:after{
        content:'.';
        width: 100%;
        display: inline-block;
        overflow: hidden;
        height: 0;
    }

text-align: justify;是多行两侧对齐。
span文本设置宽度和浮动和justify,之后设置伪类after且宽为100%,变为多行

伪类

伪类就是像hover
例如a标签一定要按顺寻link,visited,hover,active。
因为css伪类同级后面会覆盖前面。
比如把hover放在active后面,那么实际你在激活(active)链接的时候就触发了hover伪类,hover在后面覆盖了active的颜色,所以始终无法看到active的颜色

伪元素

伏击设置after清除浮动
:after{display:block;clear:both;content:"";visibility:hidden;height:0}

css的继承

visibility
text-align
line-height、color、font-family、font-size、font-style、font-weight、text-decoration、text-transform、direction。

querySelectorAll和getElementsByClassName区别

querySelectorAll 返回的是一个 Static Node List即静态列表,而 getElementsBy 系列的返回的是一个 Live Node List即动态列表。

querySelectorAll 返回的是映射 改变其值不会改变document
当dom改变会动态nodelist会改变
而getElementsByClassName 改变它就会改变document

background-origin

background-origin 属性规定 background-position 属性相对于什么位置来定位。
padding-box 背景图像相对于内边距框来定位。
border-box 背景图像相对于边框盒来定位。
content-box 背景图像相对于内容框来定位。
x% y% 使用百分比,左上角是0%0%,右下角是100%100%

z-index

这是因为有z-index的父盒子中的子盒子,其z-index值无论多大,都是相对父盒子而言的,所以只是决定它在这个父盒子区域的层级显示顺序而已~无法遮盖掉父盒子的兄弟盒子中z-index值较大的元素。
父级的z-index比兄弟盒子小的话,那子盒子中z-index设多大最后还是在父兄弟盒子的下面。

css设置图片的大小

img标签的话,可以直接设置宽高,它会自动缩放,如果单设置一项的话,它只会按比例缩放。

如果是背景图片

width: 100%;
height: 200px;  
background-size:100% 100%;  //按照当前div的宽高进行自动缩放。
background-repeat: no-repeat;
background-image: url();  //背景图片

一张高清图片,怎么保证其在不同移动端设备上的显示效果?
可以用rem设置它的宽高

CSS父子margin塌陷

如果子div中有margin-top会外层div塌陷。
解决设置:父设置,overflow:hidden即可。

子margin为百分数,即以父级的宽度乘以百分比。

常见块元素和行内元素

常用的块级元素:
  address , center , div , dl , form , h1 , h2 , h3 , h4 , h5 , h6 , menu , ol , p , table , ul , li

常用内联的元素:
  a , b , br , em , font , img , input , label , select , small , span , textarea

CSS选择器优先级

  1. 在属性后面加!important会覆盖任何样式
  2. 作为style属性写在元素内的样式
  3. id选择器
  4. 类选择器
  5. 标签选择器
  6. 通配选择器

四种定位css定位

  1. static 是默认值/静态定位
  2. realative 相对定位,相对自身定位,不脱离文档流。
  3. absolute 绝对定位,相对于最近的已定位元素,脱离文档流。
  4. fixed 固定定位,相对窗口定位,脱离文档流
  5. sticky 粘性定位,在目标区域时是realative相对定位,滚动超出目标区域时是fixed固定定位。
    兼容性较差:这个属性可以使用的浏览器只有FireFox和iOS的Safari

css水平垂直居中

水平居中:
1,行内元素

text-align: center;

2,有宽度的块元素

margin: 0 auto;

垂直居中:
1,单行内容垂直居中:

line-height: height; //height父级高度

2,绝对定位

position: absolute;
top: 50%;
transform: translate(0, -50%);

3,flex布局

display: flex;
flex-direction: column;
justify-content: center;

css怎么让div充满屏幕

    width:100%;
    height: 100%;
    background-color: red;
    position: absolute; /*设置个absolute就可以全屏了*/

visibility:hidden,display:none和opacity:0比较

visibility: hidden;
1、元素会被隐藏,但是不会消失,依然占据空间。
2、visibility: hidden会被子类继承,子类也可以通过显示的设置visibility: visible;来反隐藏。
3、visibility: hidden;不会触发该元素已经绑定的事件。
4、visibility: hidden;动态修改此属性会引起重绘。

display: none;
1、display: none;不占据空间(毕竟都不熏染啦),所以动态改变此属性时会引起重排。
2、display: none;不会被子类继承,但是···子类是不会显示的,毕竟都一起被kill啦。
3,不会触发绑定事件

opacity=0
1、opacity=0只是透明度为100%,元素隐藏,依然占据空间。
2、opacity=0会被子元素继承,且,子元素并不能通过opacity=1,进行反隐藏。不能。
3、opacity=0的元素依然能触发已经绑定的事件。

BFC:块级格式化上下文

一个块格式化上下文(block formatting context) 是Web页面的可视化CSS渲染出的一部分。

BFC的创建方法

  • 根元素或其它包含它的元素;
  • 浮动 (元素的float不为none);
  • 绝对定位或固定定位元素 (元素的position为absolute或fixed);
  • 行内块inline-blocks(元素的 display: inline-block);
  • overflow的值不为visible的元素;

BFC特性

  • 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);
  • 处于同一个BFC中的元素相互影响,可能会发生margin collapse(margin塌陷);
  • BFC可以包含浮动,设置overflow:hidden
  • BFC子元素不会影响外部

css3的box-shadow ,box-radius transform和translation,以及动画animation

box-shadow

div
{
	box-shadow: 10px 10px 5px #888888;//第一个水平阴影右为正
	//第二个参数垂直阴影下为正,第三个是模糊程度,第四个是颜色
}

box-radius:50%//圆形

transform:变形,改变。

  • rotate() 顺时针旋转给定的角度;
  • scale()放大或缩小,根据给定的宽度(X轴)和高度(Y轴)参数:scale(2,4);
  • translate() 平移,传进x,y值,代表沿x轴和y轴平移的距离;

transition过度

div
{
width:100px;
height:100px;
background:blue;
transition:width 2s;
-moz-transition:width 2s; /* Firefox 4 */
-webkit-transition:width 2s; /* Safari and Chrome */
-o-transition:width 2s; /* Opera */
}

div:hover
{
width:300px;
}
//当鼠标放到div上宽度变为300

animation是指定keyframes的动画,具体是

	.a {
    animation:mymove 5s infinite;
    -webkit-animation:mymove 5s infinite; /*Safari and Chrome*/
    height: 100px;
    background: red;
    position: fixed;
    width: 100px;
}

@keyframes mymove
{
    from {left:0px;}
    to {left:200px;}
}
//正方形向右平移的动画

css动画优化

  1. 尽量使用 transform 生成动画,避免使用 height,width,margin,padding 等。
    使用 transform,浏览器只需要一次生成这个元素的位图,并在动画开始的时候将它提交给 GPU 去处理 。之后,浏览器不需要再做任何布局。transform 动画由GPU控制,支持硬件加速,并不需要软件方面的渲染。

  2. 尽可能少的使用box-shadows,它是性能杀手。

css性能优化

  1. CSS放在HTML页面的头部
  2. css匹配原理是从右往左的,不要在最右的放元素。
  3. 动画性能优化,多用硬件能力,gpu加速比如transform变形
  4. 用css的精灵图,css来控制图片显示

盒模型,IE模型

  • box-sizing:content-box; 在标准模型中,盒模型的宽高只是content的宽高。
  • box-sizing:border-box; IE模型中盒模型的宽高是content+padding+border的总宽高

CSS浏览器前缀

  • -moz- /* 火狐等使用Mozilla浏览器引擎的浏览器 */
  • -webkit- /* Safari, 谷歌浏览器等使用Webkit引擎的浏览器 */
  • -o- /* Opera浏览器(早期) */
  • -ms- /* Internet Explorer (不一定) */

flex的属性

align-items:center;

<style>
.a {
    display:flex;
    align-items:center; 
    width: 100%;
    height: 100%;
}
.b{
    width: 500px;
    height: 100px;
    background: red;
}

</style>

<body>
    <div class="a">
        <div class="b"></div>
        <div class="b"></div>
        <div class="b"></div>
        <div class="b"></div>
    </div>
</body>
//将子元素排成一行,并相对父级垂直居中

雪碧图和字体图标

  • 雪碧图又叫精灵图原理就是将一些小图标合并到一张图上,用css的背景定位来设置要显示的部分。
  • 字体图标:可缩放的图标,可用css修改颜色,大小等。iconfont阿里巴巴图标库。

JS操作dom相关

offsetWidth       //返回元素的宽度(包括元素宽度、内边距和边框,不包括外边距)
clientWidth        //返回元素的宽度(包括元素宽度、内边距,不包括边框和外边距),且要减去滑动块
scrollWidth       //返回元素的宽度(包括元素宽度、内边距和溢出尺寸,不包括边框和外边距),无溢出的情况,与clientWidth相同

vue相关


vue优点

  1. 双向数据绑定
  2. 组件化开发
    Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件。最后拼装
  3. Virtual DOM
    优化dom渲染操作
  4. 轻量高效
    很小

Vue的特点生态圈挺多

Vuex,vue-router,vue-cli,饿了么组件,

vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。

  1. State(存放数据)
  2. Getter(可以认为是State的计算属性)
  3. Mutation(用来同步修改state中的数据状态)
  4. Action(异步处理数据,一般提交的是mutation)

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 。
现在开发分解出很多组件进行开发,而 各个组件之间想必在逻辑上多少是有关系的。  那么组件之间的“通信”,就成了待解决问题。 在应用不断的扩展、变得越来越复杂。这当然不是我们想要的,我们希望应用的各个部分都易维护、可扩展。于是,状态管理模式冒了出来。

vuex原理

Vuex可以被看作项目中所有组件的数据中心,我们将所有组件中共享的State抽离出来,任何组件都可以访问和操作我们的数据中心.

Vuex为什么一定要用commit提交

Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

vueRouter原理

vueRouter原理:更新视图而不重新请求页面。

  • hash模式:
    hash模式#后的字段对后端没有影响,不会加载。
    原理:利用onhashchange事件来监听浏览器历历史记录记录。

  • history模式:
    需要后台正确配置,如果url匹配不到资源,要返回一个index.html
    原理:history模式利用了html5中的pushState()和replaceState() 方法。这两个方法应用于浏览器历史记录栈。
    所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。

vue的生命周期

  1. beforeCreate: 组件实例刚刚被创建,无法访问方法和数据
  2. created:组件实例创建完成,可访问数据和方法
  3. beforeMount:挂载DOM之前,模板编译之前
  4. mounted:模板编译之后,可获取dom,一般发起后端请求
  5. beforeUpdate:组件更新之前
  6. updated:组件更新之后
  7. activated:keep-alive组件被激活
  8. deactivited: keep-alive组件被移除
  9. beforeDestory:组件被销毁前
  10. destoryed:组件被销毁后

vue的computed

是在数据改变时计算的computed的值是一个缓存,只有它依赖中的数据发生改变,缓存才改变。
例子: return Date.now();它就永久不更新缓存,因为Date.now()不是vue中的响应式依赖。

destory作用

销毁监听事件

vue的双向数据绑定原理

vue采用数据劫持结合订阅发布者模式,来实现双向数据绑定。
通过Object.defineProperty()来劫持各个属性,当数据改变触发getter和setter,发布消息给订阅者。订阅者再施行相应操作更改视图。

Vue数据双向绑定是通过数据劫持结合订阅者-发布者模式模式方式来实现的.Object.defineProperty()给属性绑定getter和setter方法对数据进行劫持监听;然后创建一个订阅器来在getter里收集订阅者。watcher就是在自身实例化的时候触发getter往订阅器里添加自己,当数据改变时,就会触发setter

1,实现数据侦听听器observer,能对所有属性侦听。
2,实现指令解析器compiler,对每一个元素节点的指令进行解析,根据指令模板更新视图。
3,实现Watcher订阅者类,作为observer和compile桥梁,能够订阅并收到属性变动通知,发布者执行相应的回调函数更新视图。
4,Dep : 一个订阅者的列表类,依赖收集类,可以增加或删除订阅者,可以向订阅者发送消息
当指令解析时绑定监听者new一个Watcher,Watcher初始化将自己赋给Dep.target,再触发getter,将Dep.target加入Dep订阅者列表中,当数据变动时,setter就触发Dep的通知方法notify()通知订阅者,订阅者收到后,执行相应操作。
要给每一个监听的变量new Dep();通过闭包保存。

当执行object.defineProperty()时会执行一个new 一个dep的实类,当执行watcher时就会将自己赋给Dep.target这个全局属性,并触发getter,getter中将Dep.target加入dep的实例中,并为null空。当数据改变触发setter进而通知订阅者。

vue的Array怎么实现数组变异的

核心思想:通过创建一个拦截器来覆盖数组本身的原型对象Array.prototype。

首先用object.create()Array.prototype即数组原型对象作为参数去新建一个对象arrayMethods,
再遍历变异方法,比如push,pop,这些。并用Object.defineProperty给arrayMethods绑定这些变异方法。当调用这些方法时,首先用apply执行一下数组原型对象真正的push,pop方法。之后检查插入项,像push,shift这些。再监听插入项是不是数组,是的话再observeArray(insert)。之后触发.notify函数通知订阅者。返回arraymethods。

最后修改你要监听的数组的__proto__,即修改数组的原型对象为arraymethods,这样数组执行变异方法时就是执行arraymethods上的方法了。

object.defineProperty和Proxy

Objct.defineProperty()无法监听数组

Proxy:

  1. 可以直接监听对象而非属性
  2. Proxy可以直接监听数组的变化
  3. Proxy右多达13种拦截方式,proxy作为新标准,受到浏览器商的性能优化。

vue.nextTick作用

vue.nextTick:将延迟到下次Dom更新之后执行,dom更新是异步的。
可以用在created生命函数种,因为created时Dom还没编译。

因为vue中DOM更新是异步操作的,所以会写执行完执行栈中所有的同步任务即变量修改,Vue.nextTick()相当于在每次DOM更新后又手动添加了一个微任务,保证了该任务执行的时候DOM已经完全更新好了。

vue中的组件通信

  • 父向子传:
    使用props

  • 子向父:
    父亲=组件加 @getMessage=“getVal”
    子组件使用 $emit触发事件,父组件侦听事件

  • 兄弟组件间传值:
    先new Vue实例,在实例上用. e m i t ( ) 触 发 事 件 , 用 . emit()触发事件,用. emit().on()侦听事件,并执行相应的函数。

  • vuex传值

vue的ref获取dom值

ref=“myh3”
this.$refs.myh3

vue的自定义指令和组件怎么写

Vue.directive('demo', function (el, binding) {
  el.innerText = '123'
})

vue-router参数传递

this.$router.push({name:'Home',params:{userId: "1"}}); //传递参数
this.$router.params.userId;//获取参数数

用url传递参数
this.$router.query.userId;//获取参数

SPA(单页应用)

所有活动只有一个web页面,仅在初始化时加载HTML。更新视图而不重新请求页面。
因为是单页,在搜素引擎最佳化(seo)上的工作花功夫。

vue的虚拟dom

(1)真实Dom操作的代价高,dom虚拟只修改对象而已,所以操作代价小。
(2)Diff算法操作:
会对新旧两颗树进行深度遍历,每遍历一个节点就把该节点和新树对比,差异就记录到一个对象中,
最后将差异对象更新到视图上。

vue中的diff算法

采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。
当数据发生改变时,set方法会让调用 Dep.notify 通知所有订阅者Watcher,订阅者就会调用 patch 给真实的DOM打补丁,更新相应的视图。

patch函数接收两个参数 oldVnode 和 Vnode 分别代表新的节点和之前的旧节点。
首先sameVnode判断值不值得比较,如果不值得比较,将新节点直接把老节点整个替换了。

如果值得比较,会执行patchVnode函数进行节点比较:
节点比较有5种:

if (oldVnode === vnode),他们的引用一致,可以认为没有变化。

if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text),文本节点的比较,需要修改,则会调用Node.textContent = vnode.text。

if( oldCh && ch && oldCh !== ch ), 两个节点都有子节点,而且它们不一样,这样我们会调用updateChildren函数比较子节点,这是diff的核心,后边会讲到。

else if (ch),只有新的节点有子节点,调用createEle(vnode),vnode.el已经引用了老的dom节点,createEle函数会在老dom节点上添加子节点。

else if (oldCh),新节点没有子节点,老节点有子节点,直接删除老节点。

updateChildern函数:
oldVnode的childern有两个头尾指针,vnode的childern也要两个头尾指针。
然后两两比较,一共有4种比较方式。相等后指针往中间靠。
当其中两个能匹配上那么真实dom中的相应节点会移到Vnode相应的位置。

vue中加入key的作用

需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们。

vue中的插槽slot

当子组件模板只有一个没有属性的插槽时,父组件传入模板的整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。

vue和react的区别

1,
react一切都是javascript,xml和javascript的混合可以用jsx表达。
vue是html,css,js各自处理

2,
React:单项数据流,数据主要从父节点传递到子节点(通过props)。
Vue:双向数据绑定

3,
react会自顶向下整个diff一遍,而vue只会diff修改了数据绑定的组件

vue1和vue2的区别

  • vue2加入了v-for
  • 生命周期不同
  • 组件通讯不同

$emit的实现原理

其实就是用到数据侦听,订阅发布者模式。

vue3.0了解吗?

  • 重写了虚拟dom优化性能
  • 压缩vue体积大小,
  • vue3.0改进了对typescript的支持

node.js

node.js优缺点

Node是基于事件驱动和无阻塞的,所以非常适合处理并发请求,

不适合CPU密集型应用,因为javascript单线程,导致CPU不能释放。

node.js的中间件

中间件就是一种功能的封装方式,就是封装在程序中处理http请求的功能,中间件是在管道中执行。
调用 next(),以将控制权传递给下一个中间件。如果要结束respone即可。
app.use()使用中间件

app.use(url,func)第一个参数是客户端请求的路径,第二个参数就是中间件

node.js

Node.js采用ChormeV8作为js的解析引擎,而I/O处理方面使用了自己设计的libuv,libuv是一个基于事件驱动的跨平台抽象层.

宏任务与微任务

如果是浏览器的事件循环,单主线程的同步任务执行完,才会执行微任务中的事件比如promise,
等微任务队列全部执行完毕,才会去执行宏任务队列。

node.js的事件循环,有6个阶段,只有当切换阶段才会去执行微任务的事件,等微任务队列全部执行完,才会去执行宏任务。

node.js事件循环

事件循环分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。

  • timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
  • I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
  • idle, prepare 阶段:仅node.js内部使用
  • poll 阶段:获取新的I/O事件, 适当的条件下node.js将阻塞在这里
  • check 阶段:执行 setImmediate() 的回调
  • close callbacks 阶段:执行 socket 的 close 事件回调

例子:

setTimeout(()=>{
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)
setTimeout(()=>{
    console.log('timer2')
    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)

浏览器的事件循环:

node.js的事件循环

服务器端渲染.用户首屏时间优化 (SSR)

服务端渲染的目的是:性能优势。 在服务端生成对应的 HTML 字符串,客户端接收到对应的 HTML 字符串,能立即渲染 DOM ,最高效的首屏耗时。此外,由于服务端直接生成了对应的 HTML 字符串,对 SEO 也非常友好;

服务端渲染只是把当前的前端框架中的一部分js代码放到服务器上渲染,加载到浏览器上的html就不是一个空页面,这样可以减少首页的白屏时间,同时提高被搜索引擎检索的机会。

计算机网络相关


计算机网络七层

应用层,表示层,会话层,传输层,网络层,数据链路层,物理层

应用层:为用户的应用程序如邮件提供网络服务。

表示层:确保一个系统的应用层发送的消息可以被另一个系统的应用层读取,管理数据加密,解密。

会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路,主要在你的系统之间发起会话或者接受会话请求

传输层:定义一些传输数据的协议和端口。进行流量控制等

网络层:传送ip分组报文

数据链路层:传输有Mac地址的帧

物理层:传输二进制

HTTP请求头中各字段解释

一.通用头部
1、Cache-Control
no-cache:如果是客户端的话,说明客户端不会接收缓存过的响应,要请求最新的内容。
max-age:该参数后方会被赋值上相应的秒数,缓存的秒数。

2,Request URL字段,Request Method请求方法get/post,Status Code:200状态码。
Content-type:请求类型。

3,Connection
Connection:keep-alive就是保持持久连接的意思。也可以理解为长连接,建立一次TCP连接,再复用发送。Connection:close就是关闭

二,请求头
1,Accept
请求报文可通过一个“Accept”报文头属性告诉服务端 客户端接受什么类型的响应。

2,Cookie 客户端的Cookie就是通过这个报文头属性传给服务端的哦

3,If-None-Match:一般是带上etag,然后与服务端比较。

4,Referer:后面带一个url 就是发起请求的url地址当前页面url

5,Origin 标识跨域资源请求

三,响应头:
1,etag:是服务器当前请求的服务器资源(图片,HTML页面等)所对应的一个的字符串标识。

2,Location:Location字段一般与重定向结合着使用。

3,Set-Cookie:响应报文中会使用到该字段。

TCP和UDP的区别

TCP:面向连接,可靠传输,用于传输大量数据(流模式)。
UDP:非面向连接,不可靠传输,用于传输少量数据,速度快(数据报模式)。
TCP:(HTTP,FTP)一般用于文件传输。
UDP:(DHCP自举协议,用于获取IP地址)一般用于即使通信,qq聊天,对丢包率要求较低。

UDP若想完成可靠传输。传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。

     实现确认机制、重传机制、窗口确认机制。

RUDP 提供一组数据服务质量增强机制。

TCP的可靠性

确认机制、重传机制、滑动窗口。

协议所在层

  • 网络层:IP协议,ICMP协议,ARP协议,RARP
  • 传输层:TCP,UDP
  • 应用层:FTP,Telent,SMTP,HTTP,RIP

websocket,长连接,长轮询

短连接:所谓短连接,及连接只保持在数据传输过程,请求发起,连接建立,数据返回,连接关闭。它适用于一些实时数据请求。

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
实例:请求频繁的场景(直播,流媒体)。

短轮询:短轮询指的是在循环周期内,不断发起请求,每一次请求都立即返回结果,根据新旧数据对比决定是否使用这个结果。

长轮询: 客户端向服务器发送Ajax请求,服务器接到请求后将请求挂起,直到有新消息才返回响应信息并关闭连接(或到了设定的超时时间关闭连接),客户端处理完响应信息后再向服务器发送新的请求。

实例:WebQQ、Hi网页版、Facebook IM。

Websocket:WebSocket是Html5定义的一个新协议,与传统的http协议不同,该协议可以实现服务器与客户端之间全双工通信。简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。

websocket借用了HTTP的握手,请求头加了。
Upgrade: websocket
Connection: Upgrade
这就是websocket的核心,告诉服务器这是websocket请求,而不是http请求。
之后就可以进行全双工通信了。

scoket

socket:也叫嵌套字 ,是一组实现TCP/UDP通信的接口API,也就是说无论TCP还是UDP,通过对scoket的编程,都可以实现TCP/UCP通信。
作为一个通信链的句柄,它包含网络通信必备的5种信息:

连接使用的协议
本地主机的IP地址
本地进程的协议端口
远地主机的IP地址
远地进程的协议端口

可见,socket包含了通信本方和对方的ip和端口以及连接使用的协议(TCP/UDP)。通信双方中的一方(暂称:客户端)通过scoket(嵌套字)对另一方(暂称:服务端)发起连接请求,服务端在网络上监听请求,当收到客户端发来的请求之后,根据socket里携带的信息,定位到客户端,就相应请求,把socket描述发给客户端,双方确认之后连接就建立了。

TCP和UDP报文头

TCP报文头20字节

UDP报文头8字节

TCP拥塞控制,慢开始,拥塞避免等

慢开始的思路:当主机刚开始发送数据时,由于并不清楚网络的负荷情况,如果立即发送大量数据可能引起网络拥塞,所以从小到大逐渐发,一般是2的指数倍增大。

当到达慢开始门限时,它会执行拥塞避免算法,使拥塞窗口cwnd的值缓慢增大,其实就是”加法增大“。

当拥塞窗口值到达极限,网络就出现超时,重新进行慢开始,而且慢开始门限变为拥塞窗口极限值的一半。

快重传:可以尽早知道发生了个别报文段丢失。比如发送方接收到了3个确认M2的重复确认报文,则立即重发M2报文。

快恢复:当知道只是丢失个别报文段时,执行快重传,门限 = 拥塞控制值/2,并开始拥塞控制算法。

TCP滑动窗口机制,和流量控制

当滑动窗口为1时,发送方只有接收到ack确认报文才能发下一个序号。

首先,发送方有发送窗口,接收方有接收窗口,发送窗口内的序号号表示可以直接发送的序号。
当接收窗口的前n个序号都收到时,就会发送ack确认报文给发送方,同时接收窗口向右移n位。
发送窗口只有收到发送窗口内字节的ACK确认,才会向右移动发送窗口的左边界。

流量控制:是让发送方的速率不要太快,要让接收方来的及接收。
接收方会告诉发送方它的接收窗口是多少。

TCP四次挥手的time wait状态

客户端最后一次发送ack确认报文会进入time wait状态。如果在2MSL时间内没收到重发的FIN报文就会结束。
当进入time wait,你发的ack报文可能丢失,这样服务端就会重发FIN断开连接报文,所以客户端要维持这条连接。重发ACK报文后2MSL时间内不收到FIN报文就会断开。

TCP为什么三次握手

一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

TCP为什么四次挥手

确保数据能够完成传输。

但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

time_wait状态

close()发起主动关闭的一方,在发送最后一个ACK之后会进入time_wait的状态,也就说该发送方会保持2MSL时间之后才会回到初始状态。 在这段时间内若ack报文没到,服务端会再发送FIN报文,客户端若收到就重新发确认,否则时间到断开连接。

TCP三次握手第三次失败

可以看出当失败时服务器并不会重传ack报文,而是直接发送RTS报文段,进入CLOSED状态。这样做的目的是为了防止SYN洪泛攻击。

UDP

以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的.

因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节.
而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的.
又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节.
这个1472字节就是我们可以使用的字节数。

当我们发送的UDP数据大于1472的时候会怎样呢?
这也就是说IP数据报大于1500字节,大于MTU.这个时候发送方IP层就需要分片(fragmentation).
把数据报分成若干片,使每一片都小于MTU.而接收方IP层则需要进行数据报的重组.
这样就会多做许多事情,而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方便
无法重组数据报.将导致丢弃整个UDP数据报。

交换机和路由器的区别

交换机发生在网络的第二层数据链路层,而路由器发生在第三层网络层。
路由器可以根据IP地址寻找下一个设备,可以处理TCPIP协议,而交换机是根据MAC地址寻址的。

大文件传输注意点

  • 大文件传输,应该支持断点续传;
  • 要有文件校验,校验不对的话能自动重传;
  • 要考虑多线程分片上传,并发控制,带宽压力,限速上传;
  • 多文件排队上传;

算法相关


不稳定排序和稳定排序

记住口诀:
不稳定排序:“快选堆希”

堆排序

首先利用二叉树的性质,按序存储到数组中,如果要从小到大排序,会先形成最大堆,再将最大的数字与倒数第一个数互换,接着再忽略最后一个元素进行最大堆,再将最大的元素放到倒数第二的位置,其实就是选择排序,每次选最大的并放到相应位置。

快速排序

快速排序时间复杂度:平均O(nlogn),
最坏情况:O(n的平方),如果像冒泡的话就会O(n的平方)如果递归n次加上比较基准元素要O(n)的时间复杂度。
空间复杂度:主要在递归的层数,一般O(logn),最坏为O(n)

快排除了递归实现,还可以用栈来实现非递归实现排序,先推start和end,进去后调用partition函数返回位置p,将p+1,到end入栈,start到p-1入栈

js的sort底层实现

依稀记得是插排和快排,数组长度小于等于 22 的用插入排序 InsertionSort,比22大的数组则使用快速排序

Hash哈希的原理:

哈希表(Hash Table)是一种根据关键字直接访问内存存储位置的数据结构。
1,除留余数法
哈希表允许的地址数为m,去一个小于或等于m的质数作为除数。
则关键码转化为散列地址为hash(key) = key%p;

处理冲突的闭散列方法:1.线性探查法
当遇到同一个散列地址时,往下一个散列位置移。

二叉搜索树

它的左子树小于根节点,右子树大于根节点。
中序遍历会从小到大输出。

平衡二叉搜索树和红黑树

平衡二叉搜索树左右两个子树的高度之差不能大于1,
红黑树,非红即黑,根节点为黑。红黑树从根节点到叶子,没有一条路径会比其它路径长出两倍。

如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL树,

如果搜索,插入删除次数几乎差不多,应选择红黑树。

#操作系统

进程和线程

进程:进程是系统进行资源分配和管理资源的基本单位。指计算机运行的程序。
线程:是进程的一个执行单元,是进程中执行运算调度的最小单位。

  1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
  2. 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
  3. 进程和线程由操作系统调度

进程间通信的几种方式

  1. 基于共享存储区的通信方式
  2. 管道通信系统
    它是半双工的,即只能单向传输。管道是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件
  3. 消息队列
    消息队列是消息的链接表,有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。
  4. socket通信

内存管理方式

1,分页存储
分成多张页面,建立页表,映射到物理空间

2,分段存储
有段表,段表有起始地址,和占用空间大小。

3,段页式存储

进程上下文

进程上下文就是表示进程信息的一系列东西,包括各种变量、寄存器以及进程的运行的环境。这样,当进程被切换后,下次再切换回来继续执行,能够知道原来的状态。

不同浏览器通信

不同浏览器通信负载均衡时用socket

进程的常见状态?以及各种状态之间的转换条件?

就绪:进程已处于准备好运行的状态,即进程已分配到除CPU外的所有必要资源后,只要再获得CPU,便可立即执行。
执行:进程已经获得CPU,程序正在执行状态。
阻塞:正在执行的进程由于发生某事件(如I/O请求、申请缓冲区失败等)暂时无法继续执行的状态。

解决死锁的方案

1 当用户请求资源时将所有资源都请求完。

2.有序资源分配法
资源按某种规则统一编号,申请时必须按顺寻申请。
比如B进程申请2,1则改为申请顺序为1,2.

用户态和内核态

核心态:核心态是操作系统内核的运行状态,在这种状态下,处理机具有较高的特权,能执行一切指令,可以访问所有的寄存器和存储区。

用户态(User Mode):运行用户程序
用户态具有较低特权的执行状态,在这种状态下,处理机只能执行规定的指令,访问指定的寄存器和存储区,用户程序通常只能在这一级别执行。

数据库

MySQL的索引类型:
比如1w个电话号码数据,查找一个电话号码,它要遍历1w个数据,如果建立索引,就能快速查找到了。
我认为索引应该是就像hashmap一样,一个值对应着它的存储地址。

虚拟内存

在程序装入时,可以将程序的一部分装入内存,而将其余部分留在外存,就可以启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。

杂项


MVC与MVVM

MVC即Model-View-Controller模型-视图-控制器。

  1. 用户操作View(视图)传送指令到控制器
  2. 控制器(controller)完成逻辑业务后,要求模型(model)改变状态
  3. 模型(model)将更新的数据发送给视图(View),用户得到反馈。

MVC优缺点:
优点:实现了功能模块和显示模块的分离,同时它还提高了应用系统的可维护性、可扩展性。
缺点:不适合小型项目,增加系统结构和实现的复杂性

MVVM :即Model-view-viewmodel模型-视图-视图模型
视图(view)和模型(model)通过视图模型(viewmodel)交互。所以视图改变,数据源改变。数据源改变,视图改变。

webpack

webpack是一个对资源模块化和打包的工具。资源文件包括js,css,图片等。
loader 它就是一个转化器,将文件A转化为B。
对于plugin,它就是一个扩展器,也就是一个插件。

在webpack.config.js文件下配置打包

module.exports = {
    entry:_dirname + '/app/main.js',  //入口文件
    output:{
        path:_dirname + "/public",//打包后输出路径
        filename: "bundle.js" //文件名
    },
    module:{
        rules:[
            {
                test: /(\jsx|\js)$/, //所要处理的文件
                use: {loader: "babel-loader"}, //要使用loader
                exclude:/node-moduled/ //不要处理的文件
            }
        ]
    },
    plugins:{
        //插件
    }

}

webpack打包产物是将css和js等文件打包成一个bundle.js的文件。
使用babel或css-loader就可以将es6转换为es5,和打包css了。
现在的前端网页功能丰富,JavaScript的复杂度增加和需要一大堆依赖包,所以打包后前端页面后,减少了页面的请求。

babel原理

解析:将代码字符串解析成抽象语法树。
变换:将抽象语法树。
根据变换后的抽象语法树再生成代码字符串

js模块化

主要是封装一个类型的功能,方便其他文件调用。

  • commonJs,导出是module.exports = {},导入require(‘module’);
  • es6 导出export default={}, 导入import module from ‘module’
  • AMD,CMD

commonJs和es6模块的区别

  1. 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
    对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
    原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
  2. CommonJS模块是运行时加载,ES6模块是编译时输出接口。 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
  3. CommonJS 加载的是整个模块,即将所有的接口全部加载进来,ES6 可以单独加载其中的某个接口

requireJS和webpack区别

requireJs:是一种在线“编译”模块的方案,相当于在浏览器页面上加载一个CommonJS/AMD模块格式解释器。这样浏览器就认识了define, exports,module这些东西,也就实现了模块化。
define([‘jquery’], function(jquery){})

webpack:是一个预编译模块打包的方案,你在本地直接写JS,不管是AMD/CMD/ES6风格的模块化,它都能认识,并且编译成浏览器认识的JS。webpack还可以加载图片

$(document).ready()和window.onload

  • $(document).ready()是Dom结构绘制完毕就执行,,不必等到加载完毕。 意思就是DOM树加载完毕,就执行,不必等到页面中图片或其他外部文件都加载完毕。
  • window.onload等到页面内包括图片的所有元素加载完才能执行。

前端工程化

开发–》测试—》部署

  1. 开发规范
    比如:变量驼峰式命名,按照文档开发。
  2. 模块化
    将各个功能封装,并暴露出来
  3. 组件化
    比如ui组件化,使它可以复用。
  4. 自动化
    机械的劳动就让机械去做,比如js合并用webpack

了解哪些设计模式

  • 单例模式: 保证一个类仅有一个实例,并提供一个访问他的全局访问点。例如框架中的数据库连接

单例模式分为饿汉式和懒汉式。

  饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。                

  懒汉式单例模式:在类加载时不初始化。

饿汉模式:
优点:只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。
缺点:这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。

懒汉模式:
即用到时,再去创建实例,当多个线程时可能不安全。

/**
 *饿汉式单例模式(线程安全)
 **/
public class Singleton{
    private static Singleton instance = new Singleton();
    //私有构造方法
    private Singleton(){}
    //静态方法为调用者提供单利对象 
    public static Singleton getInstance(){
        return instance;
    } 
}

 /**
  * 懒汉式(线程不安全)
  */
public class Singleton
{
    private static Singleton instance;
    private Singleton(){}
    private static Singleton getInstnce()
    {
        if(instance == null)
        {
            instance = new Singleton();
        }
        return instance;
    }
}
  • js工厂模式:通过使用工程方法而不是new关键字。将创建实例封装在一个方法中,将所有实例化的代码集中在一个位置防止代码重复。

js的工厂模式是用来创建对象的,通常一个函数传入参数,函数中new一个Object对象,并将参数加入到新对象中,最后返回这个对象。

java
简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
抽象类,具体实现类,工厂类,客户端。

普通工厂模式:
首先定义工厂接口:之后实现工厂类。在客户端加多了代码。普通工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行,调用工厂类的create去创造产品。

  • 观察者模式: 一个对象通过添加一个方法使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。例如实现实现消息推送

面向对象编程和函数式编程的区别

面向对象编程,这种编程是把问题看作由对象的属性与对象所进行的行为组成。基于对象的概念,以类作为对象的模板,以对象为中心,来思考并解决问题。
封装性:含有私有数据和公有数据,封装性能使数据更加安全依赖的就是类的特性。
继承性:子类继承父类的共有数据和函数等
多态性:不同对象执行同意函数会形成多种行为。

函数式编程,顾名思义,这种编程是以函数思维做为核心,在这种思维的角度去思考问题。这种编程最重要的基础是λ演算,接受函数当作输入和输出。
函数是一等公民:函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数。
数据不可变:因为所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
闭包和惰性求值:惰性求值需要时再求值
函数柯里化:
简单的说就是柯里化是把一个多参数函数转换为一个嵌套的一元函数的过程

const addCurried = function(x){
    return function(y){
        return x + y; 
    }
}

// 使用方法
addCurried(4)(4)
// 8

能够进行延迟计算,就像add(1)(2)一样,1比2先传入,2就会被延迟计算,在特定的场景里,有一定的应用意义。

面向过程和面向对象的区别

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

重载和重写

重载就是在一个类里面,方法名字相同,而参数不同。
重写就是子类重写父类的方法

utf-8和Unicode区别

Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。汉字可能要2-3个字节。
UTF-8就是使用变长字节表示,顾名思义,就是使用的字节数可变,这个变化是根据 Unicode 编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多。

异步,同步,阻塞,非阻塞,并发,并行

同步异步:浏览器首先发送第一个请求,等待服务器回复后,再发送第二个请求,依次类推,直到所有请求完成。浏览器发送第一个请求,可以不用等待服务器返回,可以继续发送第二个请求。

并发并行:并发是两个cpu做两件事,并行就是一个cpu做两件事,吃饭电话来了,停止吃饭去接电话。其实只是各执行一段cpu时间。

阻塞非阻塞:例如进行需要read数据,阻塞方式操作流程是:如果没有数据,则read会一直等着数据到来,才能进行后续的动作;而非阻塞则是read没有到数据后,则可以进行后续的动作,当有数据的时候再回来读取。

express,koa区别

不同点: express出现的早,koa是express原班人马打造的更小便捷的框架。
Koa 摒弃了‘被人诟病’的回调,采用 generator 的方式。Koa支持es6语法

angular.js

它使用脏检查。Angular,当 watcher 越来越多时会变得越来越慢,因为作用域内的每一次变化,所有 watcher 都要重新计算。

为什么要用单向数据流替代双向绑定?

单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,状态变化通过手动调用通知,源头易追溯,没有“暗箱操作”。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。

缺点就是由于都是“暗箱操作”,我们无法追踪局部状态的变化(虽然大部分情况下我们并不关心),潜在的行为太多也增加了出错时 debug 的难度。同时由于组件数据变化来源入口变得可能不止一个.

token加密

可用jwt来生成,根据你的规则生成相应的token

做SEO优化

seo优化其实就是让搜索引擎更快的找到你的网址

根据浏览器搜索规则,来写相应的关键词,利于搜索引擎搜索。

seo站内优化:301永久重定向,404页面,要求登录、强制使用cookies,域名好记短

seo外优化包含:论坛博客发外链

Prerender.io

Linux常用命令

  • ls 列出目录所有文件,包含以.开始的隐藏文件
  • cd 要进入的目录
  • pwd 查看当前路径
  • mkdir 新建文件
  • rm -rf test 删除test及以下文件夹

git add和git push

git add:把要提交的所有修改放到暂存区(Stage)

git commit:一次性把暂存区的所有修改提交到分支

package.json有哪些项

"name": "studylogin",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "zehongfan <fanzehongemail@163.com>",
 "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  },
 "dependencies": {
"axios": "^0.18.0"

项目升级版本发布需要做哪些工作

灰度测试,先在小范围测试

Typescript和javascript

Typescript是微软推出的javascript的超集,
TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
TypeScript 通过类型注解提供编译时的静态类型检查。代码运行前识别某些类型的问题。

微信小程序

微信小程序底层原理

web与Native两者掺杂,即Hybrid渲染。混合模式应用。

双线程通信方式

为什么要双线程 ? -> 为了管控安全,避免操作DOM。
小程序的渲染层和逻辑层分别由 2 个线程管理:渲染层的界面使用了 WebView 进行渲染,逻辑层采用 JsCore 线程运行 JS 脚本。

React

react中的受控组件,非受控组件;

受控组件有两个特点:1. 设置value值,value由state控制,2. value值一般在onChange事件中通过setState进行修改

就是不受state的状态值改变而改变,只是具有一个类似于defaultValue这样的初始值来设置状态。

淘宝前端适配方案

适配物理像素。

dpr是设备物理像素与逻辑像素的比值,告诉浏览器应该使用多少个屏幕的实际像素来绘制单个 CSS 像素。
当dpr为2时,设置viewport的scale为0.5,实现真正的1px逻辑像素代表1px物理像素。
它会动态设置scale

计稿对应的html的font-size。拿淘宝来说的,他们用的设计稿是750的,所以html的font-size就是75px,那1rem为75px,当屏幕宽度变化,他的html的font-size也变化。(屏幕宽度/750px)*75

  • 17
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值