面试题汇总

本文最后更新于:2023年6月14日 下午

如何区分伪元素和伪类

伪元素和伪类最根本的区别就在于是否创造了新的元素

伪类

伪类是用来定义元素特殊状态的,他可以用来设置鼠标悬停样式、元素获取焦点样式、设置链接样式等。如常见的 hover、active、link 等都是伪类。

伪元素

伪元素也称为伪对象,它不存在于 DOM 文档中、是一个虚拟的元素。它可以用来代表某个元素的子元素,但是这个子元素并不存在于文档树中。

BFC

(Block firmatting context) 块级格式化上下文

能够形成独立的渲染区域,内部元素的渲染不会影响外界

形成BFC 的常见条件

浮动元素

绝对定位

块级元素 overfolw 不是 visible

flex元素

inline-block元素

应用场景:

1.清除浮动,

2.Margin边距重叠,

3.解决当父级元素没有高度时,子级元素浮动会使父级元素高度塌陷的问题等

Q: only 选择器的作用是?

1
@media only screen and (max-width: 1024px) {argin: 0;}

A:停止旧版本浏览器解析选择器的其余部分。

Q: screen关键词是指设备物理屏幕的大小还是指浏览器的视窗?

1
@media only screen and (max-width: 1024px) {margin: 0;}

A: 浏览器视窗

href 和 src的区别

1.请求资源类型不同
href,超文本引用,用于建立文档与资源的联系,常用的有:link、a。
src,将其所指向的资源下载并应用到当前页面,常见的有script、img。
2.作用结果不同
href,用于文档与资源之间确立联系。
src,请求到的资源替换当前内容。

3.浏览器的解析不同
href,将资源解析成css文件,并行加载请求资源,不会阻塞对当前文档的处理。
src,会暂停其他资源的处理,直到该资源加载、解析和执行完毕,将其所指向资源应用到当前内容。这也是为什么把js文件放在底部而不是头部发热原因

首屏加载白屏怎么优化

1.使用CDN减少代码体积,加快请求速度;

2.SSR通过服务端把所有数据全部渲染完成再返回给客户端

3.路由懒加载

4.使用外链CSS,JS文件

5.开机GZIP压缩

6.使用骨架屏

输入url到打开页面,都做了什么

1.输入url,查找缓存,浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容,如果没有则进行下一步

2.DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中域名对应的IP地址

3.TCP握手:解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接

4.HTTP请求:浏览器发起读取文件的HTTP请求,该请求报文作为TCP三次握手的第三次数据发送给服务器;

5.HTTP响应返回数据结果;

6.通过四次挥手关闭/释放TCP连接;

7.浏览器解析数据并渲染页面

TCP的三次握手和四次挥手

三次握手是客户端和服务器之间建立连接,并进行通信的过程。相当于客户端和服务器之间你来我往的三个步骤

  • 第一次握手是建立连接,客户端发送连接请求报文,并传送规定的数据包;
  • 第二次握手是服务器端包是接收到连接请求报文,并回传规定的数据包
  • 第三次握手是客户端接收到服务器回传的数据包后,再次给服务器端发送数据包

这样就完成了客户端跟服务器的连接和数据传送;

三次握手的目的:

双方确认自己与对方的发送与接收是正常的。

四次挥手表示当前连接请求已经结束,要断开这次连接。

  • 第一次挥手是客户端对服务器发起断开请求
  • 第二次挥手是服务器表示收到这次断开请求
  • 第三次挥手是服务器表示已经断开连接;
  • 第四次挥手是客户端已经断开连接

清除浮动的四种办法

  1. clear:both; 在最后面加一个盒子
  2. overflow:hidden; 父盒子
  3. 伪元素:after 父盒子
  4. 双伪元素 父盒子

Vue的生命周期钩子

beforCreated

Created

beforeMounted

Mounted

beforeUpdated

updated

beforeDestory

destoryed

Actived

Deactived

什么是闭包,闭包解决了什么问题

闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来.

我们可以用来保存一些内容,还可以用来保护一些私有的变量。我们总结出闭包有两个作用,分别为保护和保存。

