秋招

Posted by hanara on October 19, 2021

7.30 大华笔试

一、相同和严格相同

例子

a ="";
b ="0==''";
console.log(Boolean([]==[]),//[]!=[]为true, []!==[]为true
            Boolean([]== false),//true
            Boolean(0==''),// true在普通相等模式下通过类型转换值相等就行了
            Boolean(0=='0'),//true
            Boolean(NaN==NaN))//false

false,true,true,false

var foo = {
    name: 'zhang'
}
var bar = {
    name: 'zhang'
}
var baz = foo//把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。因此baz和foo都引用同一个对象
console.log(foo == bar) // false
console.log(foo === bar) // false
console.log(foo == baz) // true
console.log(foo === baz) // true,两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,

所以在比较引用类型, 如[], {}, NaN 时普通相等也够不上,两个对象相等比指针,指针一样就一样
其他基本类型string,number,boolean在判断相等==时可通过 类型转换

其他

undefine==undefine true
null==undefined true
null===undefined false
NaN==NaN false 表示不是数字,并不是一个值,表示不确定的状态,所以不等于自身
NaN==false false
'6'== 6 true
'false'== false false
{}=={} false

字节

一、自我介绍

二、布局上下文

flex布局-用法

父盒子设置flex

容器属性

  • 排列方向flex-direction: row↑ / row-reverse↓ / column→ / column-reverse←
  • 有无换行flex-wrap:nowrap / wrap / wrap-reverse;
  • 方向+换行flex-flow:flex-direction / flex-wrap
  • 横向布局justify-content:flex-start靠左 / flex-end靠右 / center居中 / space-between两端对齐 / space-around两侧间隔相等
  • 纵向布局align-items:flex-start靠上 / flex-end靠下 / center居中 / baseline第一行文字基线对齐 / stretch未设高度或auto将拉伸占满整个容器;
  • 多根轴线align-content:flex-start靠上/ flex-end / center / space-between / space-around / stretch;

