整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

2022高频前端面试题汇总(附答案)

2022高频前端面试题汇总(附答案)

有朋友准备看看新工作,正好有空,帮忙整理了一下常见的前端面试题,整理的面试题都比较基础,主要面向初中级前端。篇幅有限,有些知识就直接贴文章链接了,大家可以自己去看~

一、CSS篇

1、什么是 BFC

BFC(Block Formatting Context)格式化上下文,是 Web 页面中盒模型布局的 CSS 渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。

形成 BFC 的条件

  • 浮动元素,float 除 none 以外的值
  • 定位元素,position(absolute,fixed)
  • display 为以下其中之一的值 inline-block,table-cell,table-caption
  • overflow 除了 visible 以外的值(hidden,auto,scroll)
  • HTML 就是一个 BFC

BFC 的特性

  • 内部的 Box 会在垂直方向上一个接一个的放置。
  • 垂直方向上的距离由 margin 决定
  • bfc 的区域不会与 float 的元素区域重叠。
  • 计算 bfc 的高度时,浮动元素也参与计算
  • bfc 就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。


2、什么是盒模型

CSS3中的盒模型有以下两种:标准盒子模型、IE盒子模型

盒模型都是由四个部分组成的,分别是margin、border、padding和content。

在标准盒模型性中

盒子在网页中实际占用:
宽=width + padding2 + border2 + margin2
高=height + padding2 + border2 + margin2

盒模型实际大小:
宽=width + padding2 + border2
高=height + padding2 + border2

在IE盒模型性中

盒子在网页中实际占用:
宽=width + margin2
高=height + margin2

盒模型实际大小:
宽=width
高=height

可以通过修改元素的box-sizing属性来改变元素的盒模型:

  • box-sizeing: content-box表示标准盒模型
  • box-sizeing: border-box表示IE盒模型


3、未知高度元素垂直居中的几种方案

当需要垂直居中的元素高度未知时,一般采用一下几种方案实现垂直居中:

使用绝对定位和transform

css复制代码.parent {
    position: relative;
    width: 100%;
    height: 400px;
}
.children {
    position: absolute;
    top: 50%;
    transform: translate(0, -50%);
}

flex实现垂直居中(最常用的)

css复制代码.parent {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 400px;
    background: #fff;
}
.children {
    background: red;
}

通过table属性

css复制代码<div class="parent">
    <div class="children">
        <div>test</div>
    </div>
</div>

.parent {
    display: table;
    text-align:center;
}

.children {
    background: #ccc;
    display: table-cell;
    vertical-align: middle;
}

.child div {
    width: 300px;
    height: 150px;
    background: red;
    margin: 0 auto;
}


4、CSS3中有哪些新特性

  • 新增各种CSS选择器
  • 圆角 (border-radius:8px)
  • 多列布局 (multi-column layout)
  • 阴影和反射 (Shadoweflect)
  • 文字特效 (text-shadow)
  • 文字渲染 (Text-decoration)
  • 线性渐变 (gradient)
  • 旋转 (transform)
  • 增加了旋转,缩放,定位,倾斜,动画,多背景

5、什么是CSS 预处理器 / 后处理器?大家为什么要使用他们?

预处理器:例如LESS、Sass、Stylus,用来预编译Sass或less,增强了css代码的复用性,还有层级、mixin、变量、循环、函数等,具有很方便的UI组件模块化开发能力,极大的提高工作效率。

后处理器:例如PostCSS,通常被视为在完成的样式表中根据CSS规范处理CSS,让其更有效;目前最常做的是给CSS属性添加浏览器私有前缀,实现跨浏览器兼容性的问题。

CSS 预处理器为 CSS 增加一些编程的特性,无需考虑浏览器的兼容性问题”,例如你可以在 CSS 中使用变量、简单的逻辑程序、函数(如右侧代码编辑器中就使用了变量$color)等等在编程语言中的一些基本特性,可以让你的 CSS 更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。


6、为什么会出现margin重叠的问题?怎么解决?

问题描述: 两个块级元素的上外边距和下外边距可能会合并(折叠)为一个外边距,其大小会取其中外边距值大的那个,这种行为就是外边距折叠。需要注意的是,浮动的元素和绝对定位这种脱离文档流的元素的外边距不会折叠。重叠只会出现在垂直方向。

计算原则: 折叠合并后外边距的计算原则如下:

  • 如果两者都是正数,那么就取最大者
  • 如果是一正一负,就会正值减去负值的绝对值
  • 两个都是负值时,用0减去两个中绝对值大的那个

解决办法: 对于折叠的情况,主要有两种:兄弟之间重叠和父子之间重叠

(1)兄弟之间重叠

  • 底部元素变为行内盒子:display: inline-block
  • 底部元素设置浮动:float
  • 底部元素的position的值为absolute/fixed

(2)父子之间重叠

  • 父元素加入:overflow: hidden
  • 父元素添加透明边框:border:1px solid transparent
  • 子元素变为行内盒子:display: inline-block
  • 子元素加入浮动属性或定位


7、flex布局

flex知识点的话,建议大家去看阮一峰老师的文章,看完应该就明白了

Flex 布局教程:语法篇
Flex 布局教程:实例篇


8、移动端媒体查询,rem, vw的理解

@media媒体查询是针对不同的媒体类型定义不同的样式,特别是响应式页面,可以针对不同屏幕的大小,编写多套样式,从而达到自适应效果,代码如下:

css复制代码@media screen and (max-width: 720px) {
    body {
        background-color: #6633FF;
    }
}

@media screen and (max-width: 640px) {
    body {
        background-color: #00FF66;
    }
}
/*
  上述的代码分别对分辨率在0~640px以及640px~720px的屏幕设置了不同的背景颜色。
*/

rem 是一个灵活的可扩展的单位,由浏览器转化像素并显示。与em单位不同,rem单位无论嵌套层级如何,都只相对于浏览器的根元素(HTML元素)的font-size

由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport, 代码如下:

js复制代码(function flexible (window, document) {
  var docEl=document.documentElement
  var dpr=window.devicePixelRatio || 1

  // adjust body font size
  function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize=(12 * dpr) + 'px'
    } else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();

  // set 1rem=viewWidth / 10
  function setRemUnit () {
    var rem=docEl.clientWidth / 10
    docEl.style.fontSize=rem + 'px'
  }

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })

  // detect 0.5px supports
  if (dpr >=2) {
    var fakeBody=document.createElement('body')
    var testElement=document.createElement('div')
    testElement.style.border='.5px solid transparent'
    fakeBody.appendChild(testElement)
    docEl.appendChild(fakeBody)
    if (testElement.offsetHeight===1) {
      docEl.classList.add('hairlines')
    }
    docEl.removeChild(fakeBody)
  }
}(window, document))

什么是vw/vh ?

vw/vh是一个相对单位(类似于em,rem)

  • vw:viewport width 视口宽度单位
  • vh:viewport height 视口高度单位

相对视口的尺寸计算结果

  • 1vw=1/100视口宽度
  • 1vh=1/100视口高度


9、移动端1px解决方案

1px的问题经常出现在移动端边框设置上,会导致设置1px边框看起来较粗,影响用户体验,对于boder的1px问题,可以通过伪元素 + transform来解决, 代码如下:

css复制代码/* 手机端实现真正的一像素边框 */
.border-1px, 
.border-bottom-1px, 
.border-top-1px, 
.border-left-1px, 
.border-right-1px {
    position: relative;
}

/* 线条颜色 */
.border-1px::after, 
.border-bottom-1px::after, 
.border-top-1px::after, 
.border-left-1px::after, 
.border-right-1px::after {
    background-color: #000;
}

/* 底边边框一像素 */
.border-bottom-1px::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    transform-origin: 0 0;
}

/* 上边边框一像素 */
.border-top-1px::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 1px;
    transform-origin: 0 0;
}

/* 左边边框一像素 */
.border-left-1px::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 1px;
    height: 100%;
    transform-origin: 0 0;
}

/* 右边边框1像素 */
.border-right-1px::after {
    content: "";
    position: absolute;
    right: 0;
    top: 0;
    width: 1px;
    height: 100%;
    transform-origin: 0 0;
    box-sizing: border-box;
}

/* 边框一像素 */
.border-1px::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border: 1px solid gray;
    box-sizing: border-box;
}

/* 设备像素比 */
/* 显示屏最小dpr为2 */
@media (-webkit-min-device-pixel-ratio: 2) {
    .border-bottom-1px::after, .border-top-1px::after {
        transform: scaleY(0.5);
    }

    .border-left-1px::after, .border-right-1px::after {
        transform: scaleX(0.5);
    }

    .border-1px::after {
        width: 200%;
        height: 200%;
        transform: scale(0.5);
        transform-origin: 0 0;
    }
}

/* 设备像素比 */
@media (-webkit-min-device-pixel-ratio: 3)  {
    .border-bottom-1px::after, .border-top-1px::after {
        transform: scaleY(0.333);
    }

    .border-left-1px::after, .border-right-1px::after {
        transform: scaleX(0.333);
    }

    .border-1px::after {
        width: 300%;
        height: 300%;
        transform: scale(0.333);
        transform-origin: 0 0;
    }
}


10、IOS全面屏安全区域问题

iOS 11

css复制代码padding-top: constant(safe-area-inset-top);
padding-right: constant(safe-area-inset-right);
padding-bottom: constant(safe-area-inset-bottom);
padding-left: constant(safe-area-inset-left);

iOS 11.2+

css复制代码padding-top: env(safe-area-inset-top);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);


