前端面试笔记

539 阅读11分钟

1.javascript知识简介

一般来说,对于js的考察核心在于,在你对你自己的知识有信心的时候。你要预判一下通常面试官会问你什么。这样才能做到面试的时候,可以自信的回答面试遇到的问题。

原理篇

对于js有几个核心的概念,需要掌握,面试通常会问到的几个比如执行上下文,原型,作用域,闭包

执行上下文:

js执行代码的时候,所包含的执行环境和变量

  • 全局执行上下文

    • this的值 指向window
    • 词法环境
    • 变量对象
  • 函数上下文

    • this的值 根据具体的情况分析

    1. 默认绑定

    绑定window

    1. 显示绑定

    call apply,bind

    function test(){
    console.log('test')
    }
    let obj={}
    te
    
    1. 隐式绑定
    function foo(){
    console.log(this.a)
    }
    let obj={a:'1',
    foo:foo}
    obj.fool()
    
    1. 箭头函数绑定

    箭头函数不绑定this的值,this想当于普通变量

    1. new绑定 > 会将this绑定到返回的实例对象上
    • 词法环境
    • 变量对象
  • eval上下文

代码执行过程:

1 .创建全局执行上下文 2. 执行全局上下文,遇到函数,创建函数执行上下文 3.执行函数上下文 4.执行结束,全局上下文继续执行

作用域:

变量和声明的作用范围

  • 块级作用域
  • 函数作用域 作用域链:

原型

  • 构造函数

可以通过new来创建对象的函数

function Person(name){
this.name=name
}
let p =new Person('kangkai')

symbol是构造函数么,因为symbol不支持new语法,所以不算是

  • 原型

可以用于继承方法和属性的对象 ,javascript是基于原型的语言,每一个对象都拥有一个原型,挂载在构造函数的prototype

Person.prototyep==p.__proto__
  • 原型链

每个对象都一个原型,通过__proto__指向原型对象,原型对象还有原型,通过这样一层层,最终原型链的顶端是null,这个就叫原型链

p.__proto__==Person.prototype
p.__proto__.__proto__=Object,prototype
p.__proto__.__proto__.__proto__=null
  • 属性查找机制: 根据原型链一步一步查找属性的机制,先查找对象本身,在查找原型对象,沿着原型链一直到原型对象为null
  • 属性修改机制: 只会修改对象本身的属性

闭包

可以访问上层函数的变量的函数

函数科里化

在一个函数的里面,填充部分函数,然后返回一个新函数的过程叫做函数科里化