项目属性:

  • 排列顺序order:num仅限整数
  • 放大比例flex-grow: num
  • 缩小比例flex-shrink:num
  • 放大缩小基准flex-basis:numpx
  • 综合 flex: none / [ <’flex-grow’> <’flex-shrink’>? / <’flex-basis’>
  • 单个对齐align-self: auto默认继承父元素 / flex-start / flex-end / center / baseline / stretch

flex布局-顶部导航栏

  • header—display:flex实现nav与div水平排列,alignItems:center实现垂直方向在header块里居中
    • nav—flex实现a与ul水平排列,center实现垂直方向在nav块里居中,margin-right:auto推开div块使div靠右
      • a>img—设置inline-box
      • ul>li*3—设置inline-box
    • div:登陆注册—设置inline-box
header{
    display:flex;align-items: center; background-color: rgb(221, 221, 221);
}
/* nav{
    display: flex; align-items: center; margin-right: auto;
} */
nav{
    display: flex; align-items: center;
}
header nav {
    margin-right: auto;/* 和设置 div{margin-left:auto效果是一样的} */
}

这里header nav{margin-right:auto}和直接把这条属性写在nav里区别:在nav排版设置了margin值的情况下,为了保持推走右边的div块,使此条权重更大,覆盖设置

flex布局-搜索输入布局

  • form—flex
    • input*2—设置鼠标点击伪元素:text框边大 ```html
<form action="">
    <input type="submit" value="搜索" >
    <input type="text" >
</form> ```

这里flex-grow=2无效,所以说明(1,0)的情况比较特殊,其他子块为0时,它自身为1就一定会占据剩下所有空间?

flex布局-两栏布局

  • div.main—设置flex,wrap
    • nav—设置固定宽度
    • div.content—设置放大比例为1,占据固定宽度以外的其他空间
      div.main{
        display:flex;
        flex-wrap:wrap;
        /* 在页面不断缩小的时候会将盒子往下一行排列 */
      }
      div.main nav{
        flex: 0 0 300px;
        /* 侧边栏固定宽度 */
      }
      div.main div.content{
        flex:1 0 0;
        /* 即把所有剩余空间给content */
      }
      

flex布局-等宽高等高布局

  • div—flex,row
    • section—flex:1,flex,column
      • img
      • p—border
div{
    display:flex;
    flex-direction:row;
}
div section{
    flex:1;
    display:flex;
    flex-direction:column;
}

flex布局-圣杯布局

  • container—flex,col,height(100vh占满整个屏幕)
    • header
    • section—flex:1,flex,row
      • main—flex:1
      • left—order:-1
<style>
    div.container{
        display: flex;
        flex-direction: column;
        height: 20vh;
    }
    section{
        flex: 1;
        display: flex;
        flex-direction: row;
    }
    div.main{
        flex: 1;
    }
    div.left,div.right{
        flex:0 0 100px;
    }
    div.left{
        order: -1;
    }
 </style>
<div class="container">
    <header style="background-color: rgb(139, 139, 240);">头部</header>
<section>
    <div class="main" style="background-color: cornflowerblue;">主要内容</div>
    <div style="background-color: cornsilk;">左列</div>
    <div style="background-color: rgb(247, 112, 139);">右边</div>
</section>
<footer style="background-color: rgb(80, 64, 67);">底部</footer>
</div>

这里设置container:100%和100vh是一样的,但是将container换成body包裹内容之后设置100%就是无效的,100vh依旧可行

flex布局-选项卡布局

  • section—flex,col
    • div.content—flex,wrap,space-around
      • div—flex:0 0 180px设置基线(比如按列排列则高度一致)
      • p
<style>
     section{
        display: flex;
        flex-direction: column;
        background-color: rgb(241, 237, 238);
    }
     div.content{
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
        align-items: center;
    }
    div.content > div{
        flex: 0 0 180px;
    }
    div.content > div:hover{
        align-self: stretch;
    }
    div.content {
        flex: 1;
    }
    div > p{
        font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
        font-size: small
    }
</style>
<section>
    <h1 style="background-color: rgb(241, 237, 238);">项目经历</h1>
    <div class="content">
        <div  style="background-color: rgb(120, 188, 197);">
             <div >个人博客</div> 
             <p>网络纷杂的世界里的独居小屋</p> 
</div>
<div  style="background-color: rgb(149, 201, 180);">
    <div>藏书阁</div>
    <p>让读过的书成为记忆的一部分</p> 

</div>
<div style="background-color: rgb(102, 217, 233);">
    <div>时间轴记录</div>
    <p>时间长河里标记的光亮</p> 
</div>
</div>
</section>

flex布局-朋友圈布局

  • section—flex,row,wrap
    • img头像—width,height
    • div.content—flex,col
      • h1用户名
      • p文字内容
      • img配图—width,height
      • div.more—flex,space-between
        • p时间—设置color,font-size
        • dotdot两小点
          • div—圆点的写法
          • div

grid布局-等高布局

  • section
    • div
      section {
        display:grid;
        grid-auto-flow:column;
      }
      

插入两个小tip实现视觉等高(两列内容高度不一致时)

  • section—设置宽高,颜色(和相对短的div设置为相同颜色)
    • div— 左浮动

用负边距实现等高

  • section—设置宽高,overflow:hidden
    • div—左浮动,padding-bottom拉伸背景色使保持高度一致, margin-bottom把边框往里推
    • div—左浮动,设置不同颜色

table布局-等高布局

  • section
    • div
      section {
        display:table;
      }
      section div {
        width:367px;
        display:table-cell;
      }
      

三、外边距塌陷与合并

塌陷

其实就是子盒float撑不起来父盒子,导致子盒看起来像溢出一样。
就像就是一个大背景里套图片,图片设置了float结果背景就包裹不了
通过清除浮动的方式解决问题

  • 父块设置inline-block(这种情况会导致父块不占满一整行了,默认宽度为内容宽度,但是可以设置宽度)
  • 父块设置overflow: hidden/auto/scroll都可,前两个没有区别;
  • 父块添加 ::after 伪元素
  • 在父元素的最后添加一个块级元素并清除浮动(和伪元素原理是一样的)
.container::after{
        content: "";
        display: block;
        clear: both;
      }
/* 不知道这两种有什么区别,先写上 */
.container-height:after {
/* 内容空;块级;高度为0;不可见;清除所有 */
        content:"";
        display:block;
        visibility:hidden;
        font-size:0;
        line-height:0;
        clear:both;
}  

合并

块级元素的上外边距和下外边距有时会合并(或折叠)为一个外边距,其大小取其中的最大者

  • 父元素添加边框(border);
  • 父元素添加内边距(padding);
  • 父子元素之间存在行内元素 /匿名元素;
  • 父子元素之间存在触发 BFC 的元素(插入一个 display: flex 的块级元素);
  • 父元素触发 BFC(overflow: auto; 等)

参考

云影sky

四、水平居中

行内元素水平居中

行内标签不能直接设置text-align,需要在父标签中设置text-align:center

文本text、图像img、按钮超链接均可

定块级元素水平居中

只需给需要居中的块级元素加margin:0 auto即可,宽度width值一定要有。没有的话默认占满整个屏幕宽度

  • inline-block,float不可以通过margin:0 auto居中,但是可以单独设置四个方向margin
  • inline的 垂直margin 无效
  • 同一行上有inline元素和inline-block元素,对ib的margin修改会影响inline ```html
水平居中


#### 不定宽块级元素水平居中
<span class="quote">如果是 不设置宽高的 普通块,没有内容则不会显示在屏幕上了,有内容则按照横向文档流显示一整行</span>
>方法1:
>+ div---table,magin:0 auto

```html
<style>
  .centerT{
      display:table;
      margin:0 auto;
      border:1px solid red;
  }
  </style>
  <div class="centerT">水平居中</div>

多个内联块

  • div—textAlign
    • div—inline-block

这种情况下默认的间隙来自于内联元素吗

方法2:

  • div—flex,justify-center默认主轴为水平
    • div

flex布局的情况下就没有间隙了

方法4:marginLeft计算水平居中(同marginTop计算垂直居中)
方法5:marginAuto水平居中(同marginAuto垂直居中,起点位置改为left,right)
方法6:transform水平居中(起点改为left:50%, translate(-50%,0))

六、水平垂直居中

818又学到了新方法:利用vertical-align:center 方法1:flex布局

  • div—flex,alignItems,height/weight
    • div

方法2:利用marginTopLeft计算

  • div—relative
    • div—absolute, left:50%; top:50%; margin-left:-50px; margin-top:-100px;

方法3:利用margin:auto

  • div—relative
    • div—absolute,top/bottom/left/right:0,margin:auto

八、css3标签选择器的权重计算

不同选择器以及关系

包含选择符

ol li

子选择符 body>strong

在body>p>strong的关系中是不适用的,子选择符不支持跨级

相邻选择符

div + p

属性选择符

  • 完全匹配
  • 开头即可
  • 包含即可
  • 结尾即可
  • 需包含独立单词

权重

!important>行内样式>ID选择器.100 > 类选择器.10 属性选择器.10 伪类选择器.10 > 元素选择器.1

行内样式总会覆盖外部样式表的任何样式,会被!important覆盖

九、伪元素伪类有哪些如何应用

首先区别伪类和伪元素

  • 伪类基于DOM产生新的状态
  • 伪元素会创建不存在DOM里的对象,但不改变DOM节点

    一些分类

    ||伪类|伪元素| |—-|—-|—-| |区别|可多个拼接
    前方后方均可出现
    修饰|单个
    后方
    新对象| |状态类|:link :visited :hover :active :focus|::before ::after| |结构类|:first-child :last-child :nth-child(n) :nth-of-type()|::first-letter ::first-line| |表单类|:checked :disabled :valid :required|::selection| |语言类|:dir :lang|::backdrop|

    伪类伪元素的例子

十、有哪些重要的HTTP协议

HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol),在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范

  • 应用层:HTTP/FTP/DNS/SMTP/Telnet
  • 传输层;TCP/UDP
  • 网络层:IP/ARP
  • 链路层/网络接口层:MAC

    十一、从浏览器输入网址返回页面

    基本过程

