前端面试指南

介绍目前常见前端面试题,参考链接 https://interview2.poetries.top/

jQuery

你觉得jQuery或zepto源码有哪些写的好的地方

  • jquery源码封装在一个匿名函数的自执行环境中,有助于防止变量的全局污染,然后通过传入window对象参数,可以使window对象作为局部变量使用,好处是当jquery中访问window对象的时候,就不用将作用域链退回到顶层作用域了,从而可以更快的访问window对象。同样,传入undefined参数,可以缩短查找undefined时的作用域链
1
2
3
4
5
6
7
(function( window, undefined ) {
//用一个函数域包起来,就是所谓的沙箱
//在这里边var定义的变量,属于这个函数域内的局部变量,避免污染全局
//把当前沙箱需要的外部变量通过函数参数引入进来
//只要保证参数对内提供的接口的一致性,你还可以随意替换传进来的这个参数
window.jQuery = window.$ = jQuery;
})( window );
  • jquery将一些原型属性和方法封装在了jquery.prototype中,为了缩短名称,又赋值给了jquery.fn,这是很形象的写法
  • 有一些数组或对象的方法经常能使用到,jQuery将其保存为局部变量以提高访问速度
  • jquery实现的链式调用可以节约代码,所返回的都是同一个对象,可以提高代码效率

jQuery 的实现原理

  • (function(window, undefined) {})(window);
  • jQuery 利用 JS 函数作用域的特性,采用立即调用表达式包裹了自身,解决命名空间和变量污染问题
  • window.jQuery = window.$ = jQuery;
  • 在闭包当中将 jQuery 和 $ 绑定到 window 上,从而将 jQuery 和 $ 暴露为全局变量

jQuery.fninit 方法返回的 this 指的是什么对象

  • jQuery.fn 的 init 方法 返回的 this 就是 jQuery 对象
  • 用户使用 jQuery() 或 $() 即可初始化 jQuery 对象,不需要动态的去调用 init 方法

jQuery.extend 与 jQuery.fn.extend 的区别

  • $.fn.extend()$.extend()jQuery 为扩展插件提拱了两个方法
  • $.extend(object); // 为jQuery添加“静态方法”(工具方法)
1
2
3
4
5
6
$.extend({
  min: function(a, b) { return a < b ? a : b; },
  max: function(a, b) { return a > b ? a : b; }
});
$.min(2,3); // 2
$.max(4,5); // 5
  • $.extend([true,] targetObject, object1[, object2]); // 对targt对象进行扩展
1
2
3
4
var settings = {validate:false, limit:5};
var options = {validate:true, name:"bar"};
$.extend(settings, options); // 注意:不支持第一个参数传 false
// settings == {validate:true, limit:5, name:"bar"}
  • $.fn.extend(json); // 为jQuery添加“成员函数”(实例方法)
1
2
3
4
5
6
7
8
9
$.fn.extend({
alertValue: function() {
$(this).click(function(){
alert($(this).val());
});
}
});

$("#email").alertValue();

jQuery 的属性拷贝(extend)的实现原理是什么,如何实现深拷贝

  • 浅拷贝(只复制一份原始对象的引用) var newObject = $.extend({}, oldObject);
  • 深拷贝(对原始对象属性所引用的对象进行进行递归拷贝) var newObject = $.extend(true, {}, oldObject);

jQuery 的队列是如何实现的

  • jQuery 核心中有一组队列控制方法,由 queue()/dequeue()/clearQueue() 三个方法组成。
  • 主要应用于 animate()ajax,其他要按时间顺序执行的事件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var func1 = function(){alert('事件1');}
var func2 = function(){alert('事件2');}
var func3 = function(){alert('事件3');}
var func4 = function(){alert('事件4');}

// 入栈队列事件
$('#box').queue("queue1", func1); // push func1 to queue1
$('#box').queue("queue1", func2); // push func2 to queue1

// 替换队列事件
$('#box').queue("queue1", []); // delete queue1 with empty array
$('#box').queue("queue1", [func3, func4]); // replace queue1

// 获取队列事件(返回一个函数数组)
$('#box').queue("queue1"); // [func3(), func4()]

