web前端面试题总结
1 HTML
1.1 常见元素
块级元素
- h1-h6 标题
- table cation tbody thead tfoot tr 表格的元素
- ul ol li dt dd dl
- form
- p
- div
行内元素
- a
- b em i 废弃标签
- code
- label
- span
- th td
行内块级元素
- img
- input
- textarea
h5
- header
- footer
- nav
- article
- aside
- datalist
- section
- video
- audio
2 CSS
2.1 @import 和 link的区别
- @import 引入的样式会面页面加载完成后才会加载,而link会和页面同时加载
- link是标签,可以通过DOM去操作,而@import不可以
2.2 实现一个左侧固定宽度200px,右边自适应布局
1 | // html |
第一种方法:浮动1
2
3
4
5
6
7
8
9
10
11.left {
float: left;
width: 200px;
height: 500px;
background: darkcyan;
}
.right {
height: 500px;
background: deepskyblue;
}
第二种方法:绝对定位1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17body {
position: relative;
}
.left {
position: absolute;
left: 0;
top: 0;
width: 200px;
height: 500px;
background: darkcyan;
}
.right {
height: 500px;
background: deepskyblue;
}
第三种方法:flex1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.parent {
display: flex;
}
.left {
width: 200px;
height: 500px;
background: darkcyan;
}
.right {
height: 500px;
background: deepskyblue;
flex-grow: 1;
}
2.3 less定义一个圆角函数
1 | .radius(@r:50%) { |
2.4 w3c标准盒模型占位宽度是如何计算的? 它与IE盒模型有什么不同?
IE67的混杂模式,宽度是算上了border + padding1
width = border-left + padding-left + con_width + padding-right + border-right
标准盒模型是分开来计算的
2.5 box-sizing的属性值content-box 和 border-box的计算方式?
content-box 是w3c标准盒模型,其中的margin border padding width都是分别计算的,其中的width属性是只针对content的宽度。而border-box是IE67的混杂模型的盒模型计算方式,其中的width属性是盒模型的border + padding + content
2.6 常见移动端适配不同屏幕
- REM布局
- 流式布局
- flex布局
2.7 引入css的方式
- 行内式
- 内嵌式
- 外链式,使用link标签
- 使用@import引入css
2.8 写出position的属性值
- static
- relative
- absolute
- fixed
2.9 用box-shadow 写出单边上阴影效果(阴影半径与模糊半径不限)
1 | div { |
box-shadow: inset h v blur spreed color
内阴影 竖直 水平 模糊 半径 颜色
2.10 常见清除浮动的方式
- 给父级指定高度
- 父级最后加空元素
- 父级overflow: hidden
- 父级也一起浮动
2.11 css3新增特性
- border-radius
- text-shadow
- box-shadow
- linear-gradient
- box-sizing
- word-warp
- column-count
- display: box
2.12 a标签伪类的顺序
link -> visited -> hover -> active
原因如下:link和visited是常态,hover和active是即时状态,即时状态想要覆盖常态就必须要放到后面,否则无效。
同样,在常态里,visited是访问后的状态,想要覆盖未访问状态就要写到其后面;
即时态里,active想要覆盖hover就要写到后面
2.13 样式权重的排序
- !import 1000
- id 100
- class 10
- tag 1
2.14 display有哪些属性值,有什么作用
- none:元素不会显示,而且改元素现实的空间也不会保留
- inline: 将元素显示为内联元素,元素前后没有换行符
- block:将元素显示为块级元素,元素前后会带有换行符
- inline-block:行内块级元素
- list-item:此元素会作为列表显示
- table:此元素会作为块级表格来显示,表格前后带有换行符
- inherit:规定从父元素继承display属性的值
- box:弹性盒模型,c3新增
2.15 Flex
2.15.1 作用在父元素上的属性
display: flex
设置这个属性后就会成为flex怪异盒模型flex-direction
设置flex主轴的方向,属性值有:row/row-reverse/colum/colum-reverse
flex-wrap
设置换行的方式,属性值有:nowrap/wrap/wrap-reverse
justify-content
设置项目在主轴上的排列方式:属性值有
- flex-start
- flex-end
- center
- space-between 两端对齐,中间均分
- space-around 两侧有间隙,这个间隙是项目之间间隙的一半
align-items
项目在交叉轴上的对齐方式:属性值有
- flex-start
- flex-end
- center
- baseline
- stretch 默认值,占满空间
align-content
多根轴的对齐方式,如果只有一根轴就不生效
2.15.2 作用在项目上的属性
order
数值越小越靠前,默认为0flex-grow
放大比例,如何去分配默认空间,0不放大,1均分,如果按其他比例,可以单独给项目设置flex-shrink
缩小比例,0不缩小,1等比例缩小,默认值是1
复合写法是flex
align-self
单独设置对齐方式
3 JavaScript
3.1 AJAX
相关知识点:
- 跨域(80%以上会问到)
- fetch(替代xhr对象的东西)
- socket
3.1.1 JSONP
跨域是浏览器的行为
跨域需要服务端的配合,如果不能修改服务端,可以自己后台转发一下请求,毕竟后台请求是没有跨域限制的
跨域的方式有几种,面试官要是问跨域的方式的话,一般就是在问JSONP。和面试官讲请JSONP的原理也算一点加分项。
相关知识点:
- 浏览器的同源,协议、域名和端口号
- script标签的src属性没有跨域的限制
- 需要服务端配合,前端需要给后台传回调名称
总而言之,就是使用script标签把数据请求回来。
一般印象中,script标签就是外链一段js代码。现在写一个服务,当浏览器访问/js
时,就send('alert("js")')
,发送一段js代码。现在看是否会执行
可以看到js代码正常执行了。这个就等于直接往全局作用域里直接写了一行alert('js')
这个时候我们再返回一个JSON串回去
可以看到,直接报错了。这个就好比,直接往全局作用域里直接赤裸裸的扔了一个对象,没有任务操作。
假如我们在全局作用域里已经定义好了一个函数1
2
3function handle(data) {
console.log(data);
}
如果用script标签请求回来的数据,能直接用这个函数处理多好。所以后台给我们处理的时候1
res.send('handle({name: "chang"})')
这个返回的数据和上面一样,也相当于直接在全局作用域里写了个1
handle({name: "chang"});
这个时候我们早已经定义好了处理函数,那么这个函数就可以直接处理数据了
所以不管哪个工具封装的JSONP都是这个操作,也就是为什么要和后台定义回调函数的名称,因为后台不知道你在JS定义什么名称的函数来处理数据,一般这个回调函数名称是需要放在queryString
中的。后台需要截取,再拼接
以下就是简单的JSONP代码1
2
3
4
5
6function xxxx(content) {
console.log(content);
}
let script = document.createElement('script');
script.src = 'http://127.0.0.1:8000?cb=xxxx';
document.body.appendChild(script);
所以综上,jsonp只能是get请求
3.1.2 fetch
fetch是之前浏览器的xhr
对象的替代方案,现在使用的话,有浏览器兼容问题,毕竟有些浏览器是不支持的,可以使用whatwg-fetch
这个包来解决一下。
和原生的xhr一样,都不会直接使用原生,而是使用封装好的库。
和原生的xhr
相比,优点:
- 使用promise
- 进而可以使用
async/await
,写的就像同步代码一样爽1
2
3
4
5
6
7try {
let response = await fetch(url);
let data = await response.json();
console.log(data);
} catch(e) {
console.log("error:", e);
}
使用的坑:
- 默认请求是不带cookie的,需要设置
fetch(url, {credentials: 'same-origin'})
同域下发送cookiefetch(url, {credentials: 'include'})
跨域下发送cookie
- 服务返回的400和500错误,是不会触发reject的,只有网络错误导致请求不能完成时,fetch才会触发reject
3.1.3 socket
一种前后台通信的方案,后台可以主动给前台推送消息,而取代之前的短轮循和长轮循
3.2 JS输出问题
3.2.1 对JS异步的理解
说出下面数字的输出顺序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23console.time('setTimeout');
console.time('setImmediate');
setImmediate(() => {
console.log(1);
console.timeEnd('setImmediate');
});
setTimeout(() => {
console.log(2);
console.timeEnd('setTimeout');
}, 0);
process.nextTick(() => console.log(3));
new Promise(function(resolve){
console.log(4);
resolve();
console.log(5);
}).then(function(){
console.log(6);
});
(() => console.log(7))();
答案:1
4 5 7 3 6 2 1
解析:同步代码先顺序执行,然后是本轮循环的异步任务和次轮循环的异步任务。process.nextTick
和Promise
就属于本轮循环的,而且process.nextTick
最先执行
其他两个属性次轮循环的异步任务,在setTimeout
的延迟时间为4ms及以下时,setTimeout
会先执行
3.2.2 JS的逗号语句
1 | var k; |
3.2.3 alert()
alert的时候会自动调用toString()
方法1
2alert([1, 3]); // -> 1, 3
alert({name: 'chang'}); // -> [object Object]
3.2.4 异步
1 | for (var i = 0; i < 5; i++) { |
结果是连续输出5个5。setTimeout执行时,i已经是5了,全部输出5很正常,现在的问题是时间间隔是多少。正确的姿势是,间隔时间是不相同的。(实验得知)
3.2.5 一些和预解释相关的东西
1 | var obj = {name: 'Joo', age: 30}; |
3.3 数组
3.3.1 数组的方法
方法 | 参数 | 返回值 | 是否影响原数组 | 发布版本 |
---|---|---|---|---|
push | length | y | ||
pop | item | y | ||
shift | item | y | ||
unshift | length | y | ||
splice | index, length, item | array | y | |
slice | index, index | array | n | |
concat | item/array | array | n | |
sort | function(){} | array | y | |
toString | no | string | n | |
join | 符号 | string | n | |
reverse | y | |||
indexOf/lastIndexOf | ||||
forEach | ||||
every | ||||
some | ||||
filter | ||||
map | ||||
reduce/reduceRight | ||||
find/findIndex | ||||
includes | ||||
fill | ||||
entries/keys | ||||
Array.from | ||||
Array.of |
3.3.2 多维数组转一维数组
第一种方法:使用递归1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var arr = [1, 2, [3, 4, [5, 6], 7], 8, [9, 10, [11]], 12];
var spreadArr = [];
function spread(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] instanceof Array) {
spread(arr[i]);
} else {
spreadArr.push(arr[i]);
}
}
}
spread(arr);
console.log(spreadArr);
3.3.3 数组相关的算法
- 冒泡排序
- 快速排序
- 插入排序
- 去重
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function bubbleSort(arr) {
let isCompleted = true;
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
isCompleted = false;
}
}
if (isCompleted) {
console.log('提前完成');
return arr;
} else {
isCompleted = true;
}
}
isCompleted = null;
return arr;
}
1 | function insertSort(arr) { |
1 | function quickSort(arr) { |
1 | function unique2(arr) { |
使用数组的filter进行去重1
2
3
4
5function unique(arr) {
return arr.filter(function(item, idx, array) {
return array.indexOf(item) === idx;
})
}
3.4 继承
3.4.1 原型式继承
1 | function A(name) { |
3.4.2 call继承
1 | function SuperType(age) { |
3.4.3 组合继承
1 | function SuperType() { |
3.4.4 原型式继承
1 | /* |
3.4.5 __proto__
1 | function SuperType() { |
3.4.6 setProtoOf
1 | /* |
3.5 其他
3.5.1 两个页面的通讯方式
- url
- localStorage
- cookie
3.5.2 统计页面中类名的个数
1 | var os = document.getElementsByTagName('*'); |
3.6 ES6 +
let/const
- 解构赋值
- 字符串
- 模板字符串
includes/startsWith/endsWith
repeat
- 函数
- 箭头函数
- 参数默认值
- 剩余参数
- 双冒号
- 数组
- 扩展运算符
entries/keys/values
Array.from
将类数组转化成数组Array.of
将一级值转成数组includes
- 对象的扩展
- 简洁表达形式
- 属性表达式
- 函数的name
Object.assign
合并对象,浅拷贝Object.keys()/Object.values()/Object.entries()
- 扩展运算符
- 解构
Symbol
第6种基本数据类型的值let s = Symbol() // 不需要new
Set
可用于数组去重1
2
3var arr = [1, 1, 1, 1];
var s = new Set(arr);
var newArr = [...s];Map
和Object
相比,key值可以是任何值proxy
做目标对象的读写操作的拦截Promise
async/await
Class/extends
Module
4 代码管理工具
4.1 GIT
常用命令(结合自己常用)
4.1.1 提交4部曲
git add
git commit
git pull
git push
在多人协作开发的时候,如果不pull
是绝对push
不上去的
4.1.2 查看提交记录
git log
如果想查看简单点就是git log --pretty=oneline
想看是某个人的提交历史git log --author=changzhn
想查看某个文件的提交历史git log -- filename
想在芒芒提交中搜索自己印象关键字git log --grep=xxx
终于找到那个提交的记录了,想看看提交了啥子内容,提交的ID截取前几位就可以了git show hash
4.1.3 分支管理
查看当前的分支
git branch
新建个分支并且切换过去git checkout -b develop
切换分支git checkout develop/release/master
注意:切换分支的时候,在当前分支修改的内容也是带过去,这一点很恶心,所以切换分支前,先使用下面这个命令,来看当前分支的状态git status
如果有修改(红色)或者添加(绿色)的内容,就要使用提交4部曲的add
和commit
来产生版本号后,才能切换分支
或者,使用下面这个命令,将这个分支上的所有修改放入一个异次元…git stash
再切换回来想恢复修改的时候可以使用git stash list
想看,往异次元中放入了几次,如果只想恢复最近的一次的话git stash pop stash@{0}
比如切到release
分支上想合并develop
分支
git merge develop
在合并的时候,很不幸运的是冲突了
这个时候就需要解决冲突,在当前分支上,使用提交4部曲,为这次解决冲突产生一个版本号
4.1.4 版本管理
找到版本号的时候想回退代码
git reset --HARD hash
比如现在将代码推到过程,后来发现这真是一次垃圾的提交,在本地使用代码回退上一个版本的时候,想再push
上去,这个时候发现是推不上去的。git push origin master -f
可以使用-f
来暴力提交