「查缺补漏」我的网易前端秘籍-如何准备面试

前言

开门见山,这篇文章,适合「初级前端」,如果你还在校招的话,或者还在求职的话,可以看看本文,找一找灵感,希望对你们有帮助呀。

先说一下最近个人情况:2020年8月底已经拿到网易有道offer, 这算是我的第一份web前端工作吧,一直以来都是自学前端的,走过很多的弯路,之前的技术栈是Vue.js,目前往react方向走。

这是我的网易面经????「面经」你可能需要的三轮网易面经

我的感受就是,自己一边梳理知识点,一边总结归纳,收获可能更大,所以打算把我梳理的部分分享出来,篇幅有点长,大家见谅呀。

覆盖的点不是很全,分享给你们,希望你们秋招一切顺利,offer收割机❤️❤️❤️

HTML系列

你是如何理解 HTML 语义化的?

让页面内容结构化,它有如下优点

1、易于用户阅读,样式丢失的时候能让页面呈现清晰的结构。
2、有利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权重。
3、方便其他设备解析,如盲人阅读器根据语义渲染网页
4、有利于开发和维护,语义化更具可读性,代码更好维护,与CSS3关系更和谐

如:

代表头部

可以跟面试官讲的更具体一点????

  • 第一个是荒野阶段,那时候前端的代码主要是后台来写的,所以那个时候写的代码主要是用table来布局的。

  • 第二阶段---美工阶段,这个时候就有专门的人来前端代码了,这个阶段的布局主要是DIV+CSS布局,但是呢有一个问题,就是不够语义化。

  • 第三个阶段-->> 前端阶段,也就是利用具有语义的标签,比如p,h1,h2,article,header,nav,main,aside,footer这些标签,使用这些正确的标签,可以表达正确的内容,也利于开发和维护。

meta viewport 是做什么用的,怎么写?

通常viewport是指视窗、视口。浏览器上(也可能是一个app中的webview)用来显示网页的那部分区域。在移动端和pc端视口是不同的,pc端的视口是浏览器窗口区域,而在移动端有三个不同的视口概念:布局视口、视觉视口、理想视口

meta有两个属性name 和 http-equiv

name

  • keywords(关键字)   告诉搜索引擎,你网页的关键字

  • description(网站内容描述)   用于告诉搜索引擎,你网站的主要内容。

  • viewport(移动端的窗口)  后面介绍

  • robots(定义搜索引擎爬虫的索引方式) robots用来告诉爬虫哪些页面需要索引,哪些页面不需要索引

  • author(作者)

  • generator(网页制作软件)

  • copyright(版权)

http-equiv

http-equiv顾名思义,相当于http的文件头作用。

有以下参数:

  • content-Type 设定网页字符集

    //旧的HTML,不推荐 //HTML5设定网页字符集的方式,推荐使用UTF-8
  • X-UA-Compatible(浏览器采用哪种版本来渲染页面)

    //指定IE和Chrome使用最新版本渲染当前页面
  • cache-control(请求和响应遵循的缓存机制)

  • expires(网页到期时间)

你用过哪些 HTML 5 标签?

「canvas画布」

const ctx = canvas.getContext('2d');  // 获取它的2d上下文
ctx.fillStyle = 'green';    // 设置笔刷的填充色
ctx.fillRect(10, 10, 100, 100);  //  利用画笔范围,矩形,比如圆

video

autoplay 布尔属性;视频马上自动开始播放,不会停下来等着数据载入结束。

「controls」   提供用户控制,允许用户控制视频的播放,包括音量,跨帧,暂停/恢复播放。

「loop」 布尔属性;指定后,会在视频结尾的地方,自动返回视频开始的地方。

「track」标签表示的是字幕

「poster」 表示的是封面


H5 是什么?-->>移动端页面

h5一般指的是开一个WebView来加载页面吧,

「WebView是一种控件,它基于webkit引擎,因此具备渲染Web页面的功能。」

基于Webview的混合开发,就是在 Anddroid (安卓)/(苹果)原生APP里,通过WebView控件嵌入Web页面。

很多APP都是外边套原生APP的壳,内容是H5页面(基于html+css+js的Web页面)。现在的移动端混合开发软件,如果对于交互渲染要求不是特别高的项目,基本都是这么玩的。

「WebView作用」

  • 显示和渲染Web页面

  • 直接使用html文件(网络上或本地assets中)作布局

  • 可和JavaScript交互调用

HTML5新特性:

  1. 本地存储特性

  2. 设备兼容特性 HTML5提供了前所未有的数据与应用接入开放接口

  3. 连接特性 WebSockets

  4. 网页多媒体特性 支持Audio Video SVG Canvas WebGL CSS3

  5. CSS3特性

增加拖放API地理定位SVG绘图canvas绘图Web WorkerWebSocket

区分普通显示屏和高清屏

  • 当devicePixelRatio值等于1时(也就是最小值),那么它普通显示屏。

  • 当devicePixelRatio值大于1(通常是1.5、2.0),那么它就是高清显示屏。

  • 不同像素的图利用媒体查询结合 devicePixelRatio 可以区分普通显示屏和高清显示屏

并给出了如下CSS设计方案:

.css{/* 普通显示屏(设备像素比例小于等于1.3)使用1倍的图 */ 
    background-image: url(img_1x.png);
}
@media only screen and (-webkit-min-device-pixel-ratio:1.5){
.css{/* 高清显示屏(设备像素比例大于等于1.5)使用2倍图  */
    background-image: url(img_2x.png);
  }
}

「服务端用nginx对图片进行处理」

想要什么样尺寸的图片自己裁切,我们提供了按比例缩放和自定尺寸的裁切方法,地址后拼接字符串就行。

「使用更小更快更强,新一代图片格式 WebP」

在实测中,webp 格式比 jpg 格式减小约 20%。这对优化用户体验,减少CDN带宽消耗有很好的效果。

「如何判断呢」

我想到一个解决的方案,就是通过User-Agent信息,可以拿到你的浏览器信息,通过对你的浏览器分类,支持webp放在白名单里,不支持的则为黑名单。判断为白名单,则直接调用,返回webp格式图片;反之,则显示原图。


DOM

事件冒泡

事件会从最内层的元素开始发生,一直向上传播,直到document对象。


    Click me!

因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是

「p -> div -> body -> html -> document」

「事件捕获」

与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