11、图片变形问题如何解决

如果图片设置的是背景图

css复制代码background-size: cover;

如果是img标签图片

css复制代码object-fit: cover;


12、css常见布局实现

可参考:常见的CSS布局


二、Javascript基础

1、JavaScript数据类型

JavaScript一共有8种数据类型

七种基本数据类型:Undefined、Null、Boolean、Number、String、Symbol(es6新增)和BigInt(es10新增)

一种复杂数据类型:Object 里面包含 Function、Array、Date等

2、JavaScript判断数据类型的几种方法

typeof

js复制代码/*
  优点:能够快速区分基本数据类型
  缺点:不能将Object、Array和Null区分,都返回object
*/
console.log(typeof 1);               // number
console.log(typeof NaN);             // number
console.log(typeof true);            // boolean
console.log(typeof 'mc');            // string
console.log(typeof Symbol)           // function
console.log(typeof function(){});    // function
console.log(typeof console.log());   // function
console.log(typeof []);              // object 
console.log(typeof {});              // object
console.log(typeof null);            // object
console.log(typeof undefined);       // undefined

instanceof

js复制代码/*
  优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象
  缺点:Number,Boolean,String基本数据类型不能判断
*/
console.log(1 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log(function(){} instanceof Object);         // true
console.log({} instanceof Function);                 // false
console.log({} instanceof Object);                   // true
js复制代码/*
  优点:精准判断数据类型
  缺点:写法繁琐不容易记,推荐进行封装后使用
*/
const toString=Object.prototype.toString;
console.log(toString.call(1));                      // [object Number]
console.log(toString.call(true));                   // [object Boolean]
console.log(toString.call('mc'));                   // [object String]
console.log(toString.call([]));                     // [object Array]
console.log(toString.call({}));                     // [object Object]
console.log(toString.call(function(){}));           // [object Function]
console.log(toString.call(undefined));              // [object Undefined]
console.log(toString.call(null));                   // [object Null]


3、intanceof 操作符的实现原理及实现

js复制代码function instanceOf(left, right) {
    let proto=left.__proto__
    let prototype=right.prototype
    while (true) {
        if (proto===null) return false
        if (proto===prototype) return true
        proto=proto.__proto__
    }
}


4、数组常用操作方法

  • push():在末尾添加一个或多个元素,修改原数组,返回值,数组新的长度
  • pop():删除数组最后一个元素,无参数,修改原数组,返回值,删除元素的值
  • unshift():向数组的开头添加一个或者多个元素,修改原数组,返回值,数组新的长度
  • shift():删除数组的第一个元素,数组长度减1,无参数,修改原数组,返回值,删除元素的值
  • reverse():颠倒数组中元素的顺序,无参数,修改原数组,返回值,新的数组
  • sort():对数组的元素进行排序,修改原数组,返回值,新的数组
  • toString():把数组转换成字符串,逗号分隔每一项,返回值,一个字符串
  • join():方法用于把数组中的所有元素转换成一个字符串,返回值,一个字符串
  • concat():连接两个或多个数组,不影响原数组,返回值,一个新的数组
  • slice():数组截取slice(begin,end),返回值,返回被截取项目的新数组
  • splice():数组删除splice(第几个开始,要删除的个数),修改原数组,返回值,返回被删除项目的新数组
  • indexOf():从前往后查找数组元素的索引号,不修改原数组,返回值,数组元素的索引号
  • lastIndexOf():从后往前查找数组元素的索引号,不修改原数组,返回值,数组元素的索引号
  • find():用于找出第一个符合条件的数组成员,返回值,数组元素
  • findIndex():用于找出第一个符合条件的数组成员的位置,返回值,索引号
  • includes():判断某个数组是否包含给定的值

5、数组去重

除了es6的new Set()去重外,可以通过以下几种方法实现数组去重

js复制代码// 利用filter去实现
Array.prototype.unique1=function() {
    return this.filter((item, index, array)=> {
        return this.indexOf(item)===index;
    });
}
js复制代码Array.prototype.unique2=function() {
    const n={}, r=[]; // n为hash表,r为临时数组
    for (let i=0; i < this.length; i++) {
        if (!n[this[i]]) { // 如果hash表中没有当前项
            n[this[i]]=true; //存入hash表
            r.push(this[i]); //把当前数组的当前项push到临时数组里面
        }
    }
    return r;
}
js复制代码// 返回后的数组顺序会乱
Array.prototype.unique3=function() {
    this.sort();
    const r=[this[0]];
    for (let i=1; i < this.length; i++) {
        if (this[i] !==this[i - 1]) {
            r.push(this[i]);
        }
    }
    return r;
}


6、执行上下文与作用域、作用域链

执行上下文(以下简称“上下文”)的概念在 JavaScript 中是颇为重要的。变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为。每个上下文都有一个关联的变量对象(variable object),而这个上下文中定义的所有变量和函数都存在于这个对象上。虽然无法通过代码访问变量对象,但后台处理数据会用到它。

全局上下文是最外层的上下文。根据 ECMAScript 实现的宿主环境,表示全局上下文的对象可能不一 样。在浏览器中,全局上下文就是我们常说的window对象,因此所有通过 var 定义的全局变量和函数都会成为 window 对象的属性和方法。使用 let 和 const 的顶级声明不会定义在全局上下文中,但在作用域链解析上效果是一样的。上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)。

每个函数调用都有自己的上下文。当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。 在函数执行完之后,上下文栈会弹出该函数上下文,将控制权返还给之前的执行上下文。ECMAScript 程序的执行流就是通过这个上下文栈进行控制的。

上下文中的代码在执行的时候,会创建变量对象的一个作用域链(scope chain)。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端。如果上下文是函数,则其活动对象(activation object)用作变量对象。活动对象最初只有一个定义变量:arguments。(全局上下文中没有这个变量。)作用域链中的下一个变量对象来自包含上下文,再下一个对象来自再下一个包含上下文。以此类推直至全局上下文;全局上下文的变量对象始终是作用域链的最后一个变量对象。

代码执行时的标识符解析是通过沿作用域链逐级搜索标识符名称完成的。搜索过程始终从作用域链 的最前端开始,然后逐级往后,直到找到标识符。(如果没有找到标识符,那么通常会报错。) 看一看下面这个例子:

js复制代码var color="blue"; 
 
function changeColor() { 
 if (color==="blue") { 
     color="red"; 
 } else { 
     color="blue"; 
 } 
} 
 
changeColor();

对这个例子而言,函数 changeColor()的作用域链包含两个对象:一个是它自己的变量对象(就 是定义 arguments 对象的那个),另一个是全局上下文的变量对象。这个函数内部之所以能够访问变量color,就是因为可以在作用域链中找到它。

此外,局部作用域中定义的变量可用于在局部上下文中替换全局变量。看一看下面这个例子:

js复制代码var color="blue"; 
 
function changeColor() { 
    let anotherColor="red"; 

    function swapColors() { 
        let tempColor=anotherColor; 
        anotherColor=color; 
        color=tempColor; 
        // 这里可以访问 color、anotherColor 和 tempColor 
    } 
 
    // 这里可以访问 color 和 anotherColor,但访问不到 tempColor 
    swapColors(); 
} 
 
// 这里只能访问 color 
changeColor();

以上代码涉及 3 个上下文:全局上下文、changeColor()的局部上下文和 swapColors()的局部 上下文。全局上下文中有一个变量 color 和一个函数 changeColor()。changeColor()的局部上下文中有一个变量 anotherColor 和一个函数 swapColors(),但在这里可以访问全局上下文中的变量 color。swapColors()的局部上下文中有一个变量 tempColor,只能在这个上下文中访问到。全局上下文和changeColor()的局部上下文都无法访问到 tempColor。而在 swapColors()中则可以访问另外两个 上下文中的变量,因为它们都是父上下文。下图展示了这个例子的作用域链。

上图中的矩形表示不同的上下文。内部上下文可以通过作用域链访问外部上下文中的一切,但外 部上下文无法访问内部上下文中的任何东西。上下文之间的连接是线性的、有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索。swapColors()局部上下文的作用域链中有 3 个对象:swapColors()的变量对象、changeColor()的变量对象和全局变量对象。swapColors()的局部上下文首先从自己的变量对象开始搜索变量和函数,搜不到就去搜索上一级变量对象。changeColor()上下文的作用域链中只有 2 个对象:它自己的变量对象和全局变量对象。因此,它不能访问 swapColors()的上下文。

7、说一下你对前端闭包的理解

闭包是指有权访问另外一个函数作用域中的变量的函数,有兴趣的可以看看几篇文章,看完会又更深刻的理解

破解前端面试(80% 应聘者不及格系列):从闭包说起


8、说一下你对call/apply/bind的理解

call/apply/bind都是用来修改this指向的

apply 和 call 的区别
apply 和 call 的区别是 call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组(方便记忆:call有两个l,表示可以传递多个参数)

bind 和 apply、call 区别
call、apply都是直接调用,bind生成的this指向改变函数需要手动调用

9、说一下你对原型、原型链的理解

JavaScript深入之从原型到原型链


10、js实现继承的几种方法

比较常见的几种:

  • 原型链继承
  • 借用构造函数继承
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承 一般只建议寄生组合式继承,因为其它方式的继承会在一次实例中调用两次父类的构造函数或有其它缺点,代码如下:
js复制代码function Parent(name) {
    this.name=name;
}
Parent.prototype.sayName=function() {
    console.log('parent name:', this.name);
}
function Child(name, parentName) {
    Parent.call(this, parentName);  
    this.name=name;    
}
function create(proto) {
    function F(){}
    F.prototype=proto;
    return new F();
}
Child.prototype=create(Parent.prototype);
Child.prototype.sayName=function() {
    console.log('child name:', this.name);
}
Child.prototype.constructor=Child;

