Justin's Words

我遇到的前端面试问题

清除浮动

clear
1
2
3
4
/* 放在浮动元素之后清除浮动 */
.clear {
clear: both;
}
构造 BFC

有关 BFC 的知识在下面有说,overflow 不为 visible 可构造一个 BFC。

1
2
3
.container {
overflow: hidden;
}
clearfix
1
2
3
4
5
6
7
8
9
10
11
.clearfix {
&:before,
&:after {
content: "";
display: table;
}
&:after {
clear: both;
}
zoom: 1;
}

在浮动层内垂直居中

给浮动层包含个 div,由于 inline-block 直接会产生空隙,消除法看下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
.container {
overflow: hidden;
&>div {
display: inline-block;
vertical-align: middle;
.left {
float: left;
}
.right {
float: right;
}
}
}

改为 table 布局:

1
2
3
4
5
6
7
8
.container {
display: table-row;
.left,
.right {
display: table-cell;
vertical-align: middle;
}
}
参考

元素同行排列

1
2
3
4
5
6
7
<div class="wrapper">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>

使用 display: inline-block;

1
2
3
.box {
display: inline-block;
}

去掉 inline-block 间的空格方法1:

1
2
3
<div class="wrapper">
<div class="box"></div><div class="box"></div><div class="box"></div><div class="box"></div><div class="box"></div>
</div>

去掉 inline-block 间的空格方法2:

1
2
3
4
5
6
.wrapper {
font-size: 0;
.box {
font-size: 16px;
}
}

使用 float

1
2
3
4
5
6
7
8
9
10
11
12
13
.wrapper {
&:before,
&:after {
display: table;
content: "";
}
&:after {
clear: both;
}
.box {
float: left;
}
}

使用 flexbox

1
2
3
.wrapper {
display: flex;
}

为什么不提倡使用 table 布局:

  1. 占用字节太多,下载慢
  2. 会堵塞页面渲染
  3. 可能得把单个、有逻辑的图片切成分散的小个
  4. 在某些浏览器上导致复制文字很诡异,用户体验差
  5. 一些样式会不起作用 (td { height: 100%; })
  6. 使用样式时非常难以优化
  7. 作用是展示,不是内容,从语义上看是不正确的
  8. 一旦写好,很难用 CSS 改变它。

CSS 垂直居中

1
2
3
<div class="wrapper">
<div class="inner"></div>
</div>

使用 table 布局:

1
2
3
4
5
6
7
8
.wrapper {
display: table-cell;
vertical-align: middle;
text-align: center;
.inner {
display: inline-block;
}
}

使用 transform

