2023.2.1 面试总结

47 阅读7分钟

1. array 数组方法

var str1 = [12,2,"hello"];
var str2 = ["world"];
console.log(str1.valueOf()==str1);
console.log(str1)
  • concat() 合并数组,并返回合并之后的数据
console.log(str1.concat())  //[12, 2, "hello", "world"]
  • join() 使用分隔符,将数组转为字符串并返回
console.log(str1.join("-")); //[12-2-hello]
  • unshift() 在第一位新增一或多个数据
console.log(str1.unshift("23")) //4
console.log(str1))//['23', 12, 2, 'hello']
  • push() 在最后一位新增一或多个数据
console.log(str1.push("23"))//4
console.log(str1)//[12, 2, 'hello', '23']
  • shift() 删除第一位,并返回删除的数据
console.log(str1.shift());//12
console.log(str1)//[2, 'hello']
  • pop() 删除最后一位,并返回删除的数据
console.log(str1.pop()); //hello
console.log(str1)//[12, 2]
  • reverse() 反转数组,返回结果
console.log(str1.reverse());//['hello', 2, 12]
  • slice(startIndex, endIndex) 截取指定位置的数组,并返回,不会改变原数组,不包含endIndex
console.log(str1.slice(0,2));//[12, 2]
  • splice(start,num,data1,data2,...) 向数组中指定位置添加,删除,替换数组中的元素,然后返回被删除/替换的元素
添加
console.log(str1.splice(0,0,32));//[] 首位,添加32
console.log(str1)// [32, 12, 2, 'hello']
删除
console.log(str1.splice(0,1));首位, 删除第一个//[12]
console.log(str1)// [2, 'hello']
替换
console.log(str1.splice(0,1,44));首位, 替换第一个//[12]
console.log(str1)// [44, 2, 'hello']
  • sort() 对数组内的数据进行排序
var str1 = [12,2,1,"hello"];
var str1 = [12,2,1,"hello"];//[1, 12, 2, 'hello']
console.log(str1.sort());
  • toString() 转换成字符串,并返回,类似于没有加任何参数的join()
console.log(str1.toString());//12,2,hello
  • valueOf() 返回数组对象的原始值
console.log(str1.valueOf()==str1);//true
  • IndexOf(value,start) 查询并返回数据的索引
    value为要查询的数据;start为可选,表示开始查询的位置,当start为负数时,从数组的尾部向前数;如果查询不到value的存在,则方法返回-1
var str1 = [12,2,"hello",0,7,0,89];
console.log(str1.indexOf(0))//3
console.log(str1.indexOf(0,-2))//5
  • forEach() 参数回调函数,遍历数组的所有项,回调函数接受三个参数,分别是value,index,self.没有返回值。
var str1 = [12,2,"hello",0,7,0,89];
var a = str1.forEach((value,index)=>{
    console.log(value+"---"+index)
})
//12---0 2---1 hello---2 0---3 7---4 0---5 89---6
console.log(a)//undefined
  • map() 同forEach,同时回调函数返回数据,组成新数组由map返回
var str1 = [12,2,"hello",0,7,0,89];
var a = str1.map((value,index)=>{
    console.log(value+"---"+index)
})
//12---0 2---1 hello---2 0---3 7---4 0---5 89---6
var a2 = str1.map((value,index)=>{
    return "a2:"+value
})
console.log(a2)//['a2:12', 'a2:2', 'a2:hello', 'a2:0', 'a2:7', 'a2:0', 'a2:89']
console.log(str1)//[12, 2, 'hello', 0, 7, 0, 89]
  • filter() 同forEach,同时回调函数返回布尔值,为true的数组组成新数组由filter返回
var str1 = [12,2,"hello","qww","www"];
var a = str1.filter((value,index)=>{
    console.log(value+"---"+index)
})
//12---0  2---1  hello---2  qww---3 www---4
var a2 = str1.filter((value,index)=>{
    return value.length>0
})
console.log(a2)// ['hello', 'qww', 'www']
console.log(str1) // [12, 2, 'hello', 'qww', 'www']

2.vue2与vue3的组件通信

