第二个面试是对我打击最大的今日头条。
今日头条近一年风头正劲,发展迅速,前端需求量很大。本来抱着很大希望去面试,结果收到的打击也最大。
不过站在现在回顾那个面试,虽然打击最大,给我的帮助和提高也是最大的。
夸张一点说,what doesn’t kill you makes stronger
应该是又挂了。面试从下午四点开始,做笔试题,然后一面,等待,然后二面,结束的时候已经8点半了
跑回公司打卡时已经9点多了,心情特别沮丧。面试的内容接近80%是我不清楚或者没接触过的,95%都是我不确定的。
我也知道自己做前端不过区区九个月,自己是有些心急了,但是每次面试被问的不得不一遍又一遍承认“这个我不了解”、“这个我不熟悉”的时候,都为自己的无能、无知和蠢笨而懊恼和沮丧。
可能现在去大公司确实超出了自己的能力范围,换句话说在能力不够的时候,想要去超出自己能力范围的地方,只能看运气。
太浮躁了。
也是由于自己目前的工作没有办法提供更多的学习机会了,所以着急。今天就去找公司的人谈换项目的事,看会有什么结果。
1、box-sizing
CSS属性box-sizing的值有哪些?分别有什么含义?
content-box
: 宽度、高度之外绘制元素的padding和borderborder-box
: 宽度、高度之内绘制元素的padding和border
==和margin没有关系==!!!
2、在HTTP响应Header中,Set-Cookie的选项有哪些?分别是什么含义?
tt_webid=6507009217324615181
: 设置 name 和 value, 必选参数,其他参数为可选Path=/
:控制那些访问能够触发cookie的发送,/
表示根路径下的文件有权限读取该cookie,只可写,不可读,path 权限有继承性Domain
: cookie 有效的域名,.toutiao.com
的设置表明在 toutiao.com 的所有域名中生效,如果www.toutiao.com
仅仅在www.toutiao.com子域名中生效expire
: 规定cookie的过期时间,是一个绝对时间,如果没设置,cookie将在session结束后(即浏览器关闭后)失效Max-Age
: 规定web文件被用户请求后的存活时间,是一个相对值,单位是秒secure
:规定是否需要在HTTPS链接传输cookie,如果需要设置为true,默认为false
3、for…in、Object.keys和Object.getOwnPropertyNames的区别
for...in
: 遍历自身及原型中所有可枚举的属性Object.keys
: 遍历自身可枚举的属性Object.getOwnPropertyNames
: 遍历自身所有属性,包括可枚举属性及不可枚举属性
可枚举是有属性的enumerable值决定的,生成不可枚举的属性使用Object.defineProerties
方法
var obj1 = {};
Object.defineProperties(obj1, {
name: {
value: "张三",
enumerable: false
}
});
4、判断数组
五种方法:
$.isArray([]); // true
[].constructor === Array; // true
[] instanceof Array; // true
typeof [] === 'object' && [].length === 0 // true
Array.isArray([]); // true
5、关于跨域
何为跨域?
跨域是指从一个域名的网页去请求另一个域名的资源。协议(http&https)、端口(80&81)、域名(baidu&google)、二级域名(news&sports)不相同,都为跨域。
跨域请求数据有哪几种方式?
- iframe + window.name
- iframe + window.postMessage
- JSONP
- CORS(
header:(Access-Control-Allow-Origin : *
) - Nginx反向代理
图片/脚本等资源有什么跨域问题?
图片、脚本不存在跨域问题
跨域请求如何携带cookie
第一种方法:nginx反向代理
第二种方法:[JSONP]方式可以携带cookie,但是只能是GET方法
第三种方法:CORS
HTTPS相关知识
简要描述HTTPS的安全机制
建立在SSL/TLS协议上,采用了公钥加密法,基本过程是:
- 客户端向服务器端索要并验证公钥。
- 双方协商生成”对话密钥”。
- 双方采用”对话密钥”进行加密通信。
前两步未握手阶段,详细过程是:
1、客户端先要服务器索要并验证公钥
2、服务器收到客户端请求后,向客户端做出回应, 内容包括服务器生成的两个随机数、加密方法、服务器证书等
3、客户端收到回应后,首先验证服务器证书,证书可信的话客户端就会从证书中取出服务器的公钥,利用预先约定好的加密方法生成“会话秘钥”,生成第三个随机数
4、服务器收到客户端的第三个随机数,计算生成本次的对话秘钥,握手阶段结束,接下来客户端和服务器进入加密通信,使用的就是HTTP协议,只不过用对话秘钥加密内容
HTTPS在WEB服务工程实践中需要注意的问题
HTTPS在未经优化的情况下速度比HTTP慢几百毫秒以上吗,影响主要来源于两方面:1、协议交互增加的网络延时;2、加解密相关的计算耗时
HTTP2和HTTPS的关系
HTTP2是在HTTPS协议的基础上实现的。HTTPS是HTTP1.1的补充,加入了SSL曾,增加其安全基础,HTTP2是HTTP1.1的全面升级。HTTP2目前在实际使用中,只用于HTTPS协议场景下,所以目前HTTP2的使用场景,都是默认安全加密的。
HTTP2是一个彻底的二进制协议,头信息和数据体都是二进制。HTTP2复用TCP链接,实现了双向的、实时的多工通信,HTTP2允许服务器推送
点击劫持
点击劫持是一种视觉欺骗,用透明的iframe或者图片覆盖页面,诱导用户点击,访问其他页面
预防方法是前端可以对当前页面地址进行判断,如果加载了iframe则重置location,更可靠的防御方法是在服务端设置HTTP头信息的X-FRAME-OPTION
属性,属性值有DENY
/SAMEORIGIN
/ALLOWFROM
XSS跨站脚本攻击,用户输入非法字符,向页面植入用户代码,防护方式是用户的输入进行过滤和处理,对标签进行转换,避免使用用户输入作为代码,做到数据与代码分离
CSRF(CORSS SITE REQUEST FORGEY):跨站伪造请求,是用户在登陆的不安全的网站后,此网站伪造用户向目标网站发出请求(携带cookie),防御方法是用户提交请求时增加随机数或验证码,服务器进行验证
TCP三次握手
第一次:建立连接时,客户端发送SYN
包到服务器,等待服务器确认。
SYN:同步序列编号(Synchronize Sequence Numbers)
第二次:服务器收到SYN
包,确认后发送SYN
+ AGK
包
第三次:客户端收到服务器的SYN
+ AGK
包后,向服务器发送确认包ACK
,成功建立连接
首屏性能优化
是一个很大很深的课题,现在简单总结一下:
- 页面模块化加载
- 考虑将DOM树简化,内容放在localStorage中,通过前端缓存加异步加载加快首页加载速度。
- 把需要请求的路径写在
dom
上(例如:data-tpl="elevator_tpl"
),用户滚动时,一旦该模块进入了视窗,则请求dom
上对应的data-tpl
地址,拿到渲染这个模块所需要的脚本和数据,不过这中间还有一层本地缓存localstorage
,如果在本地缓存中匹配到了对应的hash string
内容,则直接渲染,否则请求到数据之后更新本地缓存。localstorage
中的version
会在页面加载时候,与后端文件hash
相对比,hash
不变直接取localstorage
中的内容(当然也可以使用cookie
判断版本)
- 图片懒加载,节省流量的同时,也能减少请求数
- webpack代码分割,按需加载JS模块
- 服务端开启gzip压缩
- 合理使用缓存
- CSS雪碧图
- 减少不必要的301/302跳转,减少跳转等待时间
- 减少cookie体积,加快请求时间
TCP与HTTP的关系
TCP是传输层,HTTP是应用层,HTTP是基于TCP基础之上的。TCP就是单纯的建立连接,不涉及任何实际数据,HTTP是用来传输数据,是用于实际应用的。
TCP是底层通讯协议,定义的是数据传输和连接方式的规范
HTTP是应用层协议,定义的是传输数据的内容的规范
window.onload
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DOM加载顺序</title>
<script src="../libs/jquery.min.js"></script>
</head>
<body>
<p>1</p>
<script>
document.write('2')
</script>
<p>3</p>
<script>
window.onload = function () {
document.write('4');
document.write('5');
setTimeout(function () {
document.write('8')
}, 1000)
};
setTimeout(function () {
document.write('6')
}, 0);
setTimeout(function () {
document.write('7')
}, 1000)
</script>
</body>
</html>
最后页面展现的是:45678
分析:
123的顺序很好理解,这时候window.load
函数还没有执行,但是在第二段脚本中,setTimeOut
和window.onload
都是在DOM完全加载完毕后执行,document.write
相当重新书写了页面,导致123
被取消,在页面上消失
这里indow.onload
与document.ready
造成的效果是相同的。
九宫格布局,要求hover时单元格边框变色
解决思路:
1 用table
布局, border-collapse: collapse;
,然后hover
时利用td
内部span
的border
盖住td
的border
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>九宫格</title>
<style>
* {
margin: 0;
padding: 0;
}
.wrapper {
margin: 50px;
}
.table {
border-collapse: collapse;
}
.table td {
text-align: center;
vertical-align: middle;
border-collapse: collapse;
border: 5px solid black;
position: relative;
}
.table td span {
position: relative;
display: block;
width: 50px;
height: 50px;
line-height: 50px;
}
.table td span:hover {
position: absolute;
left: -5px;
top: -5px;
border: 5px solid red;
}
</style>
</head>
<body>
<div class="wrapper">
<table class="table">
<tr>
<td><span>1</span></td>
<td><span>2</span></td>
<td><span>3</span></td>
</tr>
<tr>
<td><span>4</span></td>
<td><span>5</span></td>
<td><span>6</span></td>
</tr>
<tr>
<td><span>7</span></td>
<td><span>8</span></td>
<td><span>9</span></td>
</tr>
</table>
</div>
</body>
</html>
2 用flex
布局,用margin
为复制盖住重复的边框,hover
时改变对应的z-index
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>九宫格</title>
<style>
* {
margin: 0;
padding: 0;
}
.wrapper {
margin: 50px;
float: left
}
.flex {
display: flex;
width: 165px;
height: 165px;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.flex-cell {
flex: 0 0 50px;
height: 50px;
line-height: 50px;
margin: -5px 0 0 -5px;
text-align: center;
border: 5px solid black;
z-index: 1;
}
.flex-cell:hover {
border: 5px solid red;
z-index: 2;
}
</style>
</head>
<body>
<div class="wrapper">
</div>
<div class="wrapper">
<div class="flex">
<div class="flex-cell">1</div>
<div class="flex-cell">2</div>
<div class="flex-cell">3</div>
<div class="flex-cell">4</div>
<div class="flex-cell">5</div>
<div class="flex-cell">6</div>
<div class="flex-cell">7</div>
<div class="flex-cell">8</div>
<div class="flex-cell">9</div>
</div>
</div>
</body>
</html>
3 任意布局,外围容器有左、上边框, 内部cell有右下边框, hover
时用:after
伪元素添加元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>九宫格</title>
<style>
* {
margin: 0;
padding: 0;
}
.wrapper2 {
width: 165px;
height: 165px;
border-style: solid;
border-color: black;
border-width: 5px 0 0 5px;
}
.cell {
float: left;
width: 50px;
height: 50px;
line-height: 50px;
border-style: solid;
border-color: black;
border-width: 0 5px 5px 0;
text-align: center;
position: relative;
}
.cell:hover:after {
content: '';
display: block;
position: absolute;
left: -5px;
top: -5px;
width: 100%;
height: 100%;
border: 5px solid red;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="wrapper2">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
<div class="cell">9</div>
</div>
</div>
</body>
</html>
5 用grid布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>九宫格</title>
<style>
* {
margin: 0;
padding: 0;
}
.wrapper {
margin: 50px;
float: left
}
.grid{
display: grid;
width: 165px;
height: 165px;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
}
.grid-cell{
display: grid;
align-items: center;
justify-content: center;
vertical-align: middle;
margin:-5px 0 0 -5px;
border: 5px solid black;
z-index: 1;
}
.grid-cell:hover{
border: 5px solid red;
z-index: 2;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="grid">
<div class="grid-cell">1</div>
<div class="grid-cell">2</div>
<div class="grid-cell">3</div>
<div class="grid-cell">4</div>
<div class="grid-cell">5</div>
<div class="grid-cell">6</div>
<div class="grid-cell">7</div>
<div class="grid-cell">8</div>
<div class="grid-cell">9</div>
</div>
</div>
</body>
</html>
原生AJAX请求
let xhr = new XMLHttpRequest();
xhr.open('GET', 'www.baidu.com');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
将原生的ajax请求改造为Promise
请求
function succeedFunc(res) {
console.log(res);
}
function failFunc(err) {
console.log(err)
}
let xhr = new XMLHttpRequest();
let promise = new Promise(function(resolve, reject) {
xhr.open('GET', 'test.txt');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
resolve(xhr.responseText)
} else {
reject(new Error(xhr.responseText))
}
};
});
promise.then(function(value) {
succeedFunc(value)
}, function(err) {
failFunc(err)
})
在一个数组中按顺序取指定数目的最小数
思路还是需要两层嵌套的循环,根据内部循环的结果返回到外层循环,再改变内层循环的指标:
let arr = [2, 3, 5, 7, 2, 1, 2, 5, 2, 5, 5];
function getMin(arr, length) {
let startIndex = 0, endIndex = arr.length - length;
let result = [];
let tempMin;
for (let i = 0; i < arr.length; i++) {
tempMin = arr[++startIndex];
for (let j = startIndex; j <= endIndex; j++) {
if (arr[j] < tempMin) {
tempMin = arr[j];
startIndex = j;
}
}
result.push(tempMin);
endIndex++;
if (result.length === length) {
break;
}
}
return result;
}
console.log(getMin(arr, 3))
九宫格异步获取数据,要求尽快,并且按顺序展示
思路就是异步操作的promise请求要同时发出,但是写入事件的回调(await)按顺序执行
同时发出,可以不加await直接发出请求,在写入的时候await promise的结果,也可以将请求放在Promise.all中,写入时直接写入
假设异步操作设这样的:
function time(ms, message, index) {
console.log(index);
return new Promise(function (resolve) {
setTimeout(resolve, ms * 1000, message)
})
}
第一种写法:
async function fetch1() {
let result1 = time(2, `${1} is complete`, `${1} is start`);
let result2 = time(4, `${2} is complete`, `${2} is start`);
console.log( await result1);
console.log( await result2);
}
第二种写法:
async function fetch2() {
console.time(1);
let [result1, result2] = await Promise.all([time(2, `${1} is complete`, `${1} is start`), time(4, `${2} is complete`, `${2} is start`)])
console.log(result1);
console.log(result2);
console.timeEnd(1)
}
利用循环的话,使用for循环
async function fetch() {
console.time(1);
let result = [];
for (let i = 1; i < 4; i++) {
let ms = 5;
result[i] = time(ms, `${i} is complete`, `${i} is start`);
}
for (let i = 1; i < 4; i++) {
console.log(await result[i] )
}
console.timeEnd(1);
}
和下面的题目是一个知识点
假设wait(n)
是一个耗时n秒的异步操作,判断总耗时
let w1 = await wait(3);
let w2 = await wait(2);
w1;
w2
因为请求是依次发出的,所以总耗时是5s
let w1 = wait(3);
let w2 = wait(2);
await w1;
await w2
异步操作请求是同时发出的,在等待w1
的3s时w2请求已经完成,在w1()
执行后立刻就可以w2()
,所以总耗时3s