团队开发时,每个开发者把自己的代码放在一个私有的作用域中,防止相互之间的变量命名冲突;把需要提供给别人的方法,通过 return 或 window.xxx 的方式暴露在全局下。

原型链

总结:

1、当一个对象查找属性和方法时会从自身查找,如果查找不到则会通过__proto__指向被实例化的构造函数的prototype

2、隐式原型也是一个对象,是指向我们构造函数的原型

3、除了最顶层的Object对象没有__proto__,其他所有的对象都有__proto__,这是隐式原型

4、隐式原型__proto__的作用是让对象通过它来一直往上查找属性或方法,直到找到最顶层的Object的__proto__属性,它的值是null,这个查找的过程就是原型链

箭头函数和普通函数的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(1)箭头函数比普通函数更加简洁
如果没有参数,就直接写一个空括号即可
如果只有一个参数,可以省去参数括号
如果有多个参数,用逗号分割
如果函数体的返回值只有一句,可以省略大括号
如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常用的就是调用一个函数:
let fn = () => void doesNotReturn()

(2) 箭头函数没有自己的this
箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中的this的指向在它在定义时一家确定了,之后不会改变。

(3)箭头函数继承来的this指向永远不会改变

(4) call()、apply()、bind()等方法不能改变箭头函数中的this指向

(5) 箭头函数不能作为构造函数使用

(6) 箭头函数没有自己的arguments

(7) 箭头函数没有prototype

(8) 箭头函数不能用作Generator函数,不能使用yeild关键字

localStorage sessionStorage cookies 有什么区别?

localStorage:以键值对的方式存储 储存时间没有限制 永久生效 除非自己删除记录
sessionStorage:当页面关闭后被清理与其他相比不能同源窗口共享 是会话级别的存储方式
cookies 数据不能超过4k 同时因为每次http请求都会携带cookie 所有cookie只适合保存很小的数据 如会话标识

Vuex有哪些基本属性?为什么 Vuex 的 mutation 中不能做异步操作?

有五种,分别是 State、 Getter、Mutation 、Action、 Module
1、state => 基本数据(数据源存放地)
2、getters => 从基本数据派生出来的数据
3、mutations => 提交更改数据的方法,同步
4、actions => 像一个装饰器,包裹mutations,使之可以异步。
5、modules => 模块化Vuex

1、Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
2、每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。

常见的水平居中

  1. ```css
    .father{

     display:flex;
     justify-content:center;
     align-items:center;
    

    }

    .son{
    ….
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    2. 绝对定位

    ```css
    .father {
    position: relative;
    }
    .son {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin: auto;
    }
  2. 绝对定位配合transform

    1
    2
    3
    4
    5
    6
    7
    8
    9
    .father {
    position: relative;
    }
    .son {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    }

Js的基本类型和引用类型

基本类型:undefined , string, number, null ,bigint,boolean,symbol

引用类型: Object,function,array

typeof/instanceof

TypeScript的 type和 interface 的区别

interface可以重复声明,type不行,继承方式不一样,type使用交叉类型方式,interface使用extends实现。在对象扩展的情况下,使用接口继承要比交叉类型的性能更好。建议使用interface来描述对象对外暴露的借口,使用type将一组类型重命名(或对类型进行复杂编程)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
interface iMan {
name: string;
age: number;
}
// 接口可以进行声明合并
interface iMan {
hobby: string;
}

type tMan = {
name: string;
age: number;
};
// type不能重复定义
// type tMan = {}

// 继承方式不同,接口继承使用extends
interface iManPlus extends iMan {
height: string;
}
// type继承使用&,又称交叉类型
type tManPlus = { height: string } & tMan;

const aMan: iManPlus = {
name: "aa",
age: 15,
height: "175cm",
hobby: "eat",
};

const bMan: tManPlus = {
name: "bb",
age: 15,
height: "150cm",
};

常见的工具类型

1
2
3
Partial:满足部分属性(一个都没满足也可)即可
Required:所有属性都需要
Readonly: 包装后的所有属性只读

computed 和 watch 的区别

使用场景:computed适用于一个数据受多个数据影响使用;watch适合一个数据影响多个数据使用。