var parent=new Parent('father');
parent.sayName();    // parent name: father

var child=new Child('son', 'father');

其他几种继承方式,可参考:JavaScript常用八种继承方案


11、深浅拷贝

深拷贝: JSON.parse(JSON.stringify(data)) 递归

浅拷贝: Object.assign() 扩展运算符(...) Array.prototype.concat() Array.prototype.slice()

12、事件循环机制/Event Loop

说说事件循环机制(满分答案来了)


13、防抖与节流

防抖: 防抖就是将一段时间内连续的多次触发转化为一次触发。一般可以使用在用户输入停止一段时间过后再去获取数据,而不是每次输入都去获取
防抖应用场景:

  • 保存、跳转、登录等按钮的频繁点击
  • 搜索框搜索


节流: 节流,顾名思义,控制流量。用于用户在与页面交互时控制事件发生的频率,一般场景是单位的时间或其它间隔内定时执行操作。一段时间内,事件在每次到达我们规定的间隔 n 秒时触发一次
节流应用场景:

  • scroll,resize, touchmove, mousemove等极易持续性促发事件的相关动画问题,降低频率
  • 浏览器播放事件


14、函数柯里化

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

js复制代码function curry(fn, args) {
    var length=fn.length;
    var args=args || [];
    return function(){
        newArgs=args.concat(Array.prototype.slice.call(arguments));
        if (newArgs.length < length) {
            return curry.call(this,fn,newArgs);
        } else {
            return fn.apply(this,newArgs);
        }
    }
}

function multiFn(a, b, c) {
    return a * b * c;
}

var multi=curry(multiFn);

multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);


15、手写代码

这里面包含了大部分的常见手写代码: JavaScript手写代码无敌秘籍



三、ES6

1、常用的es6语法有哪些

  • let、const
  • 解构赋值
  • 模板字符串
  • 箭头函数
  • 函数默认值
  • promise
  • set、map结构
  • class类
  • symbol
  • Iterator 和 for...of 循环.
  • 数值的扩展方法
  • 数组的扩展方法
  • 正则的扩展方法
  • 对象的扩展方法


2、说下var、let、const的区别

  • let、const不存在变量提升,var存在变量提升
  • 在严格模式下let、const不能重复声明,var可以重复声明
  • let、const有块级作用域,var没有块级作用域
  • const声明一个只读的常量。一旦声明,常量的值就不能改变

注: const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。


3、说说你对promise的了解

promise是异步编程的一种解决方案。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息

详细的可以参考:要就来45道Promise面试题一次爽到底


4、setTimeout、Promise、Async/Await 的区别

  • 其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行
  • promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行
  • async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行

5、class类的理解

类定义
与函数类型相似,定义类也有两种主要方式:类声明和类表达式。这两种方式都使用 class 关键 字加大括号:

js复制代码class Person {} // 类声明
const Animal=class {}; // 类表达式

与函数表达式类似,类表达式在它们被求值前也不能引用。不过,与函数定义不同的是,虽然函数 声明可以提升,但类定义不能:

js复制代码console.log(FunctionExpression); // undefined 
var FunctionExpression=function() {}; 
console.log(FunctionExpression); // function() {} 
 
console.log(FunctionDeclaration); // FunctionDeclaration() {} 
function FunctionDeclaration() {} 
console.log(FunctionDeclaration); // FunctionDeclaration() {} 
 
console.log(ClassExpression); // undefined 
var ClassExpression=class {}; 
console.log(ClassExpression); // class {} 
 
console.log(ClassDeclaration); // ReferenceError: ClassDeclaration is not defined 
class ClassDeclaration {} 
console.log(ClassDeclaration); // class ClassDeclaration {} 

类构成
类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但这些都不是必需的。 空的类定义照样有效。默认情况下,类定义中的代码都在严格模式下执行

构造函数 constructor
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。

类的实例化
class 的实例化必须通过 new 关键字

js复制代码class Example {} 
let exam1=Example(); 
// Class constructor Example cannot be invoked without 'new'

使用 new 调用类的构造函数会执行如下操作。
(1) 在内存中创建一个新对象。
(2) 这个新对象内部的[[Prototype]]指针被赋值为构造函数的 prototype 属性。
(3) 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)
(4) 执行构造函数内部的代码(给新对象添加属性)。
(5) 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。

类的所有实例共享一个原型对象

js复制代码class Example {
    constructor(a, b) {
        this.a=a;
        this.b=b;
        console.log('Example');
    }
    sum() {
        return this.a + this.b;
    }
}
let exam1=new Example(2, 1);
let exam2=new Example(3, 1);
console.log(exam1._proto_==exam2._proto_); // true 

exam1._proto_.sub=function () {
    return this.a - this.b;
}
console.log(exam1.sub()); // 1 
console.log(exam2.sub()); // 2

上面代码中,exam1和exam2都是Example的实例,它们的原型都是Example.prototype,所以__proto__属性是相等的

setter、getter
在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

静态方法 static
类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

js复制代码class Foo {
    static classMethod() {
        return 'hello';
    }
}

Foo.classMethod() // 'hello'

var foo=new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

上面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

关键字 super
super关键字用于访问和调用一个对象的父对象上的函数。

继承 extends
ES6 类支持单继承。使用 extends 关键字,就可以继承任何拥有[[Construct]]和原型的对象。 很大程度上,这意味着不仅可以继承一个类,也可以继承普通的构造函数(保持向后兼容):

js复制代码class Vehicle {} 
 
// 继承类 
class Bus extends Vehicle {} 
 
let b=new Bus(); 
console.log(b instanceof Bus); // true 
console.log(b instanceof Vehicle); // true 
 
 
function Person() {} 
 
// 继承普通构造函数 
class Engineer extends Person {} 
 
let e=new Engineer(); 
console.log(e instanceof Engineer); // true 
console.log(e instanceof Person); // true 



四、浏览器、网络基础

1、如何用js去实现一个ajax

大概步骤如下,具体要针对具体业务封装:

js复制代码// 创建 XMLHttpRequest 对象
var ajax=new XMLHttpRequest();
// 规定请求的类型、URL 以及是否异步处理请求。
ajax.open('GET', url);
// 发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
// 发送请求
ajax.send(null);  
// 接受服务器响应数据
ajax.onreadystatechange=function () {
    if (obj.readyState==4 && (obj.status==200 || obj.status==304)) { 
    }
};


2、常见的http状态码

状态码

含义

200

表示从客户端发来的请求在服务器端被正常处理了

204

无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档

301

永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替

302

临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI

304

未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。
客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源

400

表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求

401

表示未授权(Unauthorized),当前请求需要用户验证

403

表示对请求资源的访问被服务器拒绝了

404

表示服务器上无法找到请求的资源。除此之外,也可以在服务器端拒绝请求且不想说明理由时使用

500

表示服务器端在执行请求时发生了错误。也有可能是Web应用存在的bug或某些临时的故障

502

作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

503

表示服务器暂时处于超负载或正在进行停机维护,现在无法处理请求

504

网关超时, 服务器作为网关或代理,但是没有及时从上游服务器收到请求

3、ajax的请求头和响应头包含哪些东西

Request header

解释

Accept

指定客户端能够接收的内容类型,如:application/json, text/plain

Accept-Encoding

指定浏览器可以支持的web服务器返回内容压缩编码类型,如:gzip, deflate, br

Accept-Language

浏览器所希望的语言种类

Cache-Control

缓存机制,默认no-cache

Connec-Length

请求头的长度

Content-Type

发送的数据类型, 如:application/x-www-form-urlencoded,
application/json,multipart/form-data(可用来做文件上传),

Connection

表示是否需要持久连接。(HTTP 1.1默认进行持久连接)

Cookie

HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器

Host

指定请求的服务器的域名和端口号

If-Modified-Since

只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答

Referer

包含一个URL,用户从该URL代表的页面出发访问当前请求的页面

User-Agent

浏览器信息,如果Servlet返回的内容与浏览器类型有关则该值非常有用

Cookie

这是最重要的请求头信息之一


Response header

解释

Allow

服务器支持哪些请求方法(如GET、POST等)

Content-Encoding

文档的编码(Encode)方法。
只有在解码之后才可以得到Content-Type头指定的内容类型。
利用gzip压缩文档能够显著地减少HTML文档的下载时间。
Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。
因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,
为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面

Content-Length

表示内容长度。
只有当浏览器使用持久HTTP连接时才需要这个数据。
如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStream,
完成后查看其大小,然后把该值放入Content-Length头,
最后通过byteArrayStream.writeTo发送内容

Content-Type

表示后面的文档属于什么MIME类型。
Servlet默认为text/plain,但通常需要显式地指定为text/html。
由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType

Date

当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦

Expires

应该在什么时候认为文档已经过期,从而不再缓存它?

Last-Modified

文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,
该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,
否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。

Location

表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302

Refresh

表示浏览器应该在多少时间之后刷新文档,以秒计

Server

服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置

Set-Cookie

设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie

WWW-Authenticate

客户应该在Authorization头中提供什么类型的授权信息?
在包含401(Unauthorized)状态行的应答中这个头是必需的

4、为什么会产生跨域?怎么去解决跨域问题?

