备战校招第一天
- BFC(块格式化上下文)
- 首页白屏
- 函数柯里化
- bind、apply、call
- 如果阻塞了JS中的主线程会发生什么情况
- 浏览器和Node 事件循环机制区别
BFC(块格式化上下文)
常见的定位方案分为三种:普通流、浮动、绝对定位。
块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。而这里的 BFC
属于属于普通流,相当于具有 BFC
特性的元素可以看作一个独立的容器,无论容器内部的元素怎么调整,都不会影响到外部的元素。
下列情况会创建BFC:
- 根元素(
<html>
) - 浮动元素(元素的
float
不是none
) - 绝对定位元素(元素的
position
为absolute
或fixed
) - 行内块元素(元素的
display
为inline-block
) overflow
值不为visible
的块元素display
值为flow-root
的元素contain
值为layout
、content
或 paint 的元素- 弹性元素(
display
为flex
或inline-flex
元素的直接子元素) - 网格元素(
display
为grid
或inline-grid
元素的直接子元素) - …
案例1
2
3
4 <div style="border: 1px solid #000;">
<div style="width: 45%; height: 300px; background-color: red; float: left;">123</div>
<div style="width: 45%; height: 300px; background-color: blue; float: right;">456</div>
</div>
这几行代码,如果直接运行会在网页上按照我们的预期显示出来两个色块,但仔细看下,会看出父元素的高度不正常,父元素看起来向空容器一样。原因在于子元素采用浮动,而浮动流不属于普通流,是独立定位的。
解决方法,按照上述规则,使用一个合适的方法,来进行创建BFC,从而告诉浏览器 ”我的最外层div需要创建BFC,按照常规方式渲染“
。
例如:在最外层 div
添加样式 float: left
(上述规则指出只要 float
不为 none
即可创建BFC ) , 当然如果觉得 float 不够优雅,还可以使用 overflow: hidden;
。(通常情况下,我们可能会使用伪元素添加 clear:both;
占位来清除浮动,从而使最外层 div 恢复正常)。
详情请看,请看阮老师的文章。
还有经典问题 margin
重叠问题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<style>
p {
float: left;
color: #f55;
background: #fcc;
width: 200px;
line-height: 100px;
text-align: center;
margin: 100px;
}
</style>
<body>
<p>Haha</p>
<p>Hehe</p>
</body>
运行之后就会发现 两个 p标签 margin
发生重叠(同一个BFC内,两盒子垂直,就会发生 margin
塌陷重叠,而margin
值会以最大的为主。)
解决方法:
- 将 margin 换为 padding
- 利用
BFC
,在 p 外层添加个一层 div,并在新添加的元素上添加样式(overflow: hidden;),创建新的BFC,这样两个p标签不属于同一个BFC,则自然不会再发生 margin 重叠问题。
首页白屏
现在多数都使用的为Vue、React和Angular三大框架其中之一,但它们有个共同的特点,均为 JS 驱动,所以在 JS 解析前,网页处于空白页,什么都不显示,这就是我们所说的白屏。
解决方法
- SSR,服务端渲染,有明显的好处,对SEO友好,还可以加快网页打开速度。
- 预渲染,插件
prerender-spa-plugin
,编译阶段就将对应的页面渲染好,但是是没有数据的 - 骨架图,数据还未加载出来时,但会显示出网页的大致骨架,相对友好。
函数柯里化
柯里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21function curry(fn) {
const len = fn.length
return function bindFn() {
if (arguments.length < len) {
return bindFn.bind(null, ...arguments)
} else {
return fn.call(null, ...arguments)
}
}
}
function add(a, b) {
console.log(a + b)
return a + b
}
// 原版
add(1,2)
// 柯里化后
var curryAdd = curry(add)
curryAdd(1)(2)
bind、apply和call
func.bind(thisArg[, arg1[, arg2[, …]]])
调用函数后,返回绑定 this
之后的函数(以下为mdn文档中的案例),并不会执行1
2
3
4
5
6
7
8
9
10
11
12
13
14const module = {
x: 42,
getX: function() {
return this.x;
}
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // 直接调用,this指向全局
// expected output: undefined
const boundGetX = unboundGetX.bind(module); // 绑定this为module,this指向moudle
console.log(boundGetX());
// expected output: 42
func.apply(thisArg, [argsArray])
apply
接受一个参数数组 ,并会执行此函数,如果没有传递参数(或传入 null
、 undefined
)则默认指向 window
1
2
3
4
5
6
7
8
9
10
11
12const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
console.log(max);
// expected output: 7
const min = Math.min.apply(null, numbers);
console.log(min);
// expected output: 2
function.call(thisArg, arg1, arg2, …)
call
接受的是一个参数列表 ,并会执行此函数,如果没有传递参数(或传入 null
、 undefined
)则默认指向 window
1
2
3
4
5
6
7
8
9
10
11
12
13
14function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
// 将Product的this指向为Food,并传入参数
Product.call(this, name, price);
this.category = 'food';
}
console.log(new Food('cheese', 5).name);
// expected output: "cheese"
如果阻塞了JS中的主线程会发生什么情况,就是下面的代码。
1 | setTimeout(() => console.log("hello world")) |
- 首先,执行第一行,将
setTimeout
添加到宏任务队列 - 其次,执行第二行,然后进入死循环,页面卡死
- 同步代码都没有执行完,自然是不会执行宏任务队列中的任务
浏览器和Node 事件循环机制区别
浏览器和Node 环境下,microtask 任务队列的执行时机不同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
33function test () {
console.log('start')
setTimeout(() => {
console.log('children2')
Promise.resolve().then(() => {console.log('children2-1')})
}, 0)
setTimeout(() => {
console.log('children3')
Promise.resolve().then(() => {console.log('children3-1')})
}, 0)
Promise.resolve().then(() => {console.log('children1')})
console.log('end')
}
test()
// 以上代码在node11以下版本的执行结果,执行完一个阶段的所有任务。
// start
// end
// children1
// children2
// children3
// children2-1
// children3-1
// 以上代码在node11及浏览器的执行结果(顺序执行宏任务和微任务)
// start
// end
// children1
// children2
// children2-1
// children3
// children3-1
Node完整循环机制
引用官方解释文字和图片
阶段概述
定时器:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
待定回调:执行延迟到下一个循环迭代的 I/O 回调。
idle, prepare:仅系统内部使用。
轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
检测:setImmediate() 回调函数在这里执行。
关闭的回调函数:一些关闭的回调函数,如:socket.on(‘close’, …)。
参考链接
BFC
https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context
http://www.ruanyifeng.com/blog/2009/04/float_clearing.html
https://juejin.im/entry/59c3713a518825396f4f6969
面经
https://www.nowcoder.com/discuss/177482
浏览器和Node循环机制区别
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/26#issuecomment-469188501
https://juejin.im/post/5c337ae06fb9a049bc4cd218#heading-0
https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/