:one:输入网址=>:two:DNS解析得到IP地址=>:three:HTTP协议生成请求报文, TCP协议三次握手=>:four:请求资源并返回数据包=>:five:浏览器渲染与缓存

一些细节

:one:=>:two:

  • 先找浏览器的本地的缓存
  • 再找电脑硬盘里的 host 文件,有没有记录这个域名和 IP 的映射关系
  • 实在没找到,只好通过网络链路去DNS那里查询

DomainNameService 相当于一个数据库里面记录着URL和对应的IP地址

:two:=>:three:

  • 浏览器获取到了服务器对应 IP,就会向对应 IP 的服务器发送 TCP 连接请求。
  • 服务器收到请求后回应,双方经过三次握手后建立起 TCP 双向连接
  • 如果请求是 HTTPS 的,还需要在 TCP 连接上,通过 SSL 或 TLS 提供加密处理数据、验证对方身份以及数据完整性,来保证数据传输的安全

:five:

  • 浏览器拿到服务端返回的数据后根据一定的策略进行数据缓存=>在下一次请求同样数据的时候直接到缓存拿而不再请求服务器
  • 解析接收到的HTML,CSS,JS文件
    • 首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组

十二、回流重绘了解吗以及区别

重绘: 当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格,而不会影响布局的操作,比如 background-color,我们将这样的操作称为重绘。(浏览器渲染的最后绘制阶段)

回流:当渲染树中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而需要重新构建的操作,会影响到布局的操作,这样 的操作我们称为回流。(浏览器渲染的布局阶段)

所以会回流之后还会重绘吗

十三、浏览器垃圾回收和内存泄露

为什么要垃圾回收

:chestnut:当我们声明了一个变量 test,它引用了对象 {name: ‘isboyjc’},接着我们把这个变量重新赋值了一个数组对象,之前在堆里的对象就需要被回收

let test = {
  name: "isboyjc"
};
test = [1,2,3,4,5]

对引用数据类型的操作都是操作对象的引用(栈内存中)而不是实际的对象(堆内存中)。

这种分开存储的目的:栈需要维护程序执行期间的上下文的状态,如果所有数据都存放在栈空间里面,会影响到上下文切换的效率,进而影响整个程序的执行效率。

回收的两种常见算法

:chestnut:标记清除算法
  • 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0

比如当变量进入执行环境时,反转某一位(通过一个二进制字符来表示标记)

  • 然后从各个根对象开始遍历,把不是垃圾的节点改成1
  • 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间
  • 最后,把所有内存中对象标记修改为0,等待下一轮垃圾回收

    ①问题:
    清除之后,由于剩余的对象内存位置是不变=>空闲内存空间是不连续的=>内存分配问题

