前端面试基础学习笔记

文章目录


课程资源:链接:https://pan.baidu.com/s/1X26XjeSeWLzRxo00f-M6iA 提取码:fa3o

1 HTML

01 如何理解 HTML 语义化?

让人更容易读懂(增加代码可读性)

让搜索引擎更容易读懂 (SEO)

02 默认情况下,哪些 HTML 标签是块级元素、哪些是内联元素?

display: block/table; 有 div h1 h2 table ul ol p 等

display: inline/inline-block; 有 span img input button 等

2 CSS

01 盒子模型的宽度如何计算?

在这里插入图片描述

offsetWidth = ( 内容宽度 + 内边距 + 边框 ),无外边距,答案是 122px

补充:如果让 offsetWidth 等于 100px ,该如何做? 加上:box-sizing = border-box

02 margin 纵向重叠问题

在这里插入图片描述
相邻元素的 margin-top 和 margin-bottom 会发生重叠;空白内容的也会重叠;答案:15px

03 margin 负值问题

对 margin 的 top left right bottom 设置负值,有何效果?

margin-top 和 margin-left 负值,元素向上、向左移动

margin-right 负值,右侧元素左移,自身不受影响

margin-bottom 负值,下方元素上移,自身不受影响

04 BFC 理解与应用

什么是 BFC?如何应用?

Block format context ,块级格式化上下文,一块独立渲染区域,内部元素的渲染不会影响边界以外的元素

形成 BFC 的常见条件:

float 不是 none;position 是 absolute 或 fixed;display 是 flex、inline-block ;overflow 不是 visible(常用)

BFC 的常见应用:清除浮动

05 如何实现圣杯布局和双飞翼布局

圣杯布局和双飞翼布局的目的

  1. 三栏布局,中间一栏最先加载和渲染(内容最重要)
  2. 两侧内容固定,中间内容随着宽度自适应
  3. 一般用于 PC 网页

圣杯布局和双飞翼布局的技术总结

使用 float 布局

两侧使用 margin 负值,以便和中间内容横向重叠

防止中间内容被两侧覆盖,一个用 padding 一个用 margin

实现步骤

  • 圣杯布局

    <div class="main col">main</div>
    <div class="left col">left</div>
    <div class="right col">right</div>
    
    /* 1.设置main left right:float布局 */
    float: left;
    /* 2.给父元素container设置左右padding */
    padding-left: 100px;
    padding-right: 150px;
    /* 3.left往左移100%,上移到main中最左侧,覆盖main */
    margin-left: -100%;
    /* 4.left再往左移自身宽度,注意要设置当前元素定位为relative,使其相对自身移动 */
    position: relative;
    right: 100px	
    /* 5.让right往左移自身宽度 */
    margin-right: -150px;
    
  • 双飞翼布局

    <div class="main col">
       <div class="main-content">main</div>
    </div>
    <div class="left col">left</div>
    <div class="right col">right</div>
    
    /* 1.设置main left right:float布局 */
    float: left;
    /* 2. 在main中加一个div,对其设置margin,留出left right的位置*/
    margin: 0 150px 0 100px;
    /* 3.left左移100%,使其上移并到main左侧 */
    margin-left: -100%;
    /* 4.right左移自身宽度 */
    margin-left: -150px;
    

    多一个div,少相对布局

  • 补充:flex布局

    <div class="main">main</div>
    <div class="left">left</div>
    <div class="right">right</div>
    
    /* 1.将container设置为flex容器 */
    display: flex;
    /* 2.通过order设置排列顺序,left在第一位 order默认为0,越小越靠前 */
    order: -1;
    /* 3.设置main的方法比例,将剩余空间用main填充 */
    flex-grow: 1;
    /* 4.设置left的固定宽度 */
    flex-basis: 100px;
    /* 5.设置right的固定宽度 */
    flex-basis: 100px;
    

完整代码

  • 圣杯布局
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>圣杯布局</title>
	<style>
		.container {
			/* 2.给父元素设置左右padding */
			padding-left: 100px;
			padding-right: 150px;
		}
		.col {
			/* 1.设置float布局 */
            float: left;
        }
		.main {
			background-color: skyblue;
			width: 100%;
		}
		.left {
            position: relative;
			background-color: yellow;
			width: 100px;
			margin-left: -100%; /* 3.left往左移100%,上移到main内最左侧,覆盖main */
            right: 100px /* 4.再往左移自身宽度,注意要设置当前元素定位为relative,使其相对自身移动 */
		}
		.right {
			background-color: pink;
			width: 150px;
			margin-right: -150px; /* 5.让right往左移自身宽度 */
		}
	</style>
</head>
<body>
<div class="container">
	<div class="main col">main</div>
	<div class="left col">left</div>
	<div class="right col">right</div>
</div>
</body>
</html>
  • 双飞翼布局

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>双飞翼布局</title>
    	<style>
    		.col {
    			/* 1.设置float布局 */
                float: left;
            }
    		.main {
    			background-color: skyblue;
    			width: 100%;
    		}
    		.main-content {
    			/* 2. 在main中加一个div,对其设置margin,留出left right的位置*/
                margin: 0 150px 0 100px;
    		}
    		.left {
    			background-color: yellow;
    			width: 100px;
    			/* 3.left左移100%,使其上移并到main左侧 */
    			margin-left: -100%;
    		}
    		.right {
    			background-color: pink;
    			width: 150px;
    			/* 4.right左移自身宽度 */
                margin-left: -150px;
    		}
    	</style>
    </head>
    <body>
    <div class="container">
    	<div class="main col">
    		<div class="main-content">main</div>
    	</div>
    	<div class="left col">left</div>
    	<div class="right col">right</div>
    </div>
    </body>
    </html>
    
  • flex布局

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>双飞翼布局</title>
    	<style>
    		.col {
    			/* 1.设置float布局 */
                float: left;
            }
    		.main {
    			background-color: skyblue;
    			width: 100%;
    		}
    		.main-content {
    			/* 2. 在main中加一个div,对其设置margin,留出left right的位置*/
                margin: 0 150px 0 100px;
    		}
    		.left {
    			background-color: yellow;
    			width: 100px;
    			/* 3.left左移100%,使其上移并到main左侧 */
    			margin-left: -100%;
    		}
    		.right {
    			background-color: pink;
    			width: 150px;
    			/* 4.right左移自身宽度 */
                margin-left: -150px;
    		}
    	</style>
    </head>
    <body>
    <div class="container">
    	<div class="main col">
    		<div class="main-content">main</div>
    	</div>
    	<div class="left col">left</div>
    	<div class="right col">right</div>
    </div>
    </body>
    </html>
    
06 手写 clearfix
.clearfix::before,
.clearfix::after {
	content: '';
	display: table;
	clear: both;
}
.clearfix {
    *zoom: 1; /* 兼容IE低版本 */
}
07 flex 实现一个三点的色子

语法回顾:

flex-direction:弹性元素的排列方式 (row column row-reverse column-reverse)

justify-content:如何分配主轴空白空间/对齐 (center flex-start flex-end space-around space-between)

flex-flow:是否自动换行 (wrap nowrap wrap-reverse)

align-item:元素在辅轴上如何对齐/垂直对齐 (center flex-start flex-end stretch baseline )

align-self:用来覆盖当前弹性元素上的align-items

flex-grow:增长

flex-shrink:收缩

flex-basis:初始长度

order:顺序 (默认为0,值越小越靠前)

flex:flex-grow、flex-shrink 以及 flex-basis 属性的简写属性

在这里插入图片描述

08 absolute 和 relative 分别依据什么定位?
  1. relative 依据自身定位

  2. absolute 依据 最近的定位元素 定位