区别:computed属性默认会走缓存,只有依赖数据发生变化,才会重新计算,不支持异步,有异步导致数据发生变化时,无法做出相应改变;watch不依赖缓存,一旦数据发生变化就直接触发响应操作,支持异步。

浏览器渲染页面的过程

首先输入一个网址,浏览器会向服务器发起DNS请求,得到对应的IP地址(会被缓存一段时间,后续访问就不用再去向服务器查询)。之后会进行TCP三次握手与服务器建立连接,连接建立后,浏览器会代表用户发送一个初始的GET请求,通常是请求一个HTML文件。服务器收到对应请求后 ,会根据相关的响应头和HTML内容进行回复。

一旦浏览器拿到了数据,就会开始解析信息,这个过程中,浏览器会根据HTML文件去构建DOM树,当遇到一些阻塞资源时(如同步加载的script标签)会去加载阻塞资源而停止当前DOM树构建(所以能够异步的或延迟加载的就尽量异步或延迟,同时页面的脚本还是越少越好)。在构建DOM树时,浏览器的主线程被占据着,不过浏览器的预加载扫描器会去请求高优先级的资源(如css、js、字体),预加载扫描器很好的优化了阻塞问题。接下来浏览器会处理CSS生成CSSDOM树,将CSS规则转换为可以理解和使用的样式映射,这个过程非常快(通常小于一次DNS查询所需时间)。有了DOM树和CSSDOM树,浏览器会将其组合生成一个Render树,计算样式或渲染树会从DOM的根节点开始构建,遍历每一个可见节点(将相关样式匹配到每一个可见节点,并根据CSS级联去的每个节点的计算样式)。接下来开始布局,该过程(依旧是从根节点开始)会确定所有节点的宽高和位置,最后通过渲染器将其在页面上绘制。绘制完成了,并不代表交互也都生效了,因为主线程可能还无法抽出时间去处理滚动、触摸等交互,要等到js加载完成,同时主线程空闲了整个页面才是正常可用的状态。

webpack中plugin和loader分别做什么?它们之间的执行顺序?

  • loader:用于将不同类型的文件转换成webpack可以识别的文件(webpack只认识js和json)。
  • plugin:存在于webpack整个生命周期中,是一种基于事件机制工作的模式,可以在webpck打包过程对某些节点做某些定制化处理。同时plugin可以对loader解析过程中做一些处理,协同处理文件。
  • 执行顺序:两者不存在明显的先后顺序,不过webpack在初始化处理时,会优先识别到plugin中的内容。

webpack常见的优化方案

  • 基于esm的tree shaking
  • 对balel设置缓存,缩小babel-loader的处理范围,及精准指定要处理的目录。
  • 压缩资源(mini-css-extract-plugin,compression-webpack-plugin)
  • 配置资源的按需引入(第三方组件库)
  • 配置splitChunks来进行按需加载(根据)
  • 设置CDN优化

app.use的原理

vue中使用插件的方法

左边固定,右边自适应

  1. 浮动

    左边设置固定宽度,向左浮动,右边设置margin-left:100px;width:auto

  2. BFC+浮动

​ 左边设置固定宽度,向左浮动,右边设置overflow:hidden 触发BFC

  1. flex:1(flex-basic:auto,flex-shrink:1,flex-grow:1)
  2. grid布局

​ grid-template-columns:100px 1fr; 这样子的话就是右边自适应

  1. 绝对定位

​ 左元素设置为绝对定位,left:0,width:100px

​ 右元素设置为left:100

scoped的执行原理

当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,通过该属性,可以使得组件之间的样式不互相污染。

Vue中的scoped属性的效果主要是通过PostCss实现的。给一个组件中的所有dom添加了一个独一无二的动态属性,给css选择器额外添加一个对应的属性选择器,来选择组件中的dom,这种做法使得样式只作用于含有该属性的dom元素(组件内部的dom)。

1
2
3
4
5
6
7
8
.home{
color:red;
}

-------------
.home[data-v-e172323]{
color:red;
}

样式穿透的原理

普通的css语法: 在要修改的样式前添加 >>> 符号