②解决内存分配问题:
假设新建对象分配内存时需要大小为 size,由于空闲内存不连续,则需对空闲内存列表进行一次单向遍历找出 ≥size 的块才能分配

  • First-fit,找到大于等于 size 的块立即返回
  • Best-fit,遍历整个空闲列表,返回大于等于 size 的最小分块
  • Worst-fit,遍历整个空闲列表,找到最大的分块,然后切成两部分,一部分 size 大小,并将该部分返回

③缺点:

  • 内存碎片化, 容易出现很多空闲内存块,还可能会出现分配所需内存过大的对象时找不到合适的块
  • 分配速度慢,因为即便是使用 First-fit 策略,其操作仍是一个 O(n) 的操作,最坏情况是每次都要遍历到最后,同时因为碎片化,大对象的分配效率会更慢

④补充: 标记整理(Mark-Compact)算法 就可以有效地解决,它的标记阶段和标记清除算法没有什么不同,只是标记结束后,标记整理算法会将活着的对象(即不需要清理的对象)向内存的一端移动,最后清理掉边界的内存

:chestnut: 引用计数算法
  • 当声明了一个变量并且将一个引用类型赋值给该变量的时候这个值的引用次数就为 1
  • 如果同一个值又被赋给另一个变量,那么引用数加 1
  • 如果该变量的值被其他的值覆盖了,则引用次数减 1
  • 当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法被访问了,回收空间

垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的内存

let a = new Object() 	// 此对象的引用计数为 1(a引用)1
let b = a 		// 此对象的引用计数是 2(a,b引用)      +1
a = null  		// 此对象的引用计数为 1(b引用)        覆盖-1=1
b = null 	 	// 此对象的引用计数为 0(无引用)       覆盖-1=0
...			// GC 回收此对象

①问题:相互(循环)引用的内存不被清理
在函数 test 执行完成之后,对象 A 和 B 是要被清理的,而由于它们的引用数量不会变成 0 则不会被清理
假如此函数在程序中被多次调用,那么就会造成大量的内存不会被释放
:exclamation:标记清除的角度看一下,当函数结束后,两个对象都不在作用域中,A 和 B 都会被当作非活动对象清除

function test(){
  let A = new Object()
  let B = new Object()
  A.b = B
  B.a = A
}

②过去使用场景
在 IE8 以及更早版本的 IE 中,BOM 和 DOM 对象并非是原生 JavaScript 对象,它是由 C++ 实现的 组件对象模型对象(COM,Component Object Model),而 COM 对象使用 引用计数算法来实现垃圾回收,所以即使浏览器使用的是标记清除算法,只要涉及到 COM 对象的循环引用,就还是无法被回收掉,就比如两个互相引用的 DOM 对象等等,而想要解决循环引用,需要将引用地址置为 null 来切断变量与之前引用值的关系,如下 ```js // COM对象 let ele = document.getElementById(“xxx”) let obj = new Object()

// 造成循环引用 obj.ele = ele ele.obj = obj

// 切断引用关系 obj.ele = null ele.obj = null

>但在 IE9 及以后的 BOM 与 DOM 对象都改成了 JavaScript 对象,避免了上面的问题
<br>--------此处参考 JavaScript高级程序设计 第四版 4.3.2 小节

>③缺点:<br>
>+ 计数器需要占很大的位置
>+ 循环引用


#### V8优化☞分代垃圾回收

>堆空间存储的数据比较复杂,大致可以划分为下面 5 个区域:代码区(Code Space)、Map 区(Map Space)、大对象区(Large Object Space)、新生代(New Space, 1~8M 容量)、老生代(Old Space)。新生代内存是临时分配的内存,存活时间段,老生代内存是常驻内存,存活时间长。

>分代回收的意义:设置不同频率的回收=>提高了垃圾回收机制的效率
__2-1新生代内容回收__

新生代中用 Scavenge 算法来处理。所谓 Scavenge 算法,是把新生代空间对半划分为两个区域,一半是对象区域(from),一半是空闲区域 (to)。

新的对象开始被分配到 from 空间,当进行垃圾回收时
+ 存活对象复制到 to 空间保存
+ 对未存活的对象的空间进行回收。
+ 复制完成后, from 空间和 to 空间进行调换
to 空间会变成新的 from 空间,原来的 from 空间则变成 to 空间。这种算法称之为 ”Scavenge“。

 <div style="text-align:center">
<span style="font-size:10px">图来自掘金👆</span> 
</div>

__2-2老生代内存回收__

__晋升__

新生代中多次进行回收仍然存活的对象会被转移到空间较大的老生代内存中,这种现象称为晋升。以下两种情况

+ 在垃圾回收过程中,发现某个对象之前被清理过,那么将会晋升到老生代的内存空间中
+ 在空间反转的过程中,如果 to 空间中的使用量已经超过了 25% ,那么就把 from 中的对象直接晋升到老生代内存空间中

#### V8优化☞并行回收

js 处理多个执行上下文的方式为stack(先进后出,直行单线)<br><code>全停顿:</code>进行垃圾回收时就会阻塞 JavaScript 脚本的执行,需等待垃圾回收完毕后再恢复脚本执行

垃圾回收器在主线程上执行的过程中,开启多个辅助线程,同时执行同样的回收工作


>这些线程同时将对象空间中的数据移动到空闲区域,这个过程中数据地址会发生改变,所以还需要同步更新引用这些对象的指针

#### V8优化☞增量标记与懒性清理
__4-1增量标记__

因为并行回收对老生代区域的大对象清理依旧耗时=>再次优化👇

针对<code>老生代区域</code>:<br>将一次 GC 标记的过程,分成了很多小步,每执行完一小步就让应用逻辑执行一会儿,这样交替多次后完成一轮 GC 标记(如下图)

问题:
+ 确定每一小块暂停和开始的位置---<code class="wrong">三色表记法</code>
+ 在一次完整的 GC 标记分块暂停后,执行任务程序时内存中标记好的对象引用关系被修改(因为中间执行了js代码)又怎么办---<code class="wrong">写屏障</code>

__4-2懒性清理__

增量标记完成后,惰性清理就开始了

稍微延迟清理过程,让 JavaScript 脚本代码先执行

按需逐一进行清理直到所有的非活动对象内存都清理完毕, 也无需一次性清理完所有非活动对象内存

__4-2该优化的缺点__

>并没有减少主线程的总暂停的时间,甚至会略微增加,其次由于写屏障机制的成本,增量标记可能会降低应用程序的吞吐量

#### V8优化☞并发回收
主线程在执行 JavaScript 的过程中,辅助线程能够在后台完成执行垃圾回收的操作,辅助线程在执行垃圾回收的时候,主线程也可以自由执行而不会被挂起
>并发:交替做不同事的能力<br>
>并行:同时做不同事的能力



>方法缺点:它需要额外实现一些读写锁机制来控制<code>堆中的对象引用关系的变化</code>

#### 内存泄漏
不是所有无用对象内存都可以被回收的,那当不再用到的内存,没有及时回收时,我们叫它 __内存泄漏__

[垃圾回收机制参考1](https://juejin.cn/post/6981588276356317214)<br>
[垃圾回收机制参考2](https://segmentfault.com/a/1190000023162310)



### 十四、es6增加了哪些变量和特性
#### const与var

共同点:

+ 不能重复声明已存在的变量
+ 块作用域
+ 有暂时死区,不会被提升

不同点:
+ const声明变量时必须初始化变量

>作用域指的是可以访问的变量集合(相当于可以访问到该变量的区域为块)

>const:一旦定义不可修改:point_right:是指对象对应的堆内存的指针是不变的,但是指向的堆中的内容可以改变

:chestnut:1 const定义不能修改例子

```js
const person = {
    name: "hanara",
    age: 23
}
person.age = 18 // 没问题
person = {} // 报错 不能修改对象指针