// 出栈队列事件并执行
$('#box').dequeue("queue1"); // return func3 and do func3
$('#box').dequeue("queue1"); // return func4 and do func4

// 清空整个队列
$('#box').clearQueue("queue1"); // delete queue1 with clearQueue

jQuery 中的 bind(), live(), delegate(), on()的区别

  • bind() 直接绑定在目标元素上
  • live() 通过冒泡传播事件,默认document上,支持动态数据
  • delegate() 更精确的小范围使用事件代理,性能优于 live
  • on() 是最新的1.9版本整合了之前的三种方式的新事件绑定机制

是否知道自定义事件

  • 事件即“发布/订阅”模式,自定义事件即“消息发布”,事件的监听即“订阅订阅”
  • JS 原生支持自定义事件,示例:
1
2
3
4
document.createEvent(type); // 创建事件
event.initEvent(eventType, canBubble, prevent); // 初始化事件
target.addEventListener('dataavailable', handler, false); // 监听事件
target.dispatchEvent(e); // 触发事件
  • jQuery 里的fire 函数用于调用jQuery自定义事件列表中的事件

jQuery 通过哪个方法和 Sizzle 选择器结合的

  • Sizzle 选择器采取 Right To Left 的匹配模式,先搜寻所有匹配标签,再判断它的父节点
  • jQuery 通过 $(selecter).find(selecter);Sizzle 选择器结合

jQuery 中如何将数组转化为 JSON 字符串,然后再转化回来

1
2
3
4
5
6
7
8
9
10
11
12
13
// 通过原生 JSON.stringify/JSON.parse 扩展 jQuery 实现
$.array2json = function(array) {
return JSON.stringify(array);
}

$.json2array = function(array) {
// $.parseJSON(array); // 3.0 开始,已过时
return JSON.parse(array);
}

// 调用
var json = $.array2json(['a', 'b', 'c']);
var array = $.json2array(json);

jQuery 一个对象可以同时绑定多个事件,这是如何实现的

1
2
3
4
5
6
7
$("#btn").on("mouseover mouseout", func);

$("#btn").on({
mouseover: func1,
mouseout: func2,
click: func3
});

针对 jQuery 的优化方法

  • 缓存频繁操作DOM对象
  • 尽量使用id选择器代替class选择器
  • 总是从#id选择器来继承
  • 尽量使用链式操作
  • 使用时间委托 on绑定事件
  • 采用jQuery的内部函数data()来存储数据
  • 使用最新版本的 jQuery

jQuery 的 slideUp 动画,当鼠标快速连续触发, 动画会滞后反复执行,该如何处理呢

  • 在触发元素上的事件设置为延迟处理:使用 JS 原生 setTimeout 方法
  • 在触发元素的事件时预先停止所有的动画,再执行相应的动画事件:$('.tab').stop().slideUp();

jQuery UI 如何自定义组件

  • 通过向 $.widget() 传递组件名称和一个原型对象来完成
  • $.widget("ns.widgetName", [baseWidget], widgetPrototype);

jQuery 与 jQuery UI、jQuery Mobile 区别

  • jQueryJS 库,兼容各种PC浏览器,主要用作更方便地处理 DOM、事件、动画、AJAX
  • jQuery UI 是建立在 jQuery 库上的一组用户界面交互、特效、小部件及主题
  • jQuery MobilejQuery 为基础,用于创建“移动Web应用”的框架

jQuery 和 Zepto 的区别? 各自的使用场景

  • jQuery 主要目标是PC的网页中,兼容全部主流浏览器。在移动设备方面,单独推出 `jQuery Mobile
  • Zepto从一开始就定位移动设备,相对更轻量级。它的API 基本兼容jQuery`,但对PC浏览器兼容不理想

jQuery对象的特点

  • 只有 JQuery对象才能使用 JQuery 方法
  • JQuery 对象是一个数组对象

Bootstrap

什么是Bootstrap?以及为什么要使用Bootstrap?

Bootstrap 是一个用于快速开发 Web应用程序和网站的前端框架。Bootstrap是基于 HTMLCSSJAVASCRIPT

  • Bootstrap具有移动设备优先、浏览器支持良好、容易上手、响应式设计等优点,所以Bootstrap被广泛应用