vue2

父组件向子组件传递数据

  • props 父组件通过v-bind发送数据,子组件通过props接收数据
父组件:
<template>
  <div>
    <Child :msg="1212"></Child>
  </div>
</template>
<script>
import Child from "@/components/Child";

export default {
  components: {Child},
};
</script>

子组件:
<template>
  <div>
    {{msg}}
  </div>
</template>

<script lang="ts">
export default {
  name: 'Child',
  props:['msg']

};
</script>

子传父

  • $emit/v-on 子组件可以通过emit发布一个事件并传递一些参数,父组件通过v-on进行这个事件的监听
父组件:
<template>
  <div>
    <Child :msg="total" v-on:update:msg="total=$event"/>
  </div>
</template>
<script>
import Child from "@/components/Child";

export default {
  components: {Child},
  data() {
    return { total: 10000 };
  },
};
</script>

子组件:
<template>
  <div>
    {{msg}}
    <button @click="$emit('update:msg',msg-100)">
      <span>-100</span>
    </button>
  </div>
</template>

<script lang="ts">
export default {
  name: 'Child',
  props:['msg']
};
</script>

双向绑定

  • .sync 修饰符可以实现子组件与父组件的双向绑定,并且可以实现子组件同步修改父组件的值。
<Child :msg.sync="total" /> 等价于 <Child :msg="total" v-on:update:msg="total=$event"/>
<Child :msg="total" v-on:update:msg="total=$event"/>

变成

<Child :msg.sync="total" />
  • v-model 和 .sync 类似,可以实现将父组件 通过 v-model 传给子组件,子组件通过 的数据为双向绑定,子组件通过 $emit 修改父组件的数据
父组件:
<template>
  <div>
    <Child v-model:value="msg"/>
  </div>
</template>
<script>
import Child from "@/components/Child";
export default {
  components: {Child},
  data() {
    return { msg: 10000 };
  },
};
</script>

子组件:

<template>
  <div>
    <input ref="input" :value="value" @input="$emit('update:value', $event.target.value)" />
  </div>
</template>

<script lang="ts">
export default {
  props:['value'],

};
</script>

父组件访问子组件

  • ref $refs可以直接获取元素属性,同时也可以直接获取子组件实例
父组件
<template>
  <div>
    <Child ref="child"/>
  </div>
</template>
<script>
import Child from "@/components/Child";

export default {
  components: {Child},
  mounted() {
  //获取子组件属性
    const child = this.$refs.child
    console.log(child.name)
    child.someMethod('调用了子组件方法')
  }
};
</script>

子组件
<template>
  <div>
  </div>
</template>

<script lang="ts">
export default {
  props:['value'],
  data(){
    return{
      name:'张三'
    }
  },
  methods:{
    someMethod(msg){
      console.log(msg)
    }
  }
};
</script>

  • $attars

子组件使用attrs可以获得父组件除了props传递的属性和特性绑定属性(classstyle)之外的所有属性。然后还要继续传给子组件内部的其他组件,就可以通过vbind="attrs可以获得父组件除了props传递的属性和特性绑定属性 (class和 style)之外的所有属性。然后还要继续传给子组件内部的其他组件,就可以通过 v-bind="attrs"

  • $listenters

子组件使用$listeners可以获得父组件(不含.native修饰器的)所有v-on事件监听器,在Vue3中已经不再使用;但是Vue3中的attrs不仅可以获得父组件传来的属性也可以获得父组件v-on事件监听器

父组件
<template>
  <div>
    <Child @clickF="clickF" :name="name"/>
  </div>
</template>
<script>
import Child from "@/components/Child";

export default {
  components: {Child},
  data(){
    return{
      name:'张三'
    }
  },
  methods:{
    clickF(val){
      console.log(`父组件方法调被用,获取子组件:${val}`)
    }
  }
};
</script>

子组件
<template>
  <div>
    <button @click="getClickF">调用父组件方法</button>
  </div>
</template>