定位元素:absolute relative fixed body

09 居中对齐有哪些实现方式?***

水平居中

  1. inline 元素:text-align: center

  2. block 元素:margin: auto

  3. absolute 元素:left: 50% + margin-left 负值

    left:50%; 				 往右移50%
    margin-left: -1/2width;  再往左移自身宽度的一半
    

垂直居中

  1. inline 元素:line-height = height 值

  2. absolute 元素:top: 50% + margin-top 负值

    top:50%; 				
    margin-top: -1/2height; 
    注意:必须知道子元素的高度
    
  3. absolute 元素: transform(-50%, -50%) 垂直水平居中

    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    老浏览器兼容有问题
    
  4. absolute 元素:top, left, bottom, right = 0 + margin: auto

    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    既没有兼容性问题,也可以不知道子元素宽高
    

注意:2必须知道子元素宽高,3 4可以不知道

10 line-height 如何继承

在这里插入图片描述

答案:40px

  1. 写具体数值,如 30px ,则继承该值

  2. 写比例,如 2 / 1.5 ,则继承该比例

  3. 写百分比,如 200% ,则继承计算出来的值(考点)20px * 200% = 40px

11 响应式 - rem 是什么?

rem 是一个长度单位

  1. px ,绝对长度单位,最常用
  2. em ,相对长度单位,相对于父元素,不常用
  3. rem ,相对长度单位,相对于根元素,常用于响应式布局
html {
	font-size: 100px;
}
div {
	font-size: .16rem; /* 0.16rem*100px=16px */
}

vw/vh

  1. 网页视口尺寸

    window.screen.height:屏幕高度

    window.innerHeight:网页视口高度

    document.body.clientHeight:body高度

  2. vw/vh

    1vh表示网页视口高度的1/100

    1vw表示网页视口宽度的1/100

    vmax表示取vh和vw中的最大值;vmin表示取vh和vw中的最小值

12 响应式布局的常见方案?
  1. media-query,根据不同的屏幕宽度设置根元素 font-size

  2. rem,基于根元素的相对单位

/* media-query举例 */
@media only screen and (max-width: 374px) {
    /* iphone5 或者更小的尺寸,以 iphone5 的宽度(320px)比例设置 font-size */
    html {
        font-size: 86px;
    }
}
@media only screen and (min-width: 375px) and (max-width: 413px) {
    /* iphone6/7/8 和 iphone x */
    html {
        font-size: 100px;
    }
}
@media only screen and (min-width: 414px) {
    /* iphone6p 或者更大的尺寸,以 iphone6p 的宽度(414px)比例设置 font-size */
    html {
        font-size: 110px;
    }
}

3 JS

面试题 -> 第一时间看考点

3.1 变量类型和计算

知识点:

1. 变量类型:值类型 & 引用类型

在这里插入图片描述
​ 值类型和引用类型的存储是分开的

2. typeof运算符

识别所有值类型、识别函数、判断是否是引用类型(不可再细分)

在这里插入图片描述

3. 深拷贝

  /**
   * 深拷贝
   * @param {object} obj
   */
  function deepClone(obj = {}) {
    // obj是null,或不是对象和数组,即是基本数据类型,直接返回
    if (typeof obj !== 'object' || obj == null) {
      return obj
    }
    // 初始化返回结果
    let res
    if (obj instanceof Array) {
      res = []
    } else {
      res = {}
    }
    for (let key in obj) {
      // 保证key不是原型属性
      if (obj.hasOwnProperty(key)) {
        // 递归调用
        res[key] = deepClone(obj[key])
      }
    }
    return res
  }

4. 变量计算-类型转换

字符串拼接、==、if语句和逻辑运算

在这里插入图片描述
if语句和逻辑运算:

  • truly变量:经过两步非运算为true的变量
  • falsely变量:经过两步非运算为false的变量

在这里插入图片描述

01 typeof能判断哪些类型
  1. 识别所有值类型

  2. 识别函数

  3. 判断是否为引用类型

02 何时使用 === / ==

除了 == null 之外,其他都一律用 ===

03 值类型和引用类型的区别

值类型占用内存小,所以存的是值;引用类型占用内存大,所以存的是内存地址。

在这里插入图片描述

04 手写深拷贝

见知识点

3.2 原型和原型链

知识点:

1. class和继承

class:constructor、属性、方法

继承:extends、super

class People {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log('People eat')
  }
}
class Student extends People {
  constructor(name, number) {
    super(name);
    this.number = number;
  }
    sayHi() {
    console.log(
      `姓名 ${this.name},学号 ${this.number}`
    )
  }
  eat() {
    super.eat();
  }
}

2. 类型判断 instanceof

[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
  • instanceof可以判断引用类型

  • Object是所有类的父类

3. 原型和原型链

原型

在这里插入图片描述
原型关系:

  • 每个class都有显式原型 prototype
  • 每个实例都有隐式原型 __proto__
  • 实例的__proto__指向class的 prototype

基于原型的执行规则:

  • 获取属性或执行方法时,先找自身的属性和方法,找不到再自动去 __proto__中找

原型链
在这里插入图片描述

关键语句

  • 函数可以产生对象,所有对象都是靠函数产生的 new 函数()
  • 函数本身也是一个对象,由new Function() 创建
  • 所有函数都有显式原型 prototype,默认指向一个空的object对象
  • 所有对象都有隐式原型__proto__,指向其构造函数的显式原型prototype
  • 在查找对象的属性和方式时,现在自身查找,没有就去__proto__中找
  • 特殊:所有Function的__proto__指向Function的显式原型prototype,即Function = new Function fn.__proto__ === Function.prototype;Object的__proto__指向null

instanceof

判断实例对象__proto__与其构造函数的prototype是否是同一个引用,只要在其原型链上就返回true

05 如何判断一个变量是不是数组

使用instanceof进行判断,typeof是无法判断数组的。在ES6中新增了Array.isArray方法进行判断。

06 手写一个简易的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)
    })
  }
}

// const $p = new jQuery('p')
// $p.get(1)
// $p.each(elem => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))

// 插件
jQuery.prototype.dialog = function (info) {
  alert(info)
}
// "造轮子"
class myJQuery extends jQuery {
  constructor(selector) {
    super(selector);
  }
  // 扩展自己的方法
  addClass(className) {
    
  }
}
07 class的原型本质,怎么理解

原型和原型链图示+属性和方法的执行规则

在class中 ,执行时会先在自身的属性和方法上找,找不到会顺着原型去它的上一级的原型上去找

3.3 作用域和闭包

知识点

1. 作用域和自由变量

  • 作用域:全局作用域、函数作用域、块级作用域(ES6)

  • 自由变量:

    一个变量在当前作用域中没有定义但被使用了

    向上级作用域中一层层依次寻找,直到找到

    如果全局作用域中都没找到,则报错undefined

在这里插入图片描述

2. 闭包

闭包中,所有自由变量的查找,是在函数定义的地方,向上级作用域查找,不是函数执行的地方

在这里插入图片描述

结果:100;100

3. this

this取值是在函数执行时确定的,不是定义时确定的

  • 普通函数调用:全局对象window

  • 使用call apply bind:第一个参数(为空则默认指向window)

  • 作为对象方法被调用:对象.函数名():当前对象

  • 构造函数调用:new 函数():新创建的对象;

  • 箭头函数:永远指向上级作用域的this

在这里插入图片描述

08 this的不同应用场景如何取值

见知识点,5种场景

09 手写bind函数
// 模拟bind
Function.prototype.bind1 = function () {
  // 将参数拆解为数组
  const args = Array.prototype.slice.call(arguments)
  // 获取this(数组第一项)
  const t = args.shift() // shift获取数组第一项,且修改数组本身
  // this:fn1.bind()中的fn1
  const self = this

  // 返回一个函数
  return function () {
    return self.apply(t, args)
  }
}

