前端面试笔记
PS. 这些很多知识点其实都可以单拎出来讲一个篇章, 后面我会详细做下总结。
let的一个知识点
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。在代码块中,使用let命令声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死亡”
1 | // 因为setTimeout是异步函数,所以会延后执行。循环会先走完,所以i是10 |
Object.keys()和Reflect.ownKeys()的区别
Object.keys()
返回自己的可枚举属性
Reflect.ownKeys()
返回所有的自己属性,不管可枚举还是不可枚举,所以可以遍历出方法属性
实现一个浅拷贝
1 | // 浅拷贝 |
实现一个简易深拷贝
1 | // 判断是否为对象,保留是数组的情况。typeof [] === 'object' |
lodash常用方法
1 | // 过滤数组 |
数组去重
var arr = [1,2,3,3,3,3,4,5,5];
var newArr = [...new Set(arr)];
对象数组去重
1 |
|
简易数组扁平化
1 | function flatten(arr) { |
小trick
1 | // a b 两变量互换 |
实现add(1)(2)(3)…
1 | function add(x) { |
js节流与防抖
防抖:规定只在用户最后一次点击后开始计时执行事件,只要用户再次点击就从这次点击开始计时。适合防止用户暴力点击等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 防抖
function debounce(fn, delay) {
var timer = null;
// 利用闭包
return function() {
// 说明在delay时间内,内存中已经有timer在执行了
if (timer !== null) {
// 清除定时器
clearTimeout(timer);
}
// 清除后,又重新开始计时
timer = setTimeout(fn, delay);
}
}
function handle() {
console.log('I am 防抖函数处理的事件');
}
window.addEventListner('click', debounce(handle, 1000))
节流:规定在一定时间内,只执行一次事件,适合滚动加载数据
1 | // 节流 通过时间戳实现 |
一道面试题
1 | var a = { n: 1 }; |
0.1 + 0.2 === 0.3
解决方法可以设置一个误差范围值,通常称为“机器精度”(machine epsilon)
从es6开始,可以用Number.EPSILON去解决
es6之前版本写polyfill:1
2
3
4
5
6
7
8
9
10
11if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
function isNumberEqual(num1, num2) {
// Math.abs()返回一个绝对值
return Math.abs(num2 - num1) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
isNumberEqual(a, b); // true
a === b; // false
一般解决float算数问题可以用,(0.1 * 100 + 0.2 * 100)/100 === 0.3 解决
React生命周期
start
contructor
static getDerivedStateFromProps
componentWillMount
render
componentDidMount
runtime
componentWillUnmount
事件冒泡、捕获、委托
事件捕获&冒泡:DOM事件流-Document到触发事件的那个节点一层层向下捕获直到事件源,然后再向上冒泡回到document。
Tip:IE提出了事件冒泡,Netscape提出了事件捕获。
事件委托:利用事件冒泡原理,把本应该添加到某个元素上的事件委托给他的父级,从而减少DOM交互。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<div id="father">
<h5 id="child1">hello</h5>
<h5 id="child2">world</h5>
<h5>lucky</h5>
</div>
<script type="text/javascript">
// 事件委托
var father = document.getElementById('father').
father.addEventListener('click', function(e){
var e = e || window.event;
if (e.target.nodeName.toLowerCase() == 'h5') {
alert(e.target.innerHTML);
}
});
// AddEventListener的第三个参数,默认为false(冒泡流)
// 如果更改成true,就定义事件为捕获流。
</script>
redux-saga
- 集中处理了所有异步操作,异步接口一幕了解
- action是普通对象,这跟redux同步的action一模一样
- 通过effect, 方便异步接口测试
- 通过worker和watcher可以实现非阻塞异步调用,并且同时可以实现非阻塞调用下的事件监听
- 异步操作流程是可以控制的,可以随时取消异步操作
优化网页性能
gzip
减小服务器返回文件的大小,让浏览器本地解压缩 再渲染html, 但需注意下cpu损耗的问题
- 浏览器需发送一个请求头,request header里设置Accept-Encoding: gzip
- 如果服务器压缩了文件,会返回Content-Encoding: gzip
适当引入图片转base64
减少了一次Http请求数,但是Css的体积也会随之增大一些,适用于极小或者极简单(背景图)。可以当成本地图片,重复使用不会重复请求。没有跨域问题,无需考虑缓存。可以被gzip。
js文件位置
放在</body>前, 防止浏览器下载并执行js文件,阻塞了html和css的渲染。或者在<script>中加入defer参数,延迟下载该js文件直到html渲染完成。加入async参数可以让浏览器异步下载js,不影响html的渲染
减少请求数
- js/css压缩合并
- 雪碧图
- 图片懒加载
- 使用Svg图
CDN
选择优秀的CDN服务,可以适当提高网页速度
设置http header
request header中设置 Cache-Control: max-age=123456000
尽量使用ajax get
在进行ajax请求时,尽量选择get方法,这样可以使用客户端缓存,提高请求速度
前端跨域解决方案
1. jsonp
https://www.kuayu.com/admin?name=joe&callback=myCall 只适合简单跨域解决,只能用get请求方式,并且需要后端配合返回callback()
// 服务端需给回调函数返参
function myCall(res) {
// do some render function
console.log(res);
}
2. iframe相关
3. nginx代理跨域
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
nginx具体配置:
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
前端代码示例:
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
4. Nodejs中间件代理
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
5. WebSocket协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
6. 跨域资源共享(CORS)
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
7. postMessage跨域
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
a.) 页面和其打开的新窗口的数据传递
b.) 多窗口之间消息传递
c.) 页面与嵌套的iframe消息传递
d.) 上面三个场景的跨域数据传递
总结
简单的跨域请求jsonp即可,复杂的cors,窗口之间JS跨域postMessage,开发环境下接口跨域用nginx反向代理或node中间件比较方便
http缓存
缓存的开关:cache-control, pragma =>{ pragma, Expires } (过时)
缓存校验:Last-Modified, Expires: GMT时间, etag (根据类似md5算法对服务器中的资源进行计算出值,然后作对比)
#CSS
BFC
BFC: Block Fromatting Context 块级格式上下文
触发方式
- float的值不是none。
- position的值不是static或者relative。
- display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- overflow的值不是visible
三栏布局
- flex布局,左右设固定宽度,中间设置flex-grow: 1 (flex-shrink: 0)
- 利用float: left, 做圣杯布局&双飞燕布局。后者比前者多一个父div,但少了很多css代码
- 绝对定位
- grid
- table
- float + margin (left, right, main)不推荐,不利于seo