使用Bootstrap时,要声明的文档类型是什么?以及为什么要这样声明?

  • 使用Bootstrap时,需要使用 HTML5 文档类型(Doctype)。<!DOCTYPE html>
  • 因为Bootstrap使用了一些 HTML5 元素和 CSS 属性,如果在 Bootstrap创建的网页开头不使用 HTML5 的文档类型(Doctype),可能会面临一些浏览器显示不一致的问题,甚至可能面临一些特定情境下的不一致,以致于代码不能通过 W3C 标准的验证

什么是Bootstrap网格系统

Bootstrap 包含了一个响应式的、移动设备优先的、不固定的网格系统,可以随着设备或视口大小的增加而适当地扩展到 12 列。它包含了用于简单的布局选项的预定义类,也包含了用于生成更多语义布局的功能强大的混合类

  • 响应式网格系统随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列。

Bootstrap 网格系统(Grid System)的工作原理

  • (1)行必须放置在 .container class 内,以便获得适当的对齐(alignment)和内边距(padding)。
  • (2)使用行来创建列的水平组。
  • (3)内容应该放置在列内,且唯有列可以是行的直接子元素。
  • (4)预定义的网格类,比如 .row.col-xs-4,可用于快速创建网格布局。LESS 混合类可用于更多语义布局。
  • (5)列通过内边距(padding)来创建列内容之间的间隙。该内边距是通过 .rows 上的外边距(margin)取负,表示第一列和最后一列的行偏移。
  • (6)网格系统是通过指定您想要横跨的十二个可用的列来创建的。例如,要创建三个相等的列,则使用三个 .col-xs-4

对于各类尺寸的设备,Bootstrap设置的class前缀分别是什么

  • 超小设备手机(<768px):.col-xs-*
  • 小型设备平板电脑(>=768px):.col-sm-*
  • 中型设备台式电脑(>=992px):.col-md-*
  • 大型设备台式电脑(>=1200px):.col-lg-*

Bootstrap 网格系统列与列之间的间隙宽度是多少