<script >
export default {
  // props:['name'],
  mounted(){
    console.log(this.$attrs)
    console.log(this.$listeners)
  },
  methods:{
    getClickF(){
      this.$listeners.clickF('我是子组件数据')
    }
  }
};
</script>
  • provide / inject 爷传给孙的数据

provide:是一个对象,或者是一个返回对象的函数。里面包含要给子孙后代属性

inject:一个字符串数组,或者是一个对象。获取父组件或更高层次的组件provide的值,既在任何后代组件都可以通过inject获得

父组件:

<template>
  <div>
    <Child :name="name" />
  </div>
</template>
<script>
import Child from "@/components/Child";

export default {
  components: {Child},
  data(){
    return{
      name:'张三'
    }
  },
  provide(){
    return{
      name:this.name
    }
  }
};
</script>

子组件:

<template>
  <div>

  </div>
</template>

<script >
export default {
  inject:['name'],
  created() {
    console.log(this.name)
  }
};
</script>
  • parent / children

$parent 子组件获取父组件实例对象,可以直接拿到数据和方法对象。

$children 父组件获取子组件的实例,可以直接拿到数据和方法对象。

父
<template>
  <div>
    {{name}}
    <Child :name="name" />
  </div>
</template>
<script>
import Child from "@/components/Child";

export default {
  components: {Child},
  data(){
    return{
      name:'张三'
    }
  },

};
</script>
子

<template>
  <div>
    <button @click="setName">点击修改父组件name</button>
  </div>
</template>

<script >
export default {
  props:['name'],
  methods:{
    setName(){
      this.$parent.name='历史'
    }
  }
};
</script>
 methods:{
  change(){
    console.log('==>',this.$children)
  }
}
  • eventBus 兄弟组件通信可以通过一个事件中心EventBus实现,既新建一个Vue实例来进行事件的监听,触发和销毁。
// 方法一
// 抽离成一个单独的 js 文件 Bus.js ,然后在需要的地方引入
// Bus.js
import Vue from "vue"
export default new Vue()

// 方法二 直接挂载到全局
// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()

// 方法三 注入到 Vue 根对象上
// main.js
import Vue from "vue"
new Vue({
    el:"#app",
    data:{
        Bus: new Vue()
    }
})
// 在需要向外部发送自定义事件的组件内
<template>
    <button @click="handlerClick">按钮</button>
</template>
import Bus from "./Bus.js"
export default{
    methods:{
        handlerClick(){
            // 自定义事件名 sendMsg
            Bus.$emit("sendMsg", "这是要向外部发送的数据")
        }
    }
}

// 在需要接收外部事件的组件内
import Bus from "./Bus.js"
export default{
    mounted(){
        // 监听事件的触发
        Bus.$on("sendMsg", data => {
            console.log("这是接收到的数据:", data)
        })
    },
    beforeDestroy(){
        // 取消监听
        Bus.$off("sendMsg")
    }
}

vuex的核心属性有哪些

  • 核心应用是 store(仓库),它包含大部分的状态(state)
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    // 定义一个name,以供全局使用
    name: '张三',
    // 定义一个number,以供全局使用
    number: 0,
    // 定义一个list,以供全局使用
    list: [
      { id: 1, name: '111' },
      { id: 2, name: '222' },
      { id: 3, name: '333' },
    ],
  },
});

export default store;

状态管理的五大核心:state,mutation,action,module,getter.

  • state 存放数据,相当于vue里面的 data
  • mutations 同步提交更新state里面的数据

修改store/index.js 提交更新可以使用 $store.commit()

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    name: '张三',
    number: 0,
  },
  mutations: {
    // 增加nutations属性
    setNumber(state) {
      // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
      state.number = 5;
    },
  },
});

export default store;

修改 app.vue 通过 this.$store.commit

<script>
export default {
  mounted() {
    console.log(`旧值:${this.$store.state.number}`);
    this.$store.commit('setNumber');
    console.log(`新值:${this.$store.state.number}`);
  },
};
</script>

以上是简单实现mutations的方法,没有传参;如果我们想不固定传参,下面可以看看

修改store/index.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    name: '张三',
    number: 0,
  },
  mutations: {
    setNumber(state) {
      state.number = 5;
    },
    setNumberIsWhat(state, number) {
      // 增加一个带参数的mutations方法
      state.number = number;
    },
  },
});