跨域是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议任一不同,都属于跨域。

跨域解决方案:

  • 通过jsonp跨域
  • 跨域资源共享(CORS)
  • nginx代理跨域
  • nodejs中间件代理跨域
  • WebSocket协议跨域
  • postMessage跨域
  • document.domain + iframe跨域
  • location.hash + iframe
  • window.name + iframe跨域

详情见:九种跨域方式实现原理(完整版)


5、post的预请求了解过吗?

预请求就是复杂请求(可能对服务器数据产生副作用的HTTP请求方法,如put,delete都会对服务器数据进行更修改,所以要先询问服务器)。
跨域请求中,浏览器自发的发起的预请求,浏览器会查询到两次请求,第一次的请求参数是options,以检测试实际请求是否可以被浏览器接受

什么情况下发生

  • 请求方法不是get head post
  • post 的content-type不是application/x-www-form-urlencode,multipart/form-data,text/plain ([也就是把content-type设置成"application/json"])
  • 请求设置了自定义的header字段: 比如业务需求,传一个字段,方便后端获取,不需要每个接口都传

例如设置了post请求的content-type:application/json,就会发生预请求


6、从输入URL到页面展示,这中间发生了什么?

  • DNS 解析:将域名解析成 IP 地址
  • TCP 连接:TCP 三次握手
  • 发送 HTTP 请求
  • 服务器处理请求并返回 HTTP 报文
  • 浏览器解析渲染页面
  • 断开连接:TCP 四次挥手

详细可以参考: 从输入URL开始建立前端知识体系


7、说一下tcp三次握手和四次挥手

三次握手
第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_SEND 状态。
第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态。
第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接

四次挥手
第一次挥手:客户端先发送FIN报文(第24帧),用来关闭主动方到被动关闭方的数据传送,也就是客户端告诉服务器:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,客户端依然会重发这些数据),但此时客户端还可以接受数据。
第二次挥手:Server端接到FIN报文后,如果还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以服务器端先发送ACK(第25帧),告诉Client端:请求已经收到了,但是我还没准备好,请继续等待停止的消息。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。
第三次挥手:当Server端确定数据已发送完成,则向Client端发送FIN报文(第26帧),告诉Client端:服务器这边数据发完了,准备好关闭连接了。
第四次挥手:Client端收到FIN报文后,就知道可以关闭连接了,但是他还是不相信网络,所以发送ACK后进入TIME_WAIT状态(第27帧), Server端收到ACK后,就知道可以断开连接了。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,最后,Client端也可以关闭连接了至此,TCP连接就已经完全关闭了!

为了方便记忆,整理个白话文版本,以找工作和离职举例(客户端=小明,服务端=人事)

第一次握手:由小明发起,小明向招聘人事发送了一份简历,里面包含了小明的个人基本信息
第二次握手:由人事发起,人事收到小明的简历后,安排面试,面试通过后,发送了一份offer给小明
第三次握手:由小明发起,小明收到offer后,会发送一个确认offer的回复,表示已经收到offer

第一次挥手:由小明发起,告诉服务器我要离职跑路了
第二次挥手:由人事发起,告诉小明我知道你要离职了,但是你先把工作交接好
第三次挥手:由人事发起,我看你的交接流程都走完了,你可以走人了
第四次挥手:由小明发起,收到人事通知离职流程已经走完了,告诉人事我明白就不来了,人事收到消息把此人从公司注销


8、说一下浏览器缓存策略

大概流程如下:

  1. 第一次请求,无缓存,直接向服务器发请求,并将请求结果存入缓存中
  2. 第二次请求,在强制缓存时间内,缓存未过期,浏览器接使用缓存作为结果返回 200
  3. 第三次请求,强制缓存时间已过期,进入协商缓存,向服务器请求,通过在Header中携带 If-Modified-Since(对应浏览器返回的last-Modify) 或If-None-Match(对应浏览器返回的Etag) 校验缓存内容是否有更新,Etag优先级更高
  4. 缓存资源没有更新,返回 304,浏览器继续使用缓存,更新强制缓存时间
    缓存资源有更新,缓存失效,返回 200,重新返回资源和缓存标识,再存入浏览器缓存中


9、能不能说一说浏览器的本地存储?

浏览器的本地存储主要分为Cookie、WebStorage和IndexedDB, 其中WebStorage又可以分为localStorage和sessionStorage。接下来我们就来一一分析这些本地存储方案。

Cookie
HTTP Cookie,通常叫做Cookie,一开始是在客户端用于存储会话信息的。

Cookie主要构成

  • name:名称,一个唯一确定的cookie的名称,cookie的名称必须经过URL编码。
  • value:值,存储在cookie中的字符串值。值必须被URL编码。
  • Domain:域,指明cookie对哪个域有效,所有向该域发送的请求都会包含这个信息。
  • path:路径,对于指定域中的那个路径,应该向服务器发送cookie。
  • Expires/Max-Age:有效期,表示cookie的有效期。
  • HttpOnly:如果这个这个值设置为true,就不能通过JS脚本获取cookie的值。通过这个值可以有效防止XSS攻击。
  • Secure:安全标志,指定后,cookie只有在使用SSL连接的时候才能发送到服务器。

Cookie的原理

第一次访问网站时,浏览器发出请求,服务器响应请求后,会在响应头中添加一个Set-Cookie,将cookie放入响应请求中。

在第二次发起请求时,浏览器通过Cookie请求头部将cookie信息送给服务器,服务端根据cookie信息辨别用户身份。

Cookie的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。

Cookie的生成

Cookie的生成方式主要有两种:

  • 服务端设置Cookie
  • 客户端设置Cookie

服务端设置方式参考上面Cookie的原理,具体的实现方式自行查阅相关资料。客户端设置Cookie方法如下:

js复制代码document.cookie="name=zhangsan; age=20"

Cookie的缺点

  • 每个特定域名下的cookie数量有限,不同浏览器数量限制不同。如果超过数量限制后再设置Cookie,浏览器就会清除以前设置的Cookie。
  • 大小只有4kb。
  • 每次HTTP请求都会默认带上Cookie,影响获取资源的效率。
  • Cookie的获取、设置、删除方法需要我们自己去封装。

Web Storage
Web Storage分为localStorage和sessionStorage
localStorage
localStorage有以下几个特点:

  • 保持的数据永久有效,除非手动删除;
  • 大小为5M
  • 仅在客户端使用,不和服务端进行通信
  • 接口封装较好

使用方法:

js复制代码// 设置
localStorage.setItem('name', '张三')
localStorage.age='25'
// 取值
localStorage.getItem('name')
let age=localStorage.age
// 移除
localStorage.removeItem('name')
// 移除所有
localStorage.clear()

sessionStorage
sessionStorage对象存储特定于某个会话的数据,当这个会话的页签或浏览器关闭,sessionStorage也就消失了。
页面刷新之后,存储在sessionStorage中的数据仍然存在可用。
sessionStorage的特点:

  • 会话级别的浏览器存储
  • 大小为5M
  • 仅在客户端使用,不和服务端通信
  • 接口封装较好

使用方法:

js复制代码// 设置
sessionStorage.setItem('name', '张三')
sessionStorage.age='25'
// 取值
sessionStorage.getItem('name')
let age=sessionStorage.age
// 移除
sessionStorage.removeItem('name')
// 移除所有
sessionStorage.clear()

sessionStorage和localStorage的区别:localStorage的数据可以长期保留,sessionStorage的数据在关闭页面后即被清空

IndexedDB
IndexedDB,全称Indexed Database API,是浏览器中保持结构化数据的一种数据库。
IndexedDB的思想是创建一套API,方便保存和读取JavaScript对象,同时支持查询和搜索。

IndexedDB特点

  • 键值对存储:IndexedDB采用对象仓库存储数据,可以存储所有类型的数据。仓库中数据以键值对的形式保持。
  • 异步:IndexedDB操作时不会锁死浏览器,用户依然可以进行其他操作。
  • 支持事务:有学过数据库的对事务肯定不陌生。事务意味着在一系列操作中,只要有一步失败,整个事务就都取消,数据库回滚到事务执行之前,不存在只改写一部分数据的情况。
  • 同源限制:IndexedDB受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
  • 储存空间大: IndexedDB 的储存空间比 localStorage大得多,一般来说不少于 250MB,甚至没有上限。
  • 支持二进制储存: IndexedDB不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

IndexedDB的入门教程,可以查看阮一峰老师的文章:浏览器数据库 IndexedDB 入门教程

总结

  • Cookie主要用于“维持状态”,而非本地存储数据
  • Web Storage是专门为浏览器提供的数据存储机制,不与服务端发生通信
  • IndexedDB 用于客户端存储大量结构化数据


10、浏览器垃圾回收机制

介绍
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。

不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,全局变量的生命周期直至浏览器卸载页面才会结束。局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直至函数结束,而闭包中由于内部函数的原因,外部函数并不能算是结束。

还是上代码说明吧:

js复制代码function fn1() {
    var obj={ name: 'hanzichi', age: 10 };
}
function fn2() {
    var obj={ name:'hanzichi', age: 10 };
    return obj;
}

var a=fn1();
var b=fn2();

我们来看代码是如何执行的。首先定义了两个function,分别叫做fn1和fn2,当fn1被调用时,进入fn1的环境,会开辟一块内存存放对象{name: 'hanzichi', age: 10},而当调用结束后,出了fn1的环境,那么该块内存会被js引擎中的垃圾回收器自动释放;在fn2被调用的过程中,返回的对象被全局变量b所指向,所以该块内存并不会被释放。