1
2
3
.demo >>> .el-table{
border: none;
}

scss语法: 在要修改的样式前添加 ::v-deep

1
2
3
::v-deep .el-table{
border:none;
}

sass语法/less语法 : 在要修改的样式前添加 /deep/

1
2
3
.demo /deep/ .el-table{
border:none;
}

用了样式穿透后,被穿透的dom不会再加上唯一标识[data-v-xxxxxx]

水平垂直居中,垂直居中

水平垂直居中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
text-align:center;  //只对inline起作用

margin:0 auto; //margin:0 auto;是对元素自身(block)起作用,

position:absoult;
top:50%;
left:50%;
transform:translate(-50%,-50%)

display:flex;
justify-content:center;
align-item:center;

display:grid;
align-items: center;
justify-content: center;

垂直居中

1
2
3
4
5
6
7
8
9
10
11
line-height === height

vertical-align:middle


.father{
display:flex;
}
.son{
align-self:center;
}

flex 和 grid的一些属性

看mdn吧,太多了

less 和 scss

css module

git的一些操作

git stash 怎么恢复回来?

1.使用git status指令查看当前文件状态。

2.然后,使用指令git stash 将文件修改缓存。

3.使用git status指令确认当前分支没有修改内容。

4.使用指令git stash list,查看当前缓存列表。

5.使用指令git stash apply stash@{id},恢复指令ID的缓存内容,并且保留缓存条目。

6.使用git stash pop 恢复最新的stash,同时删除恢复的缓存条目。

git reset 和 git revert的区别

- git revert是用一次新的commit来回滚之前的commit,此次提交之前的commit都会被保留;
- git reset是回到某次提交,提交及之前的commit都会被保留,但是此commit id之后的修改都会被删除

es6 的新特性以及具体内容

1.新增了块级作用域(let,const),TDZ

2.提供了定义类的语法糖(class)

3.新增了一种基本数据类型(Symbol)

4.新增了变量的解构赋值

5.函数参数允许设置默认值,引入了rest参数,新增了箭头函数。

6.数组新增了一些API,如isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法。

7.对象和数组新增了扩展运算符

8.ES6新增了模块化(import / export)

9.ES6新增了Set和Map数据结构。

10.ES6原生提供Proxy构造函数,用来生成Proxy实例

11.ES6新增了生成器(Generator)和遍历器(Iterator)

12.模板字符串

13.promise

promise.all() 和 promise.race()

promise.all()返回的结果由所有的结果而定,如果返回中有一个失败,那就是失败的,如果全部成功就是成功的

promise.race()返回的结果由最先返回的那个promise来决定,第一个返回的是失败就是失败

rem 和 em 的区别

  • px是固定的单位长度,一旦设置了就无法随页面的大小而适应改变。
  • em是相对长度单位,比px更具灵活性,em的长度是相对于父元素
  • rem的长度是相对于根元素,也就是html的字体大小

怎么新建set的集合

直接 new Set() 会直接报错

Set 函数可以接受一个数组(或类似数组的对象)作为参数,用来进行数据初始化。

let i = new Set([1,2])

操作方法:
add(value)    添加数据,并返回新的 Set 结构
delete(value)   删除数据,返回一个布尔值,表示是否删除成功
has(value)    查看是否存在某个数据,返回一个布尔值
clear()      清除所有数据,没有返回值

遍历方法:

Set 提供了三个遍历器生成函数和一个遍历方法。
keys()     返回一个键名的遍历器
values()    返回一个键值的遍历器
entries()    返回一个键值对的遍历器
forEach()   使用回调函数遍历每个成员

js实现栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//stack 栈  数组就是一个很完美的栈结构
//functions: pop,push,peek,length

let stack = function () {
this.count = 0;
this.storage = {};

//给栈中添加一个元素 push
this.push = function (value) {
this.storage[this.count] = value;
this.count++;
}

//给栈中移除一个元素 pop
this.pop = function (value) {
if (this.count === 0) {
return undefined;
}

this.count--;
let result = this.storage[this.count]
delete this.storage[this.count]

return result
}

//栈的大小
this.length = function () {
return this.count;
}

//返回栈顶的元素
this.peek = function () {
return this.storage[this.count-1]
}
}