:chestnut:2 为了不影响全局变量

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

分析:

因为var函数作用域,for循环不是函数,出了for循环之后还是占用变量i,所以依旧能打印出i值

而let的块作用域,所以i属于局部变量,不会污染全局变量

:chestnut:3 执行细节

console.log(typeof a);
//1.JavaScript引擎在扫描代码时发现变量声明时,遇到var会将它们提升到当前作用域的顶端
//2.这里var变量声明提升,初始化为function, 所以输出function
a();
//1.找到所有用 a 声明的变量,在环境中「创建」这些变量
//2.将这些变量「初始化」并「赋值」为 function a(){ console.log(typeof a) }
//3.开始执行代码 a()
//4.因为这里还是属于被声明提升了,输出function
var a = 3;
//运行到这里才给a赋值3
function a() {
 console.log(typeof a);
}
console.log(typeof a);
// 好,所以这里输出number
a();
//a已经不是个函数了,输出类型错误

换成let之后直接第一句就会报错,第一句时的a还是处于在JavaScript的暂时死区(temporal dead zone)

:chestnut:4 暂时死区

let x = 1
{
  console.log(x) 
  //1. console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
  //2. 此时x并未初始化,在死区,所以会报错Uncaught ReferenceError: x is not defined
  let x = 1
}

新增数据类型

Symbol 让一个对象居右私有属性,让Symbol作为对象的key。在对象作用域外就无法打印 BigInt

箭头函数

一般函数定义:

①普普通通: const aaa =function( ){ }
②对象中定义函数: const obj ={ bbb: function( ){ }, bbb( ){ } }

箭头函数的写法

两个参数时

const sum =(num1,num2) =>{
return num1+num2
}

一个参数时

const printName = (name)=>{   
 return "hello"+ name;
}
const printName = name => "hello"+ name;

VueCLI3例子

render :function(h) ⇒ h(App)

new Vue({
  render: function (h) {
    return h(App)
  },
}).$mount('#app')

优点

  • 能够使函数的写法更简洁
  • 隐式返回值
  • 不重新绑定this值

    SetMap

1 Set.add增/delete删/has返回trueFalse查/forEach遍历

2 Map.set以健和值的方式存储数据/Map.get返回某个key的value/Map.has遍历键返回true

3 for…of