这里问题就出现了:到底哪个变量是没有用的?所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

标记清除
js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。 到目前为止,IE9+、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。

引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。

js复制代码function test() {
    var a={}; // a的引用次数为0 
    var b=a; // a的引用次数加1,为1 
    var c=a; // a的引用次数再加1,为2
    var b={}; // a的引用次数减1,为1
}

Netscape Navigator3是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。

js复制代码function fn() {
    var a={};
    var b={};
    a.pro=b;
    b.pro=a;
}
fn();

以上代码a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。

我们知道,IE中有一部分对象并不是原生js对象。例如,其内存泄露DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。

js复制代码var element=document.getElementById("some_element");
var myObject=new Object();
myObject.e=element;
element.o=myObject;

这个例子在一个DOM元素(element)与一个原生js对象(myObject)之间创建了循环引用。其中,变量myObject有一个属性e指向element对象;而变量element也有一个属性o回指myObject。由于存在这个循环引用,即使例子中的DOM从页面中移除,它也永远不会被回收。

举个栗子:

js复制代码myObject.element=null;
element.o=null;

window.onload=function outerFunction() {
    var obj=document.getElementById("element");
    obj.onclick=function innerFunction(){};
    obj=null;
};

这段代码看起来没什么问题,但是obj引用了document.getElementById('element'),而document.getElementById('element')的onclick方法会引用外部环境中的变量,自然也包括obj,是不是很隐蔽啊。(在比较新的浏览器中在移除Node的时候已经会移除其上的event了,但是在老的浏览器,特别是ie上会有这个bug)

解决办法:

最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样

js复制代码myObject.element=null;
element.o=null;

window.onload=function outerFunction(){
    var obj=document.getElementById("element");
    obj.onclick=function innerFunction(){};
    obj=null;
};

将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。

要注意的是,IE9+并不存在循环引用导致Dom内存泄露问题,可能是微软做了优化,或者Dom的回收方式已经改变

11、谈谈你对重绘和回流的理解

在讨论回流与重绘之前,我们要知道:

  1. 浏览器使用流式布局模型 (Flow Based Layout)。
  2. 浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了Render Tree。
  3. 有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
  4. 由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。

一句话:回流必将引起重绘,重绘不一定会引起回流

回流 (Reflow)

当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。

会导致回流的操作:

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等等)
  • 元素字体大小变化
  • 添加或者删除可见的DOM元素
  • 激活CSS伪类(例如::hover)
  • 查询某些属性或调用某些方法

一些常用且会导致回流的属性和方法:

  • clientWidth、clientHeight、clientTop、clientLeft
  • offsetWidth、offsetHeight、offsetTop、offsetLeft
  • scrollWidth、scrollHeight、scrollTop、scrollLeft
  • scrollIntoView()、scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

重绘 (Repaint)

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

性能影响

回流比重绘的代价要更高。

有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。

现代浏览器会对频繁的回流或重绘操作进行优化:

浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。

当你访问以下属性或方法时,浏览器会立刻清空队列:

  • clientWidth、clientHeight、clientTop、clientLeft
  • offsetWidth、offsetHeight、offsetTop、offsetLeft
  • scrollWidth、scrollHeight、scrollTop、scrollLeft
  • width、height
  • getComputedStyle()
  • getBoundingClientRect()

因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。

如何避免

CSS

  • 避免使用table布局。
  • 尽可能在DOM树的最末端改变class。
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolute或fixed的元素上。
  • 避免使用CSS表达式(例如:calc())。

JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。


12、http和https的区别

  • HTTP是明文传输,不安全的,HTTPS是加密传输,安全的多
  • HTTP标准端口是80,HTTPS标准端口是443
  • HTTP不用认证证书免费,HTTPS需要认证证书要钱
  • 连接方式不同,HTTP三次握手,HTTPS中TLS1.2版本7次,TLS1.3版本6次
  • HTTP在OSI网络模型中是在应用层,而HTTPS的TLS是在传输层
  • HTTP是无状态的,HTTPS是有状态的


13、HTTP2.0和HTTP1.X相比的新特性

  • 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
  • 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
  • header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
  • 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能


14、DNS

通俗易懂,了解什么是DNS及查询过程


15、CDN

CDN是什么?使用CDN有什么优势?


五、Vue

最全的 Vue 面试题+详解答案

Vue.js 技术揭秘

六、React

高频前端面试题汇总之React篇(上)

高频前端面试题汇总之React篇(下)

搞懂这12个Hooks,保证让你玩转React

React 源码剖析系列 - 生命周期的管理艺术

React Hooks 原理

深入理解 React 高阶组件

七、性能优化

前端性能优化手段从以下几个方面入手:加载优化、执行优化、渲染优化、样式优化、脚本优化

加载优化:减少HTTP请求、缓存资源、压缩代码、无阻塞、首屏加载、按需加载、预加载、压缩图像、减少Cookie、避免重定向、异步加载第三方资源

执行优化:CSS写在头部,JS写在尾部并异步、避免img、iframe等的src为空、尽量避免重置图像大小、图像尽量避免使用DataURL

渲染优化:设置viewport、减少DOM节点、优化动画、优化高频事件、GPU加速

样式优化:避免在HTML中书写style、避免CSS表达式、移除CSS空规则、正确使用display:display、不滥用float等

脚本优化:减少重绘和回流、缓存DOM选择与计算、缓存.length的值、尽量使用事件代理、尽量使用id选择器、touch事件优化

加载优化

  1. 减少HTTP请求:尽量减少页面的请求数(首次加载同时请求数不能超过4个),移动设备浏览器同时响应请求为4个请求(Android支持4个,iOS5+支持6个)
  • 合并CSS和JS
  • 使用CSS精灵图
  1. 缓存资源:使用缓存可减少向服务器的请求数,节省加载时间,所有静态资源都要在服务器端设置缓存,并且尽量使用长缓存(使用时间戳更新缓存)
  • 缓存一切可缓存的资源
  • 使用长缓存
  • 使用外联的样式和脚本
  1. 压缩代码:减少资源大小可加快网页显示速度,对代码进行压缩,并在服务器端设置GZip
  • 压缩代码(多余的缩进、空格和换行符)
  • 启用Gzip
  1. 无阻塞:头部内联的样式和脚本会阻塞页面的渲染,样式放在头部并使用link方式引入,脚本放在尾部并使用异步方式加载
  2. 首屏加载:首屏快速显示可大大提升用户对页面速度的感知,应尽量针对首屏的快速显示做优化
  3. 按需加载:将不影响首屏的资源和当前屏幕不用的资源放到用户需要时才加载,可大大提升显示速度和降低总体流量(按需加载会导致大量重绘,影响渲染性能)
  • 懒加载
  • 滚屏加载
  • Media Query加载
  1. 预加载:大型资源页面可使用Loading,资源加载完成后再显示页面,但加载时间过长,会造成用户流失
  • 可感知Loading:进入页面时Loading
  • 不可感知Loading:提前加载下一页
  1. 压缩图像:使用图像时选择最合适的格式和大小,然后使用工具压缩,同时在代码中用srcset来按需显示( 过度压缩图像大小影响图像显示效果)
  • 使用TinyJpg和TinyPng压缩图像
  • 使用CSS3、SVG、IconFont代替图像
  • 使用img的srcset按需加载图像
  • 选择合适的图像:webp优于jpg,png8优于gif
  • 选择合适的大小:首次加载不大于1014kb、不宽于640px
  • PS切图时D端图像保存质量为80,M端图像保存质量为60
  1. 减少Cookie:Cookie会影响加载速度,静态资源域名不使用Cookie
  2. 避免重定向:重定向会影响加载速度,在服务器正确设置避免重定向
  3. 异步加载第三方资源:第三方资源不可控会影响页面的加载和显示,要异步加载第三方资源

执行优化

  1. CSS写在头部,JS写在尾部并异步
  2. 避免img、iframe等的src为空:空src会重新加载当前页面,影响速度和效率
  3. 尽量避免重置图像大小:多次重置图像大小会引发图像的多次重绘,影响性能
  4. 图像尽量避免使用DataURL:DataURL图像没有使用图像的压缩算法,文件会变大,并且要解码后再渲染,加载慢耗时长

渲染优化

  1. 设置viewport:HTML的viewport可加速页面的渲染
  2. html复制代码
  3. <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">
  4. 减少DOM节点:DOM节点太多影响页面的渲染,尽量减少DOM节点
  5. 优化动画
  • 尽量使用CSS3动画
  • 合理使用requestAnimationFrame动画代替setTimeout
  • 适当使用Canvas动画:5个元素以内使用CSS动画,5个元素以上使用Canvas动画,iOS8+可使用WebGL动画
  1. 优化高频事件:scroll、touchmove等事件可导致多次渲染
  • 函数节流
  • 函数防抖
  • 使用requestAnimationFrame监听帧变化:使得在正确的时间进行渲染
  • 增加响应变化的时间间隔:减少重绘次数
  1. GPU加速:使用某些HTML5标签和CSS3属性会触发GPU渲染,请合理使用(过渡使用会引发手机耗电量增加) HTML标签:video、canvas、webgl CSS属性:opacity、transform、transition