let add=function(a){
 return function(b){
 return a+b
}
add(1)(2)

继承

原型链继承

将子类的原型对象,指向父类实例

function Father(){}
function Children(){}
Children.prototype=new Father()

构造继承

通过call改变子类的this的值

function Children(){
Father.call(this)
}

实例继承

function Children(){
let instance=new Father()
return instacne
}

拷贝继承

将父类的属性和方法拷贝到子类实例

组合继承

调用父类的构造,并且将父类的实例作为子类的原型返回

function Children(){
Father.call(this)
}
Children.prototype=new Father()
Children.prototype.constructor=Children

组合继承

es6 extends

class Father(){
  constructor(){}
}
class Children extends Father{
   constructor(){
   super()
   }
}

基础数据结构

  1. 数组 map:遍历数组返回新数组
[1,2,3].map(x=>x+1) //[2,3,4]
function Stack(){
this.data=[]
}
Stack.prptype.push=function(data){
this.data.push(data)
}
Stack.proptype.pop=function(){
this.data.pop()
}

3.队列

function Queue(){
this.data=[]
}
Queue.prototype.push=function(data){
this.data.push(data)
}

4.链表

function Node(data){
this.data=data
this.next=null
}
function List(){
this.head=null
}

5.树

function Node(data,left,right){
this.data=data
this.left=left
this.right=right
}
function Tree(){
this.root=null
}

手写代码

es5实现let,const

  • let
 for(let i=0;i<5;i++){
        console.log(i)
      }

babel实现方式

 for(let _i=0;_i<5;_i++){
        console.log(_i)
      }

自执行函数方式

 (function(){
        for(var i=0;i<5;i++){
         console.log(i)
        }
      })()
  • const
 function _const(key,value){
        const desc={
          value,
          writeable:false
        }
        Object.defineProperty(window,key,desc)
      }

call apply bind的手写

call,apply 和bind都是用来改变this的指向,将this的值指向传入的对象,call和apply的区别在于参数,参数是多个和apply的参数是列表,bind的区别在于,bind返回的是一个函数,call和apply实际上都是立刻执行的函数

  • 获取this的值
  • 获取参数的值
  • 返回值
  • 边界处理
//call编写
Function.prototype.myCall()=function (context) {
      context=context?Object(context):window
      context.fn=this
      let args=arguments.slice(1)
      let result=context.fn(...args)
      delete context.fn
      return result
    }
    
//apply编写
 Function.prototype.myApply = function (context, paramter) {
        context = context ? Object(context) : window
        context.fn = this
        let result
        if (!paramter) {
          result = context.fn()
        } else {
          result = context.fn(...paramter)
        }
        delete context.fn
        return result
      }

//bind 编写
 Function.prototype.myBind = function (context) {
        let self = this
        let args = Array.prototype.slice.call(arguments, 1)
        let fNop = function () {}
        var fBound = function () {
          return function () {
            let bindArgs = Array.prototype.slice.call(arguments)
            self.apply(context, args.concat(bindArgs))
          }
        }
        fNop.prototype = fBound.prototype
        fBound.prototype = new fNop()
        return fBound
      }

new的手写

new是构建一个实例,首先要知道new的过程中做了什么 1.创建一个对象实例 2.原型绑定 3.this指向当前实例 3.返回实例

function create(){
Con=[].shift.call(arguments)//获取构造函数,并删除argumnets的第一个对象
let obj=Object.create(Con.prototype//创建空对象并连接原型
let ret=Con.apply(obj,arguments)//绑定this的值
return ret instanceof Object? ret:obj
}

节流防抖的手写

  • 节流
 function throttle (fn, delay) {
        let flag = true
        let timer = null
        return function (...args) {
          let context = this
          if (!flag) return
          flag = false
          clearTimeout(timer)
          timer = setTimeout(function () {
            fn.apply(context.args)
            flag = true
          }, delay)
        }
      }
  • 防抖 简单的来说是在某个时间段内,多次触发,只执行最后一次
debounce: function (fn, delay) {
      let timer = null
      return function (...args) {
        let context = this
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
          fn.apply(context, args)
        }, delay)
      }
    },

发布订阅的手写

 function Subject() {
        this.observers=[]
      }
      Subject.prototype={
        subscribe(observer){
          this.observers.push(observer)
        }
        unsubscribe(observerRemove){
          this.observers=this.observers.filter(observer=>{
            return observer!==observerRemove
          })

        },
        broadcast(){
          this.observers.forEach(obsever=>{
            obsever.call()
          })
        }

浅拷贝和深拷贝的手写

  • 浅拷贝 对于浅拷贝来说,核心是只需要拷贝属性即可,不需要递归到深层次去拷贝
///考虑边界条件
function shallowCopy(source){
        let target={}
        for(let key in source){
          if(Object.prototype.hasOwnProperty.call(source,key)){
            target[key]=source[key]
          }
        }
        return target
      }
  • 深拷贝 递归实现

promise的手写

手写instanceof

  function my_instance_of (leftVaule, rightVaule) {
        if (typeof leftVaule !== 'object' || leftVaule === null) return false
        let rightProto = rightVaule.prototype,
          leftProto = leftVaule.__proto__
        while (true) {
          if (leftProto === null) {
            return false
          }
          if (leftProto === rightProto) {
            return true
          }
          leftProto = leftProto.__proto__
        }
      }

手写事件委托

  ul.addEventListener('click',e=>{
       console.log(e,e.target)
       if(e.target.tagName.toLowerCase()==='li'){
         console.log('print')
       }
     })
 function delegate (element, eventType, fn, seletor) {
        seletor.addEventListener(eventType, e => {
          let el = e.target
          while (!el.match(seletor)) {
            if (element === el) {
              el = null
              break
            }
            el = el.parentNode
          }
          el && fn.call(el, e, el)
        }, true)
        return element
      }

数组去重

let res=array.filter((items,index,array)=>{
        return array.indexOf(items)===index
      })
      return res
      let unique_3 = arr => arr.reduce((pre, cur) => pre.includes(cur) ? pre : [...pre, cur], []);

 let obj={}
    return array.filter((item,index,array)=>{
      return obj.hasOwnProperty(typeof item+item)?false:(obj [typeof  item+item]=true))
    })

实现科里化

   let curring=(fn,...args)=>{
      fn.length>args.length?(...arguments)=> curring(fn ...args,...arguments):fn(...args)
      }