4 使用数组的结构直接将key/value分别解构出来

numberSet.forEach(function (number) {  
 return numberSet   
 })
for (const c of cities) {       
 console.log(c[1]);   
 }
for (const [key, value] of cities) {     
  console.log(value);   
 }
var r = arr.filter(function (s) {    
 return s && s.trim(); 
}); 
var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {  
  console.log(element); 
 console.log(index);
 console.log(self); 
 return true;
});

十五、宏任务和微任务

存在的理由

Js单线程=>进程阻塞=>两种任务执行模式:同步和异步
在异步模式下任务队列分为宿主(浏览器,node)发起的宏任务(Task)和JS自身发起的微任务(Microtask)

注:浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。

大致过程

  • 所有同步任务都在主线程上,形成一个执行栈
  • 主线程之外还有一个“任务队列”,只要异步任务有了运行结果 ,就在任务队列中放一个事件
  • 当执行栈中所有的任务执行完了,就去看看任务队列中有没有需要执行的事件 ,如果有的话,就结束它们的等待,进入执行栈 ,开始执行。
  • 主线程不断重读上面三步

    解释

    宏任务:

  • script代码

  • 用户交互事件的回调函数, I/O

:chestnut:如鼠标点击事件触发以后,里面的回调函数就会被调入任务队列

  • 定时器setTimeout()/ setInterval()/ setImmediate()

微任务:

  • Promise.then()/catch()/finally()
  • async/await
  • MutationObserve
  • Object.observe

调用栈

  • 调用栈记录了你进入一个函数以后去到哪里
  • JS引擎会在你调用一个函数以前把这个函数需要用到的环境推到一个数组–调用栈
  • 等到函数执行完JS会把环境pop出来然后return到之前的环境继续执行代码

任务队列

  • 一种先进先出的一种数据结构

事件循环

  • 选择当前要执行的任务队列,选择任务队列中最先进入的任务
  • 如果任务队列为空即null,则执行跳转到微任务(MicroTask)的执行步骤。
  • 将事件循环中的任务设置为已选择任务。
  • 执行任务。
  • 将事件循环中当前运行任务设置为null。
  • 将已经运行完成的任务从任务队列中删除。
  • microtasks步骤:进入microtask检查点。
  • 更新界面渲染。
  • 返回第一步。

异步任务不会立即放入执行栈,而是由浏览器处理得到结果后放入任务队列,等执行栈执行完毕后 根据时间间隔 执行任务队列

代码示例

   function app() {
      setTimeout(() => {
        console.log("1-1");
    // 5. setTimeout()继续放入任务队列尾部
        Promise.resolve().then(() => {
          console.log("2-1");
    // 6. Promise.then微任务,放入当前宏任务的微任务队列
        });
      });
      console.log("1-2");
    // 1.全局同步代码【console.log("1-2")】 首先进入调用栈,执行并在控制台输出1-2后出调用栈,输出1-2
      Promise.resolve().then(() => {
        console.log("1-3");
    // 2.Promise.then微任务【console.log("1-3")】放入微任务队列 :在微任务队列里pop出最先进入的任务,在调用栈里运行Promise回调,输出1-3
    // 7.同步代码全部执行完,进入
        setTimeout(() => {
          console.log("3-1");
    // 3. setTimeout宏任务【console.log("3-1")】放入宏任务队列
        });
      });
    }
    app();
    //4. app()进入调用栈

十六、面试代码

let/var

所谓提升(hoist)是把所有变量声明拉到函数作用域的顶部

if (true) {
  console.log(a);
// let. 没有初始化,在死区,所以输出没有初始化
// var. 定义提升,但尚未赋值,输出undefine
  let a = 1;
}
console.log(a);
// let. 被作用域限制,依旧输出没有初始化(实际运行的时候已经停在了第一个console.log)
// var. var不受块作用域限制,依旧能找到被赋值后的a, 输出1

let输出结果:Uncaught ReferenceError: Cannot access ‘a’ before initializationa没有初始化
var输出结果:undefine 1

分析:

  • 运行到第一个console.log就报错,因为let作用域为块级,变量a没有被声明提升
  • var为函数作用域(跳出当前块依旧能访问到var的变量):arrow_right:存在声明提升(声明了变量a但此时未给a赋值):arrow_right:所以console.log后为undefine
    再在if语句中对a赋值;跳出了选择块if{}之后依然占用变量名, 依旧存在a=1:arrow_right:第二个console.log输出1

Promise/宏任务/微任务

async function f1() {
     console.log('1');
     await f2();
     console.log('2');
}
async function f2() {
  console.log('3');
}
setTimeout(function() {
  console.log(0);
}, 0);
f1();
Promise.resolve().then(function() {
  console.log(4);
  return Promise.resolve().then(function() {
    console.log(5);
  });
});
new Promise(function(resolve) {
  console.log(6);
  resolve();
}).then(function() {
  console.log(7);
});
console.log(8);
  • setTimeout()只是将事件插入了”任务队列”,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。
  • 关于setTimeout(fn,0):setTimeout即使是0毫秒,也不会立即执行,而是堆在队列尾部等待,因此要等到同步任务和”任务队列”现有的事件都处理完,才会得到执行。