// 测试
function fn1(a, b, c) {
  console.log('this', this)
  console.log(a, b, c)
  return 'fn1'
}
const fn2 = fn1.bind1({x: 100}, 10, 20, 30)
const res = fn2()
console.log(res)
// 结果
// this {x: 100}
// 10 20 30
// fn1
10 实际开发中闭包的应用场景,举例说明

隐藏数据,eg.做题个简单的cache工具

// 闭包影藏数据,只提供API
function createCache() {
  const data = {} // 闭包中的数据,被隐藏,不被外界访问
  return {
    set: function (key, val) {
      data[key] = val
    },
    get: function(key) {
      return data[key]
    }
  }
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
11 创建10个<a>标签,点击时弹出对应序号
let a
for (let i = 0; i < 10; i++) { // 将i作为块级作用域
  a = document.createElement('a')
  a.innerHTML = i + '<br>'
  a.addEventListener('click', e => {
    e.preventDefault()
    alert(i)
  })
  document.body.appendChild(a)
}

3.4 异步和单线程

知识点

1. 异步和单线程

  • JS是单线程语言,同时只能做一件事

  • 浏览器和nodejs以支持JS启动进程,如Web Worker

  • JS和DOM渲染共用同一个线程,因为JS可修改DOM结果

  • 遇到等待(网络请求、定时任务)不能卡住,需要异步

  • 异步是基于callback函数形式

在这里插入图片描述

2. 应用场景

在这里插入图片描述

  1. callback hell Promise

在这里插入图片描述
在这里插入图片描述

12 同步和异步的区别是什么

异步是JS单线程,异步不会阻塞代码执行,同步会阻塞

13 手写用Promise加载一张图片
// Promise
function loadImg(src) {
  return new Promise((resolve, reject) => {
    const img = document.createElement('img')
    img.onload = () => {
      resolve(img)
    }
    img.onerror = () => {
      const err = new Error(`图片加载失败 ${src}`)
      reject(err)
    }
    img.src = src
  })
}
// 单张
// const url = 'http://www.dell-lee.com/imgs/vue3/tomato.png'
// loadImg(url).then(img => {
//   console.log(img.width)
//   return img
// }).catch(err => console.log(err))

// 多张 
// 使用Promise解决 回调地狱
const url1 = 'http://www.dell-lee.com/imgs/vue3/tomato.png'
const url2 = 'http://www.dell-lee.com/imgs/vue3/cherry.png'
loadImg(url1).then(img1 => {
  console.log(img1.width)
  return loadImg(url2) // return的是Promise实例
}).then(img2 => {
  console.log(img2.width)
}).catch(err => console.log(err))
14 前端使用异步的场景有哪些
  1. 网络请求,eg. ajax图片加载
  2. 定时任务,eg. setTimeout
15 setTimeOut

在这里插入图片描述

输出顺序:1 3 5 4 2

3.5 JS异步-进阶

知识点

1. event loop(事件循环、事件轮询)

  • JS是单线程运行的
  • 异步要基于回调来实现
  • event loop就是异步回调的实现原理

JS如何执行?

  • 从前到后,一行一行执行
  • 如果某一行执行报错,则停止下面代码的执行
  • 先把同步代码执行,再执行异步

event loop过程

在这里插入图片描述

  • Browser console:打印输出
  • Call Stack:真正触发执行某行代码
  • callback queue 回调函数的队列
  • Web APIs:处理定时或异步的API
  • Event Loop:同步代码执行完毕时触发

执行详细过程:

  1. 现将第一行代码console.log(‘Hi’)放入调用栈Call Stack,执行完后再Browser console打印输出,最后清空调用栈

  2. 执行setTimeout函数,将setTimeout函数放入调用栈Call Stack,setTimeout函数是由浏览器定义的,也就是Web APIs

在setTimeout中,把第一个参数cb1放到定时器timer中,设置定时5s,放入Web APIs, 5s之后再将cb1放入Callback Queue

执行完成之后,清空Call Stack调用栈,Web APIs不变

  1. 执行最后一行,与第一行一样。此时Web APIs中cb1依然存在,因为5s定时还没结束,执行最后一行只需要几毫秒

  2. 此时所有代码执行完毕,不会再有内容进入Call Stack,即同步代码执行完成,启动Event Loop

    Event Loop不断循环,等待获取Callback Queue中的内容

    当定时的5s结束之后,cb1从Web APIs进入Callback Queue

    此时Event Loop获取到cb1,则立即将cb1放入Call Stack,清空Callback Queue

    Call Stack执行cb1函数,在Browser console打印输出,最后清空调用栈

总结过程:

  • 同步代码,一行一行放在Call Stack执行
  • 遇到异步,会先记录下,等待时机(定时、网络请求)
  • 时机到了,移动到Callback Queue
  • 如果Call Stack为空(即同步代码执行完成)Event Loop开始工作
  • 轮询查找Callback Queue,如有则移动到Call Stack执行,然后继续轮询查找
  • 异步(setTimeout、ajax)DOM事件都是使用回调,基于event loop实现的

2. promise 进阶

  • 三种状态

    • pending -> resolved 或 pending -> rejected 变化不可逆

      // 1. pending
      const p1 = new Promise((resolve, reject) => {})
      console.log('p1', p1) // pending
      
      // 2.resolved
      const p2 = new Promise((resolve, reject) => {
        setTimeout(() => { resolve() })
      })
      console.log('p2', p2) // pending
      setTimeout(() => console.log('p2-setTimeout', p2)) // resolved
      
      // 3.rejected
      const p3 = new Promise((resolve, reject) => {
        setTimeout(() => { reject() })
      })
      console.log('p3', p3)
      setTimeout(() => console.log('p3-setTimeout', p3)) // rejected
      
  • 状态的变化和表现

    • pending不会触发then、catch

    • resolved会触发then

    • rejected会触发catch

  • then catch对状态的影响

    • then正常执行返回resolved,有报错返回rejected

    • catch正常执行返回resolved,有报错返回rejected

    • then catch 会继续返回 Promise ,此时可能会发生状态变化!!!

      // 1. then正常执行返回resolved
      const p1 = Promise.resolve().then(() => { return 100 })
      console.log('p1', p1) // resolved
      
      // 2.then有报错返回rejected
      const p2 = Promise.resolve().then(() => {
        throw new Error('err')
      })
      console.log('p2', p2) // rejected
      
      // 3. catch正常执行返回resolved
      const p3 = Promise.reject('my error').catch(
        error => { console.log(error)
        })
      console.log('p3', p3) // resolved
      p3.then(() => { // 注意 resolved触发then回调
        console.log('p3 then ') // p3 then
      })
      
      // 4.catch有报错返回rejected
      const p4 = Promise.reject('my error').catch(error => {
        throw new Error('catch err')
      })
      console.log('p4', p4) // rejected 
      // rejected会触发catch
      p4.then(() => {
        console.log('p4 then')
      }).catch(() => {
        console.log('p4 catch') // p4 catch,此处then正常执行,返回一个resolved的Promise
      })
      
  • then catch的链式调用(常考)

    • 3个面试题

      // Promise.resolve()返回一个resolved的Promise,会触发then,不会触发catch
      Promise.resolve().then(() => { 
          console.log(1) 
      }).catch(() => {
          console.log(2)
      }).then(() => { // 前面的then正常执行,返回resolved的Promise,会触发then
          console.log(3)
      })
      // 结果: 1 3
      
      // Promise.resolve()返回一个resolved的Promise,会触发then
      Promise.resolve().then(() => { 
          console.log(1) 
          throw new Error('erro1') // then有报错,返回rejected的Promise,会触发catch
      }).catch(() => { // catch正常执行,返回resolved的Promise,会触发then
          console.log(2)
      }).then(() => {
          console.log(3)
      })
      // 结果:1 2 3
      
      // Promise.resolve()返回一个resolved的Promise,会触发then
      Promise.resolve().then(() => { 
          console.log(1)
          throw new Error('erro1') // then有报错,返回rejected的Promise,会触发catch
      }).catch(() => { // catch正常执行,返回resolved的Promise,不会触发catch
          console.log(2)
      }).catch(() => {
          console.log(3)
      })
      // 结果:1 2
      

3. async、await

  • 语法介绍

    • 异步回调 callback hell

    • Promise then catch链式调用,但也是基于回调函数

    • async/await用同步的语法编写异步代码,彻底消灭回调函数

      function loadImg(src) {
        return new Promise((resolve, reject) => {
          const img = document.createElement('img')
          img.onload = () => {
            resolve(img)
          }
          img.onerror = () => {
            reject(new Error(`图片加载失败 ${src}`))
          }
          img.src = src
        })
      }
      
      async function loadImg1() {
        const src1 = 'http://www.imooc.com/static/img/index/logo_new.png'
        return await loadImg(src1)
      }
      
      async function loadImg2() {
        const src2 = 'https://avatars3.githubusercontent.com/u/9583120'
        return await loadImg(src2)
      }
      
      // 匿名函数:await要执行,需要用一个匿名函数包裹
      // 同步的写法,异步的代码
      (async function () {
        const img1 = await loadImg1()
        const img2 = await loadImg2()
        console.log(img1.width, img2.width)
      })()
      
  • 和Promise的关系

    • async/await是消灭异步回调的终极武器,但和Promise并不互斥,反而两者相辅相成

      • 执行async函数,返回的是Promise对象

      • await相当于Promise的then

      • try…catch可捕获异常,代替了Promise的catch

        // 1.执行async函数,返回的是一个Promise对象
        // 如果返回的是一个值,会封装成Promise对象返回
        async function fn1() {
          return 100
        }
        // const res1 = fn1()
        // console.log('res1', res1) // Promise对象,resolved状态,可以触发then
        // res1.then(data => {
        //   console.log('data', data) // 100
        // })
        
        // 2.await相当于Promise then
        !(async function () {
          const p1 = Promise.resolve(300)
          const data = await p1
          console.log('data', data) // 300
        })()
        
        !(async function () {
          const data1 = await 400 // await Promise.resolve(400) 将值封装成Promise
          console.log('data1', data1) // 400
        })()
        
        !(async function () {
          const data2 = fn1()
          console.log('data2', data2) // 100
        })()
        
        // 3.try...catch相当于Promise的catch
        !(async function () {
          const p3 = Promise.reject('err') // rejected状态
          try {
            const res = await p3
            console.log('res', res)
          } catch (e) {
            console.log(e)
          }
        })()
        
        // 注意:常考
        !(async function () {
          const p4 = Promise.reject('err1') // rejected状态
          const res = await p4 // await->then,此处不是catch,报错 => 解决办法:上面的try catch
          console.log('res', res)
        })()
        
    • 总结

      • async 封装 Promise
      • await 处理 Promise 成功
      • try…catch 处理 Promise 失败
  • 异步的本质

    • async/await是消灭异步回调的终极武器

    • JS还是单线程,还是有异步,还是基于event loop

    • async/await只是一个语法糖,但是糖很香!

      async function async1 () {
        console.log('async1 start') // 2 执行async函数时,要立即执行函数的 (重要)
        await async2() 
        // 关键在这一步,await后面,都可以看作是callback中的内容,最后执行,即异步
        // 类似:event loop, setTimeout(cb1)
        // eg1. setTimeout (function () { console.log('async1 end')  })
        // eg2. Promise.resolve().then(() => { console.log('async1 end')  }) // 微任务、宏任务
        console.log('async1 end') // 5
      }
      
      async function async2 () {
        console.log('async2') // 3 (重要)
      }
      
      console.log('script start') // 1
      async1()
      console.log('script end') // 4 同步代码执行完毕
      
  • for…of

    • for…in(以及forEach 、for)是常规的同步遍历

    • for…of 常用于异步的遍历

      function muti(num) {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve(num * num)
          }, 1000)
        })
      }
      const nums = [1, 2, 3]
      // 同步
      // nums.forEach(async i => {
      //   const res = await muti(i)
      //   console.log(res) // 1s之后,1 4 9同时出来
      // })
      
      // 异步
      // await需要async的匿名函数包裹
      !(async function() {
        for (let i of nums) {
          const res = await muti(i)
          console.log(res) // 每隔1s输出1 4 9
        }
      })()
      

