慕课网《前端跳槽面试必备技巧》——关于基础面
本篇介绍的是面试的一/二面,即基础面需要准备的内容。原课程链接:前端跳槽面试必备技巧
一面/二面(基础知识面)
这一面的面试技巧:
- 准备要充分
- 知识要系统
- 沟通要简洁
- 内心要诚实
- 态度要谦虚
- 回答要灵活
切忌:
基础面相对答案相对统一,所以要简洁回答;而相比于第二/三面,可以多回答一点,但是要注意成体系,有逻辑性。
诚实的回答,不要自己说“我看过,我忘记了”这种,不会就是不会,可以去请教面试官。
关于知识,不要说话太死或者太满,评价一个事情的时候,由于我们往往认识不足,才会得到一个片面的观点。
页面布局
题目:假设调试已知,请写出三栏布局,其中左栏、右栏宽度各为300px,中间自适应。

回答技巧:
题干的要求真的是这么简单吗?
解析:浮动+绝对定位是基础答案。加分项:第三种是Flex布局,第四种是Table布局,第五种网格布局Grid布局。
这道题的答案应该怎么写?技巧在哪里?
如果我要证明我的实力,我应该答上几种答案。
下面是答案:
1 |
|
答完题之后,面试可能最会问的是:
怎么延伸,这一种方案的优缺点;
把高度去掉,中间的高度比较高,需要左右一样会撑开。
- Table、Flex布局可以自适应。
兼容性怎么样。
- 对于浮动缺点:常见需要清除浮动;优点,兼容性比较好。
- 绝对定位缺点:脱离了文档流;优点:快捷;
- flex布局:在移动端适配比较好;
- 表格布局:优点,对于高度的适应比较好,兼容性比较好;缺点:当其中一个单元格高度超出了之后,会改变其他的单元格的高度;
- 网格布局:优点代码量简化;缺点:兼容性对于IE不是友好。
浮动解决方案,为什么中间内容比较高,超出了左侧元素后,为什么会浮动到左侧? 使用浮动布局的时候,不让元素超出自己的边界,自动延伸(答案:创建BFC,见CSS盒模型)
页面布局小结
- 语义化掌握到位(不要通篇DIV)
- 页面布局理解深刻(理解原理)
- CSS基础知识扎实(table, flex, grid基础知识)
- 思维灵活且积极上进
- 代码书写规范
页面布局的变通/变种
三栏布局
- 左右宽度固定,中间自适应
- 上下高度固定,中间自适应
两栏布局
- 左宽度固定,右自适应
- 右宽度固定,左自适应
- 上高度固定,下自适应
- 下高度固定,上自适应
只有多思考,多准备,才能建立知识体系。
CSS盒模型
题目:谈谈你对CSS盒模型的认识
准备的知识:
- 基本概念:标准模型 + IE模型
- 标准模型与IE模型的区别(宽高不同)
- CSS如何设置这两种模型
- JS如何设置获取盒模型对应的宽和高
- 实例题(根据盒模型解释边距重叠)
- BFC(Block Formatting Context-块级格式化上下文,边距重叠解决方案) IFC(Inline Formatting Context-行级格式化上下文)
BFC布局规则:
- 内部的Box会在垂直方向,一个接一个地放置。
- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
- 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
- BFC的区域不会与float box重叠,常用来清除浮动和布局。
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- 计算BFC的高度时,浮动元素也参与计算
IFC布局规则:
- 框会从包含块的顶部开始,一个接一个地水平摆放。
- 摆放这些框的时候,它们在水平方向上的外边距、边框、内边距所占用的空间都会被考虑在内。在垂直方向上,这些框可能会以不同形式来对齐:它们可能会把底部或顶部对齐,也可能把其内部的文本基线对齐。能把在一行上的框都完全包含进去的一个矩形区域,被称为该行的行框。水平的margin、padding、border有效,垂直无效。不能指定宽高。
- 行框的宽度是由包含块和存在的浮动来决定。行框的高度由行高计算这一章所描述的规则来决定。
回答技巧:
- 基本概念:


标准模型与IE模型的区别,IE模型包含Padding进行计算Content内部部分的宽;
设置模型的方式:
1
2
3box-sizing: content-box;
box-sizing: border-box;JS有几种方式:
1
2
3
4
5
6
7dom.style.width/height,取的是内联属性,如果是link的样式取不到的。
dom.currentStyle.width/height 只有IE支持
window.getComputedStyle(dom).width/height
dom.getBoundingClientRect().width/height实例题:
元素重叠要注意:
- 兄弟元素的重叠,重叠的原则取最大值
- 空元素,取margin-top,margin-bottom一个最大值作为边距
BFC解决边距重叠解决方案:
- BFC基本概念
- BFC原理(垂直方向会重叠,不会清除FLEX布局,不会影响外面的元素,Float元素会参与计算)
- 如何创建BFC(overflow:hidden/visible,float不为None,position值不为relative或者static,display属性table/table-cell)
- BFC使用场景
1 |
|
DOM事件
- 基本概念:DOM事件的级别;
DOM事件类 | 事件级别 |
---|---|
DOM0 | element.onclick=function(){} |
DOM2 | element.addEventListener(‘click’, function(){}, false) |
DOM3 | element.addEventListener(‘keyup’,function(){}, false) |
IE中
attachListener
;DOM3增加了一些事件类型,鼠标、键盘事件;
DOM事件模型(冒泡、捕获)
捕获是从上往下,冒泡是从下至上;
捕获代码演示:
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
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Event</title>
</head>
<body>
<div id="ev">
<style>
#ev {
width: 300px;
height: 100px;
background: red;
color: #fff;
text-align: center;
line-height: 100px;
}
</style>
目标元素
</div>
<script>
var ev = document.getElementById('ev')
window.addEventListener('click', function(){
console.log('window capture')
}, true)
document.addEventListener('click', function(){
console.log('document capture')
}, true)
document.documentElement.addEventListener('click', function(){
console.log('html capture')
}, true)
document.body.addEventListener('click', function(){
console.log('body capture')
}, true)
ev.addEventListener('click', function(){
console.log('ev capture')
}, true)
</script>
</body>在
console
里面打印的信息:1
2
3
4
5window capture
document capture
html capture
body capture
ev capture上面的输出顺序不受定义先后顺序的影响,说明捕获的流程是从上至下,一层又一层。冒泡是相反的,可以把
true
全改成false
来试验一下。DOM事件流;
事件流是指用户侧触发了事件之后,事件是如何传递并最终在目标对象上触发相应的操作的。
一个完整的事件流包含:1. 捕获阶段;2. 目标阶段;3. 冒泡阶段
描述DOM事件捕获的具体流程;
window -> document -> html标签 -> body -> 父元素 -> 子元素 -> 目标元素
Event对象的常见应用
event.preventDefault()
阻止默认事件event.stopPropagation()
阻止冒泡event.stopImmediatePropagation()
事件响应优先级,A,B同时绑定在某元素上,如果在A触发中,加入该方法,即B元素上的事件就不会触发event.currentTarget
事件委托,多个元素,给父级元素绑定事件的时候,currentTarget代表的是绑定事件的父级元素event.target
获取被点击的元素参考资料1:
the currentTarget refers to the element that the event listener directly attached to while the target still refers to the specific
<a>
we clicked.JavaScript Event Delegation, and event.target vs. event.currentTarget
参考资料2:
e.target
= The thing under the mouse (as ben says… the thing that triggers the event).e.currentTarget
= The thing before the dot… (see below)
自定义事件
1
2
3
4
5var eve = new Event('customEventName')
elem.addEventListener('customEventName', function(){
console.log('customEventName')
})
elem.dispatchEvent(eve)Event
对象可以指定事件名,但是当需要给事件再传递一些数据的时候使用CustomEvent
对象;
HTTP协议
HTTP协议的主要特点
- 简单快速:UII统一资源符,资源是是固定的
- 灵活:HTTP头部分有数据类型,通过HTTP协议可以完成不同类型的数据传输
- 无连接:连接一次就会断掉
- 无状态:客户端与服务端是相对独立的,服务端没有记录客户的状态的
服务端通过session的方式来记录用户信息与HTTP协议本身无关,HTTP协议未记录客户端的相关信息
HTTP报文的组成部分
HTTP报文组成有请求报文 + 响应报文;
请求报文有请求行(HTTP方法、页面地址、HTTP协议及版本)、请求头(key-value值及类型)、空行、请求体;
响应报文有状态行、响应头、空行、响应 体;HTTP方法
- GET 获取资源
- POST 传输资源
- PUT 更新资源
- DELETE 删除资源
- HEAD 获得报文首部
POST和GET的区别
- GET在浏览器回退时无害的,而POST会再次请求
- GET产生的URL地址可以被收藏,而POST不可以
- GET请求会被浏览器主动缓存,而POST不会,除非手动设置
- GET请求只能URL编码,而POST支持多种编码方式
- GET请求参数会完整保留在浏览器历史记录里,而POST中的参数不会被保留
- GET请求在URL中传递的参数有长度限制(基本是2KB),而POST没有限制
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
- GET参数通过URL传递,POST放在Request Body中
- HTTP状态码
- 1xx: 指示信息-表示请求已接收,继续处理
- 2xx:成功-表示请求已被成功接收
- 3xx:重定向-要完成请求必须进行更进一步的操作
- 4xx:客户端错误-请求有语法错误或请求无法实现
- 5xx:服务器错误-服务器未能实现合法的请求
常见的状态码:
200 OK:客户端请求成功
206 Partial Content:客户发送了一个带有Range头的GET请求,服务器完成了它
301 Moved Permanently:所请求的页面已经转移至新的URL
302 Found:所请求的页面已经临时转移至新的URL
304 Not Modified:客户端有缓冲的文档并发出了一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
400 Bad Request:客户端请求有语法错误,不能被服务器所理解
401 Unauthorized:请求未经制空权,这个 状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden:对被请求的页面被禁止
404 Not Found: 请求资源不存在
500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
503 Server Unavailable:请求未完成,服务器临时过载或当机,一段时间可能恢复正常
什么是持久连接
HTTP协议采用“请求-应用”模式,当使用普通模式,即非Keep-Alive模式时,每个请求/应答客户和服务器都要新建立一个连接,完成之后立即断开连接(HTTP协议为无连接的协议)
当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。
HTTP协议1.1版本才开始支持Keep-Alive模式
什么是管线化
在使用持久连接的情况下,某个连接上的消息的传递类似于:
请求1->响应1->请求2->响应2->请求3->响应3
某个连接上的消息变成了类似这样
请求1->请求2->请求3->响应1->响应2->响应3
管线化的特点:
- 管线化机制通过持久连接完成,仅HTTP/1.1支持此技术
- 只有GET和HEAD请求可以进行管线化,而POST则有所限制
- 初次创建连接时,不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1版本的协议
- 管线化不会影响响应到来的顺序,如上面的例子所示,响应返回的顺序并未改变
- HTTP/1.1 要求服务器端支持管线化,但并不要求服务器端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
- 由于上面提到的服务端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因为现代浏览器如Chrome和Firefox默认并未开启管线化支持
面向对象内容部分
原型链
创建对象有几种方法
方法一:
1
2var o1 = { name: 'o1'}
var o11 = new Object({ name: 'o11'})方法二:
1
2var M = function(){this.name = 'o2'}
var o2 = new M()方法三:(这个是很多同学答不上来的一种方法)
1
var P = { name: 'o3' }
原型、构造函数、实例、原型链
- 任何函数都可以当作是构造函数
- 只要是函数被new使用了,后面的就是一个构造函数
- 构造函数可以通过New来生成一个实例
- 函数都有一个prototype属性,这个是JS引擎给函数初始化的一个属性。
原型链的基本功能:任何一个实例对象,通过原型链找到它上面的原型对象的方法和属性都是被实例所共享的。
一个简单的例子:
1
2
3
4
5
6
7
8
9
10<script>
var M = function(name){this.name= name}
var o3 = new M('o3')
M.prototype.say = function() {
console.log('say hi')
}
var o5 = new M('o5')
</script>函数才有prototype,对象是没有prototype的;只有实例对象才有__proto__对象。
instanceof的原理
上面实例中的例子:
1
2
3
4
5
6
7
8
9
10
11
12o3 instanceof M
返回true,原因,o3是M的是一实例。
o3 instanceof Object
返回true,原因,只要是原型链上的构造函数,都被看成是o3的一个构造函数
o3.__proto === M.prototype
返回true
M.prototype.__proto__ === Object.prototype
返回true
o3.__proto__.constructor === M
返回true
o3.__proto__.contructor === Object
返回falsenew运算符
按照New原理,写一个New2方法:
1
2
3
4
5
6
7
8
9var new2 = function(func){
var o = Object.create(func.prototype)
var k = func.call(o)
if (typeof k === 'object') {
return k
} else {
return o
}
}Console里面试验一下:
1
2
3
4
5
6
7
8
9
10
11
12
13o6 = new2(M)
这里的M.__proto__: Object
o6 instanceof M
返回true,o6是M的一个实例
o6 instanceof Object
返回true
o6.__proto__.constructor === M
返回true
M.prototype.walk = function(){console.log('walk')}
o6.walk()
返回walk
o3.walk()
返回walk
面向对象类
类与实例
- 类的声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 类的声明
*/
function Animal () {
this.name = 'name'
}
/**
* ES6中的Class的声明
*/
class Animal2 {
constructor () {
this.name = name;
}
}- 生成实例
1
2
3
4/**
* 实例化
*/
console.log(new Animal(), new Animal2())类与继承
- 如何实现继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22/**
* 借助构造函数实现继承
*/
function Parent1 () {
this.name = 'parent1'
this.play = [1,2,3]
}
Parent1.prototype.say = function () {
console.log('say Parent')
}
function Child1 () {
Parent1.call(this) // apply 改变了函数执行的上下文
this.type = 'child1'
}
console.log(new Child1())
var s1 = new Child2()
var s2 = new Child2()
console.log(s1.play, s2.play)
s1.play.push(4)
console.log(s1.play, s2,play)
输出[1,2,3,4]使用构造函数实现的继承,缺点:无法继承父类原型对象上的方法,而是只能继承构建函数内的属性
1
2
3
4
5
6
7
8
9
10
11/**
* 借助原型链实现继承
*/
function Parent2 () {
this.name = 'parent2'
}
function Child2 () {
this.type = 'child2'
}
Child2.prototype = new Parent2()
console.log(new Child2())console中来验证一下:
1
new Child2().__proto === Child2.prototype
缺点:因为子类是继承了父类的prototype,那么当改变父类的原型对象的值的时候,所有的实例都会跟着变化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* 组合方式
*/
function Parent3 () {
this.name = 'parent3'
this.play = [1,2,3]
}
function Child3 () {
Parent3.call(this)
this.type = 'child3'
}
Child3.prototype = new Parent3()
var s3 = new Child3()
var s4 = new Child3()
console.log(s3.play, s4.play)
s3.play.push(4)
console.log(s3.play, s4.play)Console中打印:
1
(4) [1, 2, 3, 4] (3) [1, 2, 3]
思考一下,有什么缺点?
Child3 new会实例化父类,prototype中又会实例化父类,父类的实例化执行了多次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* 组合继承的优化
*/
function Parent4 () {
this.name = 'parent4'
this.play = [1,2,3]
}
function Child4 () {
Parent4.call(this)
this.type = 'child4'
}
Child4.prototype = Parent4.prototype
var s5 = new Child4()
var s6 = new Child4()
console.log(s5.play, s6.play)
s5.play.push(4)
console.log(s5.play, s6.play)
console.log(s5 instanceof Child4, s5 instanceof Parent4)
console.log(s5.constructor)Console中打印
1
2(3) [1, 2, 3] (3) [1, 2, 3]
(4) [1, 2, 3, 4] (3) [1, 2, 3]这种方式有没有不足?
不能通过
instanceof
来判断s5,s6是Child的实例,还是Parent直接实例出来的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* 组合继承优化2
*/
function Parent5 () {
this.name = 'parent5'
this.play = [1,2,3]
}
function Child5 () {
Parent5.call(this)
this.type = 'child5'
}
Child5.prototype = Object.create(Parent5.prototype) // __proto__
/*这样不仅实现了继承,而且还实现了原型对象的隔离*/
Child5.prototype.constructor = Child5
var s7 = new Child5()
var s8 = new Child5()
console.log(s7 instanceof Child5, s7 instanceof Parent5)
console.log(s7.constructor)继承的几种方式
构建函数 + 原型链
通信
跨域通信、普通的通信
什么是同源策略及限制
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。
源:协议 + 域名 + 端口
- Cookie、LocalStorage和IndexDB无法读取
- DOM无法获得
- AJAX请求不能发送
前后端如何通信
(1)AJAX
(2)WebSocket
(3)CORS “跨域资源共享”(Cross-origin resource sharing)
如何创建Ajax(Asynchronous JavaScript and XML)
(1)XMLHttpRequest对象的工作流程
(2)兼容性处理
(3)事件的触发条件
(4)事件的触发顺序
跨域通信的几种方式
(1)JSONP
参考源码:jsonp.js
原理是什么?怎么实现的?
主要原理是:客户端告诉服务端Callback(回调函数名称,比如:jsonp),客户端定义一个全局的jsonp函数,通过执行jsonp函数,获取函数里面的数据。
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 jsonp]
* @param {[type]} url [description]
* @param {[type]} onsucess [description]
* @param {[type]} onerror [description]
* @param {[type]} charset [description]
* @return {[type]} [description]
*/
util.jsonp = function (url, onsuccess, onerror, charset) {
var callbackName = util.getName('tt_player');
window[callbackName] = function () {
if (onsuccess && util.isFunction(onsuccess)) {
onsuccess(arguments[0]);
}
};
var script = util.createScript(url + '&callback=' + callbackName, charset);
script.onload = script.onreadystatechange = function () {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
script.onload = script.onreadystatechange = null;
// 移除该script的 DOM 对象
if (script.parentNode) {
script.parentNode.removeChild(script);
}
// 删除函数或变量
window[callbackName] = null;
}
};
script.onerror = function () {
if (onerror && util.isFunction(onerror)) {
onerror();
}
};
document.getElementsByTagName('head')[0].appendChild(script);
};(2)Hash
参考源码:Ajax参考
1
2
3
4
5
6
7
8// 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B
// 在A中伪代码如下:
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'data';
// 在B中的伪代码如下
window.onhashchange = function () {
var data = window.location.hash;
};(3)postMessage
1
2
3
4
5
6
7
8
9// postMessage
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Bwindow.postMessage('data', 'http://B.com');
// 在窗口B中监听
Awindow.addEventListener('message', function (event) {
console.log(event.origin);
console.log(event.source);
console.log(event.data);
}, false);(4)Websocket
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// Websocket【参考资料】http://www.ruanyifeng.com/blog/2017/05/websocket.html
var ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = function (evt) {
console.log('Connection open ...');
ws.send('Hello WebSockets!');
};
ws.onmessage = function (evt) {
console.log('Received Message: ', evt.data);
ws.close();
};
ws.onclose = function (evt) {
console.log('Connection closed.');
};(5)CORS
1
2
3
4
5
6
7
8
9// CORS【参考资料】http://www.ruanyifeng.com/blog/2016/04/cors.html
// url(必选),options(可选)
fetch('/some/url/', {
method: 'get',
}).then(function (response) {
}).catch(function (err) {
// 出错了,等价于 then 的第二个参数,但这样更好用更直观
});
安全
安全分类分CSRF——跨站请求伪造(英语:Cross-site request forgery), XSS——跨域脚本攻击(英语:Cross-site scripting)
CSRF可能会考到:
- 基本概念与缩写
- 攻击原理
防御措施
(1)Token验证
(2)Referer验证
(3)隐藏令牌
XSS可能会考到:
- 攻击原理
- 防御措施
参考:Web安全-XSS
XSS是向页面注入JS,让JS去做它想做的事情;CSRF利用漏洞去执行接口,CSRF依赖用户需要登录网站。
算法
基本的算法题:
- 排序
- 堆栈、队列、链表
- 递归
- 波兰式和逆波兰式