执行同步任务阶段

  • 宏任务1,setTimeout,等待时间进入任务区宏任务队列在0ms(4ms)的时候放入了一段setTimeout代码
  • 宏任务2,f1(), f2(), new Promise
    • 微任务 f1() 异步代码
    • 微任务 Promise.resolve().then() 异步代码
      • return Promise.resolve().then()
    • 微任务 New Promise.resolve.then() 异步代码

输出:1 3 6 8

执行异步代码:第一轮loop

  • 微任务 f1() 异步代码
  • 微任务 Promise.resolve().then() 异步代码
  • 微任务 New Promise.resolve.then() 异步代码

输出:2 4 7

执行异步代码:第一轮loop

  • return Promise.resolve().then()
    并不会放在第二轮宏任务setTimeout后面的原因是没有嵌套在其他宏任务之下,也就是说,return Promise这个微任务依旧在同步执行代码的宏任务之后,下一轮宏任务(setTimeout)之前

    输出:5

执行异步代码:第二轮loop

  • setTimeout

    输出:0

let fn = () => {
    console.log(1)
    // 1.同步任务,输出1
  let a = new Promise((resolve, reject) => {
      console.log(2)
    // 2.同步任务,输出2
    resolve(3)
    // 3.异步任务,放入队列
  })
  console.log(4)
    // 4.同步任务,输出4
  return a
    //5.函数返回a=3
}
fn().then(data => console.log(data))
    //6.输出3
function asyncFunction() {  //(1)
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Async Hello World');
    }, 300);
  });
}

asyncFunction().then(function(value) {  //(2)
  console.log(value); //300ms后打印 "Async Hello World"
}).catch(function(error) {  //(3)
  console.log(error);
})

执行(1)处函数,会返回一个Promise对象,Promise对象内部在300ms后执行resolve()方法,这个方法调用(2)处的then()方法,并传入参数,如果Promise对象内部出现任何错误(比如平台不支持setTimeout方法),就会执行(3)处的catch()发放,并把错误作为参数传入

十七、参考

johnnie_wang-简书
宏任务微任务讲解–来自掘金
事件循环–来自光光同学
从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节

816投递shopee快手旷视

817面试题准备

一、CORS跨域

script/img/frame/link标签具有跨域特性,可以引用一些其他网站的资源

1.造成跨域请求错误的原因☞同源策略