async/await总结

  • 解决了异步回调,是一个很香的语法糖
  • async/await和Promise的关系 重要
  • 异步的本质
  • for…of的使用

4. 微任务、宏任务

  • 什么是宏任务macroTask 什么是微任务microTask

    • 宏任务:setTimeout,setInterval,Ajax,DOM事件

    • 微任务:Promise async/await

    • 微任务执行时机比宏任务要早

      console.log(100)
      // 宏任务
      setTimeout(() => {
        console.log(200)
      })
      // 微任务
      Promise.resolve().then(() => {
        console.log(300)
      })
      console.log(400)
      // 结果: 100 400 300 200
      
  • event loop和DOM渲染

    • JS是单线程的,和DOM渲染共用一个线程

    • JS执行的时候,需要留一些时机供DOM渲染

      event loop补充环节

      • 每次Call Stack空闲后,即每次轮询结束,即同步任务执行完成
      • 需要尝试DOM渲染,DOM结构如果改变则重新渲染
      • 之后再触发下一次轮询event loop
        在这里插入图片描述
  • 微任务和宏任务的区别

    • 宏任务:DOM渲染后触发,如setTimeout

    • 微任务:DOM渲染前触发,如Promise

      const $p1 = $('<p>一段文字</p>')
      const $p2 = $('<p>一段文字</p>')
      const $p3 = $('<p>一段文字</p>')
      $('#container')
        .append($p1)
        .append($p2)
        .append($p3)
      
      console.log('length', $('container').children().length) // 3
      // alert('本次 call stack结束,DOM结果已更新,但尚未触发渲染')
      // alert会阻断js执行,也会阻断DOM渲染,便于查看效果
      
      // 微任务:DOM渲染前触发
      Promise.resolve().then(() => {
        console.log('length1', $('container').children().length) // 3
        alert('Promise then')  // DOM没有渲染
      })
      // 宏任务:DOM渲染后触发
      setTimeout(() => {
        console.log('length2', $('container').children().length) // 3
        alert('setTimeout') // DOM渲染了
      })
      
    • 从event loop解释,为什么微任务执行更早?

      • 执行setTimeout时,先将Call Stack中的setTimeout放入Web APIs中,等待时机,时机到了再放入Callback Queue中,有event loop机制在回到Call Stack中执行。
        • Event Loop触发之前,会有2步操作:1.等待Call Stack空闲,2.DOM渲染
      • 执行Promise或async/await时,先等待时机,再将其放入micro task queue,不会放入Web APIs
        • 微任务是ES6语法规定的,宏任务是浏览器规定的

      xxxx`

宏任务、微任务总结:

  • 宏任务有哪些?微任务有哪些》微任务触发时机更早 (重点)
  • 微任务、宏任务、DOM渲染的关系,以及在event loop的过程
16 描述 event loop 运行机制(可画图)
  • 回顾event loop过程
  • 和DOM渲染的关系
  • 微任务、宏任务在event loop过程中的不同处理
17 宏任务和微任务的区别
  • 宏任务:setTimeout,setInterval,Ajax,DOM事件
  • 微任务:Promise async/await
  • 微任务执行时机比宏任务要早
18 Promise 哪几种状态,如何变化?
  • pending -> resolved 或 pending -> rejected 变化不可逆

  • 状态的变化和表现

    • pending不会触发then、catch

    • resolved会触发then

    • rejected会触发catch

    • then catch对状态的影响

      • then正常执行返回resolved,有报错返回rejected

      • catch正常执行返回resolved,有报错返回rejected

      • then catch 会继续返回 Promise ,此时可能会发生状态变化!!!

19 场景题:Promise catch 连接 then

见知识点中,then catch的链式调用的3个面试题

20 场景题:async/await语法
async function fn() {
  return 100
}
!(async function () {
  const a = fn() // ??
  const b = await fn() // ??
})()
  • 执行async函数,返回的是Promise对象,所有a是一个Promise对象

  • await是Promise的then,所以b是100

  !(async function () {
    console.log('start')
    const a = await 100
    console.log('a', a)
    const b = await Promise.resolve(200)
    console.log('b', b)
    const c = await Promise.reject(200)
    console.log('c', c)
    console.log('end')
  })()
  // 执行完毕,打印哪些内容 ?
  • 结果:start 100 200
  • await是Promise的then,当c的await后面是rejected时,其没有then,所有后面都不会执行
21 场景题:Promise 和 setTimeout 顺序
console.log(100)
// 宏任务
setTimeout(() => {
    console.log(200)
})
// 微任务
Promise.resolve().then(() => {
    console.log(300)
})
console.log(400)
// 结果: 100 400 300 200
22 场景题:各类异步执行顺序问题
async function async1() {
  console.log('async1 start') // 2
  await async2()
  // await后面都作为回调内容 —— 微任务
  console.log('async2 end') // 6
}
async function async2() {
  console.log('async2') // 3
}

console.log('script start') // 1
setTimeout(() => { // 宏任务 setTimeout
  console.log('setTimeout') // 8
}, 0)

async1()

// 初始化Promise时,传入的函数会立刻执行
new Promise(resolve => {
  console.log('promsie1') // 4
  resolve()
}).then(() => { // 微任务
  console.log('promsie2') // 7
})
console.log('script end') // 5
// 同步代码执行完毕 event loop - call stack被清空
// 接下来执行微任务
// 尝试触发DOM渲染
// 触发 Event Loop,执行宏任务

4 JS Web API

JS基础知识:变量的类型和计算、原型和原型链、作用域和闭包

JS Web API:DOM、BOM、事件绑定、ajax、存储

4.1 DOM

Document Object Model

知识点

1. DOM本质

  • 从html文件解析出来的树

2. DOM节点操作

  • 获取DOM节点

  • attribute:修改html属性,会改变html结构

  • property:修改对象属性,不会体现到html结构中 (推荐)

  • attribute、property都可能引起DOM重新渲染

    // 1. 获取DOM节点
    const div1 = document.getElementById('div1') // 元素
    const divList = document.getElementsByTagName('div') // 集合
    console.log('divList.length', divList.length)
    console.log('divList[1]', divList[1])
    
    // 2. property 形式
    const pList = document.getElementsByTagName('p')
    const p1 = pList[0]
    p1.style.width = '100px'
    console.log( p1.style.width )
    p1.className = 'red'
    console.log( p1.className )
    console.log(p1.nodeName)
    console.log(p1.nodeType) // 1
    
    // 3. attribute
    p1.setAttribute('data-name', 'imooc')
    console.log( p1.getAttribute('data-name') )
    p1.setAttribute('style', 'font-size: 50px;')
    console.log( p1.getAttribute('style') )
    

3. DOM结构操作

  • 新增、插入节点

  • 获取子元素列表,获取父元素

  • 删除子元素

    const div1 = document.getElementById('div1')
    const div2 = document.getElementById('div2')
    
    // 新建节点
    const newP = document.createElement('p')
    newP.innerHTML = 'this is newP'
    // 插入节点
    div1.appendChild(newP)
    // 移动节点
    const p1 = document.getElementById('p1')
    div2.appendChild(p1)
    // 获取父元素
    console.log( p1.parentNode )
    // 获取子元素列表
    const div1ChildNodes = div1.childNodes
    console.log( div1.childNodes )
    const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
        if (child.nodeType === 1) {
            return true
        }
        return false
    })
    console.log('div1ChildNodesP', div1ChildNodesP)
    // 删除节点
    div1.removeChild( div1ChildNodesP[0] )
    

4. DOM性能

  • 避免频繁操作DOM

  • 对DOM查询做缓存

    // 不缓存DOM查询结果
    for (let i = 0; i < document.getElementsByTagName('p').length; i ++) {
      // 每次徐娜换,都会计算length,频繁进行DOM查询
    }
    
    // 缓存DOM查询结果
    const pList = document.getElementsByTagName('p')
    const length = pList.length
    for (let i = 0; i < length; i ++) {
      // 缓存 length,只进行一次DOM查询
    }
    
  • 将频繁操作改为一次性操作

    const list = document.getElementById('list')
    
    // 创建一个文档片段,此时还没有插入到 DOM 结构中
    const frag = document.createDocumentFragment()
    // 执行插入
    for (let i  = 0; i < 20; i++) {
        const li = document.createElement('li')
        li.innerHTML = `List item ${i}`
        // 先插入文档片段中
        frag.appendChild(li)
    }
    // 都完成之后,再统一插入到 DOM 结构中
    list.appendChild(frag)
    
23 DOM是哪种数据结构
  • 树 (DOM树)
24 DOM操作的常用API
  • DOM节点操作
  • DOM结构操作
  • attr & property
25 attr和property的区别
  • attribute:修改html属性,会改变html结构

  • property:修改对象属性,不会体现到html结构中 (推荐)

  • attribute、property都可能引起DOM重新渲染

26 一次性插入多个DOM节点,考虑性能
// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment()
// 执行插入
for (let i  = 0; i < 20; i++) {
    const li = document.createElement('li')
    li.innerHTML = `List item ${i}`
    // 先插入文档片段中
    frag.appendChild(li)
}
// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag)

4.2 BOM

Browser Object Model

知识点

  • navigator
  • screen
  • location
  • history
// navigator
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome)
// screen
console.log(screen.width)
console.log(screen.height)
// location
console.log(location.href) //网址
console.log(location.protocol) //协议
console.log(location.host) //域名
console.log(location.search) //取网址中的参数,一般是?后面的符号
// history
history.back() //后退
history.forward() //前进
27 如何识别浏览器的类型
28 分析拆解url各个部分

4.3 事件

1. 事件绑定

// 通用的事件绑定函数
function bindEvent(elem, type, fn) {
  elem.addEventListener(type, fn);
}

// 事件绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', event => {
  console.log(event.target) // 获取触发的元素
  event.preventDefault(); // 阻止默认行为
  alert('click')
})

2. 事件冒泡

<div id="div1">
   <p id="p1">激活</p>
   <p id="p2">取消</p>
   <p id="p3">取消</p>
   <p id="p4">取消</p>
</div>
<div id="div2">
   <p id="p5">取消</p>
   <p id="p6">取消</p>
</div>
// 事件冒泡
const p1 = document.getElementById('p1')
bindEvent(p1, 'click', event => {
  event.stopPropagation(); // 阻止冒泡
  console.log('激活')
})

const body = document.body
bindEvent(body, 'click', event => {
  console.log('取消')
})

3. 事件代理

  • 基于冒泡机制

  • 代码简洁

  • 减少浏览器内存占用

  • 不要滥用

    // 事件代理
    const div3 = document.getElementById('div3')
    bindEvent(div3, 'click', event => {
      event.preventDefault() // 阻止默认行为
      const target = event.target
      if (target.nodeName === 'A') {
        alert(target.innerHTML)
      }
    })
    
29 编写一个通用的事件监听函数
// 更完整的写法
function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector
    selector = null
  }
  elem.addEventListener(type, event => {
    const target = event.target
    if (selector) {
      // 代理绑定
      if (target.matches(selector)) {
        fn.call(target, event)
      }
    } else {
      // 普通绑定
      fn.call(target, event)
    }
  })
}

// 普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', function (event) {
  // console.log(event.target) // 获取触发的元素
  event.preventDefault() // 阻止默认行为
  alert(this.innerHTML)
})

// 代理绑定
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function (event) {
  event.preventDefault()
  alert(this.innerHTML)
})

注意:使用到this,注意不要用箭头函数

30 描述事件冒泡的流程
  • 基于DOM树形结构
  • 事件会顺着触发元素往上冒泡
  • 应用场景:代理
31 无线下拉的图片列表,如何监听每个图片的点击?
  • 事件代理
  • 用e.target 获取触发元素
  • 用matches来判断是否触发元素

4.4 Ajax

Ajax:Asynchronous Javascript And XML ,异步 JavaScript 和 XML,是指一种用于创建快速动态网页的技术。

在无需重新加载整个网页的情况下,能够更新部分网页的技术

知识点

1. XMLHttpRequest

// get请求
const xhr = new XMLHttpRequest()
xhr.open('GET', './test.json', true) // true:异步 false:童女不
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(JSON.parse(xhr.responseText))
    } else {
      console.log('其他')
    }
  }
}
xhr.send(null)


// post请求
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(JSON.parse(xhr.responseText))
    }
  }
}
const postData = {
  username: 'mys',
  password: '111'
}
xhr.send(JSON.stringify(postData))

2. 状态码

  • xhr.readyState

    • 0:未初始化,还没有调用send()方法

    • 1:载入,已调用send()方法,正在发送请求

    • 2:载入完成,send()方法执行完成,已经接收到全部响应内容

    • 3:交互,正在解析响应内容

    • 4:完成,响应内容解析完成,可以在客户端调用

  • xhr.status

    • 2xx:表示成功处理请求
      • 200: 请求成功
    • 3xx:需要重定向,浏览器直接跳转
      • 301:永久重定向
      • 302:临时重定向
      • 304:Not Modified,未修改过
    • 4xx:客户端请求错误
      • 401:未授权
      • 403:没有访问权限
      • 404: 没有对应资源
    • 5xx:服务端错误
      • 501:

3. 跨域:同源策略,跨域解决方案

  • 什么是跨域(同源策略)

    • ajax请求时,浏览器要求当前网页和server必须同源(安全)

    • 同源:协议、域名、端口,三者必须一致

    • 加载图片 css js可无视同源策略

      • <img src=跨域的图片地址 />
      • <link href=跨域的CSS地址 />
      • <script src=跨域的js地址></script>
      1. <img/>可用于统计打点,可使用第三方统计服务
      2. <link/> <script>可使用CDN,CDN一般都是外域
      3. script>可实现JSONP
    • 跨域

      • 所有的跨域都必须经过server端允许配合
      • 未经server端允许就实现跨域,说明浏览器有漏洞,危险信息号
  • JSONP

    • 问题:访问一个网址,服务端一定返回一个html文件吗?
      • 不是,服务器可以任意动态拼接数据返回,只要符合html格式要求
      • 同理 <script src="xxx/getData.js"> 动态拼接数据,符合js格式即可
    • <script>可绕过跨域限制
    • 服务器可任意动态拼接数据返回
    • 只要服务端愿意返回,<script>就可以获得跨域数据
  • CORS(服务端支持)

在这里插入图片描述

  • 实际项目中,ajax常用工具

    • jQuery (不常用)

      • 没有用到Promise
    • fetch

      • 使用XMLHttpRequest
      • 注意:fetch返回的Promise不会被标记为reject,即使状态码是404或500,只有当网络故障或请求被阻止时,才会被标记为reject
      • fetch不会从服务端发送或接收任何cookie
      • 详细可参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
    • axios (常用)

      • 使用XMLHttpRequest
      • 支持Promise
      • 详细可参考:http://axios-js.com/docs/
    • 示例

      // 1.jQuery
      $(function(){
        //请求参数
        var list = {};
        $.ajax({
          //请求方式
          type : "POST",
          //请求的媒体类型
          contentType: "application/json;charset=UTF-8",
          //请求地址
          url : "http://127.0.0.1/admin/list/",
          //数据,json字符串
          data : JSON.stringify(list),
          //请求成功
          success : function(result) {
            console.log(result);
          },
          //请求失败,包含具体的错误信息
          error : function(e){
            console.log(e.status);
            console.log(e.responseText);
          }
        });
      });
      
      // 2.fetch
      fetch('http://example.com/movies.json')
        .then(response => response.json())
        .then(data => console.log(data));
      async function postData(url = '', data = {}) {
        const response = await fetch(url, {
          method: 'POST', // *GET, POST, PUT, DELETE
          mode: 'cors',
          cache: 'no-cache',
          credentials: 'same-origin',
          headers: {
            'Content-Type': 'application/json'
          },
          redirect: 'follow',
          referrerPolicy: 'no-referrer',
          body: JSON.stringify(data)
        });
        return response.json();
      }
      postData('https://example.com/answer', { answer: 42 })
        .then(data => {
          console.log(data);
        });
      
      // 3.axios
      const axios = require('axios');
      axios.get('/user?ID=12345')
        .then(function (response) {
          console.log(response);
        })
        .catch(function (error) {
          console.log(error);
        })
        .then(function () {
        });
      
32 手写一个简易的ajax
function ajax(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText))
        } else if (xhr.status === 404) {
          reject(new Error('404 not found'))
        }
      }
    }
    xhr.send(null)
  })
}
const url = './test.json'
ajax(url)
  .then(res => console.log(res))
  .catch(err => console.log(err))
33 跨域的常用实现方式
  • JSONP
  • CROS

4.5 存储

知识点

1. cookie

  • 本身用于浏览器和server通讯
  • 被“借用”到本地存储(localStorage和sessionStorage是由html5提出来的)
  • 可用 document.cookie=''来修改
  • 缺点
    • 存储最大:4KB
    • http请求时需要发送到服务端,增加请求数量
    • 只能用document.cookie=''来修改,太简陋,并且每次有新的变量就是向后追加

2. localStorage和sessionStorage

  • HTML5专门为存储而设计,最大可存5M
  • API简单易用 setItem getItem
  • 不会随着http请求被发送出去
  1. localStorage 数据会永久存储,除非代码或手动删除
  2. sessionStorage数据只存在于当前会话,浏览器关闭则清空
  3. 一般用localStorage会更多一些

可以通过浏览器F12的Application操作

34 描述cookie localStorage sessionStorage区别
  • 容量
  • API易用性
  • 是否跟随http请求发送

5 HTTP

需要调用后端的接口,提交、获取数据——http协议

事先需要掌握ajax

知识点

1. http状态码

  • 状态码分类
    • 1xx:服务器收到请求
    • 2xx:请求成功
    • 3xx:重定向
    • 4xx:客户端错误
    • 5xx:服务端错误
  • 常见状态码
    • 200 成功
    • 301 永久重定向(配合location,浏览器自动处理)
      • 浏览器会记住301所返回的location,下次直接访问location
      • 应用:当老的域名到期,使用301使得浏览器记住新的域名,每次访问新域名即可
    • 302 临时重定向(配合location,浏览器自动处理)
      • 下次还是访问的原来的地址
      • 应用:比如在百度搜索css,随便点开一个链接,浏览器会先跳转到百度的一个地址,再通过302跳转到指定链接
    • 304 资源未被修改
    • 404 资源为找到
    • 403 没有权限
    • 500 服务器错误
    • 504 网关超时
  • 关于协议和规范
    • 就是一个规范,要求大家都跟着执行,不要违反规范(例如IE)

2. http methods

这些都是规范!

  • 传统的methods

    • get 获取服务器数据
    • post 向服务器提交数据
    • 简单的网页功能,就这两个操作
  • 现在的methods

    • get 获取数据
    • post 新建数据
    • patch/put更新数据
    • delete 删除数据
  • Restful API

    • 一种新的API设计方法

    • 传统的API设计:把每个url当做一个功能

    • Restful API设计:把每个url当做一个唯一的资源

      • 如何设计成一个资源?

        1. 尽量不用url参数

          • 传统的API设计:/api/list?pageIndex=2

          • Restful API设计:/api/list/2

        2. 用method表示操作类型

          • 传统的API设计:
            • post 请求:/api/create-blog
            • post 请求:/api/update-blog?id=100
            • get 请求:/api/get-blog?id=100
          • Restful API设计
            • post 请求:/api/blog
            • patch 请求:/api/blog/100
            • get 请求:/api/blog/100

3. http headers

  • 常见的Request Headers

    • Accept 浏览器可接受的数据格式
    • Accept-Encoding 浏览器可接收的压缩算法,如 gzip
    • Accept-Language 浏览器可接收的语言,如 zh-CN
    • Connection:keep-alive 一次TCP连接重复使用
    • cookie:同域请求都会带上cookie
    • Host
    • User-Agent:UA,浏览器信息
    • Content-type:发送数据的格式,如application/json
  • 常见的Response Headers

  • Content-type:返回数据的格式

  • Content-length:返回数据的大小,字节

  • Content-Encoding:返回数据的压缩算法

  • Set-Cookie

  • 自定义headers

    • axios-js.com/docs/#Request-Config

      headers: { 'X-Requested-With': 'XMLHttpRequest' },
      
  • 缓存相关的headers

    • Cache-Control Expires
    • Last-Modified If-Modified-Since
    • ETag If-None-Match

4. http缓存

webpack 哈希

  • 关于缓存的介绍

    • 什么是http缓存?
      • 第一次访问一个网站时,需要加载网站的资源,比如html css js img等,让服务器返回相应的资源
      • 当再次请求这个网站时,可能会请求同样的资源,所以可以利用缓存策略,将资源先缓存起来
    • 为什么需要缓存?
      • 提高网页访问的速度
      • 减少请求次数,降低服务器的压力,提高性能
      • 减少冗余的数据传输,节省网络流量和带宽
    • 哪些资源可以被缓存?——静态资源(js css img)
  • http缓存策略(强制缓存+协商缓存)(重要)

    1. 强制缓存

      • Cache-Control

        • 在Response Headers中
        • 控制强制缓存
        • eg.缓存一年的时间: Cache-Control: max-age=31536000(单位:s)
        1. max-age:设置s级别的最大缓存时间
        2. no-cache:不用本地缓存,直接向服务端请求
        3. no-store:不用本地缓存和服务端的缓存措施 (不常见)
        4. private:只允许最终用户做缓存,如电脑、手机
        5. public:允许中间路由做缓存
      • Expires

        • 同在Response Headers中
        • 同为控制缓存过期
        • 已被Cache-Control代替

在这里插入图片描述

  1. 协商缓存(对比缓存)

    • 服务端缓存策略(不是缓存在服务端,是服务端来判断能不能用缓存的资源)
    • 服务端判断客户端资源,是否和服务端资源一样
    • 一致返回304,否则返回200和新的资源

    资源标识:在Response Headers中,有2种

    • Last-Modified 资源的最后修改时间

      • If-Modified-Since带的是最新Last-Modified的值,如果没有被修改,返回204,否则返回资源新的Last-Modified
    • ETag 资源的唯一标识(一个字符串)

    • Last-Modified和ETag

      • 优先使用ETag
      • Last-Modified只能精确到秒级
      • 如果资源被重复生成,而内容不变,则ETag更精确

在这里插入图片描述

在这里插入图片描述

  • 刷新操作方式,对缓存的影响

    • 三种刷新操作
      • 正操操作:地址栏输入url,跳转链接,前进、后退
      • 手动刷新:F5,点击刷新按钮,右击菜单刷新
      • 强制刷新:ctrl+F5
    • 不同的刷新操作,不同的缓存策略
      • 正操操作:强制缓存有效,协商缓存有效
      • 手动刷新:强制缓存失效,协商缓存有效
      • 强制刷新:强制缓存失效,协商缓存失效
01 http常见的状态码有哪些?
  • 范围

  • 常见状态码

02 http常见的header有哪些?
  • Request
  • Response
  • 自定义
  • 缓存相关
03 什么是Restful API?
  • methods
  • 传统API与Restful API
04 描述一下http的缓存机制(重要)
  • 强制
  • 协商
  • 304状态码

6 开发环境

  • 了解实际工作情况
  • 开发环境的工具,能体现工作产出的效率
  • 聊天为主,不会问具体问题
  1. git
  2. 调试工具
  3. 抓包
  4. webpack/babel
  5. linux常用命令

1. git

  • 介绍

    • 最常用的代码版本管理工具

    • 大型项目,需要多人协作,必须熟用git

    • 下载安装

    • git服务端常见的有:github coding.net

    • 大公司自己搭建内网git服务

  • 常用git命令

    • git add .
    • git checkout xxx
    • git commit -m ‘xxx’
    • git push origin master
    • git pull origin master
    • git branch
    • git checkout -b xxx / git checkout xxx
    • git merge xxx
  • 演示 coding.net

    • 登录并创建项目

    • 新增电脑公钥,我的公钥位置:C:\Users\联想.ssh\id_rsa.pub

    • 通过ssh克隆项目:git clone git@e.coding.net:qianmys/git-demo/git-demo.git自己仓库的ssh地址

    进入项目:cd git-demo
    查看状态:git status
    查看分支:git branth
    查看新增内容:git diff
    提交某个文件:git add xxx
    提交全部文件:git add .
    记录修改:git commit -m '第一次修改'
    查看提交记录:git log
    做配置:git config user.name mys
    查看当前提交的内容:git show
    撤销所有修改:git checkout .
    撤销某个修改:git checkout xxx
    提交:git push origin master
    拉取:git pull origin master
    
    // 多人合作
    创建新的分支:git checkout -b xxx
    切换分支:git checkout master
    	切换到自己的分支,在自己的分支中进行操作
    
    // 合并 
    eg:分支:master login
    1.拉取全部分支:git fetch
    2.切换到login:git checkout login
    3.拉取login:git pull origin login
    4.切换到master:git checkout master
    5.合并:git merge login 
    6.推送到远程:git push origin master
    
    // 如果当前进度没有写完,需要修改别的分支的bug
    把当前进度先放到一边:git stash
    切换了分支,修改BUG后,再回到原来的分支,原来的内容不变
    把当前进度重新开放出来:git stash pop
    

2. chrome调试工具

  • 一般面试不会考察,但是是必备技能
  • 调试工具
    • Elements:展示DOM结构,调试CSS
    • Console:打印内容
    • debugger:断点(可以在代码中写debugger进行调试)
    • Network
    • Application:cookie Storage的位置
    • Source:源码

3. 抓包

  • 移动端h5网页,查看网络请求,需要工具抓包

  • windows一般用fiddler,MaxOS一般用charles

  • 抓包过程

    • 手机和电脑连在同一个局域网
    • 将手机代理到电脑上
    • 手机端浏览网页即可抓包
    • 查看网络请求
    • 网址代理
    • https

4. webpack和babel

  • 安装与使用步骤

    • 安装babel包 npm install @babel/core @babel/preset-env babel-loader -D
    • 使用步骤:xxx
  • 模块化规范

    • import

    • export

    • default

  • webpack生产环境

    • scripts:
      	"build": "webpack --config webpack.prod.js"
      	
      	npm run build
      	
      output:
      	filename: 'bundle.[contenthash].js'
      

5. linux命令

  • 公司的线上机器一般都是linux

  • 测试机也需要用linux

  • 测试机或线上机出现问题,本地不能复现,需要排查

    常用linux命令

7 运行环境

  • 运行环境即浏览器(server端有node.js)
  • 浏览器要下载网页代码,渲染出页面,期间会执行若干JS
  • 要保证代码在浏览器中:稳定且高效

7.1 网页加载过程

知识点

1. 加载资源的形式

  • html代码

  • 媒体文件,如图片、视频

  • js css

2. 加载资源的过程

  • DNS解析:域名 -> IP地址

    域名更好记忆

    不同地方IP地址不一样

    需要域名解析服务

  • 浏览器根据IP地址向服务器发起http请求

    真正发送请求的核心模块是操作系统内部服务

    http请求还涉及到tcp连接,三次握手等

  • 服务器处理http请求,并返回给浏览器

3. 渲染页面的过程

  • 根据HTML代码生成DOM Tree

  • 根据CSS生成CSSOM

  • 将DOM Tree和CSSOM整合,形成Render Tree

  • 根据Render Tree渲染页面

    • 遇到 <script>则暂停渲染,优先加载并执行JS代码,完成后再继续
      • JS和DOM共有同一个线程
  • 直至把Render Tree渲染完成

4. 问题

  • 为什么要把CSS放在head中?

    • CSS放在head标签中时, 先加载CSS, 之后解析CSS构建CSSOMTree, 于此同时构建DOMTree, CSSOMTree和DOMTree都构建完毕之后开始构建RenderTree, 计算布局渲染网页

    • 如果放在body尾部,DOMTree构建完成之后便开始构建RenderTree,并计算布局渲染网页, 等加载解析完css之后, 开始构建CSSOMTree, 并和DOMTree重新构建RenderTree, 重新计算布局渲染网页

  • 为什么遇到 <script>要停止渲染?

    • 因为JS代码可能会修改当前的渲染结果
  • 为何把JS放到body最后?

    • 因为浏览器在渲染HTML的时候是从上到下依次执行,遇到script标签则会停止DOM树的渲染,优先下载js文件,如果文件很大,则导致页面加载时间过长,影响用户体验
  • window.onload和DOMContentLoaded

    • window.addEventListener('load', () => {
        // 页面的全部资源加载完才会执行,包括图片、视频
      })
      document.addEventListener('DOMContentLoaded', () => {
        // DOM 渲染完即可执行,此时图片、视频可能还没有加载完
      })
      // 所有会选择使用:第二种 DOMContentLoaded
      
01 从输入url到渲染出页面的整个过程
  • 下载资源:各个资源类型,下载过程
  • 渲染页面:结合html css js 图片等
02 window.onload和DOMContentLoaded的区别
  • window.onload 资源全部加载完才能执行
  • DOMContentLoaded DOM渲染完即可执行,图片可能尚未下载

7.2 性能优化 体验优化

1. 性能优化

  • 综合性问题,没有标准答案,尽量全面

  • 细节问题可能会单独提问:手写防抖、节流

  • 性能优化原则:

    • 多使用内存、缓存或其他方法
    • 减少CPU计算量,减少网络加载耗时
    • 适用于所有编程性能优化——空间换时间

2. 如何入手

  • 让加载更快
    • 减少资源体积:压缩代码
    • 减少访问次数:合并代码,SSR服务器端渲染,缓存
    • 使用更快的网络:CDN
  • 让渲染更快
    • CSS放在head,JS放在body下面
    • 尽早开始执行JS,用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存
    • 频繁DOM操作,合并到一起插入DOM结构
    • 节流throttle 防抖 debounce

3. 示例

  • 资源合并
  • 缓存
    • 静态资源加hash后缀,根据文件内容计算hash
    • 文件内容不变,则hash、url不变
    • hash、url不变,则会自动触发http缓存机制,返回304
  • CDN
  • SSR
    • 服务端渲染:将网页和数据一起加载,一起渲染
    • 非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
    • 早先的JSPASP PHP,现在的Vue React SSR
  • 懒加载
    • 预览图
  • 缓存DOM
  • 多个DOM操作一起插入DOM结构
  • 尽早开始JS执行

在这里插入图片描述

4. 防抖 debounce

  • 场景

    • 监听一个输入框,文字变化后触发change事件
    • 直接用keyup事件,则会频繁出发change事件
    • 防抖:用户输入结束或暂停时,才会触发change事件
  • 示例

    function debounce(fn, delay = 500) {
      let timer = null // timer是闭包中的 重要
      return function() {
        // 如果timer有值,先清空
        if (timer) {
          clearTimeout(timer)
        }
        // 设置定时器
        timer = setTimeout(() => {
          fn.apply(this, arguments) // 传入this和参数
          timer = null // 清空定时器
        }, delay) // delay延时时间
      }
    }
    
    input1.addEventListener('keyup', debounce(function () {
      // 模拟触发change事件
      console.log(input1.value)
    }, 600))
    

5. 节流 throttle

  • 场景

    • 拖拽一个元素时,要随时拿到该元素被拖拽的位置
    • 直接用drag事件,会频繁触发,很容易导致卡顿
    • 节流:无论拖拽速度多快,都会每隔100ms触发一次
  • 示例

    function throttle(fn, delay = 100) {
      let timer = null
      return function() {
        if (timer) return
        timer = setTimeout(() => {
          fn.apply(this, arguments) // arguments:在函数里面也能监听event
          timer = null
        }, delay)
      }
    }
    div1.addEventListener('drag', throttle(function (event) {
      console.log(event.offsetX, event.offsetY)
    }, 100))
    

7.3 安全

1. XSS跨站请求攻击

  • 场景

    • 一个博客网站,在上面发表一篇博客,其中嵌入 <script>脚本
    • 脚本内容:获取cookie,发送到我的服务器(服务器配合跨域)
    • 发布这篇博客,有人查看,可以获取到访问者的cookie
  • 预防

    • 替换特殊字符,如: < 变为 &lt; >变为 &gt;

    • <script>变为 &lt;script&gt; 直接显示,不会作为脚本执行

    • 前后端都要做替换

    • <script>alert(document.cookie)</script>
      &lt;script&gt;alert(document.cookie)&lt;/script&gt;
      
  • 工具使用参考:https://www.npmjs.com/package/xss

2. XSRF跨站请求伪造

  • 场景
    • 你正在购物,看中了某个商品,商品id是100
    • 付费接口是xxx.com/pay?id=100,但没有任何验证
    • 我是攻击者,我看中了一个商品,id是200
    • 我向你发送一封电子邮件,邮件标题很吸引人
    • 但邮件正文隐藏着 <img src=xxx.com/pay?id=200 />
    • 你一看邮件,就帮我买了id是200的商品
  • 预防
    • 使用post接口
    • 增加验证,如密码、短信验证码、指纹等
01 常见的web前端攻击方式有哪些?
  • XSS
  • XSFR
  • 7
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值