数组flat

let arrMap=(array)=>{
       return array.reduce((res,cur)=>{
         if(Array.isArray(cur)){
           return [...res,arrMap(cur)]
         }else{
           return [...res,cur]
         }
       },[])
     }
function arrDep(arr,deep) {
      return d>0?arr.reduce((acc,val)=>acc.contact(Array.isArray(val)?arrDep(val,deep-1):val))
     }

实现sleep

function sleep(time){
return new Promise((resolve)=>{
setTimeout(()=>{},time)
}
}

实现promise.all 和promise.race

Promise.all = function (arr) {
        return new Promise((resolve, reject) => {
          if (arr.length === 0) {
            return resolve([])
          } else {
            let res = []
            let count = 0
            for (let i = 0; i < arr.length; i++) {
              if (!(arr[i] instanceof Promise)) {
                res[i] = arr[i]
                if (++count === arr.length) {
                  resolve(res)
                }
              } else {
                arr[i].then(data => {
                  res[i] = data
                  if (++count === arr.length) {
                    resolve(res)
                  }
                }, error => {
                  reject(error)
                })
              }
            }
          }
        })
      }

es6篇

解构赋值

数组解构赋值

let [a,b]=[1,2]
let [a=1,b]=[3] 

对象的解构赋值

let {foo,bar}={foo:'foo',bar:'bar'}
const {log}=console

字符串的解构赋值

ler [a,b,c]='hello'

数组的扩展

扩展运算符 ...

[].push(...[1,2,3,4])

Array.from :将类数组对象转换换成数组

let obj={ a:1,b:2,c:3}
Array.from(obj)//[1,2,3]
Array.from(obj,x=>x*2)//对每个元素进行处理,并返回

Array.of:将一组值转换为数组

Array.of(1,2,3)//[1,2,3]

includes:

[1,2,3].includes(3)

flat:拉平数组 参数拉平的层数 参数INfinity 无论多少层都拉平

[1,2,[3,4],[5]].flat()//[1,2,3,4,5]
[1,2,[3,4],[5,[6,7]]].flat(2)//[1,2,3,4,5,6,7]

对象扩展

Object.is 判断两个对象是否相等 严格相等 Nan==Nan +0!=-0

Object.is({},{}) //false
object.is(+0,-0) //false

Object.assign 合并对象

let b={}
let a={a:'hello'}
Object.assugn(b,a)
console.log(b)

Object.getOwnPropertyDescriptor Object.getPrototypeof

proxy

symbol

es6新增的数据类型,代表独一无二的值 symbol('a') 不能使用new

set,map

set不重复的元素集合 map键值对,键值可以是任何类型

  • weakMap 结构与map类似,区别是键值只接受对象,可以避免内存泄漏,直接被垃圾回收
  • weakSet

promise原理

封装异步操作

promise.all

将多个promise实例包装成一个promise实例

Promise.all([promsie1,promise2])

参数是一个包含若干promise实例的数组,如果数组里不是promise,则会用Promise.resolve方法包装,每个promise都fullfilled,外部promise才会fullfilled,触发回调函数,如果有一个rejected,则promise变成rejected

promise.race

async和await

async return语句会返回一个promise对象

async function(){
await setTimeout({},1000)

export&import

  • export 导出 export default 匿名导出
  • import 导入

let const

let,const都是块级作用域

{ let a=1}
console.log(a)
for(var i=0;i<10;i++){
a[i]=function(){
console.log(i)
}
}
a[6]()//10
for(let i=0;i<10;i++){
let i ='abc'
console.log(i)
}//'abc' 'abc' 'abc'

for循环的父作用域和子作用域可以声明相同的变量

const的值是不可以改变的

const a=1
a=2//错误  
const b=[]
b.push(1)

var 声明的变量会出现变量提升的现象

a=1
var a

暂时性死区

for(var i=1;i<5;i++){
console.log(i)
}
//此时i输出的值是多少

class 类

class person{
construct(name){
this.name=name
}

箭头函数

function hello(a,b){
return a+b
}
(a,b)=>a+b
function test(){
console.log('hello')
}
()=>{console.log('hello'}

this的指向不在是函数本身 不能使用new来作为构造函数

面试经典题

用js实现一个无限循环的动画 1.用setTimeout实现

      let e =document.getElementsByClassName('e')
      let left=0
      let flag=true
      setInterval(()=>{
        left==0?flag=true:left==100?flag=false:''
        flag?e.style.left=` ${left++}px`:e.style.left=`${left--}px`
      },1000/60)

2.requestAnimateFrame

//浏览器兼容性处理
window.requestAnimationFrame=function () {
        return window.requestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          function (callback) {
            window.setTimeout(callback,1000/60)
          }

      }()
      let e=document.getElementsByClassName('e')
      let flag=true
      let left=0
      function render() {
        left==0?flag=true:left==100?flag=false:''
        flag?e.style.left=`${left++}px`:e.style.left=`${left--}px`
      }
      (function animloop() {
        render()
        window.requestAnimationFrame(animloop)
      })()

面试高频考点

  • 1.typeof

对于引用类型的除了function,其他都是判断是object 判断null也是object ,undefined的类型是undefined

  • 2.object 键值对,键必须是字符串,如果不是字符串会调用tostring方法进行转换, 如果object会转换成 object object

  • 3.tosting转换

    • 转换成字符串 字符串连接符+ 一边是字符串
    • 转换成number 关系运算符> ,<+(有一边是数字)
    • 转换成bool值 逻辑非运算符会转换成bool值

2.vue框架

一般面试前端的技术栈最起码会有vue和react其中的一个作为主要掌握的框架

mvvm模式

model-view-viewmodel

什么是vue

vue是一套渐进式构建前台界面的javascript框架。vue的特点包含组件系统,声明式渲染,大规模式路由

vue的生命周期

vue一般会经过四个阶段

  1. 创建阶段(beforeCreated/created)

    beforecreated:实例刚刚创建,data等选项还未加载

    created:实例属性加载完成,dom还未创建

  2. 挂载阶段(beforeMounted/mounted)

    beforeMounted:template元素已经编译好,但是尚未挂载

    mountede:模板已经挂载,生成真实dom元素

  3. 更新阶段(beforeUpdated/updated)

    beforeUpdated:执行diff语法,比较UI是否需要更新

    updated:组件更新结束

  4. 销毁阶段(beforedestroyed/destroyed)

    beforeDestroyed:销毁开始

    destroyed:销毁结束

vue是怎么渲染界面的

vue对于template的编译,是先将template模板编译成为AST,抽象语法树,在通过generate函数生成render函数,最后生成VNOe,在通过渲染成dom

AST

** 抽象语法树**将代码转换成树状结构

babel编译原理

1.将es6代码解析成AST 2.对AST进行遍历转译得到新的AST 3.新的AST转换成ES5

vue是如何实现数据劫持的

vue利用的是object.definePropoty

手写实现

<div id="app">
<input type="text" id='txt'>
<p id="show-txt"></p>
</div>
 let obj={}
        Object.defineProperty(obj,'txt',{
          set:function (newValue){
            document.getElementById('txt').value=newValue
            document.getElementById('show-txt').innerHTML=newValue

          },get:function () {
            return obj
          }
        })
        document.addEventListener('keyup',function (e) {
          obj.txt=e.target.value

        })
      }

vue虚拟dom

建设dom 树diff 同层对比,输出patch 更新dom 遍历patch,局部更新dom

function diff(oldTree,newTree){
let patch={}
DFS(oldTree,newTree,0,patch)
return patch
}
function DFS(oldTree,newTree,deep,patch){

}

vuenexttick更新

在一次事件内,多次触发更新dom,vue是不会立刻更新dom的,而是通过将更新动作上发到异步队列中,一次性的更新dom,如果需要实时触发,利用this.$nexttcik

vue列表为什么要有key

key对于列表的作用来说,是就地复用,如果没有key值,则当同一节点来,key的意义在于排序和重复使用,diff算法的时候,快速找到节点

3.算法

对于大多数前端,实际上涉及的算法其实并不算很多,但是面试的时候会多多少少都会遇到,对于这些基础的算法,多少需要掌握

数据结构

字符串

字符串常用操作 indexof lastIndexof charat split replace

  • 字符串的常见面试题

    • 字符串正则替换

    1.给定一个字符串 'hello jhello hello',将空格替换成20%

    let s='hello jhello hello'
    s.replace(/\s/g,'20%')
    

    2.字符串统计出现最多的字符 思路:哈希表,建立字符和count的统计 3.字符串逆序 思路转换成数组,在拼成字符串 4. 字符串全排列: 给定几个字母,排成的全部排列的组合是多少 思路:回溯算法 回溯算法的框架

数组

数组常用操作 push pop shift pop unshift splice

链表

function Node(data){
    this.data=data
    thi.next=null
}
function LinkedList(){
this.head=null
}

栈的特点:先进后出

function stack(){
this.value=[]
}
stack.propotype.push=function(val){
this.value.push(val)
}
stack.propotype.pop=function(){
this.value.shift()
}

队列

function Node(data,left,right){
this.data=data
this.left=left
this.right=right
}
function Tree(root){
this.root=root
}

排序

冒泡算法 基础

两两比较,较大交换到前面去

function bubuleSort(arr){
for(let i=0;i<arr.length;i++){
let complete=true
for(let j=i+1;j<arr.length;j++){
if(arr[i]<arr[j]){
[arr[i],arr[j]]=[arr[j],arr[i]]
}
}
if(complete){
break

}

插入排序 基础

将左边的部分视为已经排列好的,比较右边的部分然后,一次比较插入

归并排序 基础

取最左边的值,然后将数组组成比当前值左边大右边小的状态,然后在递归进行左边和右边的部分

选择排序 基础

每一次循环,找到最小的值,

快速排序 基础

回溯算法

回溯法算术框架:定义最后需要的排列组合结果 定义当前路径的和 定义

双指针

双指针算术框架: 定义遍历的左右指针

贪心算法

动态规划

我不会

DFS BFS

  • 深度优先遍历 DFS

  • 广度优先遍历 BFS

4.浏览器知识

浏览器绘制界面原理

以googlev8引擎为例,浏览器解析html生dom树,解析css生成css规则树,html和css合成渲染树,渲染树通过调用api布局绘制成为界面。

javascript的加载会影响dom的解析么

javascript的加载和解析会阻塞dom树的生成和解析,遇到js代码后,会将控制权交给javascript引擎,等js解析和执行完后,控制权才会重新给回dom解析

标签 dfer和async的区别

defer 延迟加载,元素解析完成后执行脚本 async 异步加载,会阻塞元素渲染

什么是重绘和回流

  • 重绘 渲染树的元素的样式发生了改变,但是布局并没有发生改变,比如background_color dom发生改变的时候
  • 回流 渲染树的元素尺寸布局隐藏与否发生的了改变,大小高度改变

如何减少重绘和回流

  • 使用transform替代top
  • 使用visiblity替代display:none
  • 减少使用table布局
  • 在for循环中不要多次改变样式

浏览器事件循环

在JavaScript中,任务分为两种一种Task宏任务 ,另外一种microTask微任务

  • 宏任务 setTimeout,setInterval I/O
  • 微任务 Process.nextTick,Promise

先执行宏任务,在清空微任务

浏览器缓存

强缓存和协商缓存,什么是强缓存,什么是协商缓存 根据什么来标识的,强缓存是强制明智能 http头cache-control和expire来标识http请求的属性

强缓存

  • cache-contol Max-age是标明过期的时间
  • expire 是标明过期的具体时间,由于客户端和服务端的时间不一致,建议使用cache-contorl,同时拥有两个属性的时候,cache-control会覆盖

第一次请求的时候保存缓存到浏览器里面,第二次通过http头部属性的cache-control和expire属性,判断是否缓存是否过期,不过期即命中缓存。过期便要向服务器发送请求数据是否有变化,如果有变化,则要更新缓存数据

协商缓存

当判断缓存过期的时候,这个时候通过LastModified属性来判断是否需要更新缓存副本

  • lastModified
  • if-modified-since 比较两次请求时间内是否有过修改
  • ETag 资源唯一标识符,随response返回
  • 浏览器获取缓存的过程 根据httpheader去判断是否命中强缓存,命中直接获取,不在发送请求到服务器 强缓存未命中的时候,客户端会发送请求到服务器,去验证是否命中协商缓存,如果命中,服务器返回,但是不返回资源,客户端根据请求的结果,获取缓存,协商缓存也未命中的时候,就需要返回资源给客户端

浏览器跨标签页通讯

通过共享的一些中间介质进行通讯

浏览器跨域

jsonp

function jsonp(url,jsonCallBack,success) {
       const script=document.createElement('script')
       script.url=url
       script.async=true
       script.type='text/script'
       window[jsonCallBack]=function (data) {
         success&&success(data)
       }
       document.body.appendChild(script)


     }

cros

设置 ACESS-ALLOW-ORIGIN:true

xss攻击

注入恶意代码

  1. cookie设置httponly
  2. 转义页面上输入和输出内容

csrf攻击

跨域请求伪造,防护

  1. get不修改数据

  2. 不能让第三方网站访问用户的cookie

  3. 设置白名单

  4. 请求校验

css知识

盒模型

dom元素采取的布局模型

  • 标准盒模型 div的宽高不包含边界和内外填充,只是内容的高度 border-siziing:content-box
  • 怪异盒模型 div的宽高是包含边界和内填充 box-sizing:border-box

bfc

块级格式上下文,每个人box都是独立的渲染区,与外界互不影响 其实简单的来说每个box都是独立的,不会受外界影响的块

触发bfc

  • overflow属性不为visible
  • flex布局
  • float的属性
  • display为inline-block
  • position为absolute,fixed

清除浮动

  • 通过增加尾元素来 :after
  • clear:both
  • 父级设置BFC
  • 父级设置高度

常见的手写css布局

垂直居中

div {
text-align :center;'


}

右边宽度固定左边自适应

1.flex布局实现,左边固定宽度,右边flex:1 父容器display:flex

  .parent{
    display: flex;
  }
  .left{
    flex: 1;
    background: black;
  }
  .right{
    width:200px;
    background: red;
  }

2.float属性实现:float加margin right

 .left{
    width: 200px;
    background: black;
    float: right;
    height: 100%;
  }
  .right{
    height: 200px;
    margin-right:200px;
    background: red;

  }

水平垂直居中的布局

1.transform结合position

.parent{
position:relative;
}
.view{
 width: 200px;
    background: black;
    float: right;
    height: 100%;
    position: absolute;
    top:50%;
    left:50%;
    transform:translate(-50%,-50%) ;
    }

sass less

类css语言通过webpack编译成真正的css,,常用语法 变量,minxin复用

4.webpack篇

webpack是javascript的模块化打包工具,通过分析模块之间的依赖,最终将所有模块打包成一份或者多份代码包(bundle) 核心概念

  • entry 入口文件,webpack会从此文件开始编译分析
  • output 出口 打包后创建bundle的路径
  • module 模块
  • chunk 代码块
  • loader 模块加载器
  • plugin 插件

webpack的打包流程

  • 初始化:读取配置文件,初始化配置,创建compile对象
  • 编译:挂载插件监听,从入口文件开始编译
  • 编译:根绝不同的文件,加载不同的loader进行编译
  • 打包:将打包好的代码块包装成一个chunk,根据依赖和配置,输出固定内容
  • 输出:输出到对应的output目录生成文件

webpack常用的loader

  • fileLoader
  • tsloader
  • styleloader
  • cssloader
  • babelloader

webpack 热更新原理

websocket

webpack打包优化

5.项目相关

对你的项目进行优化

  • 减少http请求
  • 减少dns查询
  • 使用cdn
  • 减少操作dom
  • 压缩js,css字体图片

vue 性能优化

  1. 代码层面优化
  • 通过object.freeze() 来冻结不需要变化的数据,减少响应系统的负担
  • 防抖和节流
  • 利用挂载节点会替换优化白屏问题
  • 组件库的按需引入
  1. 项目打包的优化
  • 异步按需引入组件
  • 代码分割
  • js压缩
  • 提取第三方库,并使用cdn引入
  • 压缩图片
  1. 项目部署的优化
  • 识别gzip压缩是否开启

5. http相关知识补充

http协议

http1.0

http1.1

  • 长连接复用
  • host指定虚拟站点
  • 断点续传,身份认证,状态管理,cache缓存

http2.0

  • 多路复用
  • 首部压缩

https协议:

  • 证书
  • ssl加密
  • 端口443

常见状态码

  • 1xx
  • 200
  • 404
  • 400
  • 500

websocket协议

长连接的

6. 前端设计模式

  1. 单例模式
let sigleton = (function () {
        // 隐藏class 的构造函数
        function FooService () {}
        return {
          //创建和获取单例的函数和对象
          getInstance: function () {
            if (!fooService) {
              fooService = new FooService()
            }
            return fooService
          }
        }
      })()
  1. 观察者模式
 function Subject() {
        this.observers=[]
      }
      Subject.prototype={
        subscribe(observer){
          this.observers.push(observer)
        }
        unsubscribe(observerRemove){
          this.observers=this.observers.filter(observer=>{
            return observer!==observerRemove
          })

        },
        broadcast(){
          this.observers.forEach(obsever=>{
            obsever.call()
          })
        }