export default store;

修改app.vue

<script>
export default {
  mounted() {
    console.log(`旧值:${this.$store.state.number}`);
    this.$store.commit('setNumberIsWhat', 666);
    console.log(`新值:${this.$store.state.number}`);
  },
};
</script>
  • getters 在 state 中的数据基础上进一步对数据加工,与组件的computed一样

在store/index.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    name: '张三',
    number: 0,
    list: [
      { id: 1, name: '111' },
      { id: 2, name: '222' },
      { id: 3, name: '333' },
    ],
  },
  // 在store对象中增加getters属性
  getters: {
    getMessage(state) {
      // 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
      return `hello${state.name}`;
    },
  },
});

export default store;

在app.vue中

export default {
  mounted() {
    // 注意不是$store.state了,而是$store.getters
    console.log(this.$store.state.name);
    console.log(this.$store.getters.getMessage);
  },
};
  • actions 异步修改数据
const store = new Vuex.Store({
    state:{
        name:'张三',
        number:0,
    },
    mutations:{
        setNumberIsWhat(state,payload){
            state.name = payload.number;
        }
    },
    actions:{
        //增加actions 的属性
        setName(content){
        //增加setName方法,默认第一个参数content,其值是复制的一份store
            return new Promise(resolve => {
            //我们模拟一个异步操作,1秒后修改number ,为888
                setTimeout(()=>{
                    content.commit('setNumberIsWhat',{number:888});
                    resolve()
                },1000),
            })
        }
    }
})

修改 App.vue

async mouted(){
    console.log(`旧值:${this.$store.state.number}`);
    await this.$store.dispatch('setName');
    console.log(`新值:${this.$store.state.number}`);
}
  • module 按功能进行拆分模块

    新增一个新的仓库store2

// store2.js

const store2 = {
  state: {
    name: '我是store2',
  },
  mutations: {},
  getters: {},
  actions: {},
};

export default store2;

在store中引入我们新创建的store2模块

import Vue from 'vue';
import Vuex from 'vuex';
import { state } from './state';
import { getters } from './getters';
import { mutations } from './mutations';
import { actions } from './actions';
import store2 from './store2'; // 引入store2模块

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: { store2 }, // 把store2模块挂载到store里面
  state: state,
  getters: getters,
  mutations: mutations,
  actions: actions,
});

export default store;

访问state - 我们在App.vue测试访问store2模块中的state中的name

<template>
  <div></div>
</template>

<script>
export default {
  mounted() {
    console.log(this.$store.state.store2.name); // 访问store2里面的name属性
  },
};
</script>

mixins 类似于公共的方法,减少重复

局部单页面混入 新建mixins.js页面

const myMixins = {
    data(){
        return{
            isNoData:false,
            isShow:true,
        }
    }
}
export dafault myMIxin;

在需要引入的页面中注册

import Minix form '@/components/Mixin/index.js'
export default{
    minxins;[Minix],
}

全局混入 在main.js 中注册引用

import Minix form '@/components/Mixin/index.js'
Vue.mixin(mixin)

$nextTick() 在下次DOM异步更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM

视图已经更新,dom还没有更新

屏幕截图 2023-02-03 180556.png

守卫导航

全局守卫

  • 路由进入之前 router.beforeEach
  • 路由离开之后 router.afterEach
    // main.js 入口文件
    import router from './router'; // 引入路由
    router.beforeEach((to, from, next) => { 
      next();
    });
    router.beforeResolve((to, from, next) => {
      next();
    });
    router.afterEach((to, from) => {
      console.log('afterEach 全局后置钩子');
    });

深拷贝与浅拷贝的理解

浅拷贝:创建一个新对象,这个拷贝对象有着原始对象的属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝就是内存地址。如果其中一个对象改变了地址,就会影响到另一个对象。

深拷贝:创建一个新对象和数组,将值和地址复制给拷贝对象,可以无限层级拷贝,深拷贝后的对象不会和拷贝对象互相影响。

