文章目录
- 一. Http、HTML、浏览器相关
- 1. 说一下http和https
- 2. tcp三次握手,一句话概括
- 3. TCP和UDP的区别
- 4. WebSocket的实现和应用
- 5.HTTP请求的方式,HEAD方式
- 6.web quality(无障碍)
- 7. 几个很实用的BOM属性对象方法
- 8.说一下HTML5 drag api
- 9.说一下http2.0
- 10. 补充400和401、403状态码
- 11. fetch发送2次请求的原因
- 12. Cookie、sessionStorage、localStorage的区别
- 13. 说一下web worker
- 14. 对HTML语义化标签的理解
- 15. iframe是什么?有什么缺点?
- 16. Doctype作用?严格模式与混杂模式如何区分?它们有何意义?
- 17. W3C是什么
- 18.Cookie如何防范XSS攻击
- 19 Cookie和session的区别
- 20.概括RESTFUL
- 21. 讲讲viewport(视口,视区,网页的可视区域)和移动端布局
- 22. click在ios上有300ms延迟,原因及如何解决?
- 23. addEventListener参数
- 24. iframe通信,同源和不同源两种情况,多少种方法。同源我说了,根据父页面以及cookie,不同源我说了设置子域的方法。
- 25. 介绍知道的http返回的状态码
- 26. http常用请求头
- 27. 跨域请求方法
- 28. http状态码304
- 29.call()和apply()的区别
- 30. 前端优化
- 31. GET和POST的区别
- 32. HTTP支持的方法
- 33.如何画一个三角形
- 34. box-sizing之盒子模型
- 35.浏览器缓存
- 35.HTML5新增的元素、 CSS3新增
- 36.在地址栏里输入一个URL,到这个页面呈现出来,中间会发生什么?
- 37.浏览器在生成页面的时候,会生成那两颗树?
- 38. csrf和xss的网络攻击及防范
- 39. 具体有哪些请求头是跟缓存相关的
- 二. CSS相关
- 40.盒子模型
- 42. 画一条0.5px的线
- 43.link标签和import标签的区别
- 44. transition、animation的区别
- 45.:link、:hover、:active和:visited的区别?
- 46. Flex布局
- 47. 说一下块元素和行元素
- 48. 多行元素的文本省略号
- 49. css动画和js动画的差异性
- 50. visibility=hidden, opacity=0,display:none
- 51. 双边距重叠问题(外边距折叠)
- 52. BFC(块级格式化上下文,用于清除浮动,防止margin重叠等)
- 53. position属性 比较
- 54.清除浮动
- 55. CSS选择器有哪些,优先级呢
- 56.css3新特性
- 57. 怎么样让一个元素消失
- 58. 如何实现图片在某个容器中居中的?
- 59.三栏布局的实现方式,尽可能多写,浮动布局时,三个div的生成顺序有没有影响
- 60. css预处理器有什么
- 61.相对布局和绝对布局,position:relative和absolute。
- 62. calc属性
- 63 .z-index的定位方法
- 64. inline-block、inline和block的区别;为什么img是inline还可以设置宽高
- 65. 了解重绘和重排吗,知道怎么去减少重绘和重排吗,让文档脱离文档流有哪些方法
- 66. rem和em的使用
- 67. transform:rotate()3D
- 68. 利用css制作立方体
- 三. js相关
- 1. 补充get和post请求在缓存方面的区别
- 2. 普通函数和箭头函数的this指向区别
- 3, null 和undefined的区别
- 3.let和var的区别
- 3. 说一下闭包
- 4. 说一下类的创建和继承
- 5.webpack配置文件
- 6. ajax使用
- 7.说一下如何解决异步回调地狱
- 8. 说说前端中的事件流
- 9. 如何让事件先冒泡后捕获
- 10. 说一下事件委托
- 11.说一下图片的懒加载和预加载
- 12. mouseover和mouseenter的区别
- 13. js的new操作符做了哪些事情
- 14. js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
- 15. js拖拽功能的实现
- 16. 高阶函数
- 17. 高阶函数柯里化
- 18. 发布订阅者模式
- 19.介绍一下promise,及其底层如何实现
- 17. es6的常用特性
- 18. setInterval和setTimeout的区别
- 19. 在vue脚手架中监听ref的事件是用$on("事件", callback)
- 20. for in 和for of的区别
- 21. 执行上下文和作用域链
- 22. 原型的理解
- 前端工程化
- 23. css设置高度为宽度的2倍
- 24. 为什么有了forEach,又出现了for in、for of
- 25. HTTPS相比HTTP请求速度慢,为什么,怎么加速
- 26. reduce()方法
- 27. 为什么for循环代码块中含有异步函数时只能使用let,使用var时会发现结果不对
- 28.Object的一些属性
- 29.为普通对象obj实现可迭代协议
- 30.v-show和v-if的区别
- 31. JWT是什么
- 32. 什么是SPA、SSR
- 33.export default 和export的区别
- 34. call、aplly、bind底层实现
- 35. window.load和document.ready之间的区别
- 36.描述一下浏览器缓存、服务器缓存、CDN缓存
- 37. js中严格模式和正常模式的区别
- 38. 什么是值类型,什么是引用类型
- 39.描述一下前端的堆和栈
- 40. 描述一下移动端click、touch和tap的区别
- 41.经典面试题,柯里化实现传递任意个参数以及传递多少次参数进行求和
- 42. css的grid布局
- .....持续更新中
一. Http、HTML、浏览器相关
1. 说一下http和https
(1)http和https的基本概念
http: 超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
https: 是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
https协议的主要作用是:建立一个信息安全通道,来确保数组的传输,确保网站的真实性。
区别:http传输的数据都是未加密的,也就是明文的,网景公司设置了SSL协议来对http协议传输的数据进行加密处理,简单来说https协议是由http和ssl协议构建的可进行加密传输和身份认证的网络协议,比http协议的安全性更高。
2. tcp三次握手,一句话概括
tcp采用三次握手建立tcp连接,tcp连接建立之后就可以在这个连接上传输数据了。
3. TCP和UDP的区别
(1)TCP是面向连接的,udp是无连接的即发送数据前不需要先建立链接。
(2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。 并且因为tcp可靠,面向连接,不会丢失数据因此适合大数据量的交换。
(3)TCP是面向字节流,UDP面向报文,并且网络出现拥塞不会使得发送速率降低(因此会出现丢包,对实时的应用比如IP电话和视频会议等)。
(4)TCP只能是1对1的,UDP支持1对1,1对多。
(5)TCP的首部较大为20字节,而UDP只有8字节。
(6)TCP是面向连接的可靠性传输,而UDP是不可靠的。
4. WebSocket的实现和应用
WebSocket是HTML5中的协议,支持持久连续,
websocket建立的是长连接,在一个会话中一直保持连接;而ajax是短连接,数据发送和接受完成后就会断开连接。对于服务器与客户端的双向通信,WebSocket简直是不二之选.但是,很大一部分AJAX的使用场景仍然是传统的请求-响应形式,比如获取json数据、post表单之类。另外还有一种情况,也就是传输大文件、图片、媒体流的时候,最好还是老老实实用HTTP来传。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。多了下面2个属性:
Upgrade:webSocket;
Connection:Upgrade;
//告诉服务器发送的是websocket
websocket:
var monitor = new WebSocket(“ws://”+ip+path)
onOpen()、onMessage()、onClose()
客户端使用
在客户端中使用很简单,只需要开启一个 WebSocket 服务并且实现监听服务器发送的消息即可。
// 开启WebSocket服务
var ws = new WebSocket("ws://127.0.0.1:8001");
//进行监听连接成功,触发,当与服务器端建立websocket连接成功之后就会执行
ws.onopen = function (data) {
// 发送信息
ws.send("hello World");
};
ws.onmessage = function (data) {
console.log(data.data); // 接收信息
};
//当一个 WebSocket 连接被关闭时触发。也可以通过 onclose 属性来设置。
ws.onclose = function (data) {
console.log("关闭", data); //关闭
};
如上代码,我们可以使用 ws.send()发送数据,也可以适用 ws.onmessage (其实也可以使用ws.addEventListener(“message”,()=>{})进行监听)监听服务器端发送的信息数据,实现简单的实时双向传输功能。
WebSocket.close()
在服务器端进行关闭当前链接。
两个可选参数:
- code: 可选,一个数字状态码,它解释了连接关闭的原因。如果没有传这个参数,默认使用 1005
- reason: 可选,可读的字符串,它解释了连接关闭的原因。
返回的状态码
*WebSockets 的CloseEvent 会在连接关闭时发送给使用 WebSockets 的客户端。它在 WebSocket 对象的 onclose 事件监听器中使用。服务端发送的关闭码,以下为已分配的状态码。正常关闭是返回状态码1000
5.HTTP请求的方式,HEAD方式
head:类似于get请求,只不过返回的响应中没有具体的内容,用户获取报文头
options:允许客户端查看服务器的性能,比如说服务器支持的请求方式等等。
简而言之,OPTIONS请求方法的主要用途有两个:
1、获取服务器支持的HTTP请求方法;
2、用来检查服务器的性能。
6.web quality(无障碍)
即网页品质,就是指为了满足那些残障人士而设计的网站,比如网页可以调节字体大小,图片如果发生错误显示不出可以使用alt属性来描述图片
7. 几个很实用的BOM属性对象方法
Bom是浏览器对象。有哪些常用的Bom属性呢?
(1)location对象
Location 对象是 Window 对象的一个部分,可通过 window.location 属性来访问。
location.href-- 返回或设置当前文档的URL
location.search – 返回URL中的查询字符串部分。例如 http://www.dreamdu.com/dreamdu.php?id=5&name=dreamdu 返回包括(?)后面的内容?id=5&name=dreamdu
location.hash – 返回URL#后面的内容,如果没有#,返回空
location.host – 返回URL中的域名部分,例如www.dreamdu.com
location.hostname – 返回URL中的主域名部分,例如dreamdu.com
location.pathname – 返回URL的域名后的部分。例如 http://www.dreamdu.com/xhtml/ 返回/xhtml/
location.port – 返回URL中的端口部分。例如 http://www.dreamdu.com:8080/xhtml/ 返回8080
location.protocol – 返回URL中的协议部分。例如 http://www.dreamdu.com:8080/xhtml/ 返回(//)前面的内容http:
location.assign – 设置当前文档的URL
location.replace() – 设置当前文档的URL,并且在history对象的地址列表中移除这个URL location.replace(url);
location.reload() – 重载当前页面
(2)history对象
History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问
history.go() – 前进或后退指定的页面数或指定的url, history.go(num/url);
history.back() – 后退一页
history.forward() – 前进一页
(3)Navigator对象
navigator.userAgent – 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)
navigator.cookieEnabled – 返回浏览器是否支持(启用)cookie
8.说一下HTML5 drag api
拖放(Drag 和 Drop)是很常见的特性。它指的是您抓取某物并拖入不同的位置。
拖放是 HTML5 标准的组成部分:任何元素都是可拖放的。
dragstart:事件主体是被拖放元素,在开始拖放被拖放元素时触发。
darg:事件主体是被拖放元素,在正在拖放被拖放元素时触发。
dragenter:事件主体是目标元素,在被拖放元素进入某元素时触发。
dragover:事件主体是目标元素,在被拖放在某元素内移动时触发。
dragleave:事件主体是目标元素,在被拖放元素移出目标元素是触发。
drop:事件主体是目标元素,在目标元素完全接受被拖放元素时触发。
dragend:事件主体是被拖放元素,在整个拖放操作结束时触发
<!DOCTYPE HTML>
<html>
<head>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>
</head>
<body>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<img id="drag1" src="img_logo.gif" draggable="true" ondragstart="drag(event)" width="336" height="69">
</body>
</html>
9.说一下http2.0
首先补充一下,http和https的区别,相比于http,https是基于ssl加密的http协议
简要概括:http2.0是基于1999年发布的http1.0之后的首次更新。
提升访问速度(可以对于,请求资源所需时间更少,访问速度更快,相比http1.0)
允许多路复用:多路复用允许同时通过单一的HTTP/2连接发送多重请求-响应信息。改善了:在http1.1中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制(连接数量),超过限制会被阻塞。
二进制分帧:HTTP2.0会将所有的传输信息分割为更小的信息或者帧,并对他们进行二进制编码
首部压缩
服务器端推送
10. 补充400和401、403状态码
(1)400状态码:请求无效
产生原因:
前端提交数据的字段名称和字段类型与后台的实体没有保持一致
前端提交到后台的数据应该是json字符串类型,但是前端没有将对象JSON.stringify转化成字符串。
解决方法:
对照字段的名称,保持一致性
将obj对象通过JSON.stringify实现序列化
(2)401状态码:当前请求需要用户验证
(3)403状态码:服务器已经得到请求,但是拒绝执行
11. fetch发送2次请求的原因
fetch发送post请求的时候,总是发送2次,第一次状态码是204,第二次才成功?
原因很简单,因为你用fetch的post请求的时候,导致fetch 第一次发送了一个Options请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求。
12. Cookie、sessionStorage、localStorage的区别
共同点:都是保存在浏览器端,并且是同源的
Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。 (key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)
localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)
补充说明一下cookie的作用:
保存用户登录状态。例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。 cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个 月、一年等。
跟踪用户行为。例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。
13. 说一下web worker
JavaScript语言采用的是单线程模型(同一时间只能做一件事),也就是说,所有任务只能在一个线程上完成,一次只能做一件事。
web worker 对象的出现 ,就是为了javascript创造多线程环境(同一时间能做多件事),语序主线程创建worker线程,将一些任务分配给后者运行。在主线程运行的同时,worker线程(外部的j其他s文件)在后台运行,两者互不干扰。生成一个专用worker
创建一个新的worker很简单。你需要做的是调用Worker()
的构造器,指定一个脚本的URI来执行worker线程(main.js):
var myWorker = new Worker('worker.js');
你可以通过postMessage()
方法(用于想worker线程发送数据)和onmessage
()用于接收传递过来的数据事件处理函数触发workers的方法。当你想要向一个worker发送消息时,你只需要这样做(main.js):
first.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
second.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
在worker中接收到消息后,我们可以写这样一个事件处理函数代码作为响应(worker.js):
onmessage = function(e) {
console.log('Message received from main script');
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
console.log('Posting message back to main script');
postMessage(workerResult);
}
onmessage处理函数允许我们在任何时刻,一旦接收到消息就可以执行一些代码,代码中消息本身作为事件的data属性进行使用。这里我们简单的对这2个数字作乘法处理并再次使用postMessage()方法,将结果回传给主线程。
终止worker
如果你需要从主线程中立刻终止一个运行中的worker,可以调用worker的terminate
方法:
myWorker.terminate();
14. 对HTML语义化标签的理解
HTML5语义化标签是指正确的标签包含了正确的内容,结构良好,便于阅读,比如nav表示导航条,类似的还有article、header、footer等等标签。
15. iframe是什么?有什么缺点?
定义:iframe 元素会创建包含另外一个文档的内联框架(即行内框架),也就是可以用src=“”元素引入其他页面在一个框内显示
提示:可以将提示文字放在之间,来提示某些不支持iframe的浏览器
缺点:
会阻塞主页面的onload事件
搜索引擎无法解读这种页面,不利于SEO(网站优化)
iframe和主页面共享连接池,而浏览器对相同区域有限制所以会影响性能。
16. Doctype作用?严格模式与混杂模式如何区分?它们有何意义?
DOCTYPE是document type (文档类型) 的缩写。声明位于文档的最前面,处于标签之前,它不是html标签。主要作用是告诉浏览器的解析器使用哪种HTML规范或者XHTML规范来解析页面。
不存在或者形式不正确会导致HTML或XHTML文档以混杂模式呈现,就是把如何渲染html页面的权利交给了浏览器,有多少种浏览器就有多少种展示方式。因此要提高浏览器兼容性就必须重视Doctype声明于文档最前面,告诉浏览器以何种方式来渲染页面,这里有两种模式,严格模式和混杂模式。
**严格模式:**又称标准模式,是指浏览器按照W3C标准来解析代码,呈现页面
**混杂模式:**又称为怪异模式或者兼容模式,是指浏览器按照自己的方式来解析代码,使用一种比较宽松的向后兼容的方式来显示页面。
17. W3C是什么
w3c是万维网联盟,是一个致力于改善网络的组织,其中w3c是www的基本标准,并且对web进行标准化,从而来促进web之间的合作。
w3c最主要的工作就是对网络规范的规定,并且这些规范对通信协议进行了详细的描述,其中就包括对HTML5做出了详细的规范,目前HTML5的规范已经完成了,但是并不能算w3c的标准,html5是有完整的编程环境和跨平台性,同时为了减少浏览器的碎片,目前w3c开始对互操作性进行标准化。
在w3c中,网页是有三部分组成,包括结构,表现和行为。
18.Cookie如何防范XSS攻击
参考回答:
XSS(跨站脚本攻击)是指攻击者在返回的HTML中嵌入javascript脚本,为了减轻这些攻击,需要在HTTP头部配上,
set-cookie:http-only这个属性可以防止XSS,它会禁止javascript脚本来访问cookie。
secure 这个属性告诉浏览器仅在请求为https的时候发送cookie。
//设置cookie
response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly");
//设置多个cookie
response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly");
response.addHeader("Set-Cookie", "timeout=30; Path=/test; HttpOnly");
//设置https的cookie
response.addHeader("Set-Cookie", "uid=112; Path=/; Secure; HttpOnly");
19 Cookie和session的区别
当客户端第一次向服务器端发送请求时,服务器端会创建一个session,同时会生成该session对应的sessionId,并传回客户端,以后当客户端再次发送请求时,都会带上这个sessionId,这样服务器端就知道此客户进程的状态了。其实在前端,sessionId可存放在cookie中,可通过document.cookie来设置cookie。
cookie和session都是用来跟踪浏览器用户身份的会话方式
Cookie和session都可用来存储用户信息,cookie存放于客户端,session存放于服务器端,因为cookie存放于客户端有可能被窃取,所以cookie一般用来存放不敏感的信息,比如用户设置的网站主题,敏感的信息用session存储,比如用户的登陆信息,session可以存放于文件,数据库,内存中都可以,cookie可以服务器端响应的时候设置,也可以客户端通过JS设置cookie会在请求时在http首部发送给客户端,cookie一般在客户端有大小限制,一般为4K。
客户端每次请求服务器的时候会发送 当前会话的session_id,服务器根据当前session_id判断相应的用户数据标志,以确定用户是否登录,或具有某种权限。
// 实现session功能
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}));
//客户端js设置cookie
document.cookie = "name="+userName;
//服务器端设置cookie
var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
res.setHeader('status', '200 OK');
res.setHeader('Set-Cookie', 'isVisit=true;domain=.yourdomain.com;path=/;max-age=1000');
res.write('Hello World');
res.end();
}).listen(8888);
console.log('running localhost:8888')
//浏览器端拿到cookie
document.cookie
HTTP是一种无状态的协议,为了分辨链接是谁发起的,需自己去解决这个问题。不然有些情况下即使是同一个网站每打开一个页面也都要登录一下。而Session和Cookie就是为解决这个问题而提出来的两个机制。
1、cookie 和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。
2、两个都可以用来存私密的东西,同样也都有有效期的说法,区别在于session是放在服务器上的,过期与否取决于服务期的设定,cookie是存在客户端的,过去与否可以在cookie生成的时候设置进去。
20.概括RESTFUL
(Representational State Transfer)Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
21. 讲讲viewport(视口,视区,网页的可视区域)和移动端布局
简要介绍:前端开发中,静态网页通常需要适应不同分辨率的设备,常用的自适应解决方案包括媒体查询、百分比、rem和vw/vh等
像素是网页布局的基础,一个像素表示了计算机屏幕所能显示的最小区域,像素分为两种类型:css像素和物理像素。
视口:
广义的视口,是指浏览器显示内容的屏幕区域,狭义的视口包括了布局视口、视觉视口和理想视口。
媒体查询+rem实现响应式页面
rem指的是元素大小以html根元素的字体大小来成倍增大的,而em是根据自己上一层父元素的字体大小来改变自己的大小
在前面我们说到,不同端的设备下,在css文件中,1px所表示的物理像素的大小是不同的,因此通过一套样式,是无法实现各端的自适应。由此我们联想:
使用@media媒体查询可以针对不同的媒体类型定义不同的样式,特别是响应式页面,可以针对不同屏幕的大小,编写多套样式,从而达到自适应的效果。举例来说:
@media screen and (max-width: 960px){
html{
background-color:#FF6699
font-size:100px; //设置html的字体大小,之后在html中的一些标签中就可以使用rem为单位根据html文字大小动态调整自己的长度或者宽度大小了。
}
}
span {
width: 2rem; //这里等价于width长为200px
}
@media screen and (max-width: 768px){
body{
background-color:#00FF66;
}
}
@media screen and (max-width: 550px){
body{
background-color:#6633FF;
}
}
@media screen and (max-width: 320px){
body{
background-color:#FFFF00;
}
}
但是媒体查询的缺点也很明显,如果在浏览器大小改变时,需要改变的样式太多,那么多套样式代码会很繁琐。
采用Bootstrap框架实现自适应响应式布局
也就是调用bootstrap框架里面自带的类(比如.container)就能够实现自适应
总结
其中单独制作移动端页面一般采用前三种,但最多的是使用flex和rem混合布局;
响应式布局可以使用媒体查询和rem搭配,或者直接使用Bootstrap框架进行配置页面。
22. click在ios上有300ms延迟,原因及如何解决?
原因:因为有得时候是因为双击屏幕产生缩放,当用户点击屏幕后,手机会等待300ms左右判断用户是否还会点击进行页面放大,如果没有就是普通的点击事件,否则就是方法操作。
(1)粗暴型,禁用缩放 user-scalable=“no”
user-scalable=no表明这个页面不可缩放,也就是浏览器禁用的双击缩放事件并且同时会去掉300ms点击延迟。 但这个方案也有缺点,就是完全禁用了双击缩放,当我们需要放大文字或者图片时无法满足我们的需求。(2)利用FastClick,其原理是:
FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即触发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
使用fastclick需要先引入fastclick模块,npm install fastclick,然后在js文件中引入后再写js代码如下:
js版本
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
jQuery版本
$(function() {
FastClick.attach(document.body);
});
Common JS模块的系统方式
var attachFastClick = require('fastclick');
attachFastClick(document.body);
注意:在移动端触摸屏幕事件是touchStart touchmove、touchend
在pc端是鼠标事件:mousedown、mouseup
23. addEventListener参数
addEventListener(event, function, useCapture)
其中,event指定事件名;function指定要事件触发时执行的函数;useCapture指定事件是否在捕获或冒泡阶段执行。
若useCapture为true,则会进行冒泡,子父元素都有click事件,则会先触发自己的父元素的点击事件(因为冒泡了)
24. iframe通信,同源和不同源两种情况,多少种方法。同源我说了,根据父页面以及cookie,不同源我说了设置子域的方法。
链接:https://www.nowcoder.com/questionTerminal/98fdf5c3530e49959bd29309d17bbb3c?orderByHotValue=1&page=1&onlyReference=false
来源:牛客
iframe通信,同源和不同源两种情况
同域:即父子页面相互调用
父调用子页面用contentWindow属性
子调用父页面用parent属性
一、父页面调用子页面
1、先得到子页面的document
document.getElementById(‘FrameId’).contentWindow.document
contentWindow属性用法:
2、得到子页面的window
document.getElementById(‘FrameId’).contentWindow.window
重载子页面:document.getElementById(‘FrameId’).contentWindow.window.location.reload(true);
或者 $(’#FrameId’).attr(‘src’,’…/list’);
3、得到子页面的的变量
doucment. iframe的name属性值 . 子页面变量名称 (document.frameName.temp)
二、子页面调用父页面
1、父页面document : window.parent.document
2、获得父页面变量 : parent.变量名称
3、调用事件 : window.parent.XXX();
跨域:
主域:由两个或两个以上的字母构成,中间由点号隔开,整个域名只有1个点号
csdn.net
子域:是在主域名之下的域名,域名内容会有多个点号
未跨主域,跨子域
两个域的js文件中设置document.domain=主域名 即可
跨主域
location.hash
(B操作A)
1、动态改变location.hash,iframe不会重载
2、无论跨域与否,iframe内可以获取自己的location.hash
3、只要域名相同就能通信,即使ABC三层嵌套
25. 介绍知道的http返回的状态码
http状态码有哪些,这也是一个很高频的面试问题。
一般大家都知道404页面不存在,500服务器错误,301重定向,302临时重定向,200ok,401未授权、404资源未找到。
可以重点介绍三个状态码及相关的知识,他们分别是304协商缓存,101协议升级,以及307hsts跳转。
100 Continue 继续。客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
200 OK 请求成功。一般用于GET与POST请求
204:请求被受理但没有资源可以返回
206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
**302 Found 临时移动。**与301类似。但资源只是临时被移动。客户端应继续使用原有URI
301比较常用的场景是使用域名跳转。302用来做临时跳转 比如未登陆的用户访问用户中心重定向到登录页面。
304协商缓存
浏览器缓存分为强缓存和协商缓存,优先读取强缓存。
如果服务器端响应304,则是告知客户端资源直接从缓存中取就可以,
如果返回200,则是向客户端返回的是新的资源
对于强缓存而言,是浏览器优先读取的,如果不能命中强缓存,则就向服务器端发送请求,再检查是否命中协商缓存。
协商缓存也就是对于有协商缓存策略的服务器来说,当浏览器端向服务器端请求资源文件时,会一同把请求和资源标识(比如etag)发送到服务器端,服务器端会对比资源是否过期(这里是对比资源标识是否一致),如果一致这说明是资源未过期,服务器端返回304状态码标识协商缓存,意思是让客户端直接读取自己缓存中的资源就可以;如果资源过期了,则返回状态码200,并把最新资源和最新标识进行发送,客户端得到最新数据后会进行更新缓存。
305 Use Proxy 使用代理。所请求的资源必须通过代理访问
400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留,将来使用
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
500 Internal Server Error 服务器内部错误,无法完成请求 请求报文语法有误,服务器无法识别(浏览器端没有根据服务器端的参数进行请求,可能缺少参数)
503 Service Unavailable 服务器正忙 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
26. http常用请求头
设置请求头,使用: xhr.setRequestHeader();
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));
注意: xhr.setRequestHeader() 必须在xhr.open()之后, xhr.send()之前调用;
协议头 | 说明 |
---|---|
Accept | 可接受的响应内容类型(Content-Types)。 |
Accept-Charset | 可接受的字符集 |
Accept-Encoding | 可接受的响应内容的编码方式。 |
Accept-Language | 可接受的响应内容语言列表。 |
Accept-Datetime | 可接受的按照时间来表示的响应内容版本 |
Authorization | 用于表示HTTP协议中需要认证资源的认证信息 |
Cache-Control | 用来指定当前的请求/回复中的,是否使用缓存机制。 |
Connection | 客户端(浏览器)想要优先使用的连接类型 |
Cookie | 由之前服务器通过Set-Cookie(见下文)设置的一个HTTP协议Cookie |
Content-Length | 以8进制表示的请求体的长度 |
Content-MD5 | 请求体的内容的二进制 MD5 散列值(数字签名),以 Base64 编码的结果 |
Content-Type | 请求体的MIME类型 (用于POST和PUT请求中) |
Date | 发送该消息的日期和时间(以RFC 7231中定义的"HTTP日期"格式来发送) |
Expect | 表示客户端要求服务器做出特定的行为 |
From | 发起此请求的用户的邮件地址 |
Host | 表示服务器的域名以及服务器所监听的端口号。如果所请求的端口是对应的服务的标准端口(80),则端口号可以省略。 |
If-Match | 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要用于像 PUT 这样的方法中,仅当从用户上次更新某个资源后,该资源未被修改的情况下,才更新该资源。 |
If-Modified-Since | 允许在对应的资源未被修改的情况下返回304未修改 |
If-None-Match | 允许在对应的内容未被修改的情况下返回304未修改( 304 Not Modified ),参考 超文本传输协议 的实体标记 |
If-Range | 如果该实体未被修改过,则向返回所缺少的那一个或多个部分。否则,返回整个新的实体 |
If-Unmodified-Since | 仅当该实体自某个特定时间以来未被修改的情况下,才发送回应。 |
Max-Forwards | 限制该消息可被代理及网关转发的次数。 |
Origin | 发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个Access-Control-Allow-Origin的消息头,表示访问控制所允许的来源)。 |
Pragma | 与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生。 |
Proxy-Authorization | 用于向代理进行认证的认证信息。 |
Range | 表示请求某个实体的一部分,字节偏移以0开始。 |
Referer | 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer其实是Referrer这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer了。 |
TE | 浏览器预期接受的传输时的编码方式:可使用回应协议头Transfer-Encoding中的值(还可以使用"trailers"表示数据传输时的分块方式)用来表示浏览器希望在最后一个大小为0的块之后还接收到一些额外的字段。 |
User-Agent | 浏览器的身份标识字符串 |
Upgrade | 要求服务器升级到一个高版本协议。 |
Via | 告诉服务器,这个请求是由哪些代理发出的。 |
Warning | 一个一般性的警告,表示在实体内容体中可能存在错误。 |
27. 跨域请求方法
如果跨域请求资源,就会遇到Cross-Origin Resource Sharing(CORS)跨域资源共享问题
跨域,简要来说就是一个服务器上的文件,要访问另一个服务器上的文件是不被允许的是跨域的。
浏览器在发送跨域请求的时候,会有哪些过程
如果是简单请求,浏览器会先发送请求,然后判断服务器返的返回头中是否支持跨域请求,否则就会抛出跨域异常;
如果是非简单请求,浏览器会先发出OPTIONS请求方法的检测命令,判断服务器是否支持跨域请求,如果支持则发送真正的请求,如果不支持则抛出跨域异常,因此一个非简单请求每次会发送两个请求,后面跨域解决方案会讲到缓存OPTIONS预检请求
跨域解决方案
方案1: 禁用浏览器跨域校验,即允许跨域访问,(这种方案不可取,不可能让所有的浏览器设置允许跨域访问)
方案2: 动态创建script标签来实现跨域请求,
jsonp的缺点:http请求只支持get方法;jsonp在调用失败的时候不会返回各种HTTP状态码
jsonp则是动态添加
前端:
<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>跨域测试</title>
<script src="js/jquery-1.7.2.js"></script>
<script>
//回调函数
function showData (result) {
var data = JSON.stringify(result); //json对象转成字符串
$("#text").val(data);
}
$(document).ready(function () {
$("#btn").click(function () {
//向头部输入一个脚本,该脚本发起一个跨域请求
$("head").append("<script src='http://localhost:9090/student?callback=showData'><\/script>");
});
});
</script>
</head>
<body>
<input id="btn" type="button" value="跨域获取数据" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
后端:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//数据
List<Student> studentList = getStudentList();
JSONArray jsonArray = JSONArray.fromObject(studentList);
String result = jsonArray.toString();
//前端传过来的回调函数名称
String callback = request.getParameter("callback");
//用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
result = callback + "(" + result + ")";
response.getWriter().write(result);
}
方案3 使用jquery的jsonp方式跨域请求
jsonp是一种跨域请求方式。主要原理是利用了script标签可以跨域请求的特点,由其src属性发送请求到服务器,服务器返回js代码,网页端接受响应,然后就直接执行了,这和通过script标签引用外部文件的原理是一样的。
json是一种数据交换的格式。
jsonp的实现方式其实就是
服务端代码不变,js代码如下:最简单的方式,只需配置ajax请求的dataType:‘jsonp’,就可以发起一个跨域请求。jsonp指定服务器返回的数据类型为jsonp格式,jsonp是不支持POST方式的,就算指定成POST方式,会自动转为GET方式;
这里的success就跟上面的showData一样,如果有success函数则默认success()作为回调函数
<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>跨域测试</title>
<script src="js/jquery-1.7.2.js"></script>
<script>
$(document).ready(function () {
$("#btn").click(function () {
$.ajax({
url: "http://localhost:9090/student",
type: "POST", //post请求方式
dataType: "jsonp",
jsonp: "callback",
success: function (data) {
var result = JSON.stringify(data);
$("#text").val(result);
}
});
});
});
</script>
</head>
<body>
<input id="btn" type="button" value="跨域获取数据" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
方案4 服务端解决跨域问题
通过在response对象中添加响应头,告诉浏览器允许跨域访问,* 号代表允许所有的请求域名,所有的请求方法跨域访问
前端在发送请求前需添加请求头告知服务器端需要使用origin
const app = express()
app.use(...)
app.setRequestHeader("origin","...");
后端返回数据前需添加响应头:
resp.addHeader("Access-Control-Allow-Origin", *);
28. http状态码304
304为协商缓存
浏览器缓存分为强缓存和协商缓存,优先读取强制缓存。:
服务端先判断所相应资源是否已经修改过,如果没有修改,则服务器端响应304,则是告知客户端资源直接从缓存中取就可以,
如果资源修改了,则返回200,且服务器同时向客户端返回的是新的修改后的资源
29.call()和apply()的区别
在js中,call和apply都是为了改变某个函数内部的this指向的。除了他们,还有bind()方法
我们可以通过改变this指向来调用其他对象的方法,而不用自己构造一些方法。
call和apply都属于Function.prototype的一个方法,所以每一个构造对象Function的实例对象都会有call和apply方法,这两个方法的作用是相同的(都是为了改变this指向),只是使用方式不同而已。
区别:传递的参数不同,bind()是重新创建一个函数,而不会立即执行
call接受的从第二个参数起是一个参数列表,而apply接受的是一个参数数组。对于bind()函数,他会改变this指向,并且会重新创建一个函数,当想要调用的时候就可以调用了()这些参数(如果有的话)作为 bind() 的第二个参数跟在 this 后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。也可以不传,不传参的话就是使用的是默认列表。
dog、cat是个对象
dog.func.call(cat,arg1,arg2), //参数要按顺序传递,当你的参数是明确知道数量时用call,而不确定时用apply
dog.func.apply(cat,[arg1,arg2])
dog.func.bind(cat,arg1)
对于bind()
function fn(a, b, c) {
return a + b + c;
}
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30, 40); // 60
30. 前端优化
优化 DOM
* 删除不必要的代码和注释包括空格,尽量做到最小化文件。
* 可以利用 GZIP 压缩文件。
* 结合 HTTP 缓存文件。
利用图片懒加载技术
优化 CSSOM
首先,DOM 和 CSSOM 通常是并行构建的,所以 CSS 加载不会阻塞 DOM 的解析。
然而,由于 Render Tree 是依赖于 DOM Tree 和 CSSOM Tree 的,
所以他必须等待到 CSSOM Tree 构建完成,也就是 CSS 资源加载完成(或者 CSS 资源加载失败)后,才能开始渲染。因此,CSS 加载会阻塞 Dom 的渲染。
由此可见,对于 CSSOM 缩小、压缩以及缓存同样重要,我们可以从这方面考虑去优化。
* 减少关键 CSS 元素数量
* 当我们声明样式表时,请密切关注媒体查询的类型,它们极大地影响了 CRP 的性能 。
优化 JavaScript
当浏览器遇到 script 标记时,会阻止解析器继续操作,直到 CSSOM 构建完毕,JavaScript 才会运行并继续完成 DOM 构建过程。
写算法时尽量时间复杂度低一些
*async: 当我们在 script 标记添加 async 属性以后,浏览器遇到这个 script 标记时会继续解析 DOM,同时脚本也不会被 CSSOM 阻止,即不会阻止 CRP。
* defer: 与 async 的区别在于,脚本需要等到文档解析后( DOMContentLoaded 事件前)执行,而 async 允许脚本在文档解析时位于后台运行(两者下载的过程不会阻塞 DOM,但执行会)。
* 当我们的脚本不会修改 DOM 或 CSSOM 时,推荐使用 async 。
* 预加载 —— preload & prefetch 。
* DNS 预解析 —— dns-prefetch 。
小结
加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。
渲染:JS/CSS优化,加载顺序,服务端渲染,pipeline。
按需引入模块:比如在引入vue中的一些模块时,可以引入自己需要的模块,而不需要把vue所包含的所有模块全引入
二、渲染完成后的页面交互优化:
防抖(debounce)/节流(throttle)
防抖(debounce)
即在第一次触发事件时,不立即执行函数,而是给出一个期限值(这里使用定时器实现)等定时器结束后再执行想要的操作,然后
输入搜索时,可以用防抖debounce等优化方式,减少http请求; //原理是当输入框监听到输入时都会先清除定时器,然后重新创建并触发定时器,当计时器结束时才会发起http请求
这里以输入框进行自动搜索时,进行防抖操作
function debounce(fn,time) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn();//arguments是传入的参数
}, time);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi,5000)); // 防抖
节流(throttle)
节流函数:只允许一个函数在N秒内执行一次(控制高频时间执行次数)。
如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
实现 这里借助setTimeout
来做一个简单的实现,加上一个状态位valid
来表示当前函数是否处于工作状态:
let valid = true
return function() {
if(!valid){
//休息时间 暂不接客
return false
}
// 工作时间,执行函数并且在间隔期内把状态位设为无效
valid = false
setTimeout(() => {
fn()
valid = true;
}, delay)
}
}
/* 请注意,节流函数并不止上面这种实现方案,
例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
*/
// 以下照旧
function showTop () {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}
window.onscroll = throttle(showTop,1000)
31. GET和POST的区别
GET和POST是HTTP请求的两种基本方法;
-
GET在浏览器回退时是无害的,而POST会再次提交请求。( 这句话简单理解就是,get会将请求参数放在请求的url中,回退操作实际上浏览器会从之前的缓存中拿结果;post每次调用都会创建新的资源。
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求在URL中传送的参数是有长度限制的,而POST没有。
-
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
-
GET请求只能进行url编码(get请求的参数只能是ascll,不是的话就需要进行url编码转化为ascll),而POST支持多种编码方式(UTF-8)。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
最直观的区别是GET参数通过URL传递,POST放在Request body中。
-
GET产生一个TCP数据包;POST产生两个TCP数据包。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
32. HTTP支持的方法
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT
33.如何画一个三角形
三角形原理:边框的均分原理,会均分矩形的直角
div {
width: 0;
height: 0;
border: 10px solid blue;
border-top-color: pink;
}
34. box-sizing之盒子模型
盒子模型有两种,content-box、border-box
box-sizing
属性被指定为下面列表中的关键字。
-
content-box
默认值,标准盒子模型。
width
与height
只包括内容的宽和高, 不包括边框(border),内边距(padding),外边距(margin)。注意: 内边距、边框和外边距都在这个盒子的外部。 比如说,.box {width: 350px; border: 10px solid black;}
在浏览器中的渲染的实际宽度将是 370px。 尺寸计算公式:width
= 内容的宽度height
= 内容的高度宽度和高度的计算值都不包含内容的边框(border)和内边距(padding)。
-
border-box
width
和height
属性包括内容,内边距和边框,但不包括外边距。也称为IE盒模型。注意,填充和边框将在盒子内 , 例如,.box {width: 350px; border: 10px solid black;}
导致在浏览器中呈现的宽度为350px的盒子。内容框不能为负,并且被分配到0,使得不可能使用border-box使元素消失。 尺寸计算公式:width
= border + padding + 内容的宽度height
= border + padding + 内容的高度 -
标准盒子模型:
IE盒子模型:
35.浏览器缓存
缓存分为两种:强缓存和协商缓存,根据响应头的http状态码来决定。
对于强缓存而言,是浏览器优先读取的,如果不能命中强缓存,则就向服务器端发送请求,再检查是否命中协商缓存。
协商缓存也就是对于有协商缓存策略的服务器来说,当浏览器端向服务器端请求资源文件时,会一同把请求和资源标识(比如etag)发送到服务器端,服务器端会对比资源是否过期(这里是对比资源标识是否一致),如果一致这说明是资源未过期,服务器端返回304状态码标识协商缓存,意思是让客户端直接读取自己缓存中的资源就可以;如果资源过期了,则返回状态码200,并把最新资源和最新标识进行发送,客户端得到最新数据后会进行更新缓存。
强缓存相关字段有expires(过期),cache-control。如果cache-control与expires同时存在的话,cache-control的优先级高于expires。
缓存相关字段有Last-Modified/If-Modified-Since,Etag/If-None-Match
1、强缓存
强缓存是利用 Expires 和 Cache-Control 这2个字段来控制的,控制资源缓存的时间,在有效期内不会去向服务器请求了。
- Expires 的值为一个绝对时间,是GMT格式(GMT时间就是英国格林威治时间,GMT时间 = 北京时间 - 8小时)的时间字符串,指的是缓存的具体过期时间,它描述的是时刻,是一个时间点。
Expires: Wed, 25 Jul 2028 19:19:42 GMT
表示资源会在2028-07-25 19:19:42后过期,到时候需要再次请求资源了。由于 Expires 是依赖于客户端系统时间,当修改了本地时间后,缓存可能会失效,所以一般情况,我们认为使用 Cache-Control 是更好的选择。
- Cache-Control 给 Cache-Control 设置 max-age ,表示缓存的最长时间是多少秒,定义的是时间的长短,它描述的是时间,表示的是一段时间间距,是一个相对时间。比如我想设置这个资源有效时间是3天,那么 max-age 的值就是259200(3 * 24 * 60 * 60 = 259200)。
Cache-control: max-age=259200
表示资源3天后过期,需要向服务器再次请求资源了。
Cache-Control 与 Expires 可以在服务端配置同时启用,也就是说在 response header 中,Expires 和Cache-Control 是可以同时存在,当它们同时启用的时候Cache-Control 优先级更高。
2、协商缓存
协商缓存是由服务器来确定缓存资源是否可用,当然了,需要服务器和客户端一起配合。服务器可在 response header 中包含Last-Modified字段或者ETag字段。
- Last-Modified / If-Modified-Since
Last-Modified 表示被请求资源在服务器端的最后一次修改时间,当再次请求该资源的时候,浏览器的request header中会带上If-Modified-Since,向服务器询问该资源是否有更新。 - ETag/If-None-Match
每次文件修改后服务端那边会生成一个新的 ETag ,是一个唯一文件标识符,当再次请求该资源时候,浏览器的request header中会带上If-None-Match ,这值就是之前返回的ETag ,把这个值发送到服务器,询问该资源 ETag 是否变动,有变动的话,说明该资源版本需要更新啦,客户端不能继续用缓存里的数据了。
35.HTML5新增的元素、 CSS3新增
首先html5为了更好的实践web语义化,增加了header,footer,nav,aside,section等语义化标签,
在表单方面,为了增强表单,为input增加了color,emial,data ,range等类型,
在存储方面,提供了sessionStorage,localStorage,和离线存储,通过这些存储方式方便数据在客户端的存储和获取,
在多媒体方面规定了音频和视频元素audio和vedio,
另外还有地理定位,canvas画布,拖放duag,多线程编程的web worker和websocket协议
CSS3边框如border-radius,box-shadow等;CSS3背景如background-size,background-origin等;CSS3 2D,3D转换如transform等;CSS3动画如animation等。 参考https://www.cnblogs.com/xkweb/p/5862612.html
36.在地址栏里输入一个URL,到这个页面呈现出来,中间会发生什么?
这是一个必考的面试问题,
大致上是这样一个过程:
DNS解析
TCP连接
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器解析渲染页面
tcp断开连接
**第一种答法(自己总结):**当输入一个url后,首先客户端会通过域名解析DNS协议向域名服务器解析这个域名对应的ip地址,当得到ip地址后,浏览器端会跟客户端通过三次握手建立一个TCP连接。当TCP连接建立完成之后,浏览器端会在这个可靠连接的基础上发送一个http请求报文(报文会经过应用层、传输层、网络层、数据链路层、物理层进行封装头部和尾部发送到服务器端,到达服务器端后,服务器端会对其进行拆头拆尾,直到到达服务器端的应用层对这个报文进行解析并进行相应),此时服务器端会对其进行响应资源。当客户端收到之后,就会把这个页面渲染到浏览器上(渲染过程中一些js文件会阻塞页面的渲染,所以js脚本一般都放到HTML代码的后面;之后再会处理css文件。而且在渲染过程中需要考虑缓存问题,如果缓存中有满足强缓存的数据,则不会向服务器端发送请求,而是优先读取强缓存中的数据,如果不满足强缓存,那么再向服务器端发送请求,判断是否满足协商缓存,满足则返回304则读取浏览器缓存中的数据,如果不满足则返回200,并返回最新的数据以及最新的标识)。
第二种答法(网上做法):输入url后,首先需要找到这个url域名的服务器ip,为了寻找这个ip,浏览器首先会寻找缓存,查看缓存中是否有记录,缓存的查找记录为:浏览器缓存-》系统缓存-》路由器缓存,缓存中没有则查找系统的hosts文件中是否有记录,如果没有则查询DNS服务器,得到服务器的ip地址后,浏览器根据这个ip以及相应的端口号,构造一个http请求,这个请求报文会包括这次请求的信息,主要是请求方法,请求说明和请求附带的数据,并将这个http请求封装在一个tcp包中,这个tcp包会依次经过传输层,网络层,数据链路层,物理层到达服务器,服务器解析这个请求来作出响应,返回相应的html给浏览器,因为html是一个树形结构,浏览器根据这个html来构建DOM树,在dom树的构建过程中如果遇到JS脚本和外部JS连接,则会停止构建DOM树来执行和下载相应的代码,这会造成阻塞,这就是为什么推荐JS代码应该放在html代码的后面,之后根据外部样式,内部样式,内联样式构建一个CSS对象模型树CSSOM树,构建完成后和DOM树合并为渲染树,这里主要做的是排除非视觉节点,比如script,meta标签和排除display为none的节点,之后进行布局,布局主要是确定各个元素的位置和尺寸,之后是渲染页面,因为html文件中会含有图片,视频,音频等资源,在解析DOM的过程中,遇到这些都会进行并行下载,浏览器对每个域的并行下载数量有一定的限制,一般是4-6个,当然在这些所有的请求中我们还需要关注的就是缓存,缓存一般通过Cache-Control、Last-Modify、Expires等首部字段控制。 Cache-Control和Expires的区别在于Cache-Control使用相对时间,Expires使用的是基于服务器 端的绝对时间,因为存在时差问题,一般采用Cache-Control,在请求这些有设置了缓存的数据时,会先 查看是否过期,如果没有过期则直接使用本地缓存,过期则请求并在服务器校验文件是否修改,如果上一次 响应设置了ETag值会在这次请求的时候作为If-None-Match的值交给服务器校验,如果一致,继续校验 Last-Modified,没有设置ETag则直接验证Last-Modified,再决定是否返回304
37.浏览器在生成页面的时候,会生成那两颗树?
构造两棵树,DOM树和CSSOM规则树
当浏览器接收到服务器相应来的HTML文档后,会遍历文档节点,生成DOM树,
CSSOM规则树由浏览器解析CSS文件生成,
38. csrf和xss的网络攻击及防范
CSRF
((Cross-site request forgery):跨域请求伪造。)
CSRF:跨站请求伪造,可以理解为攻击者盗用了用户的身份,以用户的名义发送了恶意请求,比如用户登录了一个网站后,立刻在另一个tab页面访问量攻击者用来制造攻击的网站,这个网站要求访问刚刚登陆的网站,并发送了一个恶意请求,这时候CSRF就产生了,比如这个制造攻击的网站使用一张图片,但是这种图片的链接却是可以修改数据库的,这时候攻击者就可以以用户的名义操作这个数据库,防御方式的话:使用验证码,检查https头部的refer,使用token
防御方式的话:使用验证码,检查https头部的refer,使用token
XSS
(XSS(Cross Site Scripting):跨域脚本攻击)
XSS的攻击原理
XSS攻击的核心原理是:不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
最后导致的结果可能是:
盗用Cookie破坏页面的正常结构,插入广告等恶意内容D-dos攻击
防御方式:可以在设置响应头的时候设置cookie采用http-only。
两者区别
区别一:
CSRF:需要用户先登录网站A,获取 cookie。XSS:不需要登录。
区别二:(原理的区别)
CSRF:是利用网站A本身的漏洞,去请求网站A的api。XSS:是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。
39. 具体有哪些请求头是跟缓存相关的
缓存分为两种:强缓存和协商缓存,根据响应的header内容来决定。
强缓存相关字段有expires,cache-control。如果cache-control与expires同时存在的话,cache-control的优先级高于expires。
协商缓存相关字段有Last-Modified/If-Modified-Since,Etag/If-None-Match
二. CSS相关
40.盒子模型
盒子模型有两种,content-box、border-box
box-sizing
属性被指定为下面列表中的关键字。
-
content-box
默认值,标准盒子模型。
width
与height
只包括内容的宽和高, 不包括边框(border),内边距(padding),外边距(margin)。注意: 内边距、边框和外边距都在这个盒子的外部。 比如说,.box {width: 350px; border: 10px solid black;}
在浏览器中的渲染的实际宽度将是 370px。 尺寸计算公式:width
= 内容的宽度height
= 内容的高度宽度和高度的计算值都不包含内容的边框(border)和内边距(padding)。
-
border-box
width
和height
属性包括内容,内边距和边框,但不包括外边距。也称为IE盒模型。注意,填充和边框将在盒子内 , 例如,.box {width: 350px; border: 10px solid black;}
导致在浏览器中呈现的宽度为350px的盒子。内容框不能为负,并且被分配到0,使得不可能使用border-box使元素消失。 尺寸计算公式:width
= border + padding + 内容的宽度height
= border + padding + 内容的高度 -
标准盒子模型:
IE盒子模型:
42. 画一条0.5px的线
理论上1px已经是最小单位了,那如何画一条0.5px的线呢?
1.采用meta viewport的方式,要记得viewport只针对于移动端,只在移动端上才能看到效果
这样子就能缩放到原来的0.5倍,如果是1px那么就会变成0.5px
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/>
2. 采用transform: scale()的方式
transform: scale(0.5,0.5); //scale为缩放比例的函数
43.link标签和import标签的区别
在html文件里
<!-- link是引入外链css文件的标签,link是属于HTML标签,@import是css提供的 -->
<link rel="stylesheet" href="">
在css文件里:
/* 利用@import引入外链css文件,@import是css提供的,而link标签是html提供 */
@import url("./04.设置http请求头.html");
区别:
link是引入外链css文件的标签,link是属于HTML标签,@import是css提供的
页面被加载时,link会同时被加载,而@import引用的css会等到页面加载结束后加载。
link是html标签,因此没有兼容性,而@import只有IE5以上才能识别。
44. transition、animation的区别
Animation和transition大部分属性是相同的,他们都是随时间改变元素的属性值,他们的主要区别是transition需要触发一个事件才能改变属性,而animation不需要触发任何事件的情况下才会随时间改变属性值,并且transition只有两个状态初始和结束,而animation可以是一帧一帧的(自己定义百分之多少)。
transition(过渡)
animation(动画)
tansition是一个过渡属性,就是一个属性从一个值过渡到另一个值,animation动画,就是在一段时间内各种属性进行变化从而达到一个动画的效果。
一、transition(过渡)
W3C中对transition的描述是:css中的transition允许css的属性值在一定的时间区间内平滑地过渡。这种效果可以在鼠标单击、获得焦点、被点击或对元素任何改变中触发,并圆滑的以动画效果改变css的属性值。
transition主要包含四个值:
transition-property(执行变换的属性),
transition-duration(执行变换的持续时间),
transition-timing-function(变换的速率变化模式),
transition-delay(变换延迟时间)。
//如:此时div盒子将会在1s后由初始状态慢慢变成:hover鼠标经过后的状态,可以看出他只有两个状态,初始和结束
div {
width: 200px;
height: 30px;
border: 1px solid pink;
transition: all 1s ease;
}
div:hover{
background-color: red;
width: 300px;
height: 300px;
}
二 . animation(动画)
动画animation有两个关键
一个是animation本身的属性值,一个是keyframes的写法,keyframes比较简单,我们先来说说这个。
我们在上面看到了transition,transition它只允许在两种状态之间进行过度,没有中间的值。
而我们把一个动画看作是许多个帧的集合,我们可以去设定每个帧的呈现(每个时刻对应的css属性值),这样的动画就有更多的可变性了。
keyfram用@keyframs开头,后面接一个动画名,然后用大括号括起来,在里面写每一个关键帧的属性。
//在0%到100%之间,我们可以设置任意多个关键帧来设定css的样式,从而达到一种动画一直在变化的效果。
@keyframes baba {
0% {
width: 10px;
height: 10px;
background-color: salmon;
}
50% {
width: 800px;
height: 800px;
background-color: red;
}
100% {
width: 300px;
height: 300px;
background-color: black;
}
}
div {
animation: baba 2s leaner infinite; //表示div盒子执行动画名为baba的动画,并维持2s匀速,且动画无限循环不结束
}
再来看看animation有哪些属性:
animation-name(动画名,也就是keyfram中定义的动画名)
animation-duration(动画持续时间)
animation-timing-function(动画变化的速率)
animation-delay(动画延迟播放的时间)
animation-iteration-count(动画循环的次数,infinite是无限次)
animation-direction(动画的方向)
animation-play-state动画的播放状态
45.:link、:hover、:active和:visited的区别?
:link表示鼠标点击之前,也称为原始状态
:hover表示鼠标悬停状态
:active表示鼠标点击状态
:visited表示鼠标点击之后状态
46. Flex布局
Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。
布局的传统解决方案,基于盒状模型,依赖display属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。
//使用flex布局使一个盒子垂直居中
//注意必须设置容器的属性使得容器里面的元素就可以垂直居中了
<div class="d1">
<div style="background-color: red;">111</div>
</div>
<style>
.d1 {
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
}
</style>
简单的分为容器属性和元素属性:
容器的属性:
flex-direction:决定主轴的方向(即子item的排列方法)
.box {
flex-direction: row | row-reverse | column | column-reverse;
}
flex-wrap:决定换行规则
.box{
flex-wrap: nowrap | wrap | wrap-reverse;
}
flex-flow:
.box {
flex-flow: <flex-direction> || <flex-wrap>;
}
justify-content:对其方式,水平主轴对齐方式
align-items:对齐方式,竖直轴线方向
项目的属性(元素的属性):
order属性:定义项目的排列顺序,顺序越小,排列越靠前,默认为0
flex-grow属性:定义项目的放大比例,即使存在空间,也不会放大
flex-shrink属性:定义了项目的缩小比例,当空间不足的情况下会等比例的缩小,如果定义个item的flow-shrink为0,则为不缩小
flex-basis属性:定义了在分配多余的空间,项目占据的空间。
flex:是flex-grow和flex-shrink、flex-basis的简写,默认值为0 1 auto。
align-self:允许单个项目与其他项目不一样的对齐方式,可以覆盖align-items,默认属性为auto,表示继承父元素的align-items
比如说,用flex实现圣杯布局
47. 说一下块元素和行元素
块元素:独占一行,并且有自动填满父元素,可以设置margin和pading以及高度和宽度
行元素:不会独占一行,width和height会失效,并且在垂直方向的padding和margin会失效。
行元素转化为块元素:
display:block;
48. 多行元素的文本省略号
width: 60px;
/* 文本超过三行则使用省略号 */
/* 将盒子模型转换为弹性伸缩盒子 ,注意flex是弹性布局*/
display: -webkit-box;
/* 设置弹性伸缩盒子的子元素排列方式 */
-webkit-box-orient:vertical;
/* 限制一个块元素文本显示行数 */
-webkit-line-clamp:3;
//只显示一行:white-space:no-wrap
/* 超出后溢出 */
overflow:hidden;
/* 超出的文字使用省略号表示 */
text-overflow: ellipsis;
49. css动画和js动画的差异性
css设置动画一般通过animation属性(animation是利用keyframe创建一个动画名)或者transition过渡属性实现,过程较为简单,动画在运行期间不能终止、取消、暂停动画
而对于原生js而言,是通过一些事件和定时器来完成。
对于jQuery是利用的animation()函数。1·1
- js动画代码相对复杂一些
- 动画运行时,对动画的控制程度上,js能够让动画暂停、取消、终止,css动画不能添加事件
- 动画性能看,js动画多了一个js解析的过程,性能不如css动画好
- 对帧速表现不好的低版本浏览器,css3可以做到自然降级
渲染线程分为main thread和compositor thread,如果css动画只改变transform(转换)和opacity(透明度),这时整个CSS动画得以在compositor(合成器) trhead完成(而js动画则会在main thread执行,然后触发compositor thread进行下一步操作)在JS执行一些昂贵的任务时,main thread繁忙,CSS动画由于使用了compositor thread可以保持流畅。
50. visibility=hidden, opacity=0,display:none
visibility=hidden,该元素隐藏起来了,但不会改变页面布局,但是不会触发该元素已经绑定的事件
opacity=0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件的
display=none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删除掉一样。
div{
width: 100px;
height: 100px;
background-color: pink;
/* // 元素直接消失 */
/* display: none; */
/* ,该元素隐藏起来了,但不会改变页面布局,但是不会触发该元素已经绑定的事件 */
visibility: hidden;
/* 该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件的 */
/* opacity: 0; */
}
51. 双边距重叠问题(外边距折叠)
多个相邻(兄弟或者父子关系)普通流的块元素垂直方向marigin会重叠,这是并列相邻的块元素属于同一个BFC(BFC(Block Formatting Context,块格式化上下文) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。),即根元素()。BFC的特性1规定 “属于同一个BFC的两个相邻容器的上下margin会重叠”,故两者上下边距发生重叠。
折叠的结果为:
根据第二条,属于同一个BFC的两个相邻的Box会发生margin重叠,所以我们可以设置,两个不同的BFC,也就是我们可以让把第二个p用div包起来,然后激活它使其成为一个BFC,给其中一个盒子的父盒子设置overflow:hidden,即可解决外边距折叠
p {
color: #f55;
background: yellow;
width: 200px;
line-height: 100px;
text-align:center;
margin: 30px;
}
div{
overflow: hidden;
}
</style>
<body>
<p>看看我的 margin是多少</p>
<div>
<p>看看我的 margin是多少</p>
</div>
</body>
52. BFC(块级格式化上下文,用于清除浮动,防止margin重叠等)
BFC也就是常说的块格式化上下文,这是一个独立的渲染区域,规定了内部如何布局,并且这个区域的子元素不会影响到外面的元素,其中比较重要的布局规则有内部box垂直放置,计算BFC的高度的时候,浮动元素也参与计算,触发BFC的规则有根元素,浮动元素,position为absolute或fixed的元素,display为inline-block,table-cell,table-caption,flex,inline-flex,overflow不为visible的元素
Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:
block-level box:display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context;
inline-level box:display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;
run-in box: css3 中才有, 这儿先不讲了。
Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)。
BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
**BFC的布局规则:**
内部的Box会在垂直方向,一个接一个地放置。
**Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。**
每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC的区域不会与float box重叠。
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
计算BFC的高度时,浮动元素也参与计算。
创建BFC
1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible //比如overflow:hidden
53. position属性 比较
固定定位fixed:
元素的位置相对于浏览器窗口是固定位置,即使窗口是滚动的它也不会移动。Fixed定位使元素的位置与文档流无关,因此不占据空间。 Fixed定位的元素和其他元素重叠。
相对定位relative:
如果对一个元素进行相对定位,它将出现在它所在的位置上。然后,可以通过设置垂直或水平位置,让这个元素“相对于”它的起点进行移动。 在使用相对定位时,无论是否进行移动,元素仍然占据原来的空间。因此,移动元素会导致它覆盖其它框。
绝对定位absolute:
绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那么它的位置相对于。 absolute 定位使元素的位置与文档流无关,因此不占据空间。 absolute 定位的元素和其他元素重叠。
粘性定位sticky:
这是一个结合了 position:relative
和 position:fixed
两种定位功能于一体的特殊定位,适用于一些特殊场景。可以使得一个元素黏在页面中,和fixed类似
.child {
position: sticky;
/* position: fixed; */
width: 100px;
height: 50px;
background-color: pink;
top: 200px;
}
下面代码与上面代码效果相同:
再给child的父元素添加相对布局:
.container {
position: relative;
height: 2000px;
border: 1px solid #333;
}
.child {
/* position: sticky; */
position: fixed;
width: 100px;
height: 50px;
background-color: pink;
top: 200px;
}
默认定位Static:
默认值。没有定位,元素出现在正常的流中(忽略top, bottom, left, right 或者 z-index 声明)。
inherit:
规定应该从父元素继承position 属性的值。
54.清除浮动
方法一:使用带clear属性的空元素
在浮动元素后使用一个空元素如
,并在CSS中赋予.clear{clear:both;}属性即可清理浮动。亦可使用或
来进行清理。
<style>
* {
margin: 0;
padding: 0;
}
.news {
background-color: gray;
border: solid 1px black;
}
.news img {
float: left;
}
.news p {
float: right;
}
.clear {
/* clear:both意思就是清除浮动 */
clear: both;
}
</style>
<div class="news">
<img src="news-pic.jpg" />
<p>some text</p>
<div class="clear"></div>
</div>
方法二:使用CSS的overflow属性
给浮动元素的容器添加overflow:hidden;或overflow:auto;可以清除浮动,另外在 IE6 中还需要触发 hasLayout ,例如为父元素设置容器宽高或设置 zoom:1。
在添加overflow属性后,浮动元素又回到了容器层,把容器高度撑起,达到了清理浮动的效果。
.news {
background-color: gray;
border: solid 1px black;
}
.news img {
float: left;
/* 清除浮动 */
overflow: hidden;
}
.news p {
float: right;
/* 清除浮动 */
overflow: hidden;
}
方法三:使用CSS的::after伪元素
结合::after 伪元素(注意这不是伪类,而是伪元素,代表一个元素之后最近的元素)和 IEhack ,可以完美兼容当前主流的各大浏览器,这里的 IEhack 指的是触发 hasLayout。
给浮动元素的容器添加一个clearfix的class,然后给这个class添加一个:after伪元素实现元素末尾添加一个看不见的块元素(Block element)清理浮动。
<div class="news clearfix">
<img src="news-pic.jpg" />
<p>some text</p>
<!-- <div class="clear"></div> -->
</div>
.clearfix::after {
content: "12";
display: block;
height: 0;
visibility: hidden;
clear: both;
}
参考https://www.cnblogs.com/ForEvErNoME/p/3383539.html
55. CSS选择器有哪些,优先级呢
id 选择器,class 选择器,标签选择器,伪元素选择器(两个冒号::after、::before),伪类选择器(一个冒号:first-child)、
属性选择器([type=“password”] {
width: 300px;
})
同一元素引用了多个样式时,排在后面的样式属性的优先级高;
样式选择器的类型不同时,优先级顺序为:id 选择器 > class 选择器=伪类选择器=属性选择器 > 标签选择器;
标签之间存在层级包含关系时,后代元素会继承祖先元素的样式。如果后代元素定义了与祖先元素相同的样式,则祖先元素的相同的样式属性会被覆盖。继承的样式的优先级比较低,至少比标签选择器的优先级低;
带有!important 标记的样式属性的优先级最高;
样式表的来源不同时,优先级顺序为:内联样式> 内部样式 > 外部样式 > 浏览器用户自定义样式 > 浏览器默认样式
56.css3新特性
开放题。CSS3边框如border-radius,box-shadow等;CSS3背景如background-size,background-origin等;CSS3 2D,3D转换如transform等;CSS3动画如animation等。 参考https://www.cnblogs.com/xkweb/p/5862612.html
div {
width: 200px;
height: 200px;
background-color: pink;
border: 1px solid red;
/* css3特性一:border-radius圆角边框 */
border-radius: 100px;
/* 特性二:box-shadow阴影 */
box-shadow: 4px 4px 10px blue;
/* 规定背景图片的尺寸 */
background-size: 10px;
}
</style>
57. 怎么样让一个元素消失
display:none; visibility:hidden; opacity: 0; 等等
58. 如何实现图片在某个容器中居中的?
- margin:auto; 父元素固定宽高,子元素设置position: absolute,margin:auto平均分配margin
- 2.css3属性transform。子元素设置position: absolute; left: 50%; top: 50%;transform: translate(-50%,-50%);即可。
- 将父元素设置成display: table, 子元素设置为单元格 display: table-cell。
- 弹性布局display: flex。设置align-items: center; justify-content: center
.child {
/* 第一种方式 定位 */
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
width: 100px;
height: 100px;
background-color: skyblue;
}
第二种方式:给父元素添加弹性盒子样式
.fat {
width: 500px;
height: 500px;
background-color: pink;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
方法3:利用display:table-cell
.parent{
display:table-cell;
vertical-align:middle;
text-align:center;
}
.child{
display:inline-block;
}
59.三栏布局的实现方式,尽可能多写,浮动布局时,三个div的生成顺序有没有影响
三栏布局一般指的是页面中一共有三栏,左右两栏宽度固定,中间自适应的布局,一共有如下五种方式实现
1.采用flex布局
让左右盒子宽度是固定值,剩余空间给中间盒子(设置中间盒子flex:1)
<div class="father">
<div class="c1">1</div>
<div class="c2">2</div>
<div class="c3">3</div>
</div>
<style>
* {
margin: 0;
padding: 0;
}
.father {
height: 500px;
display: flex;
}
.c1 {
background-color: pink;
width: 200px;
}
.c2 {
background-color: red;
flex: 1;
}
.c3 {
width: 300px;
background-color: skyblue;
}
</style>
2 利用浮动
左右盒子分别左浮动右浮动,然后中间盒子利用左右margin移到中间
/* 利用浮动实现三栏布局
.father {
height: 500px;
}
.c1 {
background-color: pink;
float: left;
width: 300px;
height: 500px;
}
.c2 {
width: 300px;
float: right;
background-color: skyblue;
height: 500px;
}
.c3 {
margin: 0 300px;
background-color: red;
height: 500px;
} */
3. 使用定位方式
采用定位进行布局
.father {
height: 500px;
position: relative;
}
.c1 {
position: absolute;
left: 0;
top: 0;
background-color: pink;
width: 300px;
height: 100%;
}
.c2 {
position: absolute;
top: 0;
right: 0;
width: 300px;
background-color: skyblue;
height: 100%;
}
.c3 {
margin: 0 300px;
background-color: red;
height: 100%;
}
- 圣杯布局实现(这里采用flex布局实现,其实也可以采用浮动实现)
圣杯布局为上下header和footer,中间为左中右盒子,并且在浏览器缩小时,左右盒子宽度保持不变,中间盒子缩小。
可采用flex布局实现:上下footer和header先采用中间大盒子先采用flex,使之上中下布局,然后再在中间大盒子采用左中右flex布局,左右盒子宽度固定,剩余宽度给中间盒子
<body>
<div id="container">
<header>header</header>
<div id="subContainer">
<div id="left">left</div>
<div id="center">center</div>
<div id="right">right</div>
</div>
<footer>footer</footer>
</div>
<style>
* {
margin: 0;
padding: 0;
}
body {
min-width: 750px;
}
#container {
display: flex;
flex-direction: column;
/* justify-content: space-between; */
width: 100vw;
height: 100vh;
}
footer ,
header {
/* flex: 1; */
width: 100%;
height: 100px;
background-color: rgba(29, 27, 27, 0.726);
font-size: 50px;
line-height: 100px;
text-align: center;
}
#subContainer {
display: flex;
flex: 1;
}
#left,
#center,
#right {
height: 100%;
}
#left {
width: 200px;
background-color: pink;
}
#center {
flex: 1;
background-color: skyblue;
}
#right{
width: 200px;
background-color: purple;
}
</style>
</body>
60. css预处理器有什么
less,sass等
61.相对布局和绝对布局,position:relative和absolute。
相对定位relative:
如果对一个元素进行相对定位,它将出现在它所在的位置上。然后,可以通过设置垂直或水平位置,让这个元素“相对于”它的起点进行移动。 在使用相对定位时,无论是否进行移动 ,元素仍然占据原来的空间。因此,移动元素会导致它覆盖其它框。
绝对定位absolute:
绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那么它的位置相对于。 absolute 定位使元素的位置与文档流无关,因此不占据空间。 absolute 定位的元素和其他元素重叠。
62. calc属性
calc用户动态计算长度值,任何长度值都可以使用calc()函数计算,需要注意的是,运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
div {
width: calc(100% - 200px);
height: 100px;
background-color: pink;
}
63 .z-index的定位方法
z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。
**注释:**元素可拥有负的 z-index 属性值。z-index默认为0
**注释:**Z-index 仅能在定位元素上奏效(例如 position:absolute;)!
64. inline-block、inline和block的区别;为什么img是inline还可以设置宽高
Block是块级元素,独占一行,能设置宽度,高度,
Inline:设置width和height无效,不独占一行
Inline-block:能设置宽度高度,不独占一行
因为img是可替换元素,规定可替换元素可以设置宽高。可替换元素的展现效果不是由css控制的,
//典型的可替换元素有
<iframe>
<video>
<audio>
<canvas>
<img>
65. 了解重绘和重排吗,知道怎么去减少重绘和重排吗,让文档脱离文档流有哪些方法
如果改变了元素的宽高
,元素的位置
,就会导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。完成重排后,要将重新构建的渲染树渲染到屏幕上,这个过程就是“重绘”。
引起重排重绘的原因有:
添加或者删除可见的DOM元素,
元素尺寸位置的改变
浏览器页面初始化,
浏览器窗口大小发生改变,重排一定导致重绘,重绘不一定导致重排,
减少重绘重排的方法有:
不在布局信息改变时做DOM查询,
使用csstext,className一次性改变属性
使用fragment
对于多次重排的元素,比如说动画。使用绝对定位脱离文档流,使其不影响其他元素
三、怎么脱离文档流?js
1:float
使用float可以脱离文档流。
注意!!!:使用float脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在该元素的周围。
2:absolute
absolute称为绝对定位,其实博主觉得应该称为相对定位,因为使用absolute脱离文档流后的元素,是相对于该元素的父类(及以上,如果直系父类元素不满足条件则继续向上查询)元素进行定位的,并且这个父类元素的position必须是非static定位的(static是默认定位方式)。
3:fixed
完全脱离文档流,相对于浏览器窗口进行定位。(相对于浏览器窗口就是相对于html)。
66. rem和em的使用
<body>
<!-- em指的是相对于使用这个盒子元素的字体大小比例所决定。1em = 1 * 自己元素的font-size -->
<!-- rem指的是相对于顶层的html字体大小来计算自己的大小 1rem = 1 * html元素的font—size-->
<div class="fat" style="margin-bottom: 1em">
<div class="child">2</div>
</div>
<div class="box1">1</div>
<style>
html {
font-size: 20px;
}
.fat {
font-size: 10px;
}
.child {
font-size: 5px;
width: 20em;
height: 10em;
background-color: red;
}
.box1 {
width: 20rem;
height: 10rem;
background-color: red;
}
</style>
</body>
67. transform:rotate()3D
3D中,x轴是水平向右的,y轴是垂直向下的,z轴是垂直于屏幕方向向外的。
perspective
perspective使得在网页中产生3D效果,就需要加上透视perspective,透视我们也称为视距(近大远小),透视的单位是像素。
perspective:300px;
透视需要写在被观察元素的父盒子上。
3D旋转rotate
3D旋转指可以让元素在三维平面内沿着x轴、y轴、z轴或者自定义轴进行旋转。
左手准则(大拇指指向xyz轴的正方向)
transform:rotateX(45deg) 表示沿着x轴正想旋转45度。(翻单杠)
transform:rotateY(45deg) 表示沿着Y轴正想旋转45度 (绕着钢管舞)
transform:rotateZ(45deg) 表示沿着Z轴正想旋转45度 (转转盘,跟2d一样)
transform:rotate3d(x,y,z,deg)表示沿着自定义周进行旋转多少度(了解即可)
transform-style:preserve-3d保持3d效果
transform-style:preserve-3d使子元素开启立体空间,把此属性加到父盒子上面
transform-origin改变元素的原点位置
/* 改变元素的原点位置为此元素的左边*/
transform-origin: left center;
68. 利用css制作立方体
先把每个面利用绝对定位脱离文档流。
再把父元素变成3d空间:transfor-style:preserve-3d;并加入透视以观察3d元素:perspective:1000px;
然后再对每个面进行旋转平移改变每个面的位置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
/* perspective: 1000px; */
}
#cube {
perspective: 3000px;
position: relative;
width: 600px;
height: 600px;
transform-style: preserve-3d;
/* perspective-origin: 50% 100px; */
/* transform: rotateX(45deg) rotateZ(45deg); */
/* 表示该容器是个3D容器 */
/* 旋转角度 */
/* transform: rotateX(45deg) rotateZ(45deg); */
transition: all 1s ease;
animation: rotate 2s infinite;
}
#cube:hover {
/* 旋转角度 */
transform: rotateX(45deg) rotateZ(225deg);
}
@keyframes rotate {
from {
/* transform: rota; */
}
to {
transform: rotateY(360deg);
}
}
#cube div {
position: absolute;
width: 300px;
height: 300px;
border: 1px solid black;
background-color: rgba(0,1,0,0.2);
left: 150px;
top: 150px;
}
#left {
/* 先让他旋转-90度,旋转之后的图形的x坐标就会更新 */
transform: rotateY(-90deg) translateX(-150px);
/* 改变元素的原点位置为此元素的左边*/
transform-origin: left center;
}
#right {
transform: rotateY(90deg) translateX(150px);
transform-origin: right;
}
#front{
transform: translateZ(150px);
}
#back {
transform: translateZ(-150px) rotateY(180deg);
}
#top {
transform: rotateX(90deg) translateY(-150px);
transform-origin: top center;
}
#bottom {
transform: rotateX(90deg) translateY(150px);
transform-origin: bottom center;
box-shadow: 0 0 100px red;
}
</style>
</head>
<body>
<div id="cube">
<div id="left"></div>
<div id="right"></div>
<div id="front"></div>
<div id="back"></div>
<div id="top"></div>
<div id="bottom"></div>
</div>
</body>
</html>
三. js相关
1. 补充get和post请求在缓存方面的区别
get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。
2. 普通函数和箭头函数的this指向区别
对于普通函数,此时this指向其调用者(如果没有人调用它,则在严格模式下为undefined,在默认情况下为window),而对于箭头函数this指向处于哪个环境中。
箭头函数其实是没有this的,箭头函数中的this只取决包裹箭头函数的第一个普通函数的this。
对箭头函数使用apply、call、bind的this绑定是无效的。重点!
3, null 和undefined的区别
首先undefined是指声明一个变量但是没有对他初始化,则此变量为undefined。比如: let c ; //c为undefined
并且undefined永远都不会显示赋值给一个变量
null:null表示的一个空对象指针。虽然null== undefined ,但是区别就是null有显示赋值的用法,而undefined没有。他们的用途是不同的。null一般是用来给一个对象在没有值的情况下,先赋值为null。
3.let和var的区别
首先let是只有在自己的块作用域下才有效(经常搭配for循环使用),而var会覆盖外部作用域的变量。ES6的let让js真正拥有了块级作用域。
其次var会变量提升。
使用var的函数作用域声明
在使用var声明变量时,变量会被自动添加最近的上下文(函数的局部上下文)。如果变量未经声明就初始化那么此变量会自动被添加到全局上下文中,这样就可以在函数外部访问函数内部没有声明的变量了。
var声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前,这也叫做变量提升。
function(){
console.log(name); //将会输出undefined
var name = "hi"
}
//上面代码等价于
function(){
var name;
console.log(name); //将会输出undefined
name = "hi"
}
使用let的函数作用域声明
ES6新增的let关键字,它的作用域是块级的。另外一点就是var 可以重复声明一个变量,重复的声明会被忽略,而let不可以。
var a = 1;
var a = 2; //var可以重复声明
let c =1;
let c =2 ; //let重复声明会报错
const关键字
const关键字除了被初始化后不能够再次赋值(为常量,一旦初始化就不能修改),其他方面和let一样。
3. 说一下闭包
一句话可以概括:闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取另一个函数作用域的变量的函数)。
或者子函数在外调用,子函数所在的父函数的作用域不会被释放。
闭包的优缺点
内存泄露的解决方案
闭包的坑点
- 引用的变量可能发生变化
function outer() {
var result = [];
for (var i = 0; i<10; i++){
result.[i] = function () {
console.info(i)
}
}
return result
}
看样子result每个闭包函数对打印对应数字,1,2,3,4,…,10, 实际不是,因为每个闭包函数访问变量i是outer执行环境下的变量i,随着循环的结束,i已经变成10了,所以执行每个闭包函数,结果打印10, 10, …, 10.
解决:
function outer() {
var result = [];
for (var i = 0; i<10; i++){
result.[i] = function (num) {
return function() {
console.info(num); // 此时访问的num,是上层函数执行环境的num,数组有10个函数对象,每个对象的执行环境下的number都不一样
}
}(i)
}
return result
}
-
this指向问题
var object = { name: ''object", getName: function() { return function() { console.info(this.name) } } } object.getName()() // underfined // 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
解决:修改this指向
var object = { name1: 'object', getName: function () { let that = this; //对于普通函数,此时this指向其调用者,而对于箭头函数this指向其上一层函数(一层一层的向外层作用域查找,直到查到为止) return function () { console.log(that.name1); } } } object.getName()() // object
- 内存泄露问题
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
alert(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
alert(id)
}
el = null // 主动释放el
}
4. 说一下类的创建和继承
ES5中的类的创建和继承
// 通过构造函数创建类
let Person = function (name, sex) {
this.name = name;
this.sex = sex;
}
// 通过原型添加方法
Person.prototype.eat = function () {
console.log("我是个人我要吃吃吃吃!");
}
// 创建子类Teacher,其继承于Person类
let Teacher = function (name, sex, school) {
Person.call(this, {
name, sex
});
this.school = school;
}
// 子类继承父类方法
// 方式一:调用构造函数继承,使得Teacher没有使用默认原型,而是将其替换为Person的实例对象,这样就可以使得Teacher可以使用Person的属性和方法
Teacher.prototype = new Person();
// 实例的__proto__是指向构造函数的原型,而构造函数的原型的__proto__指针是指向其构造函数原型的继承者,也就是继承于谁。另外一点任何构造函数都是继承于Object
// Teacher.prototype.__proto__指向Person的原型
console.log(Teacher.prototype.__proto__.constructor);
console.log(Person.prototype.__proto__); //Person默认继承于Object对象
// 方式二:Object.create()
Teacher.prototype = Object.create(Person.prototype);
// 方式三:原型链继承,遍历Person的prototype的每一个方法对Teacher的原型方法进行赋值
for (let item in Person.prototype) {
Teacher.prototype[item] = Person.prototype[item]
}
ES6中的类的创建和继承
// 通过ES6的class创建类
class Person {
// 构造器
constructor(name, age) {
this.name = name;
this.age = age;
}
// Person类的方法
eat() {
console.log(`我是${this.name},我要吃饭!`);
}
}
// 创建Teacher类,其继承于Person,extends继承
class Teacher extends Person {
constructor(name, age, school) {
super(name, age);
this.school = school;
}
teach() {
console.log("我要教你们上课哦");
}
}
5.webpack配置文件
const path = require('path')
module.exports = {
entry: './src/main.js', // 入口, 可以为相对路径, 当然绝对路径也没错
output: { // 输出配置
path: path.join(__dirname, './dist'), // 输出的目录
filename: 'bundle.js' // 输出的文件名
},
mode: 'production' // 打包的模式, production | development
}
6. ajax使用
Ajax被认为是(Asynchronous(异步) javascript And Xml的缩写)。现在,允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax.
原生ajax写法
ajax使用步骤分下面4步:
1.创建ajax对象
2.连接到服务器
3.发送请求(告诉服务器我要什么文件)
4.接收返回值
// node.js写的后台代码
const express = require("express");
const path = require("path");
const app = express();
const formidable = require("formidable");
const bodyParser = require("body-parser"); //此模块,可以获取post请求参数
app.use(bodyParser.urlencoded({ extend: true })); //配置bodyparser,
app.get("/data1", (req, res) => {
res.send("ww");
// req.query为get请求携带的参数
console.log(req.query.name, req.query.age);
})
app.get("/data2", (req, res) => {
res.send("爸爸我是数据2");
})
app.get("/data3", (req, res) => {
res.send("爸爸我是数据3");
})
app.post("/data4", (req, res) => {
// 对于post请求,则需要用到body-parser库,req.body就是post请求的参数
console.log(req.body);
res.send("post")
})
//静态资源访问
app.get("/first", (req, res) => {
res.send({ "name": "王贺" });
});
app.get("", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*"); // 解决请求头跨域问题
res.send("你他妈在玩蛇呢!");
})
app.get("/get", (req, res) => res.send(req.query));
app.get("/email", (req, res) => {
var e1 = "2838068869@qq.com";
var e2 = "2032308994@qq.com";
res.send(req.query);
// if (e1 == req.query || e2 == req.query) {
// res.send(0);
// } else res.send(1);
});
app.post('/post', (req, res) => {
res.send("post");
});
app.post('/formData', (req, res) => {
//创建formidable表单解析对象
const form = new formidable.IncomingForm();
//解析客户端传递过来的FormData对象
form.parse(req, (err, fields, files) => {
res.send(fields);
console.log(fields.username);
});
});
//实现文件上传的路由
app.post("/file", (req, res) => {
const form = new formidable.IncomingForm();
//设置客户端上传文 件的存储路径
form.uploadDir = path.join(__dirname, 'public', 'upload');
form.keepExtensions = true;
form.parse(req, (err, fields, files) => {
//将客户端传递过来的文件地址响应到客户端
res.send({
path: files.attrName.path.split('public')[1]
});
// console.log(typeof {
// path: files.attrName.path.split('public')[1]
// });
});
});
app.use(express.static(path.join(__dirname)));
console.log(1);
app.listen(7000);
console.log("服务器创建成功!");
// ajax原生写法
//第一步: 引入XMLHttpRequest模块
const XMLHttpRequest = require("XMLHttpRequest").XMLHttpRequest;
//第二步: 创建ajax对象
const xhr = new XMLHttpRequest();
// 第三步:连接服务器
xhr.open("get", "http://localhost:6000/data1");
//第四步: 发送请求
xhr.send();
// 客户端监听服务器端返回的数据,当数据接收完毕之后就会触发onload方法
xhr.onload = () => {
//第五步:responseText 接收数据
console.log(xhr.responseText);
}
jquery的ajax写法
// jquery的ajax写法
<script src="./node_modules/jquery/dist/jquery.min.js"></script>
<script>
// ajax的get请求
$.ajax({
url: "http://localhost:7000/data1",
type: "get",
data: {
name: "王贺",
age: 23
},
// 成功的回调函数
success: (data) => {
console.log(data);
},
// 失败的回调函数
error: (data) => {
console.log(3);
console.log(data);
}
})
// ajax的post请求
$.ajax({
url: "http://localhost:7000/data4",
type: "post",
data: {
name: "王贺",
age: 23
},
// 成功的回调函数
success: (data) => {
console.log(data);
},
// 失败的回调函数
error: (data) => {
console.log("请求错误");
}
})
</script>
7.说一下如何解决异步回调地狱
什么是回调地狱
就是一个异步请求套着一个异步请求,一个异步请求依赖于另一个的执行结果,使用回调的方式相互嵌套。这会导致代码很丑陋,不方便后期维护。函数作为参数,进行层层的嵌套,被称之为回调地狱。因为层层的回调,所以很难直接的观察到正确的执行结果。
// 例子:ajax请求回调地狱代码
<script src="./node_modules//jquery/dist/jquery.min.js"></script>
<script>
// 封装一个ajax请求方法
let myAjax = function (url, callback) {
$.ajax({
url,
type: "get",
data: {
name: "王贺",
age: 23
},
// 成功的回调函数
success: (data) => {
callback(data);
}
})
}
// 这样看一层叠一层就像是地狱一样,俗称为回调地狱,这样的代码太丑了太难读了
myAjax("http://localhost:7000/data1", function (res) {
console.log(res);
myAjax("http://localhost:7000/data2", function (res) {
console.log(res);
myAjax("http://localhost:7000/data3", function (res) {
console.log(res);
})
});
})
</script>
一般通过利用promise、generator、async/await来解决回调地狱
利用ES6的promise解决回调地狱
利用Promise的then和catch实现异步的操作
<script src="./node_modules//jquery/dist/jquery.min.js"></script>
<!-- 二. 利用promise的then和catch来解决回调地狱--->
<script>
// 创建一个返回promise对象的方法
function getPromise(url) {
return new Promise((resolve, reject) => {
$.ajax({
url,
type: "get",
success: function (data) {
// 执行成功的回调函数
resolve(data);
},
error: function (err) {
// 执行失败的回调
reject(err)
}
})
})
}
// then函数用于接收Promise的resolve成功的回调,then里面的回调是根据请求成功然后把里面的回调函数传给resolve
getPromise("http://localhost:7000/data1").then((res) => {
console.log(res);
}).catch(res => {
console.log(res);
})
getPromise("http://localhost:7000/data2").then((res) => {
console.log(res);
}).catch(res => {
console.log(res);
})
</script>
利用async ,await实现异步操作
<script>
// 封装一个ajax请求方法
function getPromise(url) {
return new Promise((resolve, reject) => {
、jax({
url,
type: "get",
success: function (data) {
// 执行成功的回调函数
resolve(data);
},
error: function (err) {
// 执行失败的回调
reject(err)
}
})
})
}
// 这样就可以使得在上一次请求没完成之前,不会读后面的代码,这样就保证了同步性
// 注意aysnc函数return的是一个promise对象, // async关键字必须在最接近await关键字的函数上声明
async function getData() {
// 接收的是resolve或者reject回调传回的数据
let res1 = await getPromise("http://localhost:7000/data1");
console.log(res1);
let res2 = await getPromise("http://localhost:7000/data2");
console.log(res2);
let res3 = await getPromise("http://localhost:7000/data3");
console.log(res3);
}
getData();
</script>
8. 说说前端中的事件流
页面的javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以通过给dom元素绑定事件监听函数来操作此dom元素。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。
DOM事件流包括三个阶段:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
DOM事件流顺序:当点击图中的目标元素div时,规定:先进行捕获阶段,元素从顶层的document往组件经过下级元素,直到到达目标,此时处于目标阶段,然后再执行冒泡阶段,元素从下到上逐渐冒泡到顶层根元素。
**事件冒泡:**事件冒泡是指从底部当前元素向上一层逐渐传递,比如:
你点击li之后,事件先触发li目标元素,再逐渐向上传递,如果各个元素都有事件绑定,则执行的冒泡顺序是:li => ul => div => body
<body>
<div>
<ul>
<li>冒泡捕获问题撒</li>
</ul>
</div>
</body>
**事件捕获:**跟冒泡相反,当前元素的顶级根元素先触发事件发生,然后再逐渐往下传递;
上个代码块当采用事件捕获时,各元素事件触发的顺序是:body => div => ul => li
addEventLitener(“事件”,callback, useCapture) ,addEventListener 是DOM2 级事件新增的指定事件处理程序的操作,这个方法接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
useCapture为false即冒泡规则时,此元素只有在冒泡过程中才会触发,在捕获过程中不会触发,此为重点!
相反,当useCapture为true即为捕获规则时,此元素只有在捕获过程中才能触发。
9. 如何让事件先冒泡后捕获
在DOM标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后再执行捕获事件。
个人理解:可以通过定时器,让捕获事件的事件函数先延缓执行,使其先执行冒泡的处理程序,冒泡处理程序执行完成之后再执行捕获事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>event</title>
<style>
#one {
width: 600px;
height: 600px;
background-color: green;
}
#two {
width: 400px;
height: 400px;
background-color: yellow;
}
#three {
width: 200px;
height: 200px;
background-color: deepskyblue;
}
</style>
</head>
<body>
<div id="one">one
<div id="two">two
<div id="three">three</div>
</div>
</div>
<div id="c">22</div>
<script>
var one = document.getElementById('one'),
two = document.getElementById('two'),
three = document.getElementById('three');
// one.addEventListener('click', function () {
// console.log('one捕获')
// }, true)
// two.addEventListener('click', function () {
// console.log('two捕获')
// }, true)
// three.addEventListener('click', function () {
// console.log('three捕获')
// }, true)
// one.addEventListener('click', function () {
// console.log('one冒泡')
// }, false)
// two.addEventListener('click', function () {
// console.log('two冒泡')
// }, false)
// three.addEventListener('click', function () {
// console.log('three冒泡')
// }, false)
var time = null;
one.addEventListener('click', function () {
console.log('one冒泡')
}, false)
one.addEventListener('click', function () {
time = setInterval(() => {
console.log('one捕获')
}, 10)
console.log(1);
}, true)
document.querySelector("#c").addEventListener("click", function () {
clearInterval(time);
});
// two.addEventListener('click', function () {
// console.log('two冒泡')
// }, false)
// three.addEventListener('click', function (e) {
// console.log('three冒泡')
// // 停止冒泡
// // e.stopPropagation();
// }, false)
// two.addEventListener('click', function () {
// console.log('two捕获')
// // time = null
// }, true)
// three.addEventListener('click', function () {
// console.log('three捕获')
// }, true)
</script>
</body>
</html>
10. 说一下事件委托
简介:事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。
举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。
11.说一下图片的懒加载和预加载
懒加载
懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。也就是当图片到达当前页面时才会显示,图片未在页面范围内不显示。实现方式是通过src先赋值给一个预加载图片,然后data-original是真实的图片,当页面滚动此元素滑到页面可视区时,就把data-original的值赋给src。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px;//一定记得设置图片高度
}
</style>
</head>
<body>
<img src="" class="image-item" lazyload="true" data-original="images/1.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/2.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/3.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/4.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/5.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/6.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/7.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/8.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/9.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/10.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/11.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/12.png"/>
<script>
var viewHeight =document.documentElement.clientHeight//获取可视区高度
function lazyload(){
var eles=document.querySelectorAll('img[data-original][lazyload]')
Array.prototype.forEach.call(eles,function(item,index){
var rect
if(item.dataset.original==="")
return
rect=item.getBoundingClientRect()// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
if(rect.bottom>=0 && rect.top < viewHeight){
!function(){
var img=new Image()
img.src=item.dataset.original
img.onload=function(){
item.src=img.src
}
item.removeAttribute("data-original")//移除属性,下次不再遍历
item.removeAttribute("lazyload")
}()
}
})
}
lazyload()//刚开始还没滚动屏幕时,要先触发一次函数,初始化首页的页面图片
document.addEventListener("scroll",lazyload)
</script>
</body>
</html>
jquery中采用的是jquery-lazy插件,哪个元素需要懒加载就 $(“img.lazy”).lazyload(); 并且需要给img元素设置自定义属性 data-original=“真实图片路径”
在vue中可以使用vue-lazy插件实现懒加载,使用方式是把img元素中的动态属性绑定:src改为v-lazy=""
//需要先安装插件:npm install vue-lazyload --save-dev
Vue.use(VueLazyload)
//修改图片显示方式为懒加载(将 :src 属性直接改为v-lazy)
<a href="javascript:;"><img v-lazy="'/static/img/' + item.productImage"></a>
预加载
提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
为什么需要预加载?在网页全部加载之前,对一些重要的主要内容进行加载,以提供给用户更好的体验,减少等待的时间。否则,如果一个页面的内容过于庞大,没有使用预加载技术的页面就会长时间的展现为一片空白,直到所有内容加载完毕。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
<script>
var imgs = [
'https://img0.baidu.com/it/u=2496911135,2558266612&fm=253&fmt=auto&app=120&f=GIF?w=300&h=300',
'https://img0.baidu.com/it/u=3090518909,3758581548&fm=26&fmt=auto',
'https://img0.baidu.com/it/u=2027992389,3130985476&fm=26&fmt=auto',
];
let len = imgs.length;
/**
* 遍历imgs数组,将所有图片加载出来
* 可以通过控制台查看网络请求,会发现所有图片均已加载
*/
for (let i = 0; i < len; i++) {
let imgObj = new Image(); // 创建图片对象
imgObj.src = imgs[i];
imgObj.addEventListener('load', function () { // 这里没有考虑error,实际上要考虑
console.log('imgs' + i + '加载完毕');
}, false);
}
</script>
</body>
</html>
两种技术的本质:两者的行为是相反的,一个是提前加载(预加载),一个是迟缓甚至不加载(懒加载)。
懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
12. mouseover和mouseenter的区别
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移入元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave
13. js的new操作符做了哪些事情
new 操作符新建了一个空对象,这个对象的__proto__指向构造函数的prototype,执行构造函数后返回这个对象。
14. js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
clientHeight:表示的是可视区域的高度,不包含border和滚动条
offsetHeight:表示可视区域的高度,包含了border和滚动条
scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
clientTop:表示边框border的厚度,在未指定的情况下一般为0
scrollTop:滚动后被隐藏的高度,获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)距离顶端的高度。
15. js拖拽功能的实现
首先是三个事件,分别是mousedown,mousemove,mouseup
当鼠标点击按下的时候,需要一个tag标识此时已经按下,可以执行mousemove里面的具体方法。
clientX,clientY标识的是鼠标的坐标,分别标识横坐标和纵坐标,并且我们用offsetX和offsetY来表示元素的元素的初始坐标,移动的举例应该是:
鼠标移动时候的坐标-鼠标按下去时候的坐标。
也就是说定位信息为:
鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的offetLeft.
还有一点也是原理性的东西,也就是拖拽的同时是绝对定位,我们改变的是绝对定位条件下的left
以及top等等值。
补充:也可以通过html5的拖放(Drag 和 drop)来实现
16. 高阶函数
高阶函数也就是 其参数为一个函数(回调函数)或者其返回值是一个函数。
17. 高阶函数柯里化
柯里化的概念:就是一个函数有多个参数,我们根据这个函数的参数个数转化为n个函数,每一个函数只有一个参数
//代码没写完
fn(a,b,c){
}
//上面多个参数的函数fn变为多个函数函数
f(){
const inner = ()=>{
return 判断终止条件?
}
}
f(a)
18. 发布订阅者模式
19.介绍一下promise,及其底层如何实现
概念
promise的一个最大用途,就是可以将异步操作的结果返回的数据通过then的回调函数拿到resolve回调函数所传的数据,然后再对数据进行操作。当然也可以使用await将其变为同步代码 拿到resolve()回调的数据。
Promise是一个对象,保存着未来将要结束的事件,她有两个特征:
1、对象的状态不受外部影响,Promise对象代表一个异步操作,有三种状态,pending进行中,fulfilled已成功,rejected已失败,只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也就是promise名字的由来
2、一旦状态改变,就不会再变,promise对象状态改变只有两种可能,从pending改到fulfilled或者从pending改到rejected,只要这两种情况发生,状态就凝固了,不会再改变,这个时候就称为定型resolved,
Promise的基本用法:
let promise1 = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok')
},1000)
})
promise1.then(function success(val){
console.log(val)
})
最简单代码实现promise
class PromiseM {
constructor(process) {
this.status = 'pending'
this.msg = ''
process(this.resolve.bind(this), this.reject.bind(this))
return this
}
resolve(val) {
this.status = 'fulfilled'
this.msg = val
}
reject(err) {
this.status = 'rejected'
this.msg = err
}
then(fufilled, reject) {
if (this.status === 'fulfilled') {
fufilled(this.msg)
}
if (this.status === 'rejected') {
reject(this.msg)
}
}
}
完备的手写promise
//完备的手写promise
/*
// 首看一下promise的基本使用好伐
let myPromise1 = new Promise((resolve, reject) => {
let time = setTimeout(() => {
console.log("王贺是个帅哥!");
// resolve("老铁66666!");
reject("你失败啦")
}, 1000)
});
myPromise1.then((res) => {
console.log(res);
}, (res) => {
console.log(res);
})
*/
//自己手写一个promise类
class myPromise {
// promise有三种状态,分别是pending进行中、fulfilled已成功、rejected已失败,只有异步操作的结果才可以决定当前是出于哪一种状态
// 静态常量,需要通过这个类来调用,而不是通过类的实例来调用
static PENDING = "进行中";
static FULFILLED = "已成功";
static REJECTED = "已失败";
// 实现resolve和reject的静态调用
static resolve = (parameter) => {
if (parameter instanceof myPromise) {
return parameter;
}
return new Promise((resolve, reject) => {
resolve(parameter)
})
}
static reject = (parameter) => {
// 先判断参数是否是promise对象,是的话直接返回,不是的话则创建一个promise对象,再返回
if (parameter instanceof myPromise) {
return parameter;
}
return new Promise((resolve, reject) => {
reject(parameter)
})
}
// 其参数是一个含有两个回调函数参数的函数
// fn的两个参数分别是resolve和reject
constructor(fn) {
/**
* let fn = (resolve, reject) => {
console.log(1);
resolve("我成功啦");
}
*
*/
// 初始状态设置为Pending(进行中)
this.status = myPromise.PENDING;
// 传给resolve和reject的参数,也是不管异步操作执行成功还是失败的结果
this.result = null;
// 这两数组是用来保存then传进来的两个函数(这种情况是指当执行异步代码是,防止还没执行resolve或者reject之前就执行了then,此时状态为pending
//所以需要判断then函数中在pending状态下所需要执行的操作
this.resolveCallbacks = [];
this.rejectCallbacks = [];
// 在执行所传函数之前需要先验证此函数是否会执行错误
try {
// 如果不修改this指向,则在resolve中的this.status将会报错,因为不修改this时,this指向
fn(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
// 成功的回调函数
resolve(result) {
// resolve和reject需要在事件循环末尾执行,所以需要加入定时器,resove、reject本身就是一个异步操作,肯定需要加入定时器
setTimeout(() => {
if (this.status == myPromise.PENDING) {
this.status = myPromise.FULFILLED;
this.result = result;
this.resolveCallbacks.forEach(callback => {
callback(result);
})
}
});
}
// 失败的回调函数
reject(result) {
// resolve和reject需要在事件循环末尾执行,所以需要加入定时器,resove、reject本身就是一个异步操作,肯定需要加入定时器
setTimeout(() => {
if (this.status == myPromise.PENDING) {
this.status = myPromise.REJECTED
this.result = result;
this.rejectCallbacks.forEach(callback => {
callback(result);
})
}
})
}
// then函数里面接收两个回调函数,一个成功onFULFILLED、一个失败onRejected
then(onFULFILLED, onREJECTED) {
// 为了达到链式编程,在调用then后需要重新返回一个promise对象
return new myPromise((resolve, reject) => {
// 判断then的两个参数是否都传进来了,或者可能onREJECTED为undefined,所以需要判断传过来的参数类型,如果为undefined,则就把它赋值为一个空函数
onFULFILLED = onFULFILLED === 'function' ? onFULFILLED : (res) => { console.log(res); };
onREJECTED = onREJECTED === 'function' ? onREJECTED : (res) => { console.log(res); }
// 若为执行中状态时,如果判断为此状态就说明是此时还没有执行resolve和reject函数,状态就不会为FULFILLED或REJECTED,所以就不会执行then的回调函数(即onFULFILLED和onREJECTED)
// 则需要把这两个函数暂时保存在构造函数的数组中,以备之后被resolve()执行或reject()执行
if (this.status == myPromise.PENDING) {
this.rejectCallbacks.push(onREJECTED);
this.resolveCallbacks.push(onFULFILLED);
}
setTimeout(() => {
// 判断状态,若为FULFILLED
if (this.status == myPromise.FULFILLED) {
let x = onFULFILLED(this.result);
// 我们需要判断其返回值是一个简单值还是一个promise对象,如果是一个返回值,就直接resolve,如果是promise对象,就查看promise对象返回的结果
this.resolvePromise(x, resolve, reject);
}
if (this.status == myPromise.REJECTED) {
reject(onRejected(this.result))
}
})
})
}
//定义函数用于判断返回的是普通值还是promise对象
resolvePromise(x, resolve, reject) {
//逻辑判断如果是promise对象
if (x instanceof myPromise) {
//如果是成功的回调 就把值传递进去 失败的话就把失败的原因传递进去
// x.then(success => resolve(success), error => reject(error))
//下面是简写写法
x.then(resolve, reject);
} else {
resolve(x);
}
}
}
// 自己手写的promise实例
let myPromise2 = new myPromise((resolve, reject) => {
console.log("第二步");
// resolve("我是你爹");
/**
* // 加入一个抛出异常来判断reject函数
throw new Error("报错啦");
*/
setTimeout(() => {
resolve("成功执行");
console.log("第四步");
})
});
// 因then为一个异步函数,所以then函数实现里面一定加了定时器setTimeout
myPromise2.then((res) => {
console.log(res);
}, () => { });
console.log("第三步");
Promise的静态方法Promise.all()
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
let promise1 = new Promise((resolve, reject) => {
resolve("第一步");
})
let promise2 = new Promise((resolve, reject) => {
resolve("第二步");
});
let promise3 = Promise.reject("失败")
// Promise的all方法是把多个promise对象构成一个数组作为参数传进来,然后重新返回一个promise对象,在新构成的promise对象中,他的then方法里的回调函数时一个resolve传过来的数组元素
let promise4 = Promise.all([promise1, promise2, promise3])
promise4.then((res) => {
console.log(res); //没有传入promise3时,返回[第一步,第二步]
}).catch(err => {
console.log(err); //传入[promise1, promise2, promise3]返回失败
})
需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
手写Promise.all()方法
//promiseAll()
// args为一个盛放promise元素的数组(仅仅作为数组,考虑简单)
function promiseAll(args) {
// 返回一个promise对象
return new Promise((resolve, reject) => {
if (args.length == 0) resolve([]);
let promiseResult = [];
let index = 0;
for (const item of args) {
//每个promise元素进行读取,获取他们的resolve或reject后返回的result
// 防止item不是一个promise对象,所以需要套一层
Promise.resolve(item).then((res) => {
promiseResult.push(res);
index++;
if (index == args.length) {
resolve(promiseResult)
}
}).catch(err => {
reject(err);
})
}
})
}
//正确答案写法,考虑到传入到的元素不仅仅是array,也有可能是map,set等复杂数据类型
// 输入不仅仅只有Array
// function promiseAll(args) {
// return new Promise((resolve, reject) => {
// const promiseResults = [];
// let iteratorIndex = 0;
// // 已完成的数量,用于最终的返回,不能直接用完成数量作为iteratorIndex
// // 输出顺序和完成顺序是两码事
// let fullCount = 0;
// // 用于迭代iterator数据
// for (const item of args) {
// // for of 遍历顺序,用于返回正确顺序的结果
// // 因iterator用forEach遍历后的key和value一样,所以必须存一份for of的 iteratorIndex
// let resultIndex = iteratorIndex;
// iteratorIndex += 1;
// // 包一层,以兼容非promise的情况
// Promise.resolve(item).then(res => {
// promiseResults[resultIndex] = res;
// fullCount += 1;
// // Iterator 接口的数据无法单纯的用length和size判断长度,不能局限于Array和 Map类型中
// if (fullCount === iteratorIndex) {
// resolve(promiseResults)
// }
// }).catch(err => {
// reject(err)
// })
// }
// // 处理空 iterator 的情况
// if (iteratorIndex === 0) {
// resolve(promiseResults)
// }
// }
// )
// }
let p1 = Promise.resolve("1");
let p2 = Promise.resolve("2");
let p3 = Promise.resolve("3");
let p4 = Promise.resolve("4");
// Promise.all([p1, p2, p3, p4]).then((res) => {
// console.log(res);
// });
promiseAll([p1, p2, p3, p4]).then((res) => {
console.log(res); //返回[1,2,3,4]
}).catch(err => {
console.log(err);
})
Promise的静态方法Promise.race()
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
//Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
Promise.race([promise1, promise2, promise3]).then((res) => {
console.log(res); //首先返回第一步,首先执行的时候promise1的数据
});
手写Promise.race()
//手写 promise的race()方法
promiseRace = function (array) {
if (!Array.isArray(array)) { return new Error('argument is no Array') }
return new Promise((resolve, reject) => {
array.forEach(item => {
Promise.resolve(item).then(data => {
// 注意一旦执行resolve就直接返回数据了,不在去执行以后的item了
resolve(data)
}).catch(err => {
reject(err)
})
})
})
}
17. es6的常用特性
参考回答:
promise,await/async,let、const、块级作用域、箭头函数
18. setInterval和setTimeout的区别
setInterval和setTimeout两函数都是定时器函数,不同的是setIterval是当定时器到时候会重复执行回调函数里的代码,而setTimeout不同,当定时器到时候只会执行一次。
19. 在vue脚手架中监听ref的事件是用$on(“事件”, callback)
mounted() {
var getTableList1 = () => {
console.log(1);
this.getTableList();
};
console.log(this.$refs.search); //获取refs="search"的dom元素
//给search元素添加input表单值改变change事件,并用采用防抖
this.$refs.search.$on("change", this.$debounce(getTableList1, 5000));
},
20. for in 和for of的区别
for in一般使用来遍历对象的(MDN不推荐使用for in来遍历数组,因为他会遍历到你设置数组的一些原型方法上)
比如:
let arr = [1, 3, 45, 4];
Array.prototype.set = function () {
return {
1: "wangeh"
}
}
for (const key in arr) {
console.log(key); //1 2 3 set
}
一般for in用来遍历对象,这是为了弥足for of不能遍历对象object的原因(object没有实现iterator迭代器接口,所以不能用迭代器的for of),所以就用for in 来遍历对象。
总结: 最主要区别是for in 遍历得到的属性时索引key,而for of 遍历得到的是索引对用的值value;其次就是for in不推荐遍历数组。
21. 执行上下文和作用域链
执行上下文:
执行上下文主要包括函数上下文和全局上下文。
函数上下文:
即每一个函数都有一个自己的执行上下文(也称为上下文),每一个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存于这个对象上。
全局上下文:
全局上下文是最外层的上下文,比如在浏览器中,全局上下文指的是window对象。函数上下文(上下文)在其所有代码都执行完毕后会被销毁,而全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器。
作用域链:
上下文在执行的时候会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域的最前端。如果上下文是函数,则其活动对象用作变量对象。作用域链中的下一个变量对象来自包含上下文,再下一个对象来自再下一个包含上下文。以此类推直至全局上下文。全局上下文的变量对象始终是作用域链的最后一个变量对象。
作用域链增强
虽然执行上下文主要有全局上下文和函数上下文两种,但有其他方式来增强作用域链。存在这两种方式:try/catch语句的catch块、with语句都会导致在作用域前端临时添加一个上下文。这个上下文在代码执行后会被删除。
对with语句来说,会向作用域链前端添加指定的对象;
对catch语句而言,则会创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明。
22. 原型的理解
无论何时,只要创建一个构造函数,就会按照特定的规则为这个函数创建一个prototype属性,此属性指向其原型对象。默认情况下,所有原型对象自动获得一个constructor的的属性,指向与之关联的构造函数。然后每个函数的prototype属性都有一个” _ proto_“ 属性指向对象的原型。且每一个实例都有_ _proto__属性指向实例的原型对象。
原型链
原型链定义为ES主要的继承方式(ES6之后又有了class创建类,用extends继承类),其基本思想就是通过原型 继承其父实例对象,这样子原型就会有其父原型的属性和方法。
这样子构造函数原型就会有一个_ proto 指针指向它的父原型,然后父原型的 _ proto__指针又指向Object原型,这样子通过 proto _指针相连接就构成了一个链,也称为原型链。
Fo.prototype = new Foo()之后形成的原型链。

// 实例的__proto__是指向构造函数的原型,而构造函数的原型的__proto__指针是指向其构造函数原型的父亲。另外一点任何构造函数都是继承于Object
// Teacher.prototype.__proto__指向Person的原型
//调用构造函数继承,使得Teacher没有使用默认原型,而是将其替换为Person的实例对象,这样就可以使得Teacher可以使用Person的属性和方法
Teacher.prototype = new Person();
前端工程化
23. css设置高度为宽度的2倍
- 正常使用css写,手写高度是宽度的2倍
- 使用less语法:
<style lang="less" scoped>
@width: 200px;
#box {
width: @width;
height: @width*3;
border: 1px solid pink;
}
</style>
- 用js写高度是宽度的2倍
<script>
let app = document.querySelector("#app")
console.log(app.style);
//法1,字符串截取px
// let width = app.style.width.substr(0, app.style.width.length - 2)
// 法2,将width变为数字类型
let width = parseInt(app.style.width)
console.log(width);
// app.style.width = 200 + "px"
app.style.height = 2 * width + "px"
</script>
- 使用css的var()配合calc()计算属性可实现css某一变量时另一变量的2倍
/* 在body中先定义变量 */
body {
--width: 100px;
}
#box {
width: var(--width);
/* var()配合calc()计算属性可实现 */
height: calc(var(--width)*2);
background-color: pink;
}
24. 为什么有了forEach,又出现了for in、for of
- 首先,forEach的主要作用就是用来遍历数组的,而且在forEach中,不能使用break或return来打断循环(forEach的缺点)。
- 而for in 可以break或者return。但是for in有一个最大的缺陷就是他会遍历数组元素原型上的属性。确实,for in的诞生并不是用来遍历数组的,而是遍历带有键值对的对象的。
- 而for of的诞生解决了for in的不足,他可以无缺陷的遍历数组array、set、map等有迭代器的可迭代对象。
25. HTTPS相比HTTP请求速度慢,为什么,怎么加速
首先http请求是依赖于tcp连接的,https请求不仅需要http的三次握手建立连接,他还需要ssl的握手(涉及到加密、密钥交换一些操作)进行连接,所以费时。
建议优先支持 RSA 和 ECDH_RSA 密钥交换算法,这样可以减少建立连接的时间。
26. reduce()方法
reduce()其实也是一个遍历数组的方法,只是他可以保存上一次return返回的数据
let arr = [1, 2, 3, 4, 5, 6, 7]
// 也可以理解为一个遍历数组的方法,cur为当前遍历的数组元素,prev是上次遍历数组元素return的数据, 第二个参数0为prev的初始化数据0
let sum = arr.reduce((prev, cur) => {
// console.log(cur);
console.log(prev);
return prev + cur
}, 0)
console.log(sum);
27. 为什么for循环代码块中含有异步函数时只能使用let,使用var时会发现结果不对
for循环是多个块,每执行一次循环就会产生一个块级作用域,那么如果采用var(注意var没有块级作用域概念),所以var变量不受for块作用域限制,所以每次循环var声明的变量其实都是同一个变量,导致所有块全是最终数,异步执行就自然这样了。
而使用let时有块级作用域概念,每次执行for循环都会保存这个块作用域下let声明的变量,这样在执行异步操作时就不会出现错误啦。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button type="button" class="btn" myTag='0'>aaa</button>
<button type="button" class="btn" myTag='1'>bbb</button>
<button type="button" class="btn" myTag='2'>ccc</button>
</body>
</html>
<script>
let btn = document.querySelectorAll('button');
// 使用var会执行数据异常,
//使用let可以解决
/*
for循环是多个块,每执行一次循环就会产生一个块级作用域,那么如果采用var(注意var没有块级作用域概念),所以var变量不受for块作用域限制,
所以每次循环var声明的变量其实都是同一个变量,导致所有块全是最终数,异步执行就自然这样了。
而使用let时有块级作用域概念,每次执行for循环都会保存这个块作用域下let声明的变量,这样在执行异步操作时就不会出现错误啦。
*/
for (var i = 0; i < btn.length; i++) {
btn[i].onclick = function () {
alert(i);
}
}
</script>
28.Object的一些属性
Object.values()返回一个对象的所有键值对的值形成的数组;
Object.keys()返回所有键形成的数组。
29.为普通对象obj实现可迭代协议
对象可迭代则必须为其加入可迭代协议属性[Symbol.iterator]:obj[Symbol.iterator]
// 为对象obj添加可迭代协议,使其能够使用for of迭代
let obj = {
1: "wh",
2: "xxx",
3: "wmg",
4: "ww",
length: 4,
test: function () {
console.log(1);
},
}
// 为obj加入可迭代协议,使其能够迭代
obj[Symbol.iterator] = function () {
// 自定义replace映射方法
function replace(key, value) {
// 判断此key是否不是一个数值
if (isNaN(key)) {
return undefined
}
return value
}
// 返回obj所有键值对的值构成的数组
let arr = Object.values(JSON.parse(JSON.stringify(obj, replace)))
let index = 0
let len = arr.length
console.log(arr);
// Symbol.iterator可迭代协议必须返回一个对象,此对象中包括next()方法,next()方法需要返回{value:,done},这是规定
return {
next: function () {
if (index < len) {
return {
value: arr[index++],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
for (const iterator of obj) {
console.log(iterator);
}
// let s = Symbol(Symbol.iterator)
// for (const i of obj) {
// console.log(i);
// }
let obj1 = JSON.stringify(obj)
// function replace(key, val) {
// // console.log(key);
// // console.log(val);
// console.log(isNaN(+key));
// return val
// }
// console.log(obj1);
30.v-show和v-if的区别
v-show 只是简单的控制元素的 display 属性,而 v-if 才是条件渲染(条件为真,元素将会被渲染,条件为假,元素会被销毁);
31. JWT是什么
JWT(Json web token)可以称之为一种token格式, 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
WT是由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
32. 什么是SPA、SSR
SPA,就是单页面应用,就是目前前端开发最常见的一种,整个网站由一个html页面构成。
可以这么理解:在vue中仅含有一个index.html,我们在开发时,仅仅是将app.vue动态渲染挂载到index.html中,然后根据路由的切换,来匹配不同的组件然后将组件渲染到router-view预加载组件中。这样就能仅仅写一个index.html就可以根据路由动态渲染组件元素了。
三大框架Angular Vue React都是SPA
采用spa可以使得页面内交互响应速度快,减轻服务器压力,但是项目首屏打开的很慢,需要先下载SPA所依赖的框架包及一些公共资源
SSR就是通过服务端去渲染页面,将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序,常用的比如 VUE的nuxt和React的next。
前端渲染的更快,更快的响应速度,但是同时会增加服务器压力
33.export default 和export的区别
export default导出的数据,在导入时可以改名字,并且不需要进行结构
比如:
//导出
export default let c = 2
//引入:
import c1 from "文件路径"
而对于export直接导出的数据,需要进行结构处理,比如
//导出
export let c = 2
let d = 3
let e = 4
let say = function(){}
exprt {d,e,say}
//导入,注意这里的名字一定要和导出文件中的变量保持一直
import {c,d,e,say} from "文件"
34. call、aplly、bind底层实现
call、apply、bind都是修改函数this指向的方法。不同在于call和apply都是立即执行,而bind是返回一函数,等待后续调用。
而call和apply非常类似,call除了第一个参数接收一个this指向的对象外,后续参数是接收一个参数列表call(obj,arg1,arg2…) 这里可以采用函数内置的arguments接收后续参数列表也可以采用拓展运算符…args进行收集;而bind(obj,arr)函数是接收一个数组。
注:这里是采用了ES6的拓展运算符实现。(也可以采用eval()纯ES5实现().)
call(obj,arg1,arg2…)
// call、apply、bind都是用来修改当前函数的this指向的
// 手写Function.prototype.call
Function.prototype.newCall = function (obj, ...args) {
if (obj === undefined || obj === null) {
obj = window
}
obj.fn = this //将函数设置为对象的属性,让obj对象设置一个方法,且此方法就是调用的方法
// 这里要传入参数
let res = obj.fn(...args) //让其执行
delete obj.p //当执行后就把此方法删去,这里利用delete关键字来删除对象上的属性
return res
}
var egg = {
name: "wh"
}
person.newCall(egg) //在未修改this绑定是,this指向person,也就是谁调用,this就指向谁
function person() {
console.log(this.name); //wh
}
apply(obj,argsArr)
// apply其实和call类似,只是apply的第二个参数是一个数组
Function.prototype.newApply = function (obj, arr) {
if (obj === undefined || obj === null) {
obj = window
}
obj.fn = this
let res
// 是否传递arr参数
if (arr) {
res = obj.fn(arr)
} else {
res = obj.fn()
}
delete obj.fn
return res
}
function person() {
console.log(this.name);
}
let obj = {
name: "wd"
}
person.newApply(obj, 2, 3)
bind(obj,arg1,arg2…)
Function.prototype.newBind = function (obj, ...args) {
let that = this
// 与call和apply不同,bind()绑定this的函数不会立即执行当前函数,而是返回一个函数等待以后调用
return function () {
// 其实返回的函数里面逻辑与call()绑定相同
obj.fn = that
let res = obj.fn(obj, ...args)
delete obj.fn
return res
}
}
function person() {
console.log(this.name);
}
let obj = {
name: "wd"
}
let fn = person.newBind(obj, 2, 3)
fn() //输出:wd
35. window.load和document.ready之间的区别
onload表示当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),才会触发此监听函数,所以当资源较多是,回调函数就会等待很久才能够执行,所以jquery就封装了doucment.ready方法
// onload表示当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),才会触发此监听函数
window.addEventListener("load", () => {
console.log(1);
})
//此代码等同于上面代码
window.onload = function () {
console.log(1);
}
document.ready是当仅仅dom树加载完毕之后就会触发。所以document.ready他会触发的比load早
36.描述一下浏览器缓存、服务器缓存、CDN缓存
CDN:Content Delivery Network,内容分发网络,它的存在可以避开互联网中影响数据传输的缓解,从而使内容传输速度更快,更稳定。CDN的原理是==,广泛采用各种缓存服务器,将它们分布到用户访问相对集中的网络中,在用户需要访问网站时,可以直接从最近的缓存服务器上拿出需要的内容。==
服务器缓存当前理解:当服务器中的某个数据使大量用户请求获取的,为了减少服务器向数据库请求多次高并发请求此数据而挤爆数据库,就会用到服务器缓存,将高频请求的数据存在服务器本地缓存内存中,也就是使用一个静态字典,根据key将值存在内存中,会新建一个类来保护缓存。
而浏览器缓存也就是协商缓存和强缓存,还有后来的localStorage和sessionStore这些。
37. js中严格模式和正常模式的区别
提出严格模式的目的:消除js的一些语法的不严谨之处,提高编译效率增加运行速度。
开启严格模式:
use strict;
严格模式与正常模式之前的区别:
a.创建变量
"use strict" //开启严格模式
// 在严格模式下,变量都必须先用var、let或const声明,然后再使用。
myName = "wh" //报错,变量未声明
b==.delete删除变量==
在正常模式下delete会删除失败,返回false,程序不会报错,因为delete关键字是用来删除对象属性的;
在严格模式,delete删除变量会导致程序出错,抛出syntaxError(语法错误)
//正常模式 3
let obj = { age: 23 }
delete age //会删除失败,返回false,因为delete关键字是用来删除对象属性的
delete obj.age //这样才能删除成功
console.log(age); //23
console.log(obj.age); //undefined
"use strict" //开启严格模式
let age = 23
delete age //报错syntaxError
c.对象的区别
在正常模式下,对象中由两个相同属性名是,默认用第二个替换第一个;
而在严格模式中,对象的属性名必须是唯一的。否则编译器会报错;
且会操作对象的属性导致错误:
- 为只读属性(writable:false)赋值会抛出typeError
- 为为不可配置的(nonconfigrable)的属性使用delete操作符会抛出TypeError
- 为不可扩展的(nonextensible)的对象添加属性同样会抛出TypeError
d. 函数的区别
- 命名函数的参数重名问题
在普通模式下,函数的参数名可以相同,但是在严格模式下参数名相同会报错!
- 函数中this指向不同
在正常模式下,this指向全局作用域window,而在严格模式下this指向undefined
- 函数不能创建在非函数代码块中
在严格模式中,如果将函数的创建放在if、for等非函数代码块中,就会报错
- 构造函数创建实例时new的区别
在正常模式中可以不使用new关键字创建实例,直接 Person()就能创建一个实例对象,只是里面的this是指向window的。
而在严格模式下,不能这样,因为在严格模式中构造函数里的this指向undefined
38. 什么是值类型,什么是引用类型
基本数据类型就是值类型,如string、number、Boolean、symbol、null、undefined,基本数据类型存放在栈空间中。
引用类型也就是复杂数据类型,如set、map、object、array等等。引用类型的变量名会保存在栈内存中,但是变量值会存储在堆内存中,引用类型的变量不会自动销毁,当没有引用变量引用它时,系统的垃圾回收机制会回收它
39.描述一下前端的堆和栈
栈:先进后出,自动分配释放,存放一些简单数据类型变量。
堆:用于存放引用数据类型的值,由程序员分配和释放,如果没有手动释放,则有垃圾回收机制自动释放
40. 描述一下移动端click、touch和tap的区别
click和tap都会触发点击事件,但是在移动端,click会有300-300ms延迟,所以一般都是采用tap(轻击)代替click作为点击事件。singleTap和doubleTap分别为单击和双击触发事件。
使用tap时会出现穿透,即点击会触发非当前层的点击事件(主要原因是当前元素发生了冒泡,冒泡到document)。
解决方法是使用github上的叫做fastclick的库,还是采用click实现,它能规避移动设备上click事件的延迟响应,dom ready时初始化在body上,如:
$(function(){
new FastClick(document.body);
})
touch是针对触屏手机上的触摸事件。现大多数触屏手机webkit内核提供了touch事件的监听
包含:touchstart touchmove touchend touchcancel四个事件
touchstart touchmove touchend事件可以类比于mousedown mouseover mouseup的触发
41.经典面试题,柯里化实现传递任意个参数以及传递多少次参数进行求和
指的就是将一个接收多个参数的函数,变为接收任意个参数返回一个函数的固定形式。
如:
sum(1,2)(3) //6
sum(1,2,3) //6
function add() {
// ES6的写法,深克隆一个数组对象,将arguments类数组转化为转化为数组对象
let args = Array.from(arguments)
// 也可以采用ES5写法,这里是采用数组的slice方法重新分割一个数组
// let args = Array.prototype.slice.call(arguments)
// 最终返回的函数
let newAdd = function () {
// 将之后传递的参数与之前传递的参数进行合并
args.push(...arguments)
// 进行递归返回
return newAdd
}
// toString隐形调用的特性,return一个函数会将函数隐式转化为string类型,所以我们可以重写toString方法返回最终的sum值
newAdd.toString = function () {
// pre代表之前return出去的累加结果,cur代表处理的当前数据,pre初始值设置为0
return args.reduce(function (pre, cur) {
return pre + cur
}, 0)
}
// 最终return的函数
return newAdd
}
// add()最终返回的既是一个函数又是最终求和的结果,即返回 f 6
console.log(add(2, 3)(1) == 6); //true
42. css的grid布局
grid布局即网格布局是一种二维布局,可以同时控制行和列的排布和对齐方式,grid布局由水平线和垂直线构成,两条水平线之间的区域叫做行轨道,两条垂直线的区域叫做列轨道。
使用这种布局方式只需要给外层容器(父容器)设置 display: grid; 此时容器的直接子元素就会自动称为grid布局的元素。
.grid {
display: grid
}
grid布局中的属性:
.grid {
/* 为grid盒子开启grid布局 */
display: grid;
/* 使用grid-template-column 属性指定每列的宽度,可以是固定宽度px,也可以是浮动宽度fr,fr是grid布局专用单位,代表grid剩余空间 */
/* grid-template-columns: 300px; */
/* 这里有三个数据,就说明grid的分布图为3列形式,这里都相等表示均分剩余空间 */
grid-template-columns: 1fr 1fr 1fr;
/* 使用column-gap属性设置列间距 */
column-gap: 10px;
/* 使用row-gap设置行间距 */
row-gap: 10px;
/* 或者使用gap属性统一设置,一下代码等同于上两行代码 */
gap: 10px;
}
对元素进行排列时,grid-template-areas属性的用法:
<style>
/* 使用grid布局实现三列布局 */
.grid {
/* 为grid盒子开启grid布局 */
display: grid;
/* 使用grid-template-column 属性指定每列的宽度,可以是固定宽度px,也可以是浮动宽度fr,fr是grid布局专用单位,代表grid剩余空间 */
/* grid-template-columns: 300px; */
/* 这里有三个数据,就说明grid的分布图为3列形式,这里都相等表示均分剩余空间 */
grid-template-columns: 1fr 1fr 1fr;
/* 使用column-gap属性设置列间距 */
column-gap: 10px;
/* 使用row-gap设置行间距 */
row-gap: 10px;
/* 或者使用gap属性统一设置,一下代码等同于上两行代码 */
gap: 10px;
}
.grid div {
/* width: 33vw; */
height: 70px;
background-color: skyblue;
border: 1px solid black;
}
.grid1 {
margin-top: 50px;
display: grid;
/* 排列元素我们可以使用grid-template-areas属性,注意这里是设置了三行表格,并且每列用3分进行分布的 */
grid-template-areas:
"header header header"
"sidebar content content"
"footer footer footer"
}
header {
/* 命名此空间为header */
grid-area: header;
background-color: skyblue;
height: 60px;
}
aside {
grid-area: sidebar;
background-color: red;
height: 60px;
}
main {
grid-area: content;
background-color: blue;
height: 60px;
}
footer {
grid-area: footer;
background-color: green;
height: 60px;
}
</style>
grid布局的对齐方式
grid布局的对齐方式和flex布局对齐方式类似,有水平方向的行轴和垂直方向的块轴。
在垂直方向上对齐子元素(对网格中的元素)是使用align-items:
.grid {
display: grid;
align-items: center|end //沿垂直轴居中、靠下对齐
}
在水平方向上对齐子元素是使用justify-items:
.grid {
display: grid;
justify-items: center|end|space-between //居中、靠右、两段对齐
}
当行轨道和列轨道(整体网格)小于grid容器时,还可以对轨道进行对齐:
.grid {
display: grid;
align-content: center|end; //在垂直方向上使用align-content设置对其方式:
justify-content: center|end|between //在水平方向使用justify-content设置对其方式
}
使用flex布局和grid布局实现三列表格布局:
flex布局:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
/* 使用flex布局实现三列排布 */
.grid {
display: flex;
flex-wrap: wrap;
/* box-sizing: border-box; */
width: 100vw;
/* height: 100vh; */
}
.grid div {
width: 33vw;
height: 70px;
background-color: skyblue;
border: 1px solid black;
}
/* 使用grid布局实现三列布局 */
</style>
<body>
<div class="grid">
<div class="column1">column1</div>
<div class="column2">column2</div>
<div class="column3">column3</div>
<div class="column4">column4</div>
<div class="column5">column5</div>
<div class="column6">column6</div>
</div>
</body>
</html>
grid布局实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
/* 使用flex布局实现三列排布 */
/* 使用grid布局实现三列布局 */
.grid {
/* 为grid盒子开启grid布局 */
display: grid;
/* 使用grid-template-column 属性指定每列的宽度,可以是固定宽度px,也可以是浮动宽度fr,fr是grid布局专用单位,代表grid剩余空间 */
/* grid-template-columns: 300px; */
/* 这里有三个数据,就说明grid的分布图为3列形式,这里都相等表示均分剩余空间 */
grid-template-columns: 1fr 1fr 1fr;
/* 使用column-gap属性设置列间距 */
column-gap: 10px;
/* 使用row-gap设置行间距 */
row-gap: 10px;
/* 或者使用gap属性统一设置,一下代码等同于上两行代码 */
gap: 10px;
}
.grid div {
/* width: 33vw; */
height: 70px;
background-color: skyblue;
border: 1px solid black;
}
</style>
<body>
<div class="grid">
<div class="column1">column1</div>
<div class="column2">column2</div>
<div class="column3">column3</div>
<div class="column4">column4</div>
<div class="column5">column5</div>
<div class="column6">column6</div>
</div>
</body>
</html>