前端面试总结


2017-08-22更新 添加双飞翼、圣杯布局



2017-08-30更新 添加BFC



2017-08-31更新 添加js模块化,函数节流



2017-09-01更新 添加this对象分析



2017-09-02更新 添加并发编程与事件循环



2017-09-04更新 添加浏览器渲染,重排,重绘



2017-09-06更新 添加HTTP缓存,离线应用和客户端存储



2017-09-09更新 JS事件循环,setTimeout,setInterval



2017-09-21更新 Ajax原生实现过程



2017-09-23更新 添加HTTP/HTTPS


开放性问题

为什么选择前端?

项目遇到的问题及如何解决?

技术性问题

JS类问题

作用域

参见我的博客Javascript函数和作用域总结

原型

参见我的博客Javascript原型对象和原型链

闭包

继承

  1. 面向对象和继承,object.create的实现原理?

设计模式

观察者模式,实现原理?

Jquery

  1. Jquery特性及优缺点?

ES6

  1. 特性
  2. ES6中的箭头函数可以用作构造函数吗?

Ajax

参见我的博客前端Ajax操作总结

前端JS框架React/Vue/NG

  1. NG的双向数据绑定?

面向切面编程和函数式编程

跨域

  1. postmessage和iframe怎么结合使用?
  2. 那些操作会有跨域的限制?为什么要有跨域的限制?没有跨域的限制会怎么样?

深拷贝怎么实现

js中连续触发事件的稀释方法

函数节流

参见浅谈javascript节流
在前端开发中,有时会为页面绑定resize事件,或者为一个页面元素绑定拖拽事件(其核心就是绑定mousemove),这种事件有一个特点,用户可能在一个短的时间内触发非常多次事件绑定程序。而DOM操作时很消耗性能的,这个时候如果你为这些事件绑定一些操作DOM节点的操作的话,那就会引发大量的计算,在用户看来,页面可能就一时间没有响应,这个页面一下子变卡了变慢了。甚至在IE下,如果你绑定的resize事件进行较多DOM操作,其高频率可能直接就使得浏览器崩溃。
函数节流,简单地讲,就是让一个函数无法在很短的时间间隔内连续调用,只有当上一次函数执行后过了你规定的时间间隔,才能进行下一次该函数的调用。函数节流背后的思想是指某些代码不可以在没有间断的情况下连续重复执行(代码可参见javascript高级程序设计p615)

1
2
3
4
5
6
function throttle(method, context) {
clearTimeout(method.tId);
method.tId = setTimeout(function(){
method.call(context);
}, 100);
}

函数throttle接收两个参数:要执行的函数以及在哪个作用域执行。上面这个函数首先清除了之前设置的任何定时器,然后创建一个新的定时器,将其ID存储在方法的tId属性中。如果没有给第二个参数,那么就在全局作用域中。
代码:

1
2
3
4
5
6
7
function resizeDiv(){
var div = document.getElementById('myDiv');
div.style.height = div.offsetWidth + 'px';
}
window.onresize = function(){
throttle(resizeDiv);
}

这样两次函数调用之间至少间隔100ms。

函数防抖

参见js中连续触发事件的稀释方法(函数节流、函数防抖、标识变量)
如果一直触发一个事件,函数节流的方法定义的方法永远不会执行,就像一个水龙头,如果关闭了,永远不会出水;函数防抖的方法像一个关闭水龙头不好用,当关闭水龙头隔一段时间会有滴水一样。就是一个事件如果频繁触发,会隔一段时间执行一次。
函数防抖最常见的应用场景就是用户注册时候的手机号码验证和邮箱验证了。只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。以下还是以页面元素滚动监听的例子,来进行解析
函数防抖的实现方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var debounce  = function(fn,delay,mustRunDelay){
var timer = null;
var t_start;
return function(){
var context = this;
var args = arguments;
var t_curr = new Date();
clearTimeout(timer);
if(!t_start){
t_start = t_curr;
}
if(t_curr - t_start >= mustRunDelay) {
fn.apply(context,args);
t_start = t_curr
} else {
timer = setTimeout(function(){
fn.apply(context,args);
},delay);
}
}
}

该函数接收三个参数,分别为要执行的函数,隔多长时间清除函数定时器以及多长时间需要执行一次。
如果利用函数防抖实现resize事件,实现方法如下:

1
window.onresize = throttleV2(resizeDiv,50,100);

标识变量(立flag)