1
2
3
4
5
6
7
8
9
.wrapper {
display: relative;
.inner {
display: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}

使用 flexbox

1
2
3
4
5
6
7
.wrapper {
display: flex;
justify-content: center;
.inner {
align-self: center;
}
}

适用 absolute 布局:

1
2
3
4
5
.Absolute-Center {
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}

浏览器内核

  • Blink – Chrome
  • Trident – Internet Explorer
  • Gecko – Firfox
  • Webkit – Safari
  • Presto – Opera
  • Others

CSS 盒子模型

CSS Box

BFC

BFC 全称 Block Formatting Context,块级格式化上下文,是网页中 CSS 渲染的一部分,是一个独立的渲染容器,其内部子元素不会影响到外部元素。

形成条件
  • 被根元素所包含
  • float 不为 none
  • 绝对定位元素,包括 absolutefixed
  • 块状包裹,比如 displayinline-block, table-cell, table-caption, flex, inline-flex
  • overflow 不为 visible
布局
  • BFC 内部,各个 Box 一个个垂直放置,由 BFC 的头部开始
  • 相邻之间 Box 的距离由 `margin 决定,同一个 BFC 内的相邻 Box 之间会发生 margin collapsing
  • 每个 Box 外边界(margin) 都和 BFC 左边界相接触(对于从右到左格式化,则接触右边界),浮动元素同样,除非内部 float Box 又创建一个新的 BFC
  • BFC 是一个隔离的独立容器,其内部子元素不会影响到外部元素
  • 计算 BFC 的高度时,浮动元素也参与计算
参考

Number(1) 和 new Number(1) 的区别

1Number(1) 返回的是 number 原始值,new Number(1) 返回一个 Number 对象,Number() 被用于将需要的变量转换为 number 变量。

1
2
3
4
5
6
7
8
9
10
typeof 1;    // "number"
typeof Number(1); // "number"
typeof new Number(1); // "object"

1 instanceof Number; // false
Number(1) instanceof Number; // false
new Number(1) instanceof Number; // true

1 === Number(1); // true
1 === (new Number(1)).valueOf(); // true

null 和 undefined 的区别

1
2
typeof null    // "object"
typeof undefined // undefined

undefined 指定义了但尚未被赋值,null 则是一个可分配的值,比如传给函数的参数为空可以用到 null

1
2
3
4
5
foo(bar, null, null);
null === undefined; // false
null == undefined; // true
undefined === undefined; // true
null === null; // true

克隆 DOM 节点

仅仅克隆节点不克隆其子元素:

1
document.getElementById('#foo').cloneNode();

同时克隆其子元素:

1
document.getElementById('#foo').cloneNode(true);

DOM 的增删

增加节点:

1
2
3
4
5
var childNode = document.createElement('a');
childNode.id = 'bar';
childNode.className = 'foo';
childNode.href = 'http://foo.bar';
document.getElementById('parentNode').appendChild(childNode);

删除节点:

1
2
3
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
}

函数需要的参数长度

1
(function(a, b){}).length === 2;    // true

怎么判断数组

1
Object.prototype.toString.call(givenArrVar) === '[object Array]'

数组 unique

代码片段来自:Unique values in an array

1
2
3
4
5
6
7
8
9
10
11
Array.prototype.getUnique = function(){
var u = {}, a = [];
for(var i = 0, l = this.length; i < l; ++i){
if(u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}

获取 select 的值

本题来自阿里前端笔试,改变下拉列表框能显示当前选中的值在输入框

1
2
3
4
5
6
7
8
<form name="appform">
<input type="text" name="app">
<select size="1" name="selectedApp" onchange="changeApp()">
<option value="td" selected>Taobao</option>
<option value="tm">Tmall</option>
<option value="mj">Miao</option>
</select>
</form>
1
2
3
4
5
var changeApp = function() {
var form = document.forms.appform;
var select = form.selectedApp;
form.app.value = select[select.selectedIndex].text;
}

这里还有个 selectedOptions 返回一个 element list:

1
2
document.forms.appform.selectedApp.selectedOptions;
// [<option value="td" selected>Taobao</option>]

let 和 var 区别

一个代码块内同名变量用 let 只能声明一次,而 var 可声明多次:

1
2
3
4
var a;
var a; // no error
let b;
let b; // error

letif, forECMAScript6 中的{}内自有作用域,对外部无影响:

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
function foo() {
var a = 1;
let b = 1;

if(true) {
var a = 2;
let b = 2;
}

console.log(a); // 2
console.log(b); // 1
}

function bar() {
var a = 1;
let b = 1;

for(var a = 2; false;){}
for(let b = 2; false;){}

console.log(a); // 2
console.log(b); // 1
}

var a = 1;
let b = 1;

{
var a = 2;
let b = 2;
}

console.log(a); // 2
console.log(b); // 1

简而言之,即是 var 的作用域在其最近的函数体内,let 的作用域在其最近的代码块内。

两种不同的函数声明方式区别

JavaScript 有两种函数声明方式,var fn = function(){}function fn(){},前者是运行时定义,后者是编译时定义。

1
2
3
4
5
foo();    // no error
function foo() {}

bar(); // error: bar is not a function
let bar = function() {}

变量提升

在一个var作用域内非子作用域内声明一个变量,那么在该变量被声明之前的同名变量会初始化为 undefiend,此时外部作用域同名变量将不再作用于作用域内部同名变量。

1
2
3
4
5
6
7
8
var foo = 1;
function fn1() {
console.log(foo); // undefined
var foo = 2;
foo++;
}
fn1();
console.log(foo); // 1

GET 和 POST 区别

  • GET 用于从服务器取出数据
  • POST 用于改变服务器的数据
  • 在客户端,GET 方式通过 URL 提交数据,数据在 URL 中可以看到;POST 方式,数据放在 HTML HEADER 内提交。
  • GET 提交数据最多为 1024 Bits,POST 则无限制
GET 的特性
  • GET 用于安全行为,POST 用于不安全行为
  • GET 请求可以被缓存
  • GET 请求会留在浏览器历史里
  • GET 可以作为书签保存
  • GET 链接可以分享
  • GET 可以用于 hack
用 POST 的情况
  • 修改服务器数据
  • 敏感信息
  • 长数据,多数据

AngularJS 控制器间数据共享

关键词:$emit, $broadcast, $on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
angular.module('app', [])
.controller('parentCtrl', function($scope) {
// 监听子控制器注册事件
$scope.$on('CtrlNameChange', function(event, msg) {
// 广播给所有子控制器
$scope.$broadcast('CtrlNameChangeBroadcastFromParent', msg)
});
})
.controller('ChildCtrl1', function($scope) {
$scope.change = function(name) {
// 冒泡到父控制器
$scope.$emit('CtrlNameChange', name);
}
})
.controller('ChildCtrl2', function($scope) {
// 监听父控制器广播事件
$scope.$on('CtrlNameChangeBroadcastFromParent', function(event, msg) {
$scope.ctrlName = msg;
})
})

闭合函数

什么是闭合函数
  • 一个代表函数的变量(在函数结束之后仍可访问)或一个指向函数的指针
  • 在函数结束之后其空间并未被释放而是被放在堆结构内提供访问需要
1
2
3
4
5
6
7
8
function foo() {
var greet = 'Hello world';
return function() {
console.log(greet);
};
}
var bar = foo();
bar(); // "Hello world";

以上 foo 函数包含了一个闭合,因为其内部定义了一个匿名函数 function() { console.log(greet); },一旦你在一个函数内使用了 function 关键词,你就创造了一个闭合。

在闭合函数中,函数运行完毕后不会被立即释放内存,而是保留了其引用(reference),这是其和 C 语言或其他类似语言不同的。

闭合函数只是引用,不是复制:

1
2
3
4
5
6
7
8
9
10
function foo() {
var a = 1;
var inner = function() {
console.log(a);
};
a++;
return inner;
}
var bar = foo();
bar(); // 2;

以下丧心病狂例子来自 stackoverflow

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gAlertNumber = function() { alert(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(5);
gAlertNumber(); // 5

var oldAlert = gAlertNumber;

setupSomeGlobals();
gAlertNumber(); // 666

oldAlert() // 5

/*********************************************************************************/
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}

function testList() {
var fnlist = buildList([1,2,3]);
// Using j only to help prevent confusion -- could use i.
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}

testList(); // "item3 undefined" for 3 times
// because closure is a reference, when the loop in buildList() is complete, i has turned to 3, so list[3] is undefined.

/***************************************************************************/
function sayAlice() {
var sayAlert = function() { alert(alice); }
// Local variable that ends up within closure
var alice = 'Hello Alice';
return sayAlert;
}
sayAlice()();

/*********************************************************************/
function newClosure(someNum, someRef) {
// Local variables that end up within closure
var num = someNum;
var anArray = [1,2,3];
var ref = someRef;
return function(x) {
num += x;
anArray.push(num);
alert('num: ' + num +
'\nanArray ' + anArray.toString() +
'\nref.someVar ' + ref.someVar);
}
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

前端性能优化

权威发布:Best Practices for Speeding Up Your Web Site

来自:前端工程与性能优化 – fouber

优化方向 优化手段
请求数量 合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域,webpack
请求带宽 开启GZip,精简JavaScript,移除重复脚本,图像优化
缓存利用 使用CDN,使用外部JavaScript和CSS,添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存
页面结构 将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出
代码校验 避免CSS表达式,避免重定向

什么是 CSS 表达式,比如这个:

1
2
3
body {
background-color: expression((new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
}

这种写法始于 IE5,终于 IE8,解释: Ending Expressions

取消一个 Ajax 请求

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
var xmlHttp;

if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else {
try {
xmlHttp = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
xmlHttp = new ActiveXObject('Microsoft.XMLHttp');
} catch (e) {}
}
}

if (!xmlHttp) {
console.error('Your browser don\'t support XMLHttp Object')
}

xmlHttp.onreadystatechange = function () {
try {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
console.log(xmlHttp.responseText/responseJSON/responseXML/response);
} else {
console.error('error');
}
} catch (e) {
console.error(e.description);
}
};

xmlHttp.open('POST', url, true);
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-data');
xmlHttp.send('a=2&b=1');

if (xmlHttp.readyState === 3) xmlHttp.abort();

script 属性 async, defer 区别

<script></script>; 同步加载,在浏览器解析 DOM 之前加载,可能会堵塞线程

<script async></script> 异步加载,在浏览器解析 DOM 的同时加载

<script defer></script> 在浏览器解析 DOM 完成之后加载

JSONP 跨域的原理

动态添加一个 script 标签,sciprt 内的 scr 属性是不受同源策略影响的,也就是说 JSONP 的方式和 Ajax 协议是没有关系的,这和 <image src> 是同样道理。

Redux 最重要的几个概念

store, state, dispatch

HTTP 请求方法

GET, POST, HEAD, PUT, DELETE, OPTIONS, TRACE, CONNECT

HTTP 状态响应码

1** Information 请求收到,继续处理
  • 100 Continue 客户必须继续发出请求* 101 Switch Protocols 客户要求服务器根据请求转换 HTTP 协议版本
2** Successful 操作成功收到,分析、接受
  • 200 OK 请求成功
  • 201 Created 提示知道新闻界的 URL
  • 202 Accepted 接受处理,但处理未完成
  • 203 Non-Authoritative Information 返回信息不确定或不完整
  • 204 No Content 请求收到,但返回信息为空
  • 205 Reset Content 服务器完成了请求,用户代理必须复位当前已经浏览过的文件
  • 206 Partial Content 服务器已经完成了部分用户的 GET 请求
3** Redirection 重定向
  • 300 Multiple Choices 请求的资源可在多处得到
  • 301 Moved Permanently 永久跳转
  • 302 Found 在其他地址发现了请求数据
  • 303 See Other 建议客户访问其他 URL 或访问方式
  • 304 Not Modified 客户端意见执行了 GET,但文件未变化
  • 305 Use Proxy 请求的资源必须从服务器指定的地址得到
  • 306 Unused 前一版本 HTTP 中使用的代码,现行版本中不再使用
  • 307 Temporary Redirect 申明请求的资源临时性删除
4** Client Error
  • 400 Bad Request 错误请求,如语法错误
  • 401 Unauthorized 未授权
  • 402 Payment Required 域名保留为未来使用
  • 403 Forbidden 禁止访问
  • 404 Not Found 找不到文件、查询或 URL
  • 405 Method Not Allowed 当前 HTTP 请求方法不被允许
  • 406 Not Acceptable 根据用户发送的 Accept,请求资源不可访问 …
5** Server Error
  • 500 Internal Server Error 内部服务器错误
  • 501 Not Implemented 服务器不支持需要用来完成请求的函数
  • 502 Bad Gateway 网关错误
  • 503 Service Unavailable 服务器维护中或过载
  • 504 Gateway Timeout 网关超时
  • 505 Http Version Not Supported 不支持的 HTTP 版本
参考
相同点
  • 都属于客户端或浏览器存储机制
  • 都具有同源安全策略
不同点

localStorage 和 sessionStorage 是 HTML5 的新 APIs,意味着不是所有浏览器都支持,现代浏览器基本都支持。

sessionStorage 存储的信息会在浏览器关闭后清除,但页面关闭而浏览器不关闭则不会被清除,顾名思义,其只存活在一个浏览器会话中。 如果你希望保存的信息在浏览器关闭重开后还能使用,则应该用 localStorage。

localStorage 和 sessionStorage 应该用来保存非敏感并且需要在同源页面间使用的信息,两者都能被客户端或浏览器轻易读取和改变,所以不应该依赖这两者保存敏感或安全信息。

查看浏览器是否支持 localStorage 和 sessionStorage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
window.localStorage != undefined;
window.sessionStorage != undefined

localStorage 和 sessionStorage 基本操作:

localStorage.setItem('foo', 'bar');
localStorage['foo'] = 'bar';
localStorage.foo = 'bar'
localStorage.getItem('foo'); // bar
localStorage.remvoeItem('foo');
localStorage.clear();

sessionStorage.setItem('foo', 'bar');
sessionStorage['foo'] = 'bar';
sessionStorage.foo = 'bar';
sessionStorage.getItem('foo'); // bar
sessionStorage.remvoeItem('foo');
sessionStorage.clear();

关系到敏感信息则应该用 cookie,因为cookie 相对较难被用户读取和写入,但如果用非 SSL 传输,cookie 同样可能在传输过程中被截取,尤其是在用一个公共 wifi。

cookie 安全等级比前两者高,它可以有效防御 XSS(Cross-Site Scripting)。因此可以使用 cookie 来存储一些验证信息,比如登陆 token或广告商的用户追踪。

cookie 大多被用来验证用户数据,在同源内,页面本身 HTTP 请求包括其内部的 Ajax 请求、图片、样式、脚本和字体的 HTTP 头都包含 cookie,因为不应该用 cookie 存储大量数据,事实上浏览器会对过长 cookie 进行截取的。

cookie 本身是一段字符串,sessionStorage 和 localStorage 则是以 JavaScript primitives(JSON serialise) 形式保存,可读性很高。

提取 cookie 作为对象返回:

1
2
3
4
5
6
7
8
9
10
11
12
function getCookie() {
var cookieArr = document.cookie.split('; ');
var cookieObj = {};
cookieArr.forEach(function(v, k) {
var firstEqualSign = v.indexOf('=');
var cookieKey = v.slice(0, firstEqualSign);
var cookieVal = v.slice(firstEqualSign + 1);
cookieObj[cookieKey] = cookieVal;
});

return cookieObj;
}
参考

总参考