间隙宽度为30px(一个列的每边分别是15px

如果需要在一个标题的旁边创建副标题,可以怎样操作

在元素两旁添加<small>,或者添加.smallclass

用Bootstrap,如何设置文字的对齐方式?

  • class="text-center" 设置居中文本
  • class="text-right" 设置向右对齐文本
  • class="text-left" 设置向左对齐文本

Bootstrap如何设置响应式表格?

增加class="table-responsive"

使用Bootstrap创建垂直表单的基本步骤?

  • (1)向父<form>元素添加role="form"
  • (2)把标签和控件放在一个带有class="form-group"<div>中,这是获取最佳间距所必需的;
  • (3)向所有的文本元素<input><textarea><select>添加class="form-control"

使用Bootstrap创建水平表单的基本步骤?

  • (1)向父<form>元素添加class="form-horizontal"
  • (2)把标签和控件放在一个带有class="form-group"<div>中;
  • (3)向标签添加class="control-label"

使用Bootstrap如何创建表单控件的帮助文本?

增加class="help-block"span标签或p标签。

使用Bootstrap激活或禁用按钮要如何操作?

  • 激活按钮:给按钮增加.activeclass
  • 禁用按钮:给按钮增加disabled="disabled"的属性

Bootstrap有哪些关于img的class?

  • (1).img-rounded 为图片添加圆角
  • (2).img-circle 将图片变为圆形
  • (3).img-thumbnail 缩略图功能
  • (4).img-responsive 图片响应式 (将很好地扩展到父元素)

Bootstrap中有关元素浮动及清除浮动的class?

  • (1)class="pull-left" 元素浮动到左边
  • (2)class="pull-right" 元素浮动到右边
  • (3)class="clearfix" 清除浮动

除了屏幕阅读器外,其他设备上隐藏元素的class?

`class=”sr-only”``

Bootstrap如何制作下拉菜单?

  • (1)将下拉菜单包裹在class="dropdown"<div>中;
  • (2)在触发下拉菜单的按钮中添加:class="btn dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown"
  • (3)在包裹下拉菜单的ul中添加:class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1"
  • (4)在下拉菜单的列表项中添加:role="presentation"。其中,下拉菜单的标题要添加class="dropdown-header",选项部分要添加tabindex="-1"

Bootstrap如何制作按钮组?以及水平按钮组和垂直按钮组的优先级?

  • (1)用class="btn-group"<div>去包裹按钮组;class="btn-group-vertical"可设置垂直按钮组。
  • (2)btn-group的优先级高于btn-group-vertical的优先级。

Bootstrap如何设置按钮的下拉菜单?

在一个 .btn-group 中放置按钮和下拉菜单即可。

Bootstrap中的输入框组如何制作?

  • (1)把前缀或者后缀元素放在一个带有class="input-group"中的<div>
  • (2)在该<div>内,在class="input-group-addon"<span>里面放置额外的内容;
  • (3)把<span>放在<input>元素的前面或后面。

Bootstrap中的导航都有哪些?

  • (1)导航元素:有class="nav nav-tabs"的标签页导航,还有class="nav nav-pills"的胶囊式标签页导航;
  • (2)导航栏:class="navbar navbar-default" role="navigation"
  • (3)面包屑导航:class="breadcrumb"

Bootstrap中设置分页的class?

  • 默认的分页:class="pagination"
  • 默认的翻页:class="pager"

Bootstrap中显示标签的class?

1
class="label"

Bootstrap中如何制作徽章?

1
<span class="badge">26</span>

Bootstrap中超大屏幕的作用是什么?

设置class="jumbotron"可以制作超大屏幕,该组件可以增加标题的大小并增加更多的外边距

编程题

写一个通用的事件侦听器函数

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
// event(事件)工具集,来源:github.com/markyun
markyun.Event = {
// 视能力分别使用dom0||dom2||IE方式 来绑定事件
// 参数: 操作的元素,事件名称 ,事件处理程序
addEvent : function(element, type, handler) {
if (element.addEventListener) {
//事件类型、需要执行的函数、是否捕捉
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, function() {
handler.call(element);
});
} else {
element['on' + type] = handler;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默认行为
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 获取事件目标
getTarget : function(event) {
return event.target || event.srcElement;
}

如何判断一个对象是否为数组

1
2
3
4
5
6
function isArray(arg) {
if (typeof arg === 'object') {
return Object.prototype.toString.call(arg) === '[object Array]';
}
return false;
}

冒泡排序

  • 每次比较相邻的两个数,如果后一个比前一个小,换位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var arr = [3, 1, 4, 6, 5, 7, 2];

function bubbleSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for(var j = 0; j < arr.length - i - 1; j++) {
if(arr[j + 1] < arr[j]) {
var temp;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}

console.log(bubbleSort(arr));

快速排序

采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放到右边

快速排序的思想很简单,整个排序过程只需要三步:

  • (1)在数据集之中,找一个基准点
  • (2)建立两个数组,分别存储左边和右边的数组
  • (3)利用递归进行下次比较
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var arr = [3, 1, 4, 6, 5, 7, 2];

function quickSort(arr) {
if(arr.length == 0) {
return []; // 返回空数组
}

var cIndex = Math.floor(arr.length / 2);
var c = arr.splice(cIndex, 1);
var l = [];
var r = [];

for (var i = 0; i < arr.length; i++) {
if(arr[i] < c) {
l.push(arr[i]);
} else {
r.push(arr[i]);
}
}

return quickSort(l).concat(c, quickSort(r));
}

console.log(quickSort(arr));

编写一个方法 求一个字符串的字节长度

  • 假设:一个英文字符占用一个字节,一个中文字符占用两个字节
1
2
3
4
5
6
7
8
9
10
function GetBytes(str){
var len = str.length;
var bytes = len;

for(var i=0; i<len; i++){
if (str.charCodeAt(i) > 255) bytes++;
}
return bytes;
}
alert(GetBytes("你好,as"));

bind的用法,以及如何实现bind的函数和需要注意的点

  • bind的作用与callapply相同,区别是callapply是立即调用函数,而bind是返回了一个函数,需要调用的时候再执行。 一个简单的bind函数实现如下
1
2
3
4
5
6
Function.prototype.bind = function(ctx) {
var fn = this;
return function() {
fn.apply(ctx, arguments);
};
};

实现一个函数clone

可以对JavaScript中的5种主要的数据类型,包括NumberStringObjectArrayBoolean)进行值复

  • 考察点1:对于基本数据类型和引用数据类型在内存中存放的是值还是指针这一区别是否清楚
  • 考察点2:是否知道如何判断一个变量是什么类型的
  • 考察点3:递归算法的设计
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
// 方法一:
Object.prototype.clone = function(){
var o = this.constructor === Array ? [] : {};
for(var e in this){
o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];
}
return o;
}

//方法二:
/**
* 克隆一个对象
* @param Obj
* @returns
*/
function clone(Obj) {
var buf;
if (Obj instanceof Array) {
buf = []; //创建一个空的数组
var i = Obj.length;
while (i--) {
buf[i] = clone(Obj[i]);
}
return buf;
}else if (Obj instanceof Object){
buf = {}; //创建一个空对象
for (var k in Obj) { //为这个对象添加新的属性
buf[k] = clone(Obj[k]);
}
return buf;
}else{ //普通变量直接赋值
return Obj;
}
}

下面这个ul,如何点击每一列的时候alert其index

考察闭包

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
<ul id=”test”>
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul>
// 方法一:
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=function(){
alert(this.index);
}

//方法二:
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=(function(a){
return function() {
alert(a);
}
})(i);
}

定义一个log方法,让它可以代理console.log的方法

1
2
3
4
5
6
7
可行的方法一:

function log(msg) {
console.log(msg);
}

log("hello world!") // hello world!

如果要传入多个参数呢?显然上面的方法不能满足要求,所以更好的方法是:

1
2
3
function log(){
console.log.apply(console, arguments);
};

输出今天的日期

YYYY-MM-DD的方式,比如今天是2014年9月26日,则输出2014-09-26

1
2
3
4
5
6
7
8
9
10
11
var d = new Date();
// 获取年,getFullYear()返回4位的数字
var year = d.getFullYear();
// 获取月,月份比较特殊,0是1月,11是12月
var month = d.getMonth() + 1;
// 变成两位
month = month < 10 ? '0' + month : month;
// 获取日
var day = d.getDate();
day = day < 10 ? '0' + day : day;
alert(year + '-' + month + '-' + day);

用js实现随机选取10–100之间的10个数字,存入一个数组,并排序

1
2
3
4
5
6
7
8
9
var iArray = [];
funtion getRandom(istart, iend){
var iChoice = istart - iend +1;
return Math.floor(Math.random() * iChoice + istart;
}
for(var i=0; i<10; i++){
iArray.push(getRandom(10,100));
}
iArray.sort();

写一段JS程序提取URL中的各个GET参数

有这样一个URLhttp://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,请写一段JS程序提取URL中的各个GET参数(参数名和参数个数不确定),将其按key-value形式返回到一个json结构中,如{a:'1', b:'2', c:'', d:'xxx', e:undefined}

1
2
3
4
5
6
7
8
9
function serilizeUrl(url) {
var result = {};
url = url.split("?")[1];
var map = url.split("&");
for(var i = 0, len = map.length; i < len; i++) {
result[map[i].split("=")[0]] = map[i].split("=")[1];
}
return result;
}

写一个function,清除字符串前后的空格

使用自带接口trim(),考虑兼容性:

1
2
3
4
5
6
7
8
9
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^\s+/, "").replace(/\s+$/,"");
}
}

// test the function
var str = " \t\n test string ".trim();
alert(str == "test string"); // alerts "true"

实现每隔一秒钟输出1,2,3…数字

1
2
3
4
5
6
7
for(var i=0;i<10;i++){
(function(j){
setTimeout(function(){
console.log(j+1)
},j*1000)
})(i)
}

实现一个函数,判断输入是不是回文字符串

1
2
3
4
function run(input) {
if (typeof input !== 'string') return false;
return input.split('').reverse().join('') === input;
}

数组扁平化处理

实现一个flatten方法,使得输入一个数组,该数组里面的元素也可以是数组,该方法会输出一个扁平化的数组

1
2
3
4
5
function flatten(arr){
return arr.reduce(function(prev,item){
return prev.concat(Array.isArray(item)?flatten(item):item);
},[]);
}

实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制

1
2
3
4
5
6
7
Object.prototype.clone = function(){
var o = this.constructor === Array ? [] : {};
for(var e in this){
o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];
}
return o;
}

其他

负载均衡

多台服务器共同协作,不让其中某一台或几台超额工作,发挥服务器的最大作用

  • http重定向负载均衡:调度者根据策略选择服务器以302响应请求,缺点只有第一次有效果,后续操作维持在该服务器 dns负载均衡:解析域名时,访问多个ip服务器中的一个(可监控性较弱)
  • 反向代理负载均衡:访问统一的服务器,由服务器进行调度访问实际的某个服务器,对统一的服务器要求大,性能受到 服务器群的数量

CDN

内容分发网络,基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。

内存泄漏

定义:程序中己动态分配的堆内存由于某种原因程序未释放或无法释放引发的各种问题。

js中可能出现的内存泄漏情况

结果:变慢,崩溃,延迟大等,原因:

  • 全局变量
  • dom清空时,还存在引用
  • ie中使用闭包
  • 定时器未清除
  • 子元素存在引起的内存泄露

避免策略

  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
  • 注意程序逻辑,避免“死循环”之类的 ;
  • 避免创建过多的对象 原则:不用了的东西要及时归还。
  • 减少层级过多的引用

babel原理

ES6、7代码输入 -> babylon进行解析 -> 得到AST(抽象语法树)-> plugin用babel-traverseAST树进行遍历转译 ->得到新的AST树->用babel-generator通过AST树生成ES5代码

js自定义事件

三要素: document.createEvent() event.initEvent() element.dispatchEvent()

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
// (en:自定义事件名称,fn:事件处理函数,addEvent:为DOM元素添加自定义事件,triggerEvent:触发自定义事件)
window.onload = function(){
var demo = document.getElementById("demo");
demo.addEvent("test",function(){console.log("handler1")});
demo.addEvent("test",function(){console.log("handler2")});
demo.onclick = function(){
this.triggerEvent("test");
}
}
Element.prototype.addEvent = function(en,fn){
this.pools = this.pools || {};
if(en in this.pools){
this.pools[en].push(fn);
}else{
this.pools[en] = [];
this.pools[en].push(fn);
}
}
Element.prototype.triggerEvent = function(en){
if(en in this.pools){
var fns = this.pools[en];
for(var i=0,il=fns.length;i<il;i++){
fns[i]();
}
}else{
return;
}
}

前后端路由差别

  • 后端每次路由请求都是重新访问服务器
  • 前端路由实际上只是JS根据URL来操作DOM元素,根据每个页面需要的去服务端请求数据,返回数据后和模板进行组合

❤有什么方法可以保持前后端实时通信

牛客网解释:

  • 得分点 轮询、长轮询、 iframe流、WebSocket、SSE
  • 标准回答
  • 保持前后端实时通信的方法有以下几种:
    • 轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。其缺点也很明显:连接数会很多,一个接受,一个发送。而且每次发送请求都会有Http的Header,会很耗流量,也会消耗CPU的利用率。优点就是实现简单,无需做过多的更改。缺点是轮询的间隔过长,会导致用户不能及时接收到更新的数据;轮询的间隔过短,会导致查询请求过多,增加服务器端的负担
    • 长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,如果没有新消息,就一直等待。有新消息,才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。由于http数据包的头部数据量往往很大(通常有400多个字节),但是真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费。优点是做了优化,有较好的时效性。缺点是保持连接会消耗资源; 服务器没有返回有效数据,程序超时。
    • iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长连接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。优点是消息能够实时到达;浏览器兼容好。缺点是服务器维护一个长连接会增加开销;IE、chrome、Firefox会显示加载没有完成,图标会不停旋转。
    • WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发和客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。缺点是浏览器支持程度不一致,不支持断开重连。
    • SSE(Server-Sent Event)是建立在浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。SSE 是单向通道,只能服务器向浏览器发送,因为 streaming 本质上就是下载。 优点是SSE 使用 HTTP 协议,现有的服务器软件都支持。SSE 属于轻量级,使用简单;SSE 默认支持断线重连;
  • 加分回答
    • 轮询适用于:小型应用,实时性不高
    • 长轮询适用于:一些早期的对及时性有一些要求的应用:web IM 聊天
    • iframe适用于:客服通信等
    • WebSocket适用于:微信、网络互动游戏等
    • SSE适用于:金融股票数据、看板等

综合

谈谈你对重构的理解

  • 网站重构:在不改变外部行为的前提下,简化结构、添加可读性,而在网站前端保持一致的行为。也就是说是在不改变UI的情况下,对网站进行优化, 在扩展的同时保持一致的UI
  • 对于传统的网站来说重构通常是:
    • 表格(table)布局改为DIV+CSS
    • 使网站前端兼容于现代浏览器(针对于不合规范的CSS、如对IE6有效的)
    • 对于移动平台的优化
    • 针对于SEO进行优化

什么样的前端代码是好的

  • 高复用低耦合,这样文件小,好维护,而且好扩展。
  • 具有可用性、健壮性、可靠性、宽容性等特点
  • 遵循设计模式的六大原则

对前端工程师这个职位是怎么样理解的?它的前景会怎么样

  • 前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近
    • 实现界面交互
    • 提升用户体验
    • 基于NodeJS,可跨平台开发
  • 前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,
  • 与团队成员,UI设计,产品经理的沟通;
  • 做好的页面结构,页面重构和用户体验;

你觉得前端工程的价值体现在哪

  • 为简化用户使用提供技术支持(交互部分)
  • 为多个浏览器兼容性提供支持
  • 为提高用户浏览速度(浏览器性能)提供支持
  • 为跨平台或者其他基于webkit或其他渲染引擎的应用提供支持
  • 为展示数据提供支持(数据接口)

平时如何管理你的项目

  • 先期团队必须确定好全局样式(globe.css),编码模式(utf-8) 等;

  • 编写习惯必须一致(例如都是采用继承式的写法,单样式都写成一行);

  • 标注样式编写人,各模块都及时标注(标注关键样式调用的地方);

  • 页面进行标注(例如 页面 模块 开始和结束);

  • CSSHTML 分文件夹并行存放,命名都得统一(例如style.css);

  • JS 分文件夹存放 命名以该JS功能为准的英文翻译。

  • 图片采用整合的 images.png png8 格式文件使用 - 尽量整合在一起使用方便将来的管理

  • 规定全局样式、公共脚本

  • 严格要求代码注释(html/js/css)

  • 严格要求静态资源存放路径

  • Git 提交必须填写说明

组件封装

目的:为了重用,提高开发效率和代码质量 注意:低耦合,单一职责,可复用性,可维护性 常用操作

  • 分析布局
  • 初步开发
  • 化繁为简
  • 组件抽象

Web 前端开发的注意事项

  • 特别设置 meta 标签 viewport
  • 百分比布局宽度,结合 box-sizing: border-box;
  • 使用 rem 作为计算单位。rem 只参照跟节点 html 的字体大小计算
  • 使用 css3 新特性。弹性盒模型、多列布局、媒体查询等
  • 多机型、多尺寸、多系统覆盖测试

在设计 Web APP 时,应当遵循以下几点

  • 简化不重要的动画/动效/图形文字样式
  • 少用手势,避免与浏览器手势冲突
  • 减少页面内容,页面跳转次数,尽量在当前页面显示
  • 增强 Loading 趣味性,增强页面主次关系

你怎么看待 Web App/hybrid App/Native App?(移动端前端 和 Web 前端区别?)

  • Web App(HTML5):采用HTML5生存在浏览器中的应用,不需要下载安装
    • 优点:开发成本低,迭代更新容易,不需用户升级,跨多个平台和终端
    • 缺点:消息推送不够及时,支持图形和动画效果较差,功能使用限制(相机、GPS等)
  • Hybrid App(混合开发):UI WebView,需要下载安装
    • 优点:接近 Native App 的体验,部分支持离线功能
    • 缺点:性能速度较慢,未知的部署时间,受限于技术尚不成熟
  • Native App(原生开发):依托于操作系统,有很强的交互,需要用户下载安装使用
    • 优点:用户体验完美,支持离线工作,可访问本地资源(通讯录,相册)
    • 缺点:开发成本高(多系统),开发成本高(版本更新),需要应用商店的审核

页面重构怎么操作

网站重构:不改变UI的情况下,对网站进行优化,在扩展的同时保持一致的UI。

  • 页面重构可以考虑的方面:
    • 升级第三方依赖
    • 使用HTML5CSS3ES6 新特性
    • 加入响应式布局
    • 统一代码风格规范
    • 减少代码间的耦合
    • 压缩/合并静态资源
    • 程序的性能优化
    • 采用CDN来加速资源加载
    • 对于JS DOM的优化
    • HTTP服务器的文件缓存

一些常见问题

  • 自我介绍
  • 面试完你还有什么问题要问的吗
  • 你有什么爱好?
  • 你最大的优点和缺点是什么?
  • 你为什么会选择这个行业,职位?
  • 你觉得你适合从事这个岗位吗?
  • 你有什么职业规划?
  • 你对工资有什么要求?
  • 如何看待前端开发?
  • 未来三到五年的规划是怎样的?
  • 你的项目中技术难点是什么?遇到了什么问题?你是怎么解决的?
  • 你们部门的开发流程是怎样的
  • 你认为哪个项目做得最好?
  • 说下工作中你做过的一些性能优化处理
  • 最近在看哪些前端方面的书?
  • 平时是如何学习前端开发的?
  • 你最有成就感的一件事
  • 你为什么要离开前一家公司?
  • 你对加班的看法
  • 你希望通过这份工作获得什么?

HR面

你还有其他公司的Offer吗?

  • 表明自己有三四个已经确认过的offer了(没有offer也要吹,但是不要透露具体公司)
  • 但是第一意向还是本公司,如果薪资差距不大,会优先考虑本公司
  • 再透露出,有一两个offer催得比较急,希望这边快点出结果

为什么从上一家公司离职?

  • 因为公司搬家,通勤时间三个小时
  • 因为离家远花费在路途上的时间过多,不如用来充电,因为加班多导致没有时间充电,无法提高等等

如何看待加班(996)?

  • 把加班分为紧急加班和长期加班
  • 对于紧急加班,表示这是每个公司都会遇到的情况,自己愿意牺牲时间帮助公司和团队
  • 对于长期加班,如果是自己长期加班那么会磨练自己的技能,提高自己的效率,如果是团队长期加班,自己会帮助团队找到问题,利用自动化工具或者更高效的协作流程来提高整个团队的效率,帮助大家摆脱加班

你对未来3-5年的职业规划

  • 首先表示考虑过这个问题(有规划),如何谈一谈自己的现状(结合实际).
  • 接着从工作本身出发,谈谈自己会如何出色完成本职工作,如何对团队贡献、如何帮助带领团队其他成员创造更多的价值、如何帮助团队扩大影响力.
  • 最后从学习出发,谈谈自己会如何精进领域知识、如何通过提升自己专业能力,如何反哺团队.

如何与HR谈薪资

正确的回答可以这样,并且还能够反套路一下HR

  • HR: 您期望的薪资是多少?
  • 你: 就我的面试表现,贵公司最高可以给多少薪水?

如果经验不够老道的HR可能就真会说出一个报价(如25K)来,然后,你就可以很开心地顺着这个价慢慢地往上谈了。所以这种情况下,你最终的薪资肯定是大于25K的。当然,经验老道的HR会给你一句很官方的套话:

  • HR: 您期望的薪资是多少?
  • 你: 就我的面试表现,贵公司最高可以给多少薪水?
  • HR: 这个暂且没法确定,要结合您几轮面试结果和用人部门的意见来综合评定。

虽然薪资很重要,但是我个人觉得这不是最重要的。我有以下建议:

  • 如果你觉得你技术面试效果很好,可以报一个高一点的薪资,这样如果HR想要你,会找你商量的。
  • 如果你觉得技术面试效果一般,但是你比较想进这家公司,可以报一个折中的薪资。
  • 如果你觉得面试效果很好,但是你不想进这家公司,你可以适当“漫天要价”一下。
  • 如果你觉得面试效果不好,但是你想进这家公司,你可以开一个稍微低一点的工资