let myStack = new stack();

myStack.push(1)
myStack.push(2)
myStack.push(3)

console.log(myStack); // storage: { '0': 1, '1': 2, '2': 3 },

console.log(myStack.peek()); //3
console.log(myStack.pop()); //3
console.log(myStack.length()); //2

for of 和 for in 的区别

for in 可以用在数组吗?

是可以的,因为for…in(值) 本身是对象的遍历方法,数组也属于对象,但是最好不用,数组的遍历可以用 for..of(索引) , for Each

理由如下:

  1. for…in 的属性值是字符串,可以会导致错误
  2. for…in 遍历的是可迭代对象,原型上的属性也可能会被遍历到
  3. for…in 不是按照数组下标顺序来遍历的,是按照对象属性的枚举属性

中序遍历二叉树

数组去重的几种办法

  1. Array.from(new Set(arr))
  2. 双重for循环
1
2
3
4
5
6
7
for(let i = 0; i<len;i++){
for(let j = i+1;j<len;j++){
if(arr[i] === arr[j]){
arr.splice(j,1)//删除第二个
}
}
}

3.利用indexOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function methods3(arr) {

if (!Array.isArray(arr)) {

return;

}

var array = [];

for (var i = 0; i < arr.length; i++) {

if (array .indexOf(arr[i]) === -1) {

array .push(arr[i])

}

}

return array;

}

4.利用sort排序,看看左右两个是否一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function methods4(arr) {

if (!Array.isArray(arr)) {

return;

}

arr = arr.sort()

var arrry= [arr[0]];

for (var i = 1; i < arr.length; i++) {

if (arr[i] !== arr[i-1]) {

arrry.push(arr[i]);

}

}

return arrry;

}

5.利用includes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function methods6(arr) {

if (!Array.isArray(arr)) {

return

}

var array =[];

for(var i = 0; i < arr.length; i++) {

if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值

array.push(arr[i]);

}

}

return array

}

6.利用Map,递归,filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function methods10(arr) {

let map = new Map();

let array = new Array();// 数组用于返回结果

for (let i = 0; i < arr.length; i++) {

if(map .has(arr[i])) {// 如果有该key值

map .set(arr[i], true);

} else {

map .set(arr[i], false);// 如果没有该key值

array .push(arr[i]);

}

}

return array ;

}

7.利用reduce

1
2
3
4
5
6
7
8
let arr = [1, 2, 6, 2, 1];
let filterArr = arr.reduce((pre, cur, index, array) => {
if (pre.includes(cur) === false) {
pre = [...pre, cur];
}
return pre;
}, [])
console.log(filterArr); // [1,2,6]

forEach 和 Map的区别

map有返回值,forEach没有,都不可以改变原数组,除非你在callback里面改(giao,为什么要问这种问题)

自定义一个v-model

v-model是vue中的一个语法糖,实现数据的双向绑定

1
2
3
4
<input
:value="parentData"
@input="parentData = $event.target.value"
>

vueRouter传参数的几种办法

一.router-link路由导航方式传参

1
2
3
//路由配置   {path:'/father/son/:num',name:A,component:A}
<router-link to="/path/num"></router-link>
//子组件通过 this.$route.params.num 接受参数

二、调用$router.push实现路由传参

1
2
3
4
5
6
//路由配置: {path: '/d/:id', name: D, component: D}
this.$router.push({
path:'/d/:id'
})

//子组件通过 this.$route.params.id 接受参数

三.通过路由属性name匹配路由,再根据params传递参数

1
2
3
4
5
6
7
//路由配置: {name:"B",path:'/asdsdasd/asdd',component:B} 无所谓path,但是name一定要对应
this.$router.push({
name: 'B',
params: {
context: '吴又可吴又可吴又可'
}
})

四.通过query传参数

1
2
3
4
5
6
7
// 路由配置  {path: '/c', name: 'C', component: C}
this.$router.push({
path: '/c',
query: {
context: '吴又可吴又可'
}
})

v-router的query和params的区别

webpack配置

数组的一些方法