参见js中连续触发事件的稀释方法(函数节流、函数防抖、标识变量)
在项目中需要使用scroll事件,当scroll滚动到页面底部时,发送请求验证还有没有其他的资源需要加载。如果直接使用scroll事件,当滚动到文档底部发送ajax请求的话,ajax请求会连续触发。为了解决这个问题,设置一个初始值为true的标志变量,只有标识变量为true时才能发送ajax请求。当发送ajax请求时,将标志变量设为false,收到请求的响应并处理完后将标志变量设为true

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var pageIndex = {
newPage : 1,
hotPage:1
}
var scrollFlag = {
newFlag: true,
hotFlag: true,
}
EventUtil.addHandler(window,'scroll',function(){
scrollE(newPost,'new');
})

function scrollE(ele,str){
var totalHeight = document.documentElement.scrollTop + document.body.scrollTop + document.documentElement.clientHeight ;

if(totalHeight > ele.offsetTop + ele.offsetHeight) {
if(scrollFlag[str + "Flag"]){
scrollFlag[str + "Flag"] = false ;
if(pageIndex[str + "Page"]< 10) {
pageIndex[str + "Page"]++;

var xhr=createXHR();
var postHtml = ele.innerHTML;
xhr.onreadystatechange=function(){

if(xhr.readyState==1){
ele.innerHTML = postHtml + '<div class = "loading"><div class="load-info"><span class="load-img"></span>我去拿数据 等我一会儿</div></div>'
}
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
var res = xhr.responseText;
res = JSON.parse(res);

ele.innerHTML = postHtml + res.htmlres;

if(res[str+'IsLastPage']){
ele.innerHTML = ele.innerHTML+'<div class = "final-page"><span>别拉啦 我已经加载完了<span></div>'
} else {
scrollFlag[str + "Flag"] = true;
}

}else{

console.log("request was unsuccessful:"+xhr.status);
}
}
}
xhr.open('GET','postpage/' + str + '/' + pageIndex[str + "Page"],true);
xhr.send();
}
}
}
}

javascript模块化

模块的写法

参见Javascript模块化编程(一):模块的写法

  1. 原始:污染全局变量
  2. 对象:内部属性被访问
  3. 立即执行函数:模块的基本写法
  4. 放大模式:实现继承,参数为另一模块
  5. 宽放大模式:模块网络获取,加载时间不确定,参数可以为空对象
  6. 输入全局变量:显式地将其他变量输入模块。

AMD规范

参见Javascript模块化编程(二):AMD规范

  1. 模块规范:AMD -> COMMONJS;CMD
  2. Node环境: 模块化参照Commonjs,使用require()加载模块
  3. 浏览器环境,若使用Commonjs规范,require()加载必须等待,浏览器处于假死状态。因此不能采用同步加载,只能采用异步加载。由此诞生AMD规范
  4. AMD(Asynchronous Module Definition,异步模块定义)。require([module], callback);主要有两个Javascript库实现了AMD规范:require.js和curl.js。

require.js的用法

参见Javascript模块化编程(三):require.js的用法

requirejs

实现js文件的异步加载,避免网页失去响应;管理模块之间的依赖性,便于代码的编写和维护。

AMD模块加载
1
<script src="js/require.js" data-main="js/main" defer async="true"></script>

main.js即为主程序入口

1
2
3
4
// main.js
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
// some code here
});
AMD模块定义

如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

1
2
3
4
5
6
7
8
9
// math.js
define(function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
});

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性

1
2
3
4
5
6
7
8
  define(['myLib'], function(myLib){
    function foo(){
      myLib.doSomething();
    }
    return {
      foo : foo
    };
  });

Javascript垃圾回收

参见JavaScript 内存管理 & 垃圾回收机制
垃圾回收机制原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。

方法

标记清除

js 中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个而变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

引用计数

这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。Netscape Navigator3 是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题:循环引用。循环引用指的是对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。

触发条件

IE6 的垃圾回收是根据内存分配量运行的,当环境中存在 256 个变量、4096 个对象、64K 的字符串任意一种情况的时候就会触发垃圾回收器工作,看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好嘛?但是如果环境中就是有这么多变量一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法玩了。

微软在 IE7 中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,如果垃圾回收器回收的内存分配量低于程序占用内存的 15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临界条件翻倍,如果回收的内存高于 85%,说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作智能了很多。

合理的 GC 方案

JavaScript 引擎基础 GC 方案是(simple GC):mark and sweep(标记清除),即:
  1. 遍历所有可访问的对象。
  2. 回收已不可访问的对象。
