interview准备2- 持续更新中

305 阅读13分钟

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。