样式优化

  1. 避免在HTML中书写style
  2. 避免CSS表达式:CSS表达式的执行需跳出CSS树的渲染
  3. 移除CSS空规则:CSS空规则增加了css文件的大小,影响CSS树的执行
  4. 正确使用display:display会影响页面的渲染
  • display:inline后不应该再使用float、margin、padding、width和height
  • display:inline-block后不应该再使用float
  • display:block后不应该再使用vertical-align
  • display:table-*后不应该再使用float和margin
  1. 不滥用float:float在渲染时计算量比较大,尽量减少使用
  2. 不滥用Web字体:Web字体需要下载、解析、重绘当前页面,尽量减少使用
  3. 不声明过多的font-size:过多的font-size影响CSS树的效率
  4. 值为0时不需要任何单位:为了浏览器的兼容性和性能,值为0时不要带单位
  5. 标准化各种浏览器前缀
  6. 无前缀属性应放在最后
  7. CSS动画属性只用-webkit-、无前缀两种
  8. 其它前缀为-webkit-、-moz-、-ms-、无前缀四种:Opera改用blink内核,-o-已淘汰
  9. 避免让选择符看起来像正则表达式:高级选择符执行耗时长且不易读懂,避免使用

脚本优化

  1. 减少重绘和回流
  • 避免不必要的DOM操作
  • 避免使用document.write
  • 减少drawImage
  • 尽量改变class而不是style,使用classList代替className
  1. 缓存DOM选择与计算:每次DOM选择都要计算和缓存
  2. 缓存.length的值:每次.length计算用一个变量保存值
  3. 尽量使用事件代理:避免批量绑定事件
  4. 尽量使用id选择器:id选择器选择元素是最快的
  5. touch事件优化:使用tap(touchstart和touchend)代替click(注意touch响应过快,易引发误操作)

八、webpack

Webpack面试题

当面试官问Webpack的时候他想知道什么


九、TypeScript

TypeScript免费视频图文教程(2W字)


十、数据结构与算法

算法这块呢,应该是很多人头疼的地方,没有其他方法,只能去LeetCode老老实实刷题

JavaScript 数据结构与算法之美

十一、前端安全

1、说一说XSS攻击

就是攻击者想尽一切办法将可以执行的代码注入到网页中, 主要分为以下几种

存储型(server端)

场景:见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等

攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中
  2. 用户打开目标网站时,服务端将恶意代码从数据库中取出来,拼接在HTML中返回给浏览器
  3. 用户浏览器在收到响应后解析执行,混在其中的恶意代码也同时被执行
  4. 恶意代码窃取用户数据,并发送到指定攻击者的网站,或者冒充用户行为,调用目标网站的接口,执行恶意操作

反射型(Server端)

与存储型的区别在于,存储型的恶意代码存储在数据库中,反射型的恶意代码在URL上

场景:通过 URL 传递参数的功能,如网站搜索、跳转等

攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作

Dom 型(浏览器端)

DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞

场景:通过 URL 传递参数的功能,如网站搜索、跳转等

攻击步骤:

  1. ?攻击者构造出特殊的 URL,其中包含恶意代码
  2. ?用户打开带有恶意代码的 URL
  3. ?用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行
  4. ?恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作

预防方案:
防止攻击者提交恶意代码,防止浏览器执行恶意代码

  1. 对数据进行严格的输出编码:如HTML元素的编码,JS编码,CSS编码,URL编码等等; 避免拼接 HTML;Vue/React 技术栈,避免使用 v-html / dangerouslySetInnerHTML
  2. CSP HTTP Header,即 Content-Security-Policy、X-XSS-Protection
  • 增加攻击难度,配置CSP(本质是建立白名单,由浏览器进行拦截)
  • Content-Security-Policy: default-src 'self'-所有内容均来自站点的同一个源(不包括其子域名)
  • Content-Security-Policy: default-src 'self' *.trusted.com-允许内容来自信任的域名及其子域名 (域名不必须与CSP设置所在的域名相同)
  • Content-Security-Policy: default-src yideng.com-该服务器仅允许通过HTTPS方式并仅从yideng.com域名来访问文档
  1. 输入验证:比如一些常见的数字、URL、电话号码、邮箱地址等等做校验判断
  2. 开启浏览器XSS防御:Http Only cookie,禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie
  3. 验证码

2、说一说CSRF攻击

攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的

攻击流程举例

  1. ?受害者登录 a.com,并保留了登录凭证(Cookie)
  2. ?攻击者引诱受害者访问了b.com
  3. ?b.com 向 a.com 发送了一个请求:a.com/act=xx浏览器会默认携带a.com的Cookie
  4. ?a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
  5. ?a.com以受害者的名义执行了act=xx
  6. ?攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作

攻击类型

  1. ?GET型:如在页面的某个 img 中发起一个 get 请求
  2. ?POST型:通过自动提交表单到恶意网站
  3. ?链接型:需要诱导用户点击链接

预防方案:

CSRF通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对CSRF的防护能力来提升安全性。)

  1. 同源检测:通过Header中的Origin Header 、Referer Header 确定,但不同浏览器可能会有不一样的实现,不能完全保证
  2. CSRF Token 校验:将CSRF Token输出到页面中(通常保存在Session中),页面提交的请求携带这个Token,服务器验证Token是否
    正确
  3. 双重cookie验证:
    流程:
  4. 步骤1:在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串(例如csrfcookie=v8g9e4ksfhw)
  5. 步骤2:在前端向后端发起请求时,取出Cookie,并添加到URL的参数中(接上例POST www.a.com/comment?csr…
  6. 步骤3:后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝
  7. 优点:
  8. 无需使用Session,适用面更广,易于实施
  9. Token储存于客户端中,不会给服务器带来压力
  10. 相对于Token,实施成本更低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加
  11. 缺点:
  • Cookie中增加了额外的字段
  • 如果有其他漏洞(例如XSS),攻击者可以注入Cookie,那么该防御方式失效
  • 难以做到子域名的隔离
  • 为了确保Cookie传输安全,采用这种防御方式的最好确保用整站HTTPS的方式,如果还没切HTTPS的使用这种方式也会有风险
  1. Samesite Cookie属性:Google起草了一份草案来改进HTTP协议,那就是为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,Strict 为任何情况下都不可以作为第三方 Cookie ,Lax 为可以作为第三方 Cookie , 但必须是Get请求


作者:前端老干部
链接:https://juejin.cn/post/7127217262133510158

、 介绍一下标准的CSS的盒子模型?与低版本IE的盒子模型有什么不同的?

标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin

低版本IE盒子模型:宽度=内容宽度(content+border+padding)+ margin

2、 box-sizing属性?

用来控制元素的盒子模型的解析模式,默认为content-box

context-box:W3C的标准盒子模型,设置元素的 height/width 属性指的是content部分的高/宽

border-box:IE传统盒子模型。设置元素的height/width属性指的是border + padding + content部分的高/宽

3 、CSS选择器有哪些?哪些属性可以继承?

CSS选择符:id选择器(#myid)、类选择器(.myclassname)、标签选择器(div, h1, p)、相邻选择器(h1 + p)、子选择器(ul > li)、后代选择器(li a)、通配符选择器(*)、属性选择器(a[rel="external"])、伪类选择器(a:hover, li:nth-child)

可继承的属性:font-size, font-family, color

不可继承的样式:border, padding, margin, width, height

优先级(就近原则):!important > [ id > class > tag ]

!important 比内联优先级高

4、 CSS优先级算法如何计算?

元素选择符: 1

class选择符: 10

id选择符:100

元素标签:1000

  1. !important声明的样式优先级最高,如果冲突再进行计算。
  2. 如果优先级相同,则选择最后出现的样式。
  3. 继承得到的样式的优先级最低。


5、 CSS3新增伪类有那些?

p:first-of-type 选择属于其父元素的首个元素

p:last-of-type 选择属于其父元素的最后元素

p:only-of-type 选择属于其父元素唯一的元素

p:only-child 选择属于其父元素的唯一子元素

p:nth-child(2) 选择属于其父元素的第二个子元素

:enabled :disabled 表单控件的禁用状态。

:checked 单选框或复选框被选中。

6 、如何居中div?如何居中一个浮动元素?如何让绝对定位的div居中?

div:

border: 1px solid red;margin: 0 auto; 
height: 50px;width: 80px;

浮动元素的上下左右居中:

border: 1px solid red;float: left;
position: absolute;width: 200px;
height: 100px;left: 50%;top: 50%;
margin: -50px 0 0 -100px; 

绝对定位的左右居中:

border: 1px solid black;position: absolute;
width: 200px;height: 100px;margin: 0 auto;
left: 0;right: 0; 

还有更加优雅的居中方式就是用flexbox,我以后会做整理。

7、 display有哪些值?说明他们的作用?

inline(默认)--内联

none--隐藏

block--块显示

table--表格显示

list-item--项目列表

inline-block

8、 position的值?

static(默认):按照正常文档流进行排列;

relative(相对定位):不脱离文档流,参考自身静态位置通过 top, bottom, left, right 定位;

absolute(绝对定位):参考距其最近一个不为static的父级元素通过top, bottom, left, right 定位;

fixed(固定定位):所固定的参照对像是可视窗口。

9、 CSS3有哪些新特性?

  1. RGBA和透明度
  2. background-image background-origin(content-box/padding-box/border-box) background-size background-repeat
  3. word-wrap(对长的不可分割单词换行)word-wrap:break-word
  4. 文字阴影:text-shadow: 5px 5px 5px #FF0000;(水平阴影,垂直阴影,模糊距离,阴影颜色)
  5. font-face属性:定义自己的字体
  6. 圆角(边框半径):border-radius 属性用于创建圆角
  7. 边框图片:border-image: url(border.png) 30 30 round
  8. 盒阴影:box-shadow: 10px 10px 5px #888888
  9. 媒体查询:定义两套css,当浏览器的尺寸变化时会采用不同的属性


10、 请解释一下CSS3的flexbox(弹性盒布局模型),以及适用场景?

该布局模型的目的是提供一种更加高效的方式来对容器中的条目进行布局、对齐和分配空间。在传统的布局方式中,block 布局是把块在垂直方向从上到下依次排列的;而 inline 布局则是在水平方向来排列。弹性盒布局并没有这样内在的方向限制,可以由开发人员自由操作。

试用场景:弹性布局适合于移动前端开发,在Android和ios上也完美支持。

11、 用纯CSS创建一个三角形的原理是什么?

首先,需要把元素的宽度、高度设为0。然后设置边框样式。

width: 0;height: 0;border-top: 40px solid transparent;
border-left: 40px solid transparent;border-right: 40px solid transparent;
border-bottom: 40px solid #ff0000;


12、 一个满屏品字布局如何设计?

第一种真正的品字:

  1. 三块高宽是确定的;
  2. 上面那块用margin: 0 auto;居中;
  3. 下面两块用float或者inline-block不换行;
  4. 用margin调整位置使他们居中。

第二种全屏的品字布局:

上面的div设置成100%,下面的div分别宽50%,然后使用float或者inline使其不换行。

13 、常见的兼容性问题?

  1. 不同浏览器的标签默认的margin和padding不一样。
  2. *{margin:0;padding:0;}
  3. IE6双边距bug:块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大。hack:display:inline;将其转化为行内属性。
  4. 渐进识别的方式,从总体中逐渐排除局部。首先,巧妙的使用“9”这一标记,将IE浏览器从所有情况中分离出来。接着,再次使用“+”将IE8和IE7、IE6分离开来,这样IE8已经独立识别。
{background-color:#f1ee18;/*所有识别*/.background-color:#00deff; /*IE6、7、8识别*/+background-color:#a200ff;/*IE6、7识别*/_background-color:#1e0bd1;/*IE6识别*/}
  1. 设置较小高度标签(一般小于10px),在IE6,IE7中高度超出自己设置高度。hack:给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度。
  2. IE下,可以使用获取常规属性的方法来获取自定义属性,也可以使用getAttribute()获取自定义属性;Firefox下,只能使用getAttribute()获取自定义属性。解决方法:统一通过getAttribute()获取自定义属性。
  3. Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决。
  4. 超链接访问过后hover样式就不出现了,被点击访问过的超链接样式不再具有hover和active了。解决方法是改变CSS属性的排列顺序:L-V-H-A ( love hate ): a:link {} a:visited {} a:hover {} a:active {}


14、 为什么要初始化CSS样式

因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面显示差异。

15、 absolute的containing block计算方式跟正常流有什么不同?

无论属于哪种,都要先找到其祖先元素中最近的 position 值不为 static 的元素,然后再判断:

  1. 若此元素为 inline 元素,则 containing block 为能够包含这个元素生成的第一个和最后一个 inline box 的 padding box (除 margin, border 外的区域) 的最小矩形;
  2. 否则,则由这个祖先元素的 padding box 构成。

如果都找不到,则为 initial containing block。

补充:

  1. static(默认的)/relative:简单说就是它的父元素的内容框(即去掉padding的部分)
  2. absolute: 向上找最近的定位为absolute/relative的元素
  3. fixed: 它的containing block一律为根元素(html/body)


16、CSS里的visibility属性有个collapse属性值?在不同浏览器下以后什么区别?

当一个元素的visibility属性被设置成collapse值后,对于一般的元素,它的表现跟hidden是一样的。

  1. chrome中,使用collapse值和使用hidden没有区别。
  2. firefox,opera和IE,使用collapse值和使用display:none没有什么区别。


17、 display:none与visibility:hidden的区别?

display:none 不显示对应的元素,在文档布局中不再分配空间(回流+重绘)

visibility:hidden 隐藏对应元素,在文档布局中仍保留原来的空间(重绘)

18、 position跟display、overflow、float这些特性相互叠加后会怎么样?

display属性规定元素应该生成的框的类型;position属性规定元素的定位类型;float属性是一种布局方式,定义元素在哪个方向浮动。

类似于优先级机制:position:absolute/fixed优先级最高,有他们在时,float不起作用,display值需要调整。float 或者absolute定位的元素,只能是块元素或表格。

19 、对BFC规范(块级格式化上下文:block formatting context)的理解?

BFC规定了内部的Block Box如何布局。

定位方案:

  1. 内部的Box会在垂直方向上一个接一个放置。
  2. Box垂直方向的距离由margin决定,属于同一个BFC的两个相邻Box的margin会发生重叠。
  3. 每个元素的margin box 的左边,与包含块border box的左边相接触。
  4. BFC的区域不会与float box重叠。
  5. BFC是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
  6. 计算BFC的高度时,浮动元素也会参与计算。

满足下列条件之一就可触发BFC

  1. 根元素,即html
  2. float的值不为none(默认)
  3. overflow的值不为visible(默认)
  4. display的值为inline-block、table-cell、table-caption
  5. position的值为absolute或fixed


20、 为什么会出现浮动和什么时候需要清除浮动?清除浮动的方式?

浮动元素碰到包含它的边框或者浮动元素的边框停留。由于浮动元素不在文档流中,所以文档流的块框表现得就像浮动框不存在一样。浮动元素会漂浮在文档流的块框上。

浮动带来的问题:

  1. 父元素的高度无法被撑开,影响与父元素同级的元素
  2. 与浮动元素同级的非浮动元素(内联元素)会跟随其后
  3. 若非第一个元素浮动,则该元素之前的元素也需要浮动,否则会影响页面显示的结构。

清除浮动的方式:

  1. 父级div定义height
  2. 最后一个浮动元素后加空div标签 并添加样式clear:both。
  3. 包含浮动元素的父标签添加样式overflow为hidden或auto。
  4. 父级div定义zoom


21、 上下margin重合的问题

在重合元素外包裹一层容器,并触发该容器生成一个BFC。

例子:

<div class="aside"></div>
<div class="text">
 <div class="main"></div>
</div>
<!--下面是css代码--> 
.aside { 
margin-bottom: 100px; 
 width: 100px; 
 height: 150px; 
 background: #f66;
 } 
.main { 
 margin-top: 100px; 
 height: 200px; 
 background: #fcc;
 }
 .text{ 
 /*盒子main的外面包一个div,
通过改变此div的属性使两个盒子分属于两个不同的BFC,
以此来阻止margin重叠*/
 overflow: hidden; 
//此时已经触发了BFC属性。
 }


22、设置元素浮动后,该元素的display值是多少?

自动变成display:block

23、 移动端的布局用过媒体查询吗?

通过媒体查询可以为不同大小和尺寸的媒体定义不同的css,适应相应的设备的显示。

  1. <head>里边
  2. <link rel="stylesheet" type="text/css" href="xxx.css" media="only screen and (max-device-width:480px)">
  3. CSS : @media only screen and (max-device-width:480px) {/css样式/}


24 、使用 CSS 预处理器吗?

Less sass

25、 CSS优化、提高性能的方法有哪些?

  1. 避免过度约束
  2. 避免后代选择符
  3. 避免链式选择符
  4. 使用紧凑的语法
  5. 避免不必要的命名空间
  6. 避免不必要的重复
  7. 最好使用表示语义的名字。一个好的类名应该是描述他是什么而不是像什么
  8. 避免!important,可以选择其他选择器
  9. 尽可能的精简规则,你可以合并不同类里的重复规则


26、 浏览器是怎样解析CSS选择器的?

CSS选择器的解析是从右向左解析的。若从左向右的匹配,发现不符合规则,需要进行回溯,会损失很多性能。若从右向左匹配,先找到所有的最右节点,对于每一个节点,向上寻找其父节点直到找到根元素或满足条件的匹配规则,则结束这个分支的遍历。

两种匹配规则的性能差别很大,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点),而从左向右的匹配规则的性能都浪费在了失败的查找上面。

而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 Render Tree。

27 、在网页中的应该使用奇数还是偶数的字体?为什么呢?

使用偶数字体。偶数字号相对更容易和 web 设计的其他部分构成比例关系。Windows 自带的点阵宋体(中易宋体)从 Vista 开始只提供 12、14、16 px 这三个大小的点阵,而 13、15、17 px时用的是小一号的点。(即每个字占的空间大了 1 px,但点阵没变),于是略显稀疏。

28、 margin和padding分别适合什么场景使用?

何时使用margin:

  1. 需要在border外侧添加空白
  2. 空白处不需要背景色
  3. 上下相连的两个盒子之间的空白,需要相互抵消时。

何时使用padding:

  1. 需要在border内侧添加空白
  2. 空白处需要背景颜色
  3. 上下相连的两个盒子的空白,希望为两者之和。

兼容性的问题:在IE5 IE6中,为float的盒子指定margin时,左侧的margin可能会变成两倍的宽度。通过改变padding或者指定盒子的display:inline解决。

29 、元素竖向的百分比设定是相对于容器的高度吗?

当按百分比设定一个元素的宽度时,它是相对于父容器的宽度计算的,但是,对于一些表示竖向距离的属性,例如 padding-top , padding-bottom , margin-top , margin-bottom 等,当按百分比设定它们时,依据的也是父容器的宽度,而不是高度。

30 、全屏滚动的原理是什么?用到了CSS的哪些属性?

  1. 原理:有点类似于轮播,整体的元素一直排列下去,假设有5个需要展示的全屏页面,那么高度是500%,只是展示100%,剩下的可以通过transform进行y轴定位,也可以通过margin-top实现
  2. overflow:hidden;transition:all 1000ms ease;


31、 什么是响应式设计?响应式设计的基本原理是什么?如何兼容低版本的IE?

响应式网站设计(Responsive Web design)是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本。

基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理。

页面头部必须有meta声明的viewport。

<meta name=’viewport’ content=”width=device-width, 
initial-scale=1. maximum-scale=1,user-scalable=no”>


32、 视差滚动效果?

视差滚动(Parallax Scrolling)通过在网页向下滚动的时候,控制背景的移动速度比前景的移动速度慢来创建出令人惊叹的3D效果。

  1. CSS3实现
  2. 优点:开发时间短、性能和开发效率比较好,缺点是不能兼容到低版本的浏览器
  3. jQuery实现
  4. 通过控制不同层滚动速度,计算每一层的时间,控制滚动效果。
  5. 优点:能兼容到各个版本的,效果可控性好
  6. 缺点:开发起来对制作者要求高
  7. 插件实现方式
  8. 例如:parallax-scrolling,兼容性十分好


33、 ::before 和 :after中双冒号和单冒号有什么区别?解释一下这2个伪元素的作用

  1. 单冒号(:)用于CSS3伪类,双冒号(::)用于CSS3伪元素。
  2. ::before就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于dom之中,只存在在页面之中。

:before 和 :after 这两个伪元素,是在CSS2.1里新出现的。起初,伪元素的前缀使用的是单冒号语法,但随着Web的进化,在CSS3的规范里,伪元素的语法被修改成使用双冒号,成为::before ::after

34、 你对line-height是如何理解的?

行高是指一行文字的高度,具体说是两行文字间基线的距离。CSS中起高度作用的是height和line-height,没有定义height属性,最终其表现作用一定是line-height。

单行文本垂直居中:把line-height值设置为height一样大小的值可以实现单行文字的垂直居中,其实也可以把height删除。

多行文本垂直居中:需要设置display属性为inline-block。

35 、怎么让Chrome支持小于12px 的文字?

p{font-size:10px;-webkit-transform:scale(0.8);} //0.8是缩放比例


36、 让页面里的字体变清晰,变细用CSS怎么做?

-webkit-font-smoothing在window系统下没有起作用,但是在IOS设备上起作用-webkit-font-smoothing:antialiased是最佳的,灰度平滑。

37、 position:fixed;在android下无效怎么处理?

<meta name="viewport" content="width=device-width, 
initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,
 user-scalable=no"/>

38、 如果需要手动写动画,你认为最小时间间隔是多久,为什么?

多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔为1/60*1000ms = 16.7ms。

39、 li与li之间有看不见的空白间隔是什么原因引起的?有什么解决办法?

行框的排列会受到中间空白(回车空格)等的影响,因为空格也属于字符,这些空白也会被应用样式,占据空间,所以会有间隔,把字符大小设为0,就没有空格了。

解决方法:

  1. 可以将<li>代码全部写在一排
  2. 浮动li中float:left
  3. 在ul中用font-size:0(谷歌不支持);可以使用letter-space:-3px

40、 display:inline-block 什么时候会显示间隙?

  1. 有空格时候会有间隙 解决:移除空格
  2. margin正值的时候 解决:margin使用负值
  3. 使用font-size时候 解决:font-size:0、letter-spacing、word-spacing

41、 有一个高度自适应的div,里面有两个div,一个高度100px,希望另一个填满剩下的高度

外层div使用position:relative;高度要求自适应的div使用position: absolute; top: 100px; bottom: 0; left: 0

42、 png、jpg、gif 这些图片格式解释一下,分别什么时候用。有没有了解过webp?

  1. png是便携式网络图片(Portable Network Graphics)是一种无损数据压缩位图文件格式.优点是:压缩比高,色彩好。 大多数地方都可以用。
  2. jpg是一种针对相片使用的一种失真压缩方法,是一种破坏性的压缩,在色调及颜色平滑变化做的不错。在www上,被用来储存和传输照片的格式。
  3. gif是一种位图文件格式,以8位色重现真色彩的图像。可以实现动画效果.
  4. webp格式是谷歌在2010年推出的图片格式,压缩率只有jpg的2/3,大小比png小了45%。缺点是压缩的时间更久了,兼容性不好,目前谷歌和opera支持。

43、 style标签写在body后与body前有什么区别?

页面加载自上而下 当然是先加载样式。

写在body标签后由于浏览器以逐行方式对HTML文档进行解析,当解析到写在尾部的样式表(外联或写在style标签)会导致浏览器停止之前的渲染,等待加载且解析样式表完成之后重新渲染,在windows的IE下可能会出现FOUC现象(即样式失效导致的页面闪烁问题)

44、 CSS属性overflow属性定义溢出元素内容区的内容会如何处理?

参数是scroll时候,必会出现滚动条。

参数是auto时候,子元素内容大于父元素时出现滚动条。

参数是visible时候,溢出的内容出现在父元素之外。

参数是hidden时候,溢出隐藏。

45、 阐述一下CSS Sprites

将一个页面涉及到的所有图片都包含到一张大图中去,然后利用CSS的 background-image,background- repeat,background-position 的组合进行背景定位。利用CSS Sprites能很好地减少网页的http请求,从而大大的提高页面的性能;CSS Sprites能减少图片的字节。

情提示:以下代码是我们邀请的前端架构师基于天猫门户编写的css相关代码,现在免费分享给大家,获取的方式为:

关注此头条号“互联网IT信息”——>私信发送 “天猫css” ,(注意:css全是小写)即可得到源代码的获取方式。

1. 此篇文章讲解目录:

  • 案例和由此案例重点讲解的知识点介绍

  • 案例代码实现

  • css三大特性知识点详解

2. 案例和相关知识点介绍

此案例是页面,效果如下:

此页面的技术实现解析:

使用标签选择器定义通用样式,通过css层叠性和继承性来让通用标签的样式被继承到类选择器上

此案例中主要用到了,基于此,我们会系统的将如下知识点全部讲解:

css三大特性:层叠性、继承性、优先级

3. 此案例的代码实现

第一步:使用块级标签和无序列表编写导航侧边栏的html

第二步:通过标签选择器定义ul li a的通用样式

第三步:定义外层div样式

第四步:通过类选择器定义无序列表样式,其中li和a标签的基本样式继承自第二步的标签选择器

4. 此案例的知识点详解

CSS三大特性:

层叠性、继承性、优先级

1)层叠性:

1. 给一个标签设置的样式发生冲突的时候即样式的覆盖

2. 浏览器的渲染机制是从上到下的,当有冲突的时候就采用最后的那个样式

例如:

h2.grape {color: purple;}

h2 {color: siver;}

层叠性代码实例:

<!DOCTYPEhtml>

<htmllang="en">

<head>

<metacharset="UTF-8">

<title>Document</title>

<styletype="text/css">

.two{

color: green;

}

.one{

color: red;

font-size: 30px;

}

.tree{

color: yellow;

font-size: 40px;

}

</style>

</head>

<body>

<pclass="one two tree">

一段文字

</p>

</body>

</html>

2)继承性:继承就是子标签继承了上级标签的CSS样式的属性

