题目 01
请写出一个符合 W3C 规范的 HTML 文件,要求:
页面标题为“我的页面”;
页面中引入一个外部 CSS 文件,文件路径为
/style.css
;页面中引入一个外部 CSS 文件,路径为
/print.css
,该文件仅在打印时生效;页面中引入一个外部 CSS 文件,路径为
/mobile.css
,该文件仅在设备宽度小于 500 像素时生效;页面中引入一个外部 JS 文件,路径为
/main.js
;页面中引入一个外部 JS 文件,路径为
/gbk.js
,文件编码为 GBK;页面中添加一个
<svg>
标签,里面有一个直径为 100 像素的圆圈,颜色随意;注意题目中的路径。
回答:
1 |
|
补充:
大部分是扩展知识,例如媒体查询的设置,<svg>
标签的基本用法,都能很轻松的查阅到;另外给出的文件路径不能直接使用,不然就代表根目录了,一般不会这样存放资源;要么去除斜杆,要么直接在斜杠前加个点,为了保险起见,还是加了个点,表示同一层级的目录下。
题目 02
2016年腾讯前端面试题:移动端是怎么做适配的?
回答要点:
<meta name="viewport">
;媒体查询;
动态 rem 方案。
回答:
一般要将页面宽度适配为设备宽度,除了使用 flex 布局和新的宽度单位 vw / vh 以外,在 HTML 文件的
<head>
标签中进行视口设置也是必要的,内容如下:<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
content
属性中的内容含义依次是:页面宽度等于设备宽度,用户缩放禁用,初识缩放比例 1.0 倍,最大缩放比例 1.0 倍,最小缩放比例 1.0 倍。如果还想做 PC 端页面的响应式适配,也就是让页面在不同的分辨率拥有不同的样式,除了类似 Bootstrap 中的“十二栅格”,还有就是媒体查询了,可以使用 CSS 中的媒体查询功能,来匹配不同宽度页面下的样式,例如:
1
2
3
4
5@media all and(max-width: 768px) { /* 以下样式在所有设备中最大支持到宽度为768px,超过便失效 */
body {
font: bold 12px/1.6 '微软雅黑';
}
}媒体查询是非常好用的,但是缺点是如果支持的分辨率太多,那就需要写很多的样式才行,比较麻烦。
如果是以小屏设备(主要是手机)为主,但是手机分辨率太多,这时可以使用 em 单位或 rem 单位。这两个长度单位会根据设置好的与像素的比例,在不同的页面宽度中,保持相同比例的像素大小。em 单位比较繁琐,因为它的大小是基于父元素文字大小的,如果层级过多,将会涉及大量的计算,一般不推荐使用;而 rem 的大小,是基于根元素的文字大小,这就比较统一且好算,一般将首要的支持设备页面中(或设计图的大小),根元素文字大小设置为 100px 是最好用的了,不设置为 10px 可以避免很多麻烦哟!
那么如何保证不同宽度设备下页面根元素的像素比例呢?最好还是用 JS 方法实现,在页面加载完和窗口宽度改变时更新根元素上的文字大小即可,以安卓手机的设计图宽度,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14!function (n) {
var e = n.document,
t = e.documentElement,
o = "orientationchange" in n ? "orientationchange" : "resize",
a = function () {
var n = t.clientWidth || 360;
t.style.fontSize = n / 360 * 100 + "px";
};
if (e.addEventListener) {
n.addEventListener(o, a, !1);
e.addEventListener("DOMContentLoaded", a, !1);
}
}(window);
补充:
虽然没有看过上面的系列课程,但是好歹做过一些移动端适配的小页面,基本上个人所知道的移动端适配的方法,上面都提到了。
题目 03
2017年腾讯前端实习面试题(二面):用过 CSS3 吗?实现圆角矩形和阴影怎么做?
回答:
当然用过了……
圆角矩形比较简单,可以用
border-radius
这个属性,它可以同时设置四个圆角边框,半径单位可以是像素,也可以是百分比;每个角也可以设置两个不同的半径;设置顺序是:左上、右上、右下、左下,如果只写一个,那么表示设置统一值;也可以分别设置,但比较麻烦。例如:1
2
3
4
5
6
7
8
9
10.test {
width: 100px;
height: 40px;
border-radius: 4px 6px 5px 3% / 2%;
/* 相当于 */
border-top-left-radius: 4px 2%;
border-top-right-radius: 6px 2%;
border-bottom-right-radius: 5px 2%;
border-bottom-left-radius: 3% 2%;
}阴影问的应该是盒子阴影吧……
如果是文字阴影,就一个属性
text-shadow
,属性值依次为阴影的:水平位置、垂直位置、模糊距离(程度)、颜色;两个位置是必需的,其他不写范围就是 0 ,颜色默认是文字颜色;可以给同一文字设置多个阴影,多个设置用逗号隔开。如果是盒子阴影就又复杂一些,使用
box-shadow
属性,属性值依次为阴影的:水品位置、垂直位置、模糊距离(程度)、大小、颜色、内侧或外侧;位置都是必须的,但是可以为 0 或负值,其他的都好理解,最后一个内侧和外侧意思是将阴影添加到盒子边内部还是外部;多个阴影设置可以用逗号分隔,另外,阴影可以在某些时候模拟边框哟!示例代码:1
2
3.test {
box-shadow: 3px 3px 5px 5px #ccc, 5px 5px 10px 10px #666; /* 立体效果 */
}
补充:
基本上用过的话就没什么,主要是把属性顺序搞清楚,不清楚也很容易查到文档;另外分开写的属性,实际很少用。
题目 04
出处同上:什么是闭包,闭包的用途是什么?
回答:
如果一个函数使用了它范围外的变量,那么(这个函数加这个变量)就叫做闭包;也就是说,闭包就是能够读取其他函数内部变量的函数,以及它读取的这个变量的合称!?
用途太广泛了,其实我们经常不可避免的使用着闭包,主要就是用来读取函数范围外的变量,例如:
1
2
3
4
5
6
7var a = 100;
function sum() {
return a + 20;
}
console.log(sum()); // 120另外就是作为钩子,使一些局部变量的值始终保持在内存中,以供外部使用并且不暴露,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function foo() {
var num = 100;
sum = function () {
num += 10;
};
function bar() {
console.log(num);
}
return bar;
}
var result = foo();
result(); // 100
sum();
result(); // 110循环中也可以使用闭包来解决很多问题,例如常见的循环添加点击事件:
1
2
3
4
5
6
7for (var j = 0; j < btns.length; j++) {
!function (num) {
btns[j].onclick = function () {
console.log('按钮' + (num + 1));
}
}(j);
}闭包还可以创建私有变量,外部可以获取和修改,但是却无法得知内部的结构,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function Person(name) {
var _age;
function setAge(age) {
_age = age;
}
function getAge() {
return _age;
}
return {
name: name,
setAge: setAge,
getAge: getAge
}
}
var p1 = new Person('lily');
p1.setAge(18);
console.log(p1.getAge()); // 18
补充:
虽然经常在不经意间使用最常见的“闭包”,也能理解闭包的原理,但是实际开发的时候自己用到的复杂一些的闭包不是很多,希望老师讲一些实战中哪些地方常用闭包来解决问题。
题目 05
出处同上:call()
、apply()
、bind()
的用法分别是什么?
回答:
这三个方法都能改变调用函数中 this
的指向(作用域),并且可以向调用函数传递参数;三者的用法分别是:
call()
方法的第一个参数就是用来设置this
指向的,第二个及以后的参数是指定的参数列表,适合参数较少时使用;apply()
方法的作用与call()
方法基本相同,只不过它只接收两个参数,第一个也是this
的值,而第二个是一个数组,这个数组中可以保存指定的参数列表,即使只传一个参数,也要以数组的形式,故适合参数较多时使用;bind()
方法的参数形式与call()
方法相同,但使用方式与前两种不同,前两种方法是没有返回值的,而此方法返回一个绑定了固定this
值的新函数;个人理解就是,前两种是临时工,bind()
方法是合同工……下面是三种方法的示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27const cat = {
name: '大猫',
eatFish(...args) {
console.log(args);
console.log(this.name + '吃鱼');
}
};
const dog = {
name: '二哈',
eatBone(...args) {
console.log(args);
console.log(this.name + '吃骨头');
}
};
cat.eatFish.call(dog, '汪汪', 'call'); // 二哈临时可以吃鱼;
dog.eatBone.call(cat, '喵喵', 'call'); // 大猫临时可以吃骨头;
cat.eatFish.apply(dog, ['汪汪', 'apply']); // 二哈临时可以吃鱼;
dog.eatBone.apply(cat, ['喵喵', 'apply']); // 大猫临时可以吃骨头;
let test1 = cat.eatFish.bind(dog, '汪汪', 'bind');
let test2 = dog.eatBone.bind(cat, '喵喵', 'bind');
test1(); // 二哈学会了吃鱼;
test2(); // 大猫学会了吃骨头;
补充:
call()
和 apply()
常常在继承属性的时候使用,但是 bind()
方法是新出的,个人没怎么接触过,但是都是相关联的方法,用法还是比较好理解的。
题目 06
出处同上:请说出至少 8 个 HTTP 状态码,并描述各状态码的意义。 例如:状态码 200 表示响应成功。
回答:
状态码有 5 类 60 余种,常用的一般 14 种:
1XX:称为“信息性状态码”,表示接收的请求正在处理;不常用。
2XX:称为“成功状态码”,表示请求正常处理完毕。
200 OK,表示从客户端发来的请求在服务器端被正常处理了。
204 No Content,表示服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。
206 Partial Content,表示客户端进行了范围请求,而服务器成功执行了这部分的 GET 请求。
3XX:称为“重定向状态码”,表示需要进行附加操作以完成请求。
301 Moved Permanently,永久性重定向;表示请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI。
302 Found,临时性重定向;表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问。
303 See Other,表示由于请求对应的资源存在着另一个 URI,应使用 GET 方法定向获取请求的资源。
304 Not Modified,表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但因为发生了请求不能满足条件的情况后,便会直接返回此状态码。
307 Temporary Redirect,临时重定向;该状态码与 302 Found 含义相同,但此状态码遵循浏览器标准,不会从 POST 变成 GET。
4XX:称为“客户端错误状态码”,表示服务器无法处理请求。
400 Bad Request,表示请求报文中存在语法错误;但是浏览器会像 200 OK 一样对待该状态码。
401 Unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息。
403 Forbidden,表示对请求资源的访问被服务器拒绝了,服务器没有必要给出拒绝理由,但一般是应为访问权限问题。
404 Not Found,表示服务器上无法找到请求的资源,除此之外,也可以在服务端拒绝请求且不想说明理由时使用。
5XX:称为“服务器错误状态码”,表示服务器处理请求出错。
500 Internal Server Error,表示服务器端在执行请求时发生了错误,也有可能是 Web 应用存在 bug 或某些临时的故障。
503 Service Unavailable,表示服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
其实状态码经常和状况不一致,但是可能用户察觉不到,这种是正常情况。
补充:
对于状态码,死记硬背也可以,但是会很容易忘记;如果结合实际状况记忆,就会比较深刻了。明明很方便就能查到,而且经验多了自然就记住一些常用的状态码了,不是很理解为什么要考这些。
题目 07
出处同上:请写出一个 HTTP POST 请求的内容,包括四部分。 其中第四部分的内容是username=ff&password=123,第二部分必须含有 Content-Type 字段,请求的路径为 /path。
回答:
1 | POST /path HTTP/1.1 |
补充:
请求最多包含四部分,最少包含三部分(第四部分可以为空)。
第三部分永远都是一个回车
\n
,主要是用来区分第二部分和第四部分内容的。动词有 GET(获取)、POST(新增或上传)、PUT(整体更新)、PATCH(局部更新)、DELETE(删除)、HEAD、OPTIONS等。
请求中的路径包括“查询参数”,但不包括“锚点”;因为“锚点”不是由服务器识别,而是由浏览器识别的。
如果没有写路径,那么路径默认为
/
,代表根目录。第二部分中的“Content-Type”标注了第四部分的格式。
题目 08
请说出至少三种排序的思路,这三种排序的时间复杂度分别为:
O(n * n)
O(n log2 n)
O(n + max)
回答:
冒泡排序:从第一个数字开始,每次比较相邻的两个数字,将符合条件的数字(较大或较小)与另一个数字交换位置,如此执行一轮,第一个数字便是符合条件的数字;第二轮则从第二个数字开始,轮次与数字位置关联;最后一轮结束,数字便按顺序排列好了。
快速排序:在数组中随机选一个数字(或默认第一个数字),数组中小于等于此数字的放在此数字前面,大于此数字的放在此数字后面,完成后,再对这个数字两边的数组进行相同动作,重复此过程直到剩下一个数字便结束。
基数排序:只能排正整数,基于桶排序;0 ~ 9 共 10 个桶,先根据所有数字的个位数进行入桶,即个位为 0 的按顺序放入 0 组,为 1 的放入 1 组,以此类推;完成个位入桶,在按照 10 个桶的从大到小顺序出桶,出桶完毕后再按所有数字的十位数进行入桶,重复此操作,直到所有数字的最大位数入桶出桶完毕,数字便按顺序排好了。
补充:
常见的第一种还有选择排序,第二种还有堆排序,第三种貌似就有基数排序。
题目 09
著名面试题:一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
这一题是在挖掘你的知识边界,所以你知道多少就要答多少。
回答:
浏览器需要知道这个 URL 中包含的域名对应的服务器 IP,那么首先要对域名进行 DNS 解析:浏览器对自己的 DNS 缓存、操作系统的 DNS 缓存以及操作系统的 hosts 文件逐个进行搜索,看看有没有这域名对应的 IP 地址,有便停止搜索,没有便通知操作系统发送一个 DNS 请求到本地的 DNS 服务器,DNS 服务器将解析的 IP 地址返回给操作系统,操作系统缓存一份,然后给浏览器,浏览器也缓存一份,现在浏览器得到了域名对应的 IP 地址。
得到 IP 后,浏览器便通知主机(你的电脑)开始与服务器通过 TCP 协议建立连接,这个过程分三步,又称为“三次握手”:
主机向服务器发送一个请求,表示建立连接的意愿;
服务器收到请求后,向主机发一个响应信息,表示同意连接;
主机收到了响应信息后,给服务器也发送一个响应信息,然后它们之间的连接建立完成。
主机与服务器连接成功后,浏览器便开始请求网页数据:
浏览器根据 URL 的内容生成 HTTP 请求,请求中包含文件位置、文件类型等信息,并将请求通过主机发送给服务器;
服务器收到请求,根据 HTTP 请求内容中的要求准备相应的 HTML 文件;
服务器将准备好的 HTML 文件通过主机发送给浏览器;
浏览器边接收边根据 DOM 树解析页面,如果页面上有其他需要的外部文件(如 CSS、JS文件),浏览器会继续向服务器发送请求并接收文件;
浏览器自上而下加载并展示页面,遇到外部文件请求得到后,加载并解析这些必要的文件,然后才会继续向下加载、渲染页面,这是一个同步(一条道走到黑)的过程,直到页面上所有文件加载、解析、渲染完毕。
页面展示完成后,主机与服务器断开连接,需要四步,称为“四次挥手”:
主机得知浏览器页面加载完成后,向服务器发送断开连接的请求;
服务器收到断开连接请求后,向主机发送“收到断开请求”的信息,但不会现在断开,因为可能还有数据未发送完毕;
服务器向主机发送“连接可以断开”的信息;
主机收到“连接可以断开”的信息后,向服务器发送“确认连接断开”信息,服务器收到确认信息后便断开连接;
至此,主机与服务器通信完成,页面也展示完毕了。
补充:
这是在个人理解范围内的粗糙回答,一些技术上的细节因为没有深度了解过,所以也不好照抄,例如 DNS 解析过程中涉及的 LDNS、 TCP 协议连接的过程、页面渲染中涉及的浏览器背后的工作原理等。
题目 10
著名面试题:如何实现数组去重?假设有数组 array = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4]
,你要写一个函数 unique,使得 unique(array)
的值为 [1, 5, 2, 3, 4]
,也就是把重复的值都去掉,只保留不重复的值。
要求:不要做多重循环,只能遍历一次请给出两种方案,一种能在 ES 5 环境中运行,一种能在 ES 6 环境中运行(提示 ES6 环境多了一个 Set 对象)。
回答:
1 | var arr = [3, 33, 6, 774, 1235, 23, 3, 33, 321, 6745, 3, 774]; |
补充:
第一种方法利用对象属性名不能重复的规则,循环数组,以数组项内的数据为属性名,向对象内添加属性,同时将此数据填入新数组;遇到重复数据时,由于有对象属性筛选,所以不会添加到新数组;这种方式只适合简单数组数据,复杂数据还是多重循环靠谱。
第二种是利用 indexOf()
方法只找第一个符合条件的数据下标的特性来完成筛选的。
第三种 ES6 新方法,传入 Set()
对象中的元素只会出现一次,也就是说元素是唯一的。三个点是数组展开语法,也可以将拥有 length
属性的类数组展开为数组;也可以用 Array.form()
方法。