GC的缺陷

和其他语言一样,JavaScript 的 GC 策略也无法避免一个问题:GC 时,停止响应其他操作,这是为了安全考虑。而 JavaScript 的 GC 在 100ms 甚至以上,对一般的应用还好,但对于 JS 游戏,动画连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免 GC 造成的长时间停止响应。

setInterval和setTimeout

参见我的博客Javascript中并发编程与事件循环

javascript中的this

参见我的博客this对象

js事件循环与并发编程

参见我的博客Javascript中并发编程与事件循环

CSS问题

浏览器CSS hack

  1. 各浏览器对元素的初始样式不同,可以进行reset操作

实现bootstrap栅格系统

Canvas

  1. Canvas上画个圆,可以用诸如getElementById()之类的方法获取吗?
    答:不可以,canvas是通过js绘制的图形,图形是一个一个像素画上去的,不可以获取到。但是svg是基于XML格式,内部是一个个节点,可以用DOM操作获取节点。
  2. canvas上的图像获取到吗?
    答:可以,canvas原生的toDataURL()方法获取图像的Base64编码

BFC

Formatting context是W3C CSS2.1规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
一个块格式化上下文(block formatting context) 是Web页面的可视化CSS渲染出的一部分。它是块级盒布局出现的区域,也是浮动层元素进行交互的区域。

最常见的Formatting context有Block fomatting context(简称BFC)和Inline formatting context(简称IFC)。CSS2.1 中只有BFC和IFC, CSS3中还增加了GFC和FFC.

一个块格式化上下文由以下之一创建:

  • 根元素或其它包含它的元素
  • 浮动元素 (元素的float不是none)
  • 绝对定位元素 (元素具有positionabsolutefixed)
  • 内联块 (元素具有display: inline-block)
  • 表格单元格 (元素具有display: table-cell,HTML表格单元格默认属性)
  • 表格标题 (元素具有display: table-caption, HTML表格标题默认属性)
  • 具有overflow且值不是visible的块元素,
  • display: flow-root
  • column-span: all应当总是会创建一个新的格式化上下文,即便具有column-span: all的元素并不被包裹在一个多列容器中。

BFC有一下特性:

  • 内部的Box会在垂直方向,从顶部开始一个接一个地放置。
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生叠加
  • 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  • BFC的区域不会与float box叠加。
  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。
  • 计算BFC的高度时,浮动元素也参与计算。

一个块格式化上下文包括创建它的元素内部所有内容,除了被包含于创建新的块级格式化上下文的后代元素内的元素。

块格式化上下文对于定位 (float) 与清除浮动 (clear) 很重要。定位和清除浮动的样式规则只适用于处于同一块格式化上下文内的元素。浮动不会影响其它块格式化上下文中元素的布局,并且清除浮动只能清除同一块格式化上下文中在它前面的元素的浮动。

两栏布局,左边固定,要求先加载内容区域,说出多种方法

  1. 圣杯布局

  2. 双飞翼布局

  3. flex布局

如何实现Bootstrap的栅格布局

计算机网络问题

HTTP、HTTPS协议

参见我的博客HTTP/HTTPS协议总结

TCP/IP协议

综合

URL输入后,具体的技术过程,浏览器怎么渲染的?

Web后台问题(node)

  1. 后端为什么使用node?

express/koa

自动化构建工具

  1. 你使用过的构建工具,说说对webpack的理解?

图片压缩、上传、下载和缓存

浏览器

浏览器内部标签页间的通讯用什么方式?

浏览器渲染,重排和重绘

参见我的博客浏览器渲染原理总结

客户端存储

参见我的博客Web应用客户端存储

性能优化

HTTP缓存和离线应用

参见我的博客Web缓存总结

数据库问题

  1. 数据库缓存

前端安全

  1. HTTPS
  2. sql注入
  3. xss攻击
  4. csrf攻击

移动端问题

数据结构和算法

排序

  1. 快速排序
  2. 二叉排序树

动态规划

搜索

其他

找出100万以内的质数?

参见找出100万以内的质数

参考文献:

  1. 两列布局分析
  2. Javascript模块化编程(一):模块的写法
  3. Javascript模块化编程(二):AMD规范
  4. Javascript模块化编程(三):require.js的用法
  5. 浅谈javascript节流
  6. js中连续触发事件的稀释方法(函数节流、函数防抖、标识变量)
  7. JavaScript 内存管理 & 垃圾回收机制