今日头条前端面经

5,524 阅读7分钟
原文链接: www.shuaihua.cc

吹啊吹啊我的骄傲放纵,2017年,今日立冬,虽然秋风依旧萧瑟,但阳光暖暖的,心情也跟着暖了起来。道路两旁的树木穿上金装,飒飒,发出声响,地上枯叶被风卷起,围绕着我旋转。

一些话留在后面看心情决定要不要说,先上面试题:


第一题 (css)

答案是 20px 。


第二题 (html)

DNS Prefetch,即DNS预获取,是前端优化的一部分。一般来说,在前端优化中与 DNS 有关的有两点: 一个是减少DNS的请求次数,另一个就是进行DNS预获取 。

DNS 作为互联网的基础协议,其解析的速度似乎很容易被网站优化人员忽视。现在大多数新浏览器已经针对DNS解析进行了优化,典型的一次DNS解析需要耗费 20-120 毫秒,减少DNS解析时间和次数是个很好的优化方式。DNS Prefetching 是让具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能 减少用户的等待时间,提升用户体验。

默认情况下浏览器会对页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,并且缓存结果,这就是隐式的 DNS Prefetch。如果想对页面中没有出现的域进行预获取,那么就要使用显示的 DNS Prefetch 了。

目前大多数浏览器已经支持此属性,支持版本如下:

  • Safari: 5+
  • Chrome: All
  • Firefox: 3.5+
  • Opera: Unknown
  • IE: 9+ (called “Pre-resolution” on blogs.msdn.com)

其中 Chrome 和 Firefox 3.5+ 内置了 DNS Prefetching 技术并对DNS预解析做了相应优化设置。所以 即使不设置此属性,Chrome 和 Firefox 3.5+ 也能自动在后台进行预解析 。

DNS Prefetch 应该尽量的放在网页的前面,推荐放在 后面。具体使用方法如下:

<meta http-equiv="x-dns-prefetch-control" content="on">

<link rel="dns-prefetch" href="//www.itechzero.com">

<link rel="dns-prefetch" href="//api.share.baidu.com">

<link rel="dns-prefetch" href="//bdimg.share.baidu.com">

需要注意的是,虽然使用 DNS Prefetch 能够加快页面的解析速度,但是也不能滥用,因为有开发者指出 禁用DNS预读取能节省每月100亿的DNS查询

如果需要禁止隐式的 DNS Prefetch,可以使用以下的标签:

<meta http-equiv="x-dns-prefetch-control" content="off">


第三题 (css)

描述
absolute

生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。

元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。

fixed

生成绝对定位的元素,相对于浏览器窗口进行定位。

元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。

relative

生成相对定位的元素,相对于其正常位置进行定位。

因此,"left:20" 会向元素的 LEFT 位置添加 20 像素。

static 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
inherit 规定应该从父元素继承 position 属性的值。

第四题 (RegExp)

考察的是前端模板引擎的原理。

可以看这篇 9行代码实现模板引擎.js

Tpl = function(tpl, data) {
	var fn = tpl.replace(/</g, '<').replace(/>/g, '>') //    转义 <>
		.replace(/(<%=)([\s\S]*?)(%>)/g, '$1_html_+= ($2)\n$3') // <%= %>  [\s\S]允许换行
		.replace(/(<%)(?!=)([\s\S]*?)(%>)/g, '$1\n\t$2\n$3') // <% js code %>  (?!=)不要匹配到<%= %>
		.replace(/(^|%>|%>)([\s\S]*?)(<%=|<%|$)/g, function($, $1, $2, $3) { // 边界符外的html, html 中的(\|"|\r|\n) 要转义
			return '_html_+= "' + $2.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\r?\n/g, '\\n') + '"\n'
		});
	return (fn = Function('data', 'with(data||{}){\nvar _html_=""\n' + fn + '\nreturn _html_\n}')), data ? fn(data) : fn
};

第五题 (http)

Cache-Control指定了请求和响应遵循的缓存机制。好的缓存机制可以减少对网络带宽的占用,可以提高访问速度,提高用户的体验,还可以减轻服务器的负担。

Cache-Control主要有以下几种类型:

(1) 请求Request:

  • no-cache 不要读取缓存中的文件,要求向WEB服务器重新请求
  • no-store 请求和响应都禁止被缓存
  • max-age 表示当访问此网页后的max-age秒内再次访问不会去服务器请求,其功能与Expires类似,只是Expires是根据某个特定日期值做比较。一但缓存者自身的时间不准确.则结果可能就是错误的,而max-age,显然无此问题.。Max-age的优先级也是高于Expires的。
  • max-stale 允许读取过期时间必须小于max-stale 值的缓存对象。
  • min-fresh 接受其max-age生命期大于其当前时间 跟 min-fresh 值之和的缓存对象
  • only-if-cached 告知缓存者,我希望内容来自缓存,我并不关心被缓存响应,是否是新鲜的.
  • no-transform 告知代理,不要更改媒体类型,比如jpg,被你改成png.

(2) 响应Response:

  • public 数据内容皆被储存起来,就连有密码保护的网页也储存,安全性很低
  • private 数据内容只能被储存到私有的cache,仅对某个用户有效,不能共享
  • no-cache 可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端
  • no-store 请求和响应都禁止被缓存
  • max-age 本响应包含的对象的过期时间
  • Must-revalidate 如果缓存过期了,会再次和原来的服务器确定是否为最新数据,而不是和中间的proxy
  • max-stale 允许读取过期时间必须小于max-stale 值的缓存对象。
  • proxy-revalidate 与Must-revalidate类似,区别在于:proxy-revalidate要排除掉用户代理的缓存的。即其规则并不应用于用户代理的本地缓存上。
  • -maxage 与max-age的唯一区别是,s-maxage仅仅应用于共享缓存.而不应用于用户代理的本地缓存等针对单用户的缓存. 另外,s-maxage的优先级要高于max-age.
  • no-transform 告知代理,不要更改媒体类型,比如jpg,被你改成png.

第六题(html)

  • <script src="script.js"></script> 没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

  • <script async src="script.js"></script> 有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

  • <script defer src="myscript.js"></script> 有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

然后从实用角度来说呢,首先把所有脚本都丢到 </body> 之前是最佳实践,因为对于旧浏览器来说这是唯一的优化选择,此法可保证非脚本的其他一切元素能够以最快的速度得到加载和解析。

接着,我们来看一张图咯:

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

此图告诉我们以下几个要点:

  • defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)。
  • 它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的。
  • 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用。
  • async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行。
  • 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics。

第七题(javascript)


第八题(javascript)

答案是 4;


第九题(css3)

源码:

<style>
.coin{
			background: #666;
			width: 10%;
			padding-bottom: 10%;
			background: linear-gradient(to bottom right, hsla(0, 0%, 0%, .5), hsla(0, 0%, 0%, .1));
			border-radius: 50%;
			border: 2px solid #999;
			animation: rotate-coin-y 1s linear infinite;
		}
		@keyframes rotate-coin-y{
			0%{
				transform: rotateY(0deg);
			}
			50%{
				transform: rotateY(180deg);
			}
			100%{
				transform: rotateY(360deg);
			}
		}
</style>
<div class="coin"></div>

编程题