join 连接数组变成字符串,不会改变原数组

1
2
3
let arr = [1,2,3,4];

console.log(arr.join('-')) //"1-2-3-4"

split 将字符串拆分为数组

1
2
const S = arr.join('-')
arr.split(s) //['1','2','3','4']

slice使用 slice 方法从数组中截取部分元素组合成新数组(并不会改变原数组),不传第二个参数时截取到数组的最后元素。

1
arr.slice(0,3) //[1,2,3]

splice 使用splice 方法可以添加、删除、替换数组中的元素,会对原数组进行改变,返回值为删除的元素。

删除数组元素第一个参数为从哪开始删除,第二个参数为删除的数量。

1
2
3
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.splice(1, 3)); //返回删除的元素 [1, 2, 3]
console.log(arr); //删除数据后的原数组 [0, 4, 5, 6]

concat方法用于连接两个或多个数组,元素是值类型的是复制操作,如果是引用类型还是指向同一对象

1
2
3
4
arr1 = [1,2]
arr2 = [3,4]

console.log(arr.concat(arr1,arr2))

使用 indexOf (lastIndexOf)从前向后(从后向前)查找元素出现的位置,如果找不到返回 -1

1
2
let arr = [7, 3, 2, 8, 2, 6];
console.log(arr.indexOf(2)); // 2 从前面查找2出现的位置

使用 includes 查找字符串返回值是布尔类型更方便判断

使用includes等不能查找引用类型,因为它们的内存地址是不相等的

find 方法找到后会把值返回出来

  • 如果找不到返回值为undefined

返回第一次找到的值,不继续查找

sort

sort每次使用两个值进行比较 Array.sort((a,b)=>a-b

  • 返回负数 a 排在 b 前面,从小到大
  • 返回正数 b 排在 a 前面
  • 返回 0 时不动

原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let arr = [1, 5, 3, 9, 7];
function sort(array, callback) {
for (const n in array) {
for (const m in array) {
if (callback(array[n], array[m]) < 0) {
let temp = array[n];
array[n] = array[m];
array[m] = temp;
}
}
}
return array;
}
arr = sort(arr, function(a, b) {
return a - b;
});

数组遍历

for循环

forEach

forEach使函数作用在每个数组元素上,但是没有返回值。

for…in…

遍历时的 key 值为数组的索引

for…of….

遍历时的 key 值为数组的值

map

map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。

语法

1
2
map(callbackFn)
map(callbackFn, thisArg)

在非数组对象上调用 map()

map() 方法读取 thislength 属性,然后访问每个整数索引。

1
2
3
4
5
6
7
8
9
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
};
console.log(Array.prototype.map.call(arrayLike, (x) => x ** 2));
// [ 4, 9, 16 ]

reduce

reduce() 方法是一个迭代方法。它按升序对数组中的所有元素运行一个“reducer”回调函数,并将它们累积到一个单一的值中。每次调用时,callbackFn 的返回值都作为 accumulator 参数传递到下一次调用中。accumulator 的最终值(也就是在数组的最后一次迭代中从 callbackFn 返回的值)将作为 reduce() 的返回值。

1
2
3
4
5
6
7
//每一次callbackFn返回的数据都会记录,并用于下次计算
arr = [1,2,3,4]

arr.reduce((accumulator,currentValue,currentIndex)=>{
console.log(accumulator,currentValue,currentIndex)
return accumulator+currentValue
})

展开嵌套数组

1
2
3
4
5
6
7
const flattened = [
[0, 1],
[2, 3],
[4, 5],
].reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
// flattened 的值是 [0, 1, 2, 3, 4, 5]

统计对象中值出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];

const countedNames = names.reduce((allNames, name) => {
const currCount = allNames[name] ?? 0;
return {
...allNames,
[name]: currCount + 1,
};
}, {});
// countedNames 的值是:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

VueRouter的钩子函数

  1. 全局的beforeEach(前置路由守卫):进入页面登录判断、管理员权限判断、浏览器判断

beforeEach(to,from,next)

afterEach(后置路由守卫)

  1. 单个组件的 beforeEnter
  2. 组件内的 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!