首先是意义 浏览器可以利用 CORS 机制,放行符合规范的跨域访问,阻止不合规范的跨域访问。
同源策略 协议/子域/端口 其一不同则跨域

  • Cookie、LocalStorage 和 IndexDB 访问受限
  • 无法操作跨域 DOM(常见于 iframe)
  • Javascript 发起的 XHR 和 Fetch 请求受限
    • XHR: XMLHttpRequest.在不刷新页面的情况下请求特定 URL,获取数据。可以用于获取任何类型的数据,而不仅仅是 XML。它甚至支持 HTTP 以外的协议(包括 file:// 和 FTP)
    • Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

      2.解决方法☞服务端 CORS(后端/服务器操作)

Server do: 将返回的HTTP响应报文 response 加一些响应头字段 Access-Control-* 常见字段:

  • Access-Control-Allow-Origin这个头字段的值指定了哪些站点被允许跨域访问资源。
  • Access-Control-Allow-Methods。其指明了跨域请求所允许使用的 HTTP 方法。
    // php代码,表示允许所有请求url
    header("Access-Control-Allow-Origin:*");
    

    ```json HTTP/1.1 200 OK Access-Control-Allow-Origin: https://www.mywebsite.com Access-Control-Allow-Methods: [“POST”,”GET”,”PUT”] Date: Fri, 11 Oct 2019 15:47 GM Content-Length: 29 Content-Type: application/json Server: Apache

{user: [{…}]}

#### 3.预检请求
一个CORS请求可以按照是否有预检响应来分类为简单请求和预检请求
+ 客户端发送实际HTTP前先用 OPTIONS 方法发起预检请求
+ 服务器接收到预检请求后返回一个没有 body 的 HTTP 响应,这个响应标记了服务器允许的 HTTP 方法和 HTTP Header 字段
<code class="wrong">所以怎么通过这两个字段判断是否服务器启用了CORS呢</code>

+ 浏览器收到预检响应,并检查是否应允许发送实际请求
+ 预检响应检测
    + 通过,浏览器会将实际请求发送到服务器然后返回资源
    + 没有检验通过,CORS 会阻止跨域访问,实际的请求永远不会被发送

#### 5.手动实现在CORS下使XHRFetch发送身份凭证
基于 Cookies 和 HTTP 认证信息发送身份凭证。一般而言,对于跨域 XHR 或 Fetch 请求,浏览器不会发送身份凭证信息。
#### 6.跨域请求代码的代码案例
__自写接口__
```html
<body>
    <h1>test CORS</h1>
    <script>
        function test(a){
            console.log(a);
        }
    </script>
    <!-- 请求后端接口返回数据 -->
    <!-- callback是为了通知后端使用test方法 -->
    <script src="http://local.server.com/index/kua?callback=test"></script>
</body>
// index.php
public function kua(){
    // 相当于返回了test(123)的数据
    $callback = $_GET["callback"];
    return $callback.'(123)';
}

jQuery

<body>
    <h1>test CORS</h1>
    <script>
        function test(a){
            console.log(a);
        }
    </script>
    <!-- 请求后端接口返回数据 -->
    <!-- callback是为了通知后端使用test方法 -->
    <script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
    <script>
        // 此时jQuery能够自动生成类似于“callback?xxxx”的接口代码
        $ajax({
            type: "get",
            url:"http://local.server.com/index/kua",
            dataType:"jsonp",
            header:""
            success:function(response){
                console.log(response)
            }
        });
    </script>
</body>
// index.php
public function kua(){
    // 相当于返回了test(123)的数据
    $callback = $_GET["callback"];
    return $callback.'(123)';
}

二、深拷贝和浅拷贝

1.从数据类型入手

基本类型值在内存中占据固定大小,保存在栈内存

  • undefined,null
  • Boolean
  • String,Number
  • Symbol

引用类型值如对象的内容,保存在堆中,栈中是对象的变量标识符以及对象在堆内存中的存储地址

  • Obj
  • Arr
  • Date
  • Function
  • RegExp

两种类型的简单赋值

基本类型赋值,改变赋值后的值不会影响到原始值
而引用类型复制的是指针,改变复制后的内容(堆内存内容),因为指针是一样的所以原始值也会变化

let foo = 1;
let bar = foo;
console.log(foo === bar); // -> true
// 修改foo变量的值并不会影响bar变量的值
let foo = 233;
console.log(foo); // -> 233
console.log(bar); // -> 1

let foo = {
  name: 'leeper',
  age: 20
}
let bar = foo;
console.log(foo === bar); // -> true
// 改变foo变量的值会影响bar变量的值
foo.age = 19;
console.log(foo); // -> {name: 'leeper', age: 19}
console.log(bar); // -> {name: 'leeper', age: 19}

2.对于引用数据类型而言的浅拷贝实现

Obejct_assign

let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
let obj2 = Object.assign({}, obj1);
obj2.person.name = "wade";
obj2.sports = 'football'
console.log(obj1); // 原数据一同改变{ person: { name: 'wade', age: 41 }, sports: 'basketball' }

展开运算符

展开运算符是一个 es6 / es2015特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign ()的功能相同。

对于对象

let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) //改变原数据也会影响浅拷贝的数据 obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

对于数组

所以数组里面不包含对象姑且算深拷贝,即改变新数组的某个元素不会影响到原数组
但是数组里的元素是对象则还是浅拷贝

let arr1 = [{dinner:'pancake'}]
let arr2= [... arr1]
arr2[0].dinner = 'peach';
console.log('arr1',arr1) //改变原数据也会影响浅拷贝的数据 arr1 [{dinner:'peach'}]

数组的其他浅拷贝方法

  • const ACopy=[].concat(A)
  • const ACopy=A.slice()
  • const ACopy=Array.from(A)

3.对于引用数据类型而言的深拷贝的实现

JSON.parse(JSON.stringify()) JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,产生新的对象,而且对象会开辟新的栈,实现深拷贝

let arr = [1, 3, {
    username: 'hanara'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'tjh'; 
console.log(arr, arr4)//hanara,tjh

函数库lodash的_.cloneDeep方法

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

参考

阿里

八股文就不说了 vue中的单页面复用 项目中的难点

明略科技

vue生命周期 如何判断数组

顺丰

前端缓存以及应用

美团

前端优化经验

字节跳动正式批

原型链

海康威视

  1. 不改变url下页面锚点跳转如何实现
  2. 前端路由的方式

    转转

  3. 设计模式—发布订阅模式
  4. 水平垂直居中
  5. objectDefineProperty传入数组的情况
  6. 复制一个对象,需要注意什么问题
  7. em/rem底层原理
  8. 跳台阶打印

    腾讯微保

  9. 双向绑定
  10. get,post区别
  11. webpack使用经验
  12. 用过哪些loader
  13. loader,plugin区别
  14. 放一张图片,水平垂直居中且不变形
  15. routerlink原理
  16. 用vue的遇到坑的一次经历
  17. http2.0新特性
  18. window.load
  19. 做过哪些响应式布局
  20. 事件循环里,宏任务微任务具体执行
  21. 三列布局

    竞技世界

  22. 单页面复用怎么做的,有哪些特性
  23. 如何解决单页面复用不利于Seo检索
  24. 了解哪些前端页面优化
  25. 如何实现播放器弹幕功能
  26. 如何写一个登录页面拼图组件/文字按序选择组件
  27. 用到过跨域吗,cors针对复杂请求的预检请求怎么做
  28. 用过哪些数据库
  29. 用过node写过api吗
  30. 反问