上面的例子在事件捕获的概念下发生click事件的顺序应该是

「document -> html -> body -> div -> p」

所以从上面的图片来看????1-5是捕获过程,5-6是目标阶段,6-10是冒泡阶段

addEventListener

addEventListener方法用来为一个特定的元素绑定一个事件处理函数,是JavaScript中的常用方法。

 element.addEventListener(event, function, useCapture)

重点来看看第三个参数useCapture

  • true - 事件句柄在捕获阶段执行(即在事件捕获阶段调用处理函数)

  • false- false- 默认。事件句柄在冒泡阶段执行(即表示在事件冒泡的阶段调用事件处理函数)

所以我们通常来说,默认第三个参数不写的话,是按照事件句柄在冒泡执行的。

attachEvent

兼容IE的写法,默认是事件冒泡阶段调用处理函数,写事件名时候要加上"on"前缀("onload"、"onclick"等)。

object.attachEvent(event, function)

事件代理

利用事件流的特性,我们可以使用一种叫做事件代理的方法,其实利用的就是事件冒泡的机制。

下面的内容是子元素1
        
  • li内容>>>  这是span内容123
  •         下面的内容是子元素2         
  • li内容>>>  这是span内容123
  •         下面的内容是子元素3         
  • li内容>>>  这是span内容123
  • js代码

    xxx.addEventListener('click', function (e) {
                console.log(e,e.target)
                if (e.target.tagName.toLowerCase() === 'li') {
                    console.log('打印')
                }
    })
    

    更加规范的写法????

      function delegate(element, eventType, selector, fn) {
                element.addEventListener(eventType, e => {
                    let el = e.target
                    while (!el.matches(selector)) {
                        if (element === el) {
                            el = null
                            break
                        }
                        el = el.parentNode
                    }
                    el && fn.call(el, e, el)
                },true)
                return element
            }
    

    阻止事件冒泡和默认事件

    event.preventDefault()   // 阻止默认事件
    event.stopPropagation() //阻止冒泡
    

    实现一个可以拖拽的DIV

    分割线-—---

    var dragging = false
    var position = null
    
    xxx.addEventListener('mousedown',function(e){
      dragging = true
      position = [e.clientX, e.clientY]
    })
    
    
    document.addEventListener('mousemove', function(e){
      if(dragging === false) return null
      console.log('hi')
      const x = e.clientX
      const y = e.clientY
      const deltaX = x - position[0]
      const deltaY = y - position[1]
      const left = parseInt(xxx.style.left || 0)
      const top = parseInt(xxx.style.top || 0)
      xxx.style.left = left + deltaX + 'px'
      xxx.style.top = top + deltaY + 'px'
      position = [x, y]
    })
    document.addEventListener('mouseup', function(e){
      dragging = false
    })
    

    CSS系列

    两种盒模型分别说一下

    也就是标准盒模型写起来更方便,也更规范吧。

    盒模型分为标准盒模型和怪异盒模型(IE模型)

    box-sizing:content-box   //标准盒模型
    box-sizing:border-box    //怪异盒模型
    

    「content-box」

    默认值,标准盒子模型。widthheight 只包括内容的宽和高, 不包括边框(border),内边距(padding),外边距(margin)。注意: 内边距、边框和外边距都在这个盒子的外部。比如说,.box {width: 350px; border: 10px solid black;} 在浏览器中的渲染的实际宽度将是 370px。

    尺寸计算公式:

    width = 内容的宽度

    height = 内容的高度

    宽度和高度的计算值都不包含内容的边框(border)和内边距(padding)。

    「border-box」

    widthheight 属性包括内容,内边距和边框,但不包括外边距。这是当文档处于 Quirks模式 时Internet Explorer使用的盒模型。注意,填充和边框将在盒子内 , 例如, .box {width: 350px; border: 10px solid black;} 导致在浏览器中呈现的宽度为350px的盒子。内容框不能为负,并且被分配到0,使得不可能使用border-box使元素消失。

    尺寸计算公式:

    width = border + padding + 内容的宽度

    height = border + padding + 内容的高度

    注意:如果你在设计页面中,发现内容区被撑爆了,那么就先检查一下border-sizing是什么,最好在引用reset.css的时候,就对border-sizing进行统一设置,方便管理

    如何垂直居中?

    16种方法实现水平居中垂直居中

    水平局中

    「内联元素」「宽度默认就是内容的宽度」,只需要给父级添加text-align

    .wrapper{text-align: center;}
    

    「块级元素」,将它的margin-left和margin-right设置为auto,并且块级元素一定要设置宽度,否则元素默认为100%宽度,不需要居中。

    .inner{
                display: block;
                width: 150px;
                margin: 0 auto;
            }
    // 一定要设置宽度,不然就不需要局中了
    

    两个以上的水平局中,可以将其设置为display:inline-block,在设置父级text-align

    垂直局中

    「内联元素」,第一种实用的是flex布局,这里局中的值得是相对于父盒子

    .wrapper{
                display: flex;
                align-items: center;
            }
    

    第二种,这里面指的局中是相对于自身而言的

    .inner{
                height:100px;
                line-height:100px;
    }
    

    「块级元素」

    宽高确定情况下,实用 「position absolute + 负margin」

    宽高不确定的情况下,实用「position absolute + transform」

    垂直水平局中

    子元素宽高确定的情况下,使用「position absolute + 负margin」

    子元素宽高不确定的,使用「position absolute + transform」

    .inner{
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%,-50%);
                background: blue;
            }
    

    当然了flex布局也是可以解决问题的,下面就介绍????


    两列布局

    左列定宽,右列自适应

    「float+margin」

    .left{
                float: left;
                width: 100px;
                height: 100%;
                background: rebeccapurple;
            }
            .rigth{
                height: 100%;
                margin-left: 100px; /*大于等于#left的宽度*/
                background: blue;
            }
    

    左列自适应,右列定宽

    「float+overflow」

    
            
            

    css代码????

    .rigth {
                margin-left: 10px;
                /*margin需要定义在#right中*/
                float: right;
                width: 100px;
                height: 100%;
                background-color: #0f0;
            }
    
            .left {
                overflow: hidden;
                /*触发bfc*/
                height: 100%;
                background-color: blue;
            }
    

    三列布局

    两列定宽,一列自适应

    使用float+margin实现
    
            
            
            
        

    css代码

    .wrapper {
                height: 400px;
                background: red;
                min-width: 500px;
            }
    
            .left {
                margin-left: 10px;
                float: left;
                width: 100px;
                height: 100%;
                background-color: #0f0;
            }
            .main{
                float: left;
                width: 100px;
                height: 100%;
                margin-left: 20px;
                background: brown;
            }
            .rigth {
    
                margin-left: 230px;  /*等于#left和#center的宽度之和加上间隔,多出来的就是#right和#center的间隔*/
                height: 100%;
                background-color: blue;
            }
    

    「间列自适应宽度,旁边两侧固定宽度」

    「双飞翼布局」

    实现步骤

    「html部分」

    
            
            
                
                                            

    「css部分」

    .wrapper {
                /* //确保中间内容可以显示出来,两倍left宽+right宽 */
                min-width: 600px; 
            }
    
            .left {
                float: left;
                width: 200px;
                height: 400px;
                background: red;
                margin-left: -100%;
            }
    
            .main {
                float: left;
                width: 100%;
                height: 500px;
                background: yellow;
            }
    
            .main .inner {
                /* margin水平方向要是左右两者的宽度 */
                margin: 0 200px;    
                height: 100%;
                border: 2px solid brown;
            }
            .right {
                float: left;
                width: 200px;
                height: 400px;
                background: blue;
                margin-left: -200px;
            }
    

    flex 怎么用,常用属性有哪些?

    flex 的核心的概念就是 「容器」「轴」

    父容器

    「justify-content 项目在主轴上的对齐方式」

    「align-items」 「定义项目在侧轴上如何对齐」

    子容器

    「align-self   单个项目对齐方式」

    「flex:前面三个属性的简写 是flex-grow  flex-shrink flex-basis的简写」

    flex: 0 1 auto;默认主轴是row,那么不会去放大比例,如果所有的子元素宽度和大于父元素宽度时,就会按照比例的大小去砍掉相应的大小。

    「flex-direction 决定主轴的方向  即项目的排列方向」

    row | row-reverse | column | column-reverse

    BFC 是什么?

    深入理解BFC和外边距合并(Margin Collapse)

    BFC全称是Block Formatting Context,即块格式化上下文。

    BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

    下列方式会创建「块格式化上下文」

    1. 根元素

    2. float属性不为none

    3. position为absolute或fixed

    4. display为inline-block, table-cell, table-caption, flex, inline-flex

    5. overflow不为visible

    需要背的条件????

    1. 浮动元素(元素的 float 不是 none)

    2. 绝对定位元素(元素的 position 为 absolute 或 fixed)

    3. 行内块元素

    4. overflow 值不为 visible 的块元素

    5. 弹性元素(display为 flex 或 inline-flex元素的直接子元素)

    「BFC布局规则」

    1. 内部的Box会在垂直方向,一个接一个地放置。

    2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠

    3. 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

    4. BFC的区域不会与float box重叠。

    5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

    6. 计算BFC的高度时,浮动元素也参与计算

    选择器优先级

    「css常用选择器」

    通配符:*
    ID选择器:#ID
    类选择器:.class
    元素选择器:p、a    等
    后代选择器:p span、div a   等
    伪类选择器:a:hover 等
    属性选择器:input[type="text"]  等
    

    「css选择器权重」

    !important -> 行内样式 -> #id -> .class -> 元素和伪元素 -> * -> 继承 -> 默认

    CSS新特性

    transition:过渡
    transform:旋转、缩放、移动或者倾斜
    animation:动画
    gradient:渐变
    shadow:阴影
    border-radius:圆角
    

    「transition」

    transition: property duration timing-function delay;
    // css属性名称   过渡时间  过渡时间曲线  过渡延迟时间
    

    「transform」

    transform:rotate(30deg)  旋转
    transform:translate(100px,20px)  移动
    transform:scale(2,1.5);  缩放
    transform:skew(30deg,10deg);  扭曲
    

    「animation」

    animation: move 1s linear forwards;
    // 定义动画的时间  duration 
    // 动画的名称
    // 动画的贝塞尔曲线
    // animation-fill-mode 属性规定动画在播放之前或之后,其动画效果是否可见。 
    // forwards  当动画完成后,保持最后一个属性值 
    

    清除浮动说一下

    第一种用伪元素

    .clearfix:after{
      content: "";
      display: block;
      clear: both; 
    }
    
     .clearfix{
         zoom: 1; /* IE 兼容*/
     }
    

    第二种给父容器添加 overflow:hidden 或者 auto 样式

    overflow:hidden;
    

    三种地位方案

    在定位的时候,浏览器就会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。定位时,有三种定位方案,分别是常规流,浮动已经绝对定位。

    常规流(Normal flow)

    浮动(Floats)

    绝对定位(Absolute positioning)

    获取DOM

    
            
            
    
        
       let oDiv = document.getElementById('css-cell')
            let oDiv1 = document.getElementsByTagName('div')   //集合 根据标签
            let oDiv2 = document.querySelectorAll('div')     // 集合  标签       
            let oDiv3 = document.getElementsByClassName('heart')     // className    
    

    Attribute与Property

    attribute:是HTML标签上的某个属性,如id、class、value等以及自定义属性

    property:是js获取的DOM对象上的属性值,比如a,你可以将它看作为一个基本的js对象。

    let demo11 = oDiv.getAttribute('class');
    let demo2 = oDiv.setAttribute('data-name','new-value')
    

    路由规则

    可以在不刷新页面的前提下动态改变浏览器地址栏中的URL地址,动态修改页面上所显示资源。

    「window.history的方法和属性」

    back() forward() go()

    HTML5 新方法:添加和替换历史记录的条目

    「pushState()」

    history.pushState(state, title, url); 添加一条历史记录,不刷新页面
    
    replaceState
      history.replaceState(state, title, url);  替换当前的历史记录,不刷新页面
    
    popstate 事件:历史记录发生改变时触发

    基于hash(location.hash+hashchange事件)

    我们知道location.hash的值就是url中#后面的内容,如http://www.163.com#something

    此网址中,location.hash='#something'。

    hash满足以下几个特性,才使得其可以实现前端路由:

    1. url中hash值的变化并不会重新加载页面,因为hash是用来指导浏览器行为的,对服务端是无用的,所以不会包括在http请求中。

    2. hash值的改变,都会在浏览器的访问历史中增加一个记录,也就是能通过浏览器的回退、前进按钮控制hash的切换

    3. 我们可以通过hashchange事件,监听到hash值的变化,从而响应不同路径的逻辑处理。

    window.addEventListener("hashchange", funcRef, false)
    

    如此一来,我们就可以在hashchange事件里,根据hash值来更新对应的视图,但不会去重新请求页面,同时呢,也在history里增加了一条访问记录,用户也仍然可以通过前进后退键实现UI的切换。

    触发hash值的变化有2种方法????

    to somewhere
    
    location.hash="#somewhere"
    

    JS系列

    JS基础是最重要的一个环节,所以这个专题,我也是梳理总结了很多,毕竟这个是灵魂嘛,那接下来我把我梳理的文章也总结一遍,然后我复习的部分内容也梳理出来了。

    往期文章总结

    介绍一下js数据类型

    基本数据类型,Number、String、Boolean、Undefined、Null、Symbol ,BigInt。

    比如Symbol提出是为了解决什么问题?可以往全局变量冲突讲。

    比如BigInt,解决的问题是大数问题,超过了安全数,怎么办?

    引用数据类型,数组,对象,函数。

    可以试着往它们存储问题上面答,基本数据类型的值直接保存在栈中,而复杂数据类型的值保存在堆中,通过使用在栈中保存对应的指针来获取堆中的值。

    Number.isFinite & isFinite区别

    某种程度上,都是检测「有限性」的值。两者区别在于,isFinite函数强制将一个非数值的参数转换成数值,如果能转换成数值,然后再去判断是否是「有限的」

    Number.isFinite()检测有穷性的值,这个方法不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的(finite),才返回 true

    Number.isFinite(0)    // true
    Number.isFinite('0')  // false
    Number.isFinite(Infinity) false
    isFinite('0')   // true
    isFinite('0')  // true
    isFinite(Infinity)  // false
    

    isNaN 和 Number.isNaN 函数的区别?

    // isNaN('sdasd') true
    // isNaN('21N') true
    // isNaN(NaN)  true
    // isNaN(123) false
    

    我们来看看Number.isNaN

    Number.isNaN('1232N')  // false
    Number.isNaN('1232')    // false
    Number.isNaN(21312)  // false
    Number.isNaN('sadas')  // false
    Number.isNaN(NaN)   // true
    

    什么是可迭代对象

    要成为可迭代对象, 一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性:
    

    如何判断一个类型是不是可迭代对象

    let someString = "hi";
    typeof someString[Symbol.iterator];          // "function"
    

    「结论」

    arguments对象了解吗

    这个arguments有个易错点,容易忽略的点。

    首先我们看下它的定义:arguments对象是所有(非箭头)函数中都可用的「局部变量」。此对象包含传递给函数的每个参数,第一个参数在索引0处。

    arguments对象不是一个 Array 。它类似于Array,但除了length属性和索引元素之外没有任何Array属性。

    转换成数组????

    let args = Array.prototype.slice.call(arguments)
    let args1 = Array.from(arguments)
    let args2 = [...arguments]
    

    易错点????

    当非严格模式中的函数「没有」包含剩余参数、默认参数和解构赋值,那么arguments对象中的值「会」跟踪参数的值(反之亦然),看几个题目懂了

    function func(a) { 
      arguments[0] = 99;   // 更新了arguments[0] 同样更新了a
      console.log(a);
    }
    func(10); // 99
    

    这里arguments就会跟踪a变量????

    function func(a) { 
      a = 99;              // 更新了a 同样更新了arguments[0] 
      console.log(arguments[0]);
    }
    func(10); // 99
    

    当非严格模式中的函数「有」包含剩余参数、默认参数和解构赋值,那么arguments对象中的值「不会」跟踪参数的值(反之亦然)。相反, arguments反映了调用时提供的参数:

    function func(a = 55) { 
      arguments[0] = 99; // updating arguments[0] does not also update a
      console.log(a);
    }
    func(10); // 10
    

    并且

    function func(a = 55) { 
      a = 99; // updating a does not also update arguments[0]
      console.log(arguments[0]);
    }
    func(10); // 10
    

    并且

    function func(a = 55) { 
      console.log(arguments[0]);
    }
    func(); // undefined
    

    原型

    举个例子????

    为什么我们新建的对象可以使用toString()方法,这是因为我们访问一个对象的属性时,首先会在这个对象身上找,如果没有的话,我们会通过这个对象的__proto__找到该对象的原型,然后在这个原型对象中找,这个原型对象又没有的话,就这样子通过一直找下去,这也就是「原型链概念」。直到找到原型链的尽头也就是Object.prototype。

    js 获取原型的方法?

    假设Demo是一个对象,那么有三种方式????

    获取对象属性的方法

    1. Object.keys(testObj)  返回的参数就是一个数组,数组内包括对象内可枚举属性和方法名

    2. for in 遍历的也可以,不过对于非继承的属性名称也会获取到,通过hasOwnproperty判断

    3. Object.getOwnPropertyNames(obj) 返回的参数就是一个数组,数组内包括自身拥有的枚举或不可枚举属性名称字符串,如果是数组的话,还有可能获取到length属性

    for of 和 for in区别

    「for in」

    我们直接从一段代码来看

    Array.prototype.method=function(){
      console.log(this.length);
    }
    var myArray=[1,2,4,5,6,7]
    myArray.name="数组"
    for (var index in myArray) {
      console.log(myArray[index]);
    }
    

    有哪些缺陷呢????

    「for of」

    Array.prototype.method=function(){
      console.log(this.length);
    }
    var myArray=[1,2,4,5,6,7]
    myArray.name="数组";
    for (var value of myArray) {
      console.log(value);
    }
    

    「小结」

    作用域链

    「作用域」 规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做 「作用域链」

    **函数的作用域在函数创建时就已经确定了。**当函数创建时,会有一个名为 [[scope]] 的内部属性保存所有父变量对象到其中。当函数执行时,会创建一个执行环境,然后通过复制函数的 [[scope]]  属性中的对象构建起执行环境的作用域链,然后,变量对象 VO 被激活生成 AO 并添加到作用域链的前端,完整作用域链创建完成:

    Scope = [AO].concat([[Scope]]);
    

    所以闭包,可以说是作用域链的另外一种表示形式。

    闭包的应用

    闭包的应用比较典型是定义模块,我们将操作函数暴露给外部,而细节隐藏在模块内部

    闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。
    通过使用闭包,我们可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
    
    
    函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
    

    ES6 语法知道哪些,分别怎么用?

    let const 块级作用域 箭头函数 词法this Class 解构,剩余运算符,Promise等,往这些方面展开。

    手写函数防抖和函数节流

    「节流throttle」

    规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

    function throttle(fn, delay) {
                let flag = true,
                    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)
                }
            }
    

    「防抖」

    在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

    function debounce(fn, delay) {
                let timer = null
                return function(...args) {
                    let context = this
                    if(timer) clearTimeout(timer)
                    timer = setTimeout(function(){
                        fn.apply(context,args)
                    },delay)
                }
            }
    

    手写AJAX

    function ajax(url, method) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(url, method, true)
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            if (xhr.status === 200) {
              resolve(xhr.responseText)
            } else if (xhr.status === 404) {
              reject(new Error('404'))
            }
          } else {
            reject('请求数据失败')
          }
        }
        xhr.send(null)
      })
    }
    

    数组去重

    function unique_3(array) {
        var obj = {};
        return array.filter(function (item, index, array) {
            return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
        })
    }
    

    手写bind函数

    Function.prototype.mybind = function(context, ...args) {
        return (...newArgs) => {
            return this.call(context, ...args, ...newArgs)
        }
    }
    

    实现call

    Function.prototype.mycall = function (context, ...args) {
        context = Object(context) || window
        let fn = Symbol(1)
        context[fn] = this
        let result = context[fn](...args)
        delete context[fn]
        return result
    }
    

    实现一个快排

    function quickSort(arr){
    
        if (arr.length <= 1) return arr;
        let index = Math.floor(arr.length / 2)
        let pivot = arr.splice(index, 1)[0],
            left = [],
            right = [];
        for(let i = 0; i < arr.length; i++){
            if(pivot > arr[i]){
                left.push(arr[i])
            }else{
                right.push(arr[i])
            }
        }
        return quickSort(left).concat([pivot],quickSort(right))
    }
    

    数组的扁平化

    function flatDeep(arr) {
        return arr.reduce((res, cur) => {
            if(Array.isArray(cur)){
                return [...res, ...flatDeep(cur)]
            }else{
                return [...res, cur]
            }
        },[])
    }
    

    深拷贝

    function deepClone(obj, hash = new WeakMap()) {
        if (obj instanceof RegExp) return new RegExp(obj)
        if (obj instanceof Date) return new Date(obj)
    
        if (obj === null || typeof obj !== 'object') return obj
    
        if (hash.has(obj)) return obj
    
        let res = new obj.constructor();
        hash.set(obj, res)
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                res[key] = deepClone(obj[key],hash)
            }
        }
        return res
    }
    

    实现高阶函数柯里化

    function currying(fn, ...args) {
        if (fn.length > args.length) {
            return (...newArgs) => currying(fn, ...args, ...newArgs)
        } else {
            return fn(...args)
        }
    }
    

    寄生组合式继承

    function inherit(Son, Father) {
        // 创建对象,创建父类原型的一个副本
        let prototype = Object.create(Father.prototype)
        // 增强对象,弥补因重写原型而失去的默认的constructor 属性
        prototype.construct = Son
         // 指定对象,将新创建的对象赋值给子类的原型
        Son.prototype = prototype
    }
    

    this

    「this 永远指向最后调用它的那个对象」

    主要有下面几个规则

    ECMAScript6 怎么写 class,为什么会出现 class 这种东西?

    在我看来 ES6 新添加的 class 只是为了补充 js 中缺少的一些面向对象语言的特性,但本质上来说它只是一种语法糖,不是一个新的东西,其背后还是原型继承的思想。通过加入 class 可以有利于我们更好的组织代码。在 class 中添加的方法,其实是添加在类的原型上的。

    哪些操作会造成内存泄漏?

    相关知识点:

    第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
    
    第二种情况是我们设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留
    在内存中,而无法被回收。
    
    第三种情况是我们获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回
    收。
    
    第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。
    

    Object.is()使用过吗?跟 === 和 == 区别

    JS事件循环机制了解吗

    微任务包括了 promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。

    宏任务包括了 script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲染等。

    立即执行函数是什么?

    声明一个函数,并马上调用这个匿名函数就叫做立即执行函数;也可以说立即执行函数是一种语法,让你的函数在定义以后立即执行;

    写法????

    (function () {alert("我是匿名函数")}())   //用括号把整个表达式包起来
    (function () {alert("我是匿名函数")})()  //用括号把函数包起来
    !function () {alert("我是匿名函数")}()  //求反,我们不在意值是多少,只想通过语法检查
    +function () {alert("我是匿名函数")}() 
    -function () {alert("我是匿名函数")}() 
    ~function () {alert("我是匿名函数")}() 
    void function () {alert("我是匿名函数")}() 
    new function () {alert("我是匿名函数")}() 
    

    作用:

    1. 不必为函数命名,避免了污染全局变量

    2. 立即执行函数内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量

    3. 封装变量

    什么是 JSONP,什么是 CORS,什么是跨域?

    这个我有篇文章已经总结啦,所以这里就直接跳到对应文章吧,传送门

    发布订阅者模式

    class EventEmitter {
        constructor(){
            this.list = {}
        }
        on(key,fn){
            if(!this.list[key]){
                this.list[key] = []
            }
            this.list[key].push(fn)
            return this
        }
        once(key,fn) {
            if(!this.list[key]){
                this.list[key] = []
            }
            this.list[key].push(fn)
            this.list[key].flag = this.list[key].length;
            return this
        }
        emit(key, args){
            let that = this;
            let fns = this.list[key]
            if(!fns || fns.length === 0) return false
            for(let i = 0; i < fns.length; i++) {
                fns[i].apply(this, args)
                if(fns.flag === i){
                    that.off(key,fns[i-1])
                }
            }
        }
        off(key,fn) {
            let fns = this.list[key];
            let len = fns.length,
                k = -1;
            for(let i = 0; i < len; i++) {
                if(fns[i].name === fn.name){ // 删除
                    k = i;
                    break;
                }
            }
            if(k !== -1) {
                this.list[key].splice(k,1)
            }
        }
    
        allOff(key) {
            if(key === undefined){
                this.list = {}
            }else{
                this.list[key] = []
            }
        }
    }
    
    

    下面是测试数据

    var emitter = new EventEmitter();
    
    function handleOne(a, b, c) {
        console.log('第一个监听函数', a, b, c)
    }
    
    function handleSecond(a, b, c) {
        console.log('第二个监听函数', a, b, c)
    }
    
    function handleThird(a, b, c) {
        console.log('第三个监听函数', a, b, c)
    }
    
    emitter.on("demo", handleOne)
        .once("demo", handleSecond)
        .on("demo", handleThird);
    
    emitter.emit('demo', [1, 2, 3]);
    // => 第一个监听函数 1 2 3
    // => 第二个监听函数 1 2 3
    // => 第三个监听函数 1 2 3
    
    emitter.off('demo', handleThird);
    emitter.emit('demo', [1, 2, 3]);
    // => 第一个监听函数 1 2 3
    
    emitter.allOff();
    emitter.emit('demo', [1, 2, 3]);
    // nothing
    

    浏览器相关

    这个浏览器专题的话,我之前也总结过啦,所以这里就贴出地址,有兴趣的可以去补一补基础知识,大部分的知识点下面也提及到了,就不单独拿出来梳理啦????

    往期文章总结

    Cookie V.S. LocalStorage V.S. SessionStorage V.S. Session

    其中的一个相同点,就是它们保存在浏览器端,且同源的。

    那么不同点是哪些呢????

    异同点

    分类 生命周期 存储容量 存储位置
    cookie 默认保存在内存中,随浏览器关闭失效(如果设置过期时间,在到过期时间后失效) 4KB 保存在客户端,每次请求时都会带上
    localStorage 理论上永久有效的,除非主动清除。 4.98MB(不同浏览器情况不同,safari 2.49M) 保存在客户端,不与服务端交互。节省网络流量
    sessionStorage 仅在当前网页会话下有效,关闭页面或浏览器后会被清除。 4.98MB(部分浏览器没有限制) 同上

    操作方式

    接下来我们来具体看看如何来操作localStoragesessionStorage

    let obj = { name: "TianTianUp", age: 18 };
    localStorage.setItem("name", "TianTianUp"); 
    localStorage.setItem("info", JSON.stringify(obj));
    复制代码
    

    接着进入相同的域名时就能拿到相应的值????

    let name = localStorage.getItem("name");
    let info = JSON.parse(localStorage.getItem("info"));
    复制代码
    

    从这里可以看出,localStorage其实存储的都是字符串,如果是存储对象需要调用JSONstringify方法,并且用JSON.parse来解析成对象。

    应用场景

    什么是 XSS?如何预防?

    XSS 全称是 Cross Site Scripting ,为了与CSS区分开来,故简称 XSS,翻译过来就是“跨站脚本”。

    XSS是指黑客往 HTML 文件中或者 DOM 中注入恶意脚本,从而在用户浏览页面时利用注入的恶意脚本对用户实施攻击的一种手段。

    最开始的时候,这种攻击是通过跨域来实现的,所以叫“跨域脚本”。发展到现在,往HTML文件中中插入恶意代码方式越来越多,所以是否跨域注入脚本已经不是唯一的注入手段了,但是 XSS 这个名字却一直保留至今。

    注入恶意脚本可以完成这些事情:

    1. 窃取Cookie

    2. 监听用户行为,比如输入账号密码后之间发给黑客服务器

    3. 在网页中生成浮窗广告

    4. 修改DOM伪造登入表单

    一般的情况下,XSS攻击有三种实现方式

    存储型 XSS 攻击

    存储型 XSS 攻击大致步骤如下:

    1. 首先黑客利用站点漏洞将一段恶意 JavaScript 代码提交到网站的数据库中;

    2. 然后用户向网站请求包含了恶意 JavaScript 脚本的页面;

    3. 当用户浏览该页面的时候,恶意脚本就会将用户的 Cookie 信息等数据上传到服务器。

    比如常见的场景:

    在评论区提交一份脚本代码,假设前后端没有做好转义工作,那内容上传到服务器,在页面渲染的时候就会直接执行,相当于执行一段未知的JS代码。这就是存储型 XSS 攻击。

    反射型 XSS 攻击

    反射型 XSS 攻击指的就是恶意脚本作为**「网络请求的一部分」**,随后网站又把恶意的JavaScript脚本返回给用户,当恶意 JavaScript 脚本在用户页面中被执行时,黑客就可以利用该脚本做一些恶意操作。

    举个例子:

    http://TianTianUp.com?query=
    复制代码
    

    如上,服务器拿到后解析参数query,最后将内容返回给浏览器,浏览器将这些内容作为HTML的一部分解析,发现是Javascript脚本,直接执行,这样子被XSS攻击了。

    这也就是反射型名字的由来,将恶意脚本作为参数,通过网络请求,最后经过服务器,在反射到HTML文档中,执行解析。

    主要注意的就是,「「服务器不会存储这些恶意的脚本,这也算是和存储型XSS攻击的区别吧」」

    基于 DOM 的 XSS 攻击

    基于 DOM 的 XSS 攻击是不牵涉到页面 Web 服务器的。具体来讲,黑客通过各种手段将恶意脚本注入用户的页面中,在数据传输的时候劫持网络数据包

    常见的劫持手段有:

    阻止 XSS 攻击的策略

    以上讲述的XSS攻击原理,都有一个共同点:让恶意脚本直接在浏览器执行。

    针对三种不同形式的XSS攻击,有以下三种解决办法

    「对输入脚本进行过滤或转码」

    对用户输入的信息过滤或者是转码

    举个例子????

    转码后????

    <script>alert('你受到XSS攻击了')</script>
    

    这样的代码在 html 解析的过程中是无法执行的。

    当然了对于 

    访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。

    同样也会携带相应的用户 cookie 信息,让服务器误以为是一个正常的用户在操作,让各种恶意的操作变为可能。

    3. 引诱用户点击链接

    这种需要诱导用户去点击链接才会触发,这类的情况比如在论坛中发布照片,照片中嵌入了恶意链接,或者是以广告的形式去诱导,比如:

     
      重磅消息!!!
      
    

    点击后,自动发送 get 请求,接下来和自动发 GET 请求部分同理。

    以上三种情况,就是CSRF攻击原理,跟XSS对比的话,CSRF攻击并不需要将恶意代码注入HTML中,而是跳转新的页面,利用「服务器的验证漏洞」「用户之前的登录状态」来模拟用户进行操作

    「防护策略」

    其实我们可以想到,黑客只能借助受害者的**cookie**骗取服务器的信任,但是黑客并不能凭借拿到「cookie」,也看不到 「cookie」的内容。另外,对于服务器返回的结果,由于浏览器「同源策略」的限制,黑客也无法进行解析。

    这就告诉我们,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行**CSRF**的保护。而保护的关键,是 「在请求中放入黑客所不能伪造的信息」

    「用户操作限制——验证码机制」

    方法:添加验证码来识别是不是用户主动去发起这个请求,由于一定强度的验证码机器无法识别,因此危险网站不能伪造一个完整的请求。

    「1. 验证来源站点」

    在服务器端验证请求来源的站点,由于大量的CSRF攻击来自第三方站点,因此服务器跨域禁止来自第三方站点的请求,主要通过HTTP请求头中的两个Header

    这两个Header在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。

    服务器可以通过解析这两个Header中的域名,确定请求的来源域。

    其中,「Origin」只包含域名信息,而「Referer」包含了具体的 URL 路径。

    在某些情况下,这两者都是可以伪造的,通过AJax中自定义请求头即可,安全性略差。

    「2. 利用Cookie的SameSite属性」

    可以看看MDN对此的解释

    SameSite可以设置为三个值,StrictLaxNone

    1. Strict模式下,浏览器完全禁止第三方请求携带Cookie。比如请求sanyuan.com网站只能在sanyuan.com域名当中请求才能携带 Cookie,在其他网站请求都不能。

    2. Lax模式,就宽松一点了,但是只能在 get 方法提交表单况或者a 标签发送 get 请求的情况下可以携带 Cookie,其他情况均不能。

    3. 在None模式下,Cookie将在所有上下文中发送,即允许跨域发送。

    「3. CSRF Token」

    前面讲到CSRF的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息。

    那么我们可以使用Token,在不涉及XSS的前提下,一般黑客很难拿到Token。

    可以看看这篇文章,将了Token是怎么操作的????彻底理解cookie,session,token

    Token(令牌)做为Web领域验证身份是一个不错的选择,当然了,JWT有兴趣的也可以去了解一下。

    Token步骤如下:

    「第一步:将CSRF Token输出到页面中」

    首先,用户打开页面的时候,服务器需要给这个用户生成一个Token,该Token通过加密算法对数据进行加密,一般Token都包括随机字符串和时间戳的组合,显然在提交时Token不能再放在Cookie中了(XSS可能会获取Cookie),否则又会被攻击者冒用。因此,为了安全起见Token最好还是存在服务器的Session中,之后在每次页面加载时,使用JS遍历整个DOM树,对于DOM中所有的a和form标签后加入Token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的HTML代码,这种方法就没有作用,还需要程序员在编码时手动添加Token。

    「第二步:页面提交的请求携带这个Token」

    对于GET请求,Token将附在请求地址之后,这样URL 就变成 http://url?csrftoken=tokenvalue。而对于 POST 请求来说,要在 form 的最后加上:这样,就把Token以参数的形式加入请求了。

    「第三步:服务器验证Token是否正确」

    当用户从客户端得到了Token,再次提交给服务器的时候,服务器需要判断Token的有效性,验证过程是先解密Token,对比加密字符串以及时间戳,如果加密字符串一致且时间未过期,那么这个Token就是有效的。

    非常感兴趣的,可以仔细去阅读一下相关的文章,Token是如何加密的,又是如何保证不被攻击者获取道。

    总结

    CSRF(Cross-site request forgery), 即跨站请求伪造,本质是冲着浏览器分不清发起请求是不是真正的用户本人,所以防范的关键在于在请求中放入黑客所不能伪造的信息。从而防止黑客伪造一个完整的请求欺骗服务器。

    「防范措施」:验证码机制,验证来源站点,利用Cookie的SameSite属性,CSRF Token

    JS 垃圾回收机制

    这部分的知识点,基本上看别人写的翻译,然后按照别人的思路去完成的,所以这里就推荐一篇我看的文章吧,个人觉得写的还是挺好的,所以有兴趣的可以了解一下,下面的文章????

    简单了解JavaScript垃圾回收机制

    计算机网络部分

    这个专题也十分的重要,面试大厂的话,这个你得会,不问就不要紧,但是问到你的话,必须的会,我之前梳理过一篇文章,效果还不错,这里分享给大家????

    往期文章

    HTTP 状态码知道哪些?分别什么意思?

    状态码:由3位数字组成,第一个数字定义了响应的类别

    「1xx:指示信息,表示请求已接收,继续处理」

    「2xx:成功,表示请求已被成功接受,处理。」

    200 OK:客户端请求成功
    204 No Content:无内容。服务器成功处理,但未返回内容。一般用在只是客户端向服务器发送信息,而服务器不用向客户端返回什么信息的情况。不会刷新页面。
    206 Partial Content:服务器已经完成了部分GET请求(客户端进行了范围请求)。响应报文中包含Content-Range指定范围的实体内容
    
    

    「3xx:重定向」

    301 Moved Permanently:永久重定向,表示请求的资源已经永久的搬到了其他位置。
    
    302 Found:临时重定向,表示请求的资源临时搬到了其他位置
    
    303 See Other:临时重定向,应使用GET定向获取请求资源。303功能与302一样,区别只是303明确客户端应该使用GET访问
    
    307 Temporary Redirect:临时重定向,和302有着相同含义。POST不会变成GET
    
    304 Not Modified:表示客户端发送附带条件的请求(GET方法请求报文中的IF…)时,条件不满足。返回304时,不包含任何响应主体。虽然304被划分在3XX,但和重定向一毛钱关系都没有
    
    

    「4xx:客户端错误」

    400 Bad Request:客户端请求有语法错误,服务器无法理解。
    401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。
    403 Forbidden:服务器收到请求,但是拒绝提供服务
    404 Not Found:请求资源不存在。比如,输入了错误的url
    415 Unsupported media type:不支持的媒体类型
    

    「5xx:服务器端错误,服务器未能实现合法的请求。」

    500 Internal Server Error:服务器发生不可预期的错误。
    503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
    

    长轮询和短轮询

    「短轮询」

    短轮询(Polling)的实现思路就是浏览器端「每隔几秒钟向」服务器端**发送http请求,服务端在收到请求后,不论是否有数据更新,都直接进行响应。**在服务端响应完成,就会关闭这个Tcp连接。

    function LongAjax() {
        fetch(url).then(data => {
            // 数据正确拿到后,dosometing
            
        }).catch(err => {
            // 发现错误,比如返回的数据为空等。
            console.log(err);
        });
    }
    setInterval(LongAjax, 5000);
    

    「长轮询」

    客户端发送请求后服务器端「不会立即」返回数据,服务器端会「阻塞请求」连接不会「立即断开」,直到服务器端「有数据更新或者是连接超时」才返回,客户端才再次发出请求新建连接、如此反复从而获取最新数据。

    function LongAjax() {
        fetch(url).then(data => {
         // 数据正确拿到后,
            LongPolling();
        }).catch(err => {
         // 出错或者就是超时间
            LongPolling();
            
        });
    }
    LongAjax()
    

    HTTP 缓存有哪几种?

    浏览器缓存是性能优化的一个重要手段,对于理解缓存机制而言也是很重要的,我们来梳理一下吧????

    强缓存

    强缓存两个相关字段,「「Expires」」「「Cache-Control」」

    「「强缓存分为两种情况,一种是发送HTTP请求,一种不需要发送。」」

    首先检查强缓存,这个阶段**不需要发送HTTP请求。**通过查找不同的字段来进行,不同的HTTP版本所以不同。

    Expires

    Expires即过期时间,时间是相对于服务器的时间而言的,存在于服务端返回的响应头中,在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求。比如下面这样:

    Expires:Mon, 29 Jun 2020 11:10:23 GMT
    复制代码
    

    表示该资源在2020年7月29日11:10:23过期,过期时就会重新向服务器发起请求。

    这个方式有一个问题:「「服务器的时间和浏览器的时间可能并不一致」」,所以HTTP1.1提出新的字段代替它。

    Cache-Control

    HTTP1.1版本中,使用的就是该字段,这个字段采用的时间是过期时长,对应的是max-age。

    Cache-Control:max-age=6000
    复制代码
    

    上面代表该资源返回后6000秒,可以直接使用缓存。

    当然了,它还有其他很多关键的指令,梳理了几个重要的????

    注意点:

    协商缓存

    强缓存失效后,浏览器在请求头中携带响应的缓存Tag来向服务器发送请求,服务器根据对应的tag,来决定是否使用缓存。

    缓存分为两种,「「Last-Modified」」「「ETag」」。两者各有优势,并不存在谁对谁有绝对的优势,与上面所讲的强缓存两个Tag所不同。

    Last-Modified

    这个字段表示的是**「最后修改时间」**。在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。

    浏览器接收到后,「「如果再次请求」」,会在请求头中携带If-Modified-Since字段,这个字段的值也就是服务器传来的最后修改时间。

    服务器拿到请求头中的If-Modified-Since的字段后,其实会和这个服务器中该资源的最后修改时间对比:

    ETag

    ETag是服务器根据当前文件的内容,对文件生成唯一的标识,比如MD5算法,只要里面的内容有改动,这个值就会修改,服务器通过把响应头把该字段给浏览器。

    浏览器接受到ETag值,会在下次请求的时候,将这个值作为**「If-None-Match」**这个字段的内容,发给服务器。

    服务器接收到**「If-None-Match」「后,会跟服务器上该资源的」「ETag」**进行比对????

    两者对比

    最后,「「如果两种方式都支持的话,服务器会优先考虑ETag」」

    缓存位置

    接下来我们考虑使用缓存的话,缓存的位置在哪里呢?

    浏览器缓存的位置的话,可以分为四种,优先级从高到低排列分别????

    Service Worker

    这个应用场景比如PWA,它借鉴了Web Worker思路,由于它脱离了浏览器的窗体,因此无法直接访问DOM。它能完成的功能比如:离线缓存消息推送网络代理,其中离线缓存就是**「Service Worker Cache」**。

    Memory Cache

    指的是内存缓存,从效率上讲它是最快的,从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了。

    Disk Cache

    存储在磁盘中的缓存,从存取效率上讲是比内存缓存慢的,优势在于存储容量和存储时长。

    Disk Cache VS Memory Cache

    两者对比,主要的策略????

    内容使用率高的话,文件优先进入磁盘

    比较大的JS,CSS文件会直接放入磁盘,反之放入内存。

    Push Cache

    推送缓存,这算是浏览器中最后一道防线吧,它是HTTP/2的内容。具体我也不是很清楚,有兴趣的可以去了解。

    总结

    GET 和 POST 的区别

    首先,我们的知道区别只是语义上有区别而已,但是面试的时候,肯定不能这么回答的。

    Webpack

    这个面试也是会经常考的一部分了,所以掌握它还是很有必要的,我是从0到1配过它的,所以这里我就没有梳理笔记了,嗯,下面就推荐两个文章,希望看完可以对你们有帮助。

    实打实的从0到1配置webpack????

    「一劳永逸」由浅入深配置webpack4

    针对面试的????

    关于webpack的面试题总结


    算法与数据结构

    这个专题,我目前总结了三个板块,速度有点慢,不过面试初级前端的话,应该是没有问题的,需要了解的小伙伴可以看看我梳理的三篇????

    往期文章

    如果你跟我一样,对算法也有所热爱的话,我们可以互相讨论下算法,或者关注我哒,我会一直更新算法哒。


    模块化

    将一个复杂的程序依据特定的规则(规范)封装成几个文件,然后将其组合在一起,这些只是向外暴露一些接口,或者方法,与其他模块进行通信,这样子叫做是模块化的过程。

    「为什么要模块化」,目的在于减少复杂性,减少它们相互之间的功能关系。使每个模块功能单一。

    「模块化好处」

    CommomJS

    CommonJS定义了两个主要概念:

    require函数,用于导入模块

    module.exports变量,用于导出模块

    require

    导入,代码很简单,let {count,addCount}=require("./utils")就可以了。

    require的第一步是解析路径获取到模块内容:

    module

    let count=0
    function addCount(){
        count++
    }
    module.exports={count,addCount}
    

    然后根据require执行代码时需要加上的,那么实际上我们的代码长成这样:

    (function(exports, require, module, __filename, __dirname) {
        let count=0
        function addCount(){
            count++
        }
        module.exports={count,addCount}
    });
    

    ES6模块与CommonJS的区别

    CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

    CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

    模块化开发怎么做?

    你可能感兴趣的:(lamp,scipy,makefile,crm,lighttpd)