1 常见面试题
1.1 箭头函数相关
1,箭头函数与普通函数的区别?构造函数可以使用new生成实例,箭头函数可以吗?为什么?
1, 箭头函数语法上比普通函数更加简洁(ES6中每一种函数都可以使用形参赋默认值和剩余运算符);
2, 箭头函数没有自己的THIS,它里的THIS是继承函数所处上下文中的THIS(使用call/apply等任何方式都无法改变THIS的指向);
3, 箭头函数中没有arguments对象(类数组),只能基于...args获取传递的参数集合(返回数组);
4, 箭头函数不能被new执行(因为:箭头函数没有this,也没用prototype);
5, 箭头函数有以下几种写法:
- 形参 => 返回值
- (形参,形参) => 返回值
- () => 返回值
- () => { 执行语句 }
- () => {
执行语句
return 返回值
} - (形参) => {
执行语句
return 返回值
}
代码说明如下:
//说明一
function fn(x) {
return function(y) {
return x + y;
}
}
let fn1 = x => y => x + y;
//说明二
let obj = {
name: 'OBJ'
};
function fn() {
console.log(this);
}
fn.call(obj); // {name: "OBJ"}
let fn2 = () => {
console.log(this);
}
fn2.call(obj); //Window {window: Window, self: Window, document: document, name: "", location: Location, …}
document.body.onclick = () => {
// this: window 不是当前操作的body了
};
document.body.onclick = function () {
// this: body
arr.sort(function(a, b) {
// this: window 回调函数中的this一般都是window
return a - b;
});
};
document.body.onclick = function () {
// this: body
// arr.sort(function(a, b) {
// // this: window 回调函数中的this一般都是window
// return a - b;
// });
arr.sort((a, b) => {
// this: body
return a - b;
});
};
// 说明三
let fn = (...args) => {
// console.log(arguments);Uncaught ReferenceError: arguments is not defined
console.log(args); // [10, 20, 30]
};
fn(10, 20, 30);
// 说明四
let fn = () => {
this.x = 200;
}
let f = new fn; // Uncaught TypeError: fn is not a constructor
1.2 this相关
this有以下几种情况
1,作为普通函数调用
2,使用call apply bind改变this指向
3,作为对象方法被调用
4,在class方法中调用
5,箭头函数
const zhangsan = {
name: '张三',
sayHi: {
// this -> 指当前对象
console.log(this)
},
wait() {
setTimeout(function() {
// this === window
console.log(this);
})
}
}
const zhangsan = {
name: '张三',
sayHi: {
// this -> 指当前对象
console.log(this)
},
wait() {
setTimeout(() => {
// this -> 指当前对象
console.log(this);
})
}
}
function fn1() {
console.log(this);
}
fn1() // window
fn1.call({ x: 100 }) // { x: 100 };
const fn2 = fn1.bind({ x: 200 });
fn2() // { x: 200 }
class People {
constructor(name) {
this.name = name;
this.age = 20;
}
sayHi() {
console.log(this);
}
}
const zhangsan = new People('张三');
zhangsan.sayHi() // zhangsan对象
function O() {
this.x = 1
this.print = function () {
console.log(this.x)
}
}
var o = new O()
var print = o.print
print() // this指向window
var n = {x: 2, print: print}
n.print() // this指向n这个对象
// undefined
// {x: 2, print: ƒ
2 原型链相关
2.1 class和继承
class继承主要有以下三个关键:
extends,super,扩展或重写方法
class People {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} eat something`);
}
}
class Students extends People {
constructor(name, number) {
super(name);
this.number = number;
}
sayHi() {
console.log(`姓名 ${this.name} 学号 ${this.number}` );
}
}
2.2 类型判断instanceof
const xialuo = new Students('xialuo', 20);
xialuo instanceof Students;
xialuo instanceof People;
xialuo instanceof Object;
[] instanceof Array;
[] instanceof Object;
手写instanceof
function instance_of(L, R) {
var O = R.prototype;
L = L.__proto__;
while(true) {
if(L === null) return false;
if(O === L) return true;
L = L.__proto__;
}
}
2.3 原型和原型链
// class 实际上是函数,可见是语法糖
typeof People; // "function"
typeof Students; // "function"
// 隐式原型和显示原型
console.log(xialuo.__proto__);
console.log(Students.prototype);
console.log(xialuo.__proto__ === Students.prototype); // true
原型如下图所示
原型关系如下
1,每个class都有显示原型prototype;
2,每个实例都有隐式原型__proto__;
3,实例的__proto__指向对应class的prototype;
基于原型的执行规则
1,获取属性xialuo.name或执行方法xialuo.sayHi()时;
2,现在自身属性和方法中寻找;
3,如果找不到则自动去__proto__中查找;
原型链如下图所示:
2.4 手写一个简易的jQuery,考虑插件和扩展性
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length;
for (let i = 0; i < length; i++) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index];
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i];
fn(elem);
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false);
})
}
}
// 插件
jQuery.prototype.dialog = function(info) {
alert(info);
}
// 造轮子
class myJQuery extends jQuery {
constructor(selector) {
super(selector);
}
// 扩展自己的方法
addClass(classname) {
}
style(data) {
}
}
// 使用
const $p = new jQuery('p');
$p.get(1);
$p.each(elem => console.log(elem.nodeName));
$p.on('click', () => alert('click'));
2.5 写出以下代码运行结果
function Foo() {
Foo.a = function() {
console.log(1);
}
this.a = function() {
console.log(2);
}
}
// 把Foo当作类,在原型上设置实例公有的属性方法 => a.();
Foo.prototype.a = function() {
console.log(3);
}
// 把Foo当作普通对象设置私有的属性方法 => Foo.a();
Foo.a = function() {
console.log(4);
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
//4
//2
//1
3 数组相关
1 给定两个数组,写一个方法来计算它们的交集 思考题:交叉并补?
let nums1 = [1, 2, 2, 1];
let nums2 = [2, 2];
// 输出结果[2, 2]
// 双层循环
let arr = [];
for (let i = 0; i < nums1.length; i++) {
let item1 = nums1[i];
for (let k = 0; k < nums2.length; k++) {
let item2 = nums2[k];
if(item1 === item2) {
arr.push(item1);
break;
}
}
}
console.log(arr);
// forEach循环
let arr = [];
nums1.forEach(item => nums2.includes(item) ? arr.push(item) : null);
差集
let arr = [];
nums1.forEach(item => !nums2.includes(item) ? arr.push(item) : null);
console.log(arr);
//[1, 1]
并集
let nums1 = [12, 23, 34, 23, 45, 34, 25, 46, 35];
let nums2 = [10, 35, 24, 23, 36, 47, 56];
let arr = [];
nums1.forEach((item, index) => {
let n = nums2.indexOf(item);
if(n >= 0) {
arr.push(item);
nums1.splice(index, 1);
nums2.splice(n, 1);
}
});
// [23, 35]
2 实现以下需求
let ary1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'];
let ary2 = ['A', 'B', 'C', 'D'];
//合并后的数组为['A1', 'A2', 'A", 'B1', 'B2', 'B', "C1', 'C2', 'C', 'D1', 'D2', 'D'];
实现一
ary2 = ary2.map(item => item + '石头');
let arr = ary1.concat(ary2);
arr = arr.sort((a, b) => a.localeCompare(b)).map(item => {
return item.replace('石头', '');
});
console.log(arr);
实现二
let n = 0;
for (let i = 0; i < ary2.length; i++) {
let item2 = ary2[i];
for (let k = 0; k < ary1.length; k++) {
let item1 = ary1[k];
if(item1.includes(item2)) {
n = k;
}
}
ary1.splice(n + 1, 0, item2);
}
console.log(ary1);
3 某公司1到12月份的销售额存在一个对象里面,如下:
let obj = {
1: 222,
2: 123,
5: 888
};
// 请把数据处理为以下结构
[222, 123, null, null, 888, null, null, null, null, null, null, null]
实现如下:
let arr = new Array(12).fill(null).map((item, index) => {
return obj[index + 1] || null;
});
4 以下代码输出结果
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
array中push方法如下定义:
Array.prototype.push = function @@(val) {
this[this.length] = val;
// => this.length在原来的基础上加1
return this.length;
}
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
}
obj.push(1); // this-> obj; obj[obj.length] = 1; obj[2] = 1; obj.length = 3;
obj.push(2); // this-> obj; obj[obj.length] = 2; obj[3] = 1; obj.length = 4;
打印结果如下:
let obj = {
2: 1,
3: 2,
length: 4,
push: Array.prototype.push
}
5 for in 和 for of的区别
for in
- for in循环返回的值都是数据结构的键值名;
- 遍历对象返回的对象的key值,遍历数组返回的数组下标;
- for in 循环不仅可以遍历数字键名,还会遍历原型上的值和手动添加的其他键值名;
- 特别情况下,for in 循环会以看起来任意的顺序遍历键名;
for of
- for of循环用来获取一对键值对应的值;
- 一个数据结构只要部署了Symbol.iterator属性,就被视为具有iterator接口,就可以用for of循环;
- for of不同与forEach,他可以与break, continue, return配合使用,即for of 循环可以随时推出循环
- 提供了遍历所有数据结构的统一接口;
4 柯里化
实现一个add函数,满足以下功能
add(1); //1
add(1)(2); //3
add(1)(2)(3); // 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); //6
这个需要用到函数柯里化,也就是预先处理的思想,利用闭包的机制实现。下面通过手写一个简单的bind来介绍一下柯里化思想。
let obj = {
name: 'OBJ'
};
function fn(...arg) {
console.log(this, arg);
}
document.body.onclick = fn; // => this: body arg: ev事件对象
document.body.onclick = function(ev) {
// ev事件对象:给元素的某个事件绑定方法,当事件触发时会执行这个方法,并且会把当前事件的相关
//信息传递给这个函数“事件对象”
console.log(ev);
}
现在需要实现点击的时候fn的this为obj, arg为[100, 200, 事件对象], 实现如下
document.body.onclick = fn.bind(obj, 100, 200);
document.body.onclick = function (ev) {
fn.call(obj, 100, 200, ev);
}
执行bind方法,会返回一个匿名函数,当事件触发时,我们再处理fn即可;下面手写一个简单的bind方法
(function() {
// this: 需要改变的this函数
// context:需要改变的this指向
// outerArg:其余需要传递给函数的实参信息
function myBind(context = window, ...outerArg) {
let _this = this;
return function(...innerArg) {
_this.call(context, ...outerArg.concat(innerArg));
}
}
Function.prototype.myBind = myBind;
})();
实现如下:
方式一
function currying(fn, length) {
length = length || fn.length;
return function(...args) {
if(args.length >= length) {
return fn(...args);
}
return currying(fn.bind(null, ...args), length - args.length);
}
}
function add(n1, n2, n3) {
return n1 + n2 + n3;
}
add = currying(add, 3);
add(1); //1
add(1)(2); //3
add(1)(2)(3); // 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3);
方式二
可以实现无限累加
function add() {
let args = [].slice.call(arguments);
let fn = function(){
let fn_args = [].slice.call(arguments)
return add.apply(null,args.concat(fn_args))
}
fn.toString = function(){
return args.reduce((a,b)=>a+b)
}
return fn
}
console.log(add(1)); // 1
console.log(add(1)(2)); // 2
console.log(add(1)(2)(3)); // 6
console.log(add(1)(2)(3,7)(4,5,6));// 28
console.log(add(1)(2)(3,7)(4,5,6).toString());
方式三
function currying(fn, length) {
length = length || fn.length;
return function(...args) {
if(args.length >= length) {
return fn(...args);
}
return currying(fn.bind(null, ...args), length - args.length);
}
}
let add = currying((...arg) => eval(arg.join('+')), 5);
5 函数变量及作用域
5.1 作用域,自由便量,闭包
js作用域有以下三种:
全局作用域
函数作用域
块级作用域
//es6块级作用域
if(true) {
let x = 100;
}
console.log(x) //会报错
自由便量
一个变量在当前作用域没有定义,但它被使用了
向上级作用域,一层一层依次寻找,直至找到为止
如果遇到全局作用域都没有找到,则报错xx is not defined
闭包
闭包作为作用域应用的特殊情况,有两种表现:
函数作为参数被传递
函数作为返回值被返回
//函数作为参数被传递
function print(fn) {
let a = 200;
fn();
}
let a = 100;
function fn() {
console.log(a);
}
print(fn)
//函数作为返回值被返回
function create() {
let a = 100;
return function() {
console.log(a)
}
}
let fn = create();
let a = 200;
fn();
闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方,所以上面代码均输出100;
5.2. typeof能判断的类型
typeof能识别所有的值类型,识别函数,判断是否是引用类型;
// 识别所有的值类型
let a; typeof a; //'undefined'
const str = 'abc'; typeof str; // 'string'
const n = 100; typeof n; // 'number'
const b = true; typeof b; // 'boolean'
const s = Symbol('s'); typeof s; //'symblol'
// 识别函数
typeof console.log // 'function'
typeof function () {} // 'function'
// 判断是否是引用类型(不能在继续识别)
typeof null;
typeof ['a', 'b'];
typeof { x: 100 };
5.3 变量提升和执行上下文
变量提升
js代码在正式执行之前,会做一个预处理的工作:
1,收集变量
2,收集函数
依据:var 将var后边的变量定义但是不赋值 var username = undefined;
function() {} 提前定义该函数
执行上下文
执行上下文(execute context) EC,可以理解为代码执行的环境;执行时机:代码正式进入之前会进入到执行环境;按照以下规则进行:
1,创建变量对象:
1), 变量
2), 函数及函数的参数
3), 全局:window
4), 局部:抽象的但是确实存在
2,确认this的指向
1), 全局:this -> window
2), 局部:this -> 调用其的对象
3,创建作用域链
父级作用域链 + 当前的变量对象
4,扩展:
ECObj = {
变量对象: {变量,函数,函数的行参}
scopeChain: 父级作用域 + 当前的变量对象
this:{window || 调用其的对象}
}
5.4 代码输出结果题
1 写出以下函数的执行结果
var b = 10;
(function b() {
b = 20;
console.log(b);
})();
console.log(b);
分析如下:
本应匿名的函数如果设置了函数名,在外面还是无法调用,但是在函数里面是可以使用的
let fn = function AAA() {
};
AAA();
//Uncaught ReferenceError: AAA is not defined
类似于创建常量一样,这个名字存储的值不能再被修改, 非严格模式下不会报错,但是会静默失败;
let fn = function AAA() {
AAA = 1000;
console.log(AAA); // 当前函数
};
fn();
如果使用严格模式,会报错,我们可以把AAA理解为是用const创建出来的;
let fn = function AAA() {
"use strict"
AAA = 1000; // Assignment to constant variable.
console.log(AAA); // 当前函数
};
fn();
通过以上分析,输出结果如下:
ƒ b() {
b = 20;
console.log(b);
}
10
怎么可以得到以下输出:
var b = 10;
(function b() {
b = 20;
console.log(b); // => 20
})();
console.log(b); // => 10
要想得到想要的输出,里面的b一定要是私有的,不能是全局的,可以声明或者改为行参;
答案如下:
// 声明
var b = 10;
(function b() {
var b = 20;
console.log(b); // => 20
})();
console.log(b);
//改为行参
var b = 10;
(function b(b) {
b = 20;
console.log(b); // => 20
})();
console.log(b);
2 实现下面打印结果,使其输出0~9
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
实现如下
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(() => {
console.log(i);
}, 1000);
})(i);
}
for (var i = 0; i < 10; i++) {
setTimeout((i => () => console.log(i))(i), 1000);
}
for (var i = 0; i < 10; i++) {
var fn = function (i) {
console.log(i);
};
setTimeout(fn.bind(null, i), 1000);
}
6 css相关
6.1 多人合作避免样式冲突
css-module: 配合webpack的css-loader进行打包,会为所有的class name和animation name 加local scope,避免潜在冲突;
css-module就是把css写在不同的文件中,最后通过webpack构建工具合并成一个文件。多个不同的文件有相同的类名,合并之后没有冲突的类名;在 webpack中,我们使用caa-loader来处理css文件,要启用css-loader的配置,需要将css-loader 配置的modules设置为true。
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[local]___[hash:base64:5]' //[local]___[hash:base64:5]
}
}
css-module原理非常简单, css-loader会将样式中的类名进行转换,转换为一个唯一的hash值,由于hash值是根据模块路径和类名生成,因此,不同的css模块,即使具有相同的类名,转换后的hash值也不一样;
6.2 1px 物理像素的实现
实现方式一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 px 物理像素的实现</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box{
width: 0.5rem;
height: 0.5rem;
border-bottom: 1px solid #000;
}
</style>
<!--像素比 = 物理像素/ css像素-->
</head>
<body>
<div id = 'box'>
</div>
</body>
<script type="text/javascript">
window.onload = function() {
// 像素比
var dpr = window.devicePixelRatio;
console.log(dpr)
// 缩放比率
var scale = 1/dpr;
var width = document.documentElement.clientWidth;
// 获取meta标签
var metaNode = document.querySelector('meta[name="viewport"]');
metaNode.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scalable=no');
// 页面中元素宽度,高度,比率反向乘回来
var htmlNode = document.querySelector('html');
htmlNode.style.fontSize = width * dpr + 'px';
}
</script>
</html>
实现方式二
用伪元素,定位实现1px物理像素边框的显示。再利用媒体查询去修改设备像素比等于2的,等于3的,进行scaleY缩放。设备像素比是2的,缩放.5倍,设备像素比是3的,缩放.33倍。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
position: relative;
}
.box::after{
content: "";
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000;
}
@media (-webkit-min-device-pixel-ratio: 2),(min-resolution: 2) {
.box::after{
transform: scaleY(.5);
}
}
@media (-webkit-min-device-pixel-ratio: 3),(min-resolution: 3) {
.box::after{
transform: scaleY(0.33);
}
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
6.3 用纯css创建一个三角形
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 0px;
height: 0px;
border: 100px solid;
border-top-color: red;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
/* border-right-color: blue;
border-bottom-color: deeppink;
border-left-color: yellowgreen; */
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
6.4 实现移动端rem适配
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>如何实现移动端rem适配</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: .5rem;
height: .5rem;
background: red;
}
/*html根元素字体大小设置屏幕区域宽*/
</style>
</head>
<body>
<div class="box"></div>
</body>
<script type="text/javascript">
window.onload = function() {
// 获取屏幕区域的宽度
var width = document.documentElement.clientWidth;
// 获取html
var htmlNode = document.querySelector('html');
// 设置字体大小
htmlNode.style.fontSize = width + 'px';
}
</script>
</html>
6.5 实现水平垂直居中
方式一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>如何实现水平垂直居中</title>
<style>
*{
margin: 0;
padding: 0;
}
#wrap{
width: 500px;
height: 500px;
background: grey;
position: relative;
}
#wrap .box{
width: 200px;
height: 200px;
background: pink;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<div id="wrap">
<div class="box"></div>
</div>
</body>
</html>
方式二
#wrap{
width: 500px;
height: 500px;
background: grey;
position: relative;
}
#wrap .box{
width: 200px;
height: 200px;
background: pink;
position: absolute;
top: 50%;
left: 50%;
margin-left: -100px;
margin-top: -100px;
}
方式三
#wrap{
width: 500px;
height: 500px;
background: grey;
position: relative;
}
#wrap .box{
width: 200px;
height: 200px;
background: pink;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
方式四
#wrap{
width: 500px;
height: 500px;
background: grey;
display: flex;
justify-content: center;
align-items: center;
}
#wrap .box{
width: 200px;
height: 200px;
background: pink;
}
6.6 px, em, rem ,vw, vh区别
px: px就是pixel的缩写,意为像素。px就是一张图片最小的一个点,一张位图就是千千万万的这样的点构成的。
em: 参考物是父元素的font-size,具有继承的特点,如果自身定义了font-size,按照自身来计算(浏览器默认字体是16px),整个页面1em不是一个固定的值;
rem:css3新单位,相对于根元素(html)(网页)的font-size,不会像em那样,依赖于父元素的字体的大小,而造成混乱。
vw: css3新单位,viewpoint width的缩写,视窗宽度,1vw等于视窗宽度的1%;举个例子:浏览器宽度1200px,1 vw = 1200px/100 = 12px;
vh: css3新单位,viewpoint height的缩写,视窗高度,1vh等于视窗高度的1%;举个例子:浏览器高度900px,1 vh = 900px/100 = 9px;
6.7 盒模型
6.7.1 概念
css盒模型本质上是一个盒子,封装周围的html元素,包括:外边距(margin), 边框(border), 内边距(padding), 实际内容(content) 四个属性;
css盒模型:标准模型 + IE模型
6.7.2 标准模型和IE模型的区别
计算高度和宽的方法不同
标准盒模型:盒子总宽度/高度 = width/height + padding + border + margin。(即width/heigth只是内容高度,不包含padding和border值);
IE盒子模型:盒子总宽度/高度 = width/height + margin = (内容区宽度/高度 + padding + border) + margin。(即width/height包含了padding和border值)
6.7.3 css如何设置这两种模型
标准:box-sizing: content-box;(浏览器默认设置)
IE:box-sizing: border-box;
6.8 BFC
Block Formatting Contexts: 即块级格式化上下文。是Web页面中盒模型布局和css渲染模式,指一个独立的渲染区域或者说是 一个隔离的独立容器;MDN解释:是web页面的可视化css渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素 交互的区域。 通俗就是BFC元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素。
触发BFC的方式
- body 根元素
- 浮动元素:float 除 none 以外的值
- 绝对定位元素:position (absolute、fixed)
- display 为 inline-block、table-cells、flex
- overflow 除了 visible 以外的值 (hidden、auto、scroll)
使用overflow: auto
创建一个会包含这个浮动的BFC,通常做法是设置父元素overflow: auto或者设置其他的非默认的overflow: visible的值; 设置overflow: auto创建一个新的BFC来包含这个浮动。我们的div元素现在变成布局中的迷你布局,任何子元素都会被包含进去。 但是这个会出现一些不想要的问题,比如滚动条或者一些剪切的阴影,需要注意。另外,对于后续的开发,可能不是很清楚当时为什么 使用overflow。所以可以添加一些注释来说明;
使用display: flow-root;
一个新的display属性的值,它可以创建无副作用的BFC。在父级块中使用display: flow-root可以创建新的BFC。给div设置 display: flow-root属性后,div中的所有内容都会参与BFC,浮动的内容不会从底部溢出。实际上是在创建一个行为类似于根元素 (浏览器中的html元素)的东西时,就能发现这个名字的意义:即创建一个上下文,里面进行flow layout。