1,发生的前提是:标签之间属于一种嵌套关系

2,文字颜色可以之间继承

3,字体大小可以继承

4,字体可以继承

5,行高也可以实现继承

6, 与文字有关的属性都可以,实现继承

特殊注意:

a标签超链接不能实现字体颜色的继承,字体大小可以继承

h1不可以继承文字的大小,继承过来还会做一个计算

继承性代码实例:

<!DOCTYPEhtml>

<html>

<head>

<metacharset="UTF-8">

<title></title>

<style>

div{

color: red;

font-size: 30px;

}

</style>

</head>

<body>

<div>

<ahref="">a</a>

</div>

</body>

</html>

3) 优先级

具体解释如下:

内联样式最大,内联样式的优先级最高。

ID选择器的优先级,仅次于内联样式。

类选择器优先级低于ID选择器

标签选择器低于类选择器。

补充:

权重相同时,CSS遵循就近原则。也就是说靠近元素的样式具有最大的优先级,或者说排在最后的样式优先级最大。

所有都相同时,声明靠后的优先级大。

CSS定义了一个!important命令,该命令被赋予最大的优先级。也就是说不管权重如何以及样式位置的远近,!important都具有最大优先级。

综述:

-行内样式 > 页内样式 > 外部引用样式 > 浏览器默认样式

important > 内联 > ID > 伪类|类 | 属性选择 > 标签 > 伪对象 > 通配符 > 继承

css优先级代码实例:

<!DOCTYPE>

<html>

<head>

<metahttp-equiv="Content-Type"content="text/html; charset=utf-8"/>

<title>无标题文档</title>

<style>

*{ font-size:56px;}

p{ font-size:32px; color:#60C;}

.d{ color:#F0F;}

#hei{ color:#96F;}

</style>

</head>

<body>

<h1>我是标题</h1>

<p>我是段落</p>

<p>我是段落2</p>

<pclass="d"id="hei"style="color:#FF0;">我是段落3</p>

<ahref="#">我是超链接</a>

<span>我是备胎标签</span>

</body>

</html>

大家好接下来我们会邀请前端架构师以连载的方式,并且结合阿里天猫商城的门户,系统讲解Css的专业知识,欢迎大家关注头条号“互联网IT信息”。