赋值

const obj = {
  name: 'lin'
}

const newObj = obj
obj.name = 'xxx' // 改变原来的对象

console.log('原来的对象', obj)
console.log('新的对象', newObj)

console.log('两者指向同一地址', obj == newObj) 
//原来的对象 {name: 'xxx'}
//新的对象 {name: 'xxx'}
//两者指向同一地址 true

浅拷贝

  1. Object.assign({},object)
const person = {
    name:'zs',
    hobby: { like: 'running' }
}
let newPerson = Object.assign({},person)
newPerson.name='ls'
newPerson.hobby.like = 'singing'
console.log(newPerson)//hobby: {like: 'singing'} name: "ls"
//对比原数据,基本类型数据没有影响,数据类型地址改变,影响原数据
console.log(person)// hobby: {like: 'singing'} name: "zs"
  1. 扩展运算符(...) 类似于 Object.assign({},object)
  • 拷贝对象
const person = {
    name:'zs',
    hobby: { like: 'running' }
}
let newPerson ={...person}
newPerson.name='ls'
newPerson.hobby.like = 'singing'
console.log(newPerson)//hobby: {like: 'singing'} name: "ls"
//对比原数据,基本类型数据没有影响,数据类型地址改变,影响原数据
console.log(person)// hobby: {like: 'singing'} name: "zs"
  • 拷贝数组
const person = ['name','hobby','person',{age:20}]
let newPerson =[...person]

newPerson[0] = 'newName'
newPerson[person.length-1].age = 30
//["newName","hobby","person",{age: 30}]
console.log(person)
//对比原数据,基本类型数据没有影响,数据类型地址改变,影响原数据
//["name","hobby","person",{age: 30}]
  1. Array.prototype.concat()
let myArr = ['old', null, undefined, true,{ hobby: 'undefined' } ]
    hobby: undefined
}];
let copyMyarr1 = myArr.concat();
​
copyMyarr1[0] = 'nickname';
copyMyarr1[copyMyarr1.length - 1].hobby = 'books'console.log(copyMyarr1); //[ 'nickname', null, undefined, true, { hobby: 'books' } ]
//对比原数据,数组copyMyarr1的改变影响了数组myArr的改变
console.log(myArr); //[ 'old', null, undefined, true, { hobby: 'books' } ]

  1. Array.prototype.slice()
let myArr = ['old', null, undefined, true, {
    hobby: undefined
}]; //原数据[ 'old', null, undefined, true, { hobby: 'undefined' } ]
let copyMyarr2 = myArr.slice();
copyMyarr2[0] = 'nickname';
copyMyarr2[copyMyarr2.length - 1].hobby = 'books'
console.log(copyMyarr2); //[ 'nickname', null, undefined, true, { hobby: 'books' } ]
//对比原数据,数组copyMyarr2的改变影响了数组myArr的改变
console.log(myArr); //[ 'old', null, undefined, true, { hobby: 'books' } ]

深拷贝

const obj = JSON.parse(JSON.stringity())

缺点:

  • 不支持 Date、正则、undefined、函数等数据
  • 不能拷贝循环引用的对象
  1. 使用递归的方式进行对象(数组)的深拷贝

	//函数拷贝
    const copyObj = (obj = {}) => {
    		//变量先置空
            let newobj = null;  

            //判断是否需要继续进行递归
            if (typeof (obj) == 'object' && obj !== null) {
                newobj = obj instanceof Array ? [] : {};
                //进行下一层递归克隆
                for (var i in obj) {
                    newobj[i] = copyObj(obj[i])
                }
                //如果不是对象直接赋值
            } else newobj = obj;
            
            return newobj;    
        }



//模拟对象
let obj = {
	numberParams:1,
	functionParams:() => {
		console.log('昨天基金全是绿的,只有我的眼睛是红的');
	},
	objParams:{
		a:1,
		b:2
	}
}

const newObj = copyObj(obj); //这样就完成了一个对象的递归拷贝

obj.numberParams = 100;  //更改第一个对象的指
console.log(newObj.numberParams); //输出依然是1 不会跟随obj去改变