Failure is part of learning. we should never give up the struggle in life

js vue 面试题浅谈

JavaScript

1.常用遍历数组的方式


    const arr = [1, 2, 3, 4, 5];

    // 1.for 支持continue和break
    console.log('for循环遍历');
    for (let i = 0; i < arr.length; i++) {
        console.log(arr[i]);
    }

    // 2.while 支持continue和break
    console.log('while循环遍历');
    let i = 0;
    while (i < arr.length) {
        console.log(arr[i]);
        i++;
    }

    // 3.for...of 支持return和break
    console.log('for...of 循环遍历');
    for (let item of arr) {
        console.log(item);
    }

    // 4.forEach 不会改变原值且不能打断 (除非使用抛出异常的方法打断)
    // 返回值为undefined
    console.log('forEach循环遍历');
    arr.forEach(item => {
        console.log(item);
    });
    // 完整参数 Array.prototype.forEach(function(value, index, arr), thisValue)

    // 5.map 对每一个数组元素应用函数,并返回一个新的数组
    console.log('map循环遍历');
    const mapArr = arr.map(item => {
        console.log(item);
        return item * 2;
    });
    console.log(mapArr);

    // 6.filter 返回符合条件的数组
    // 若无符合条件的数组则返回空数组
    console.log('filter循环遍历');
    arr.filter(value => console.log(value))

    // 7.reduce 累加器
    // 返回一个值
    console.log('reduce循环遍历');
    const reduceArr = arr.reduce((pre, cur) => {
        console.log(pre, cur);
        return pre + cur;
    }, 0);
    console.log(reduceArr);

    // 8.find 返回符合条件的第一个数组元素
    // 若无符合条件的数组元素则返回undefined
    console.log('find循环遍历');
    const findArr = arr.find(item => console.log(item));
    console.log(findArr);

    // 9.some 返回布尔值,只要有一个满足条件就返回true
    console.log('some循环遍历');
    const someArr = arr.some(item => console.log(item));
    console.log(someArr);

    // 10.every 返回布尔值,只有所有都满足条件才返回true 不支持break和continue
    console.log('every循环遍历');
    const everyArr = arr.every(item => {
        console.log(item)
        return true; //不可省略 否则会退出循环
    });
    console.log(everyArr);

2.this是什么

this是指全局上下文,默认指向window,严格模式下指向undefined

    // this 指向
    // 1.在普通函数中
    function myFunction() {
        console.log(this); // 输出:undefined(在全局作用域中)
    }

    myFunction(); // 输出:Window

    // 2.在类的实例方法
    class MyClass {
        constructor() {
            this.name = 'John';
        }

        sayName() {
            console.log(this.name); // 输出:John
        }
    }

    const myInstance = new MyClass();
    myInstance.sayName(); // 输出:John

    // 3.在箭头函数中 会优先寻找离得最近的非箭头函数的this
    const myArrowFunction = () => {
        console.log(this); // 输出:undefined(在全局作用域中)
    };

    myArrowFunction(); // 输出:Window

    // call、bind、apply可以改变this的指向
    
    // 1.call
    const person1 = {
        name: '张三',
        sayName: function () {
            console.log(this.name);
        }
    };

    const person2 = {
        name: '李四'
    };

    person1.sayName.call(person2); // 输出:李四

    // 2.bind 不会立即调用函数,而是返回一个改变了this后的新函数
    // 注意:bind不支持break和continue
    const person3 = {
        name: '王五',
        sayName: function () {
            console.log(this.name);
        }
    };

    const person4 = {
        name: '赵六'
    }

    person3.sayName.bind(person4)(); // 输出:赵六

    // 3.apply 
    const person5 = {
        name: '钱七',
        sayName: function (age) {
            console.log(this.name + ',' + age);
        }
    };
    const person6 = {
        name: '孙八'
    }

    person5.sayName.apply(person6, [25]); // 输出:孙八,25

    // call和apply的区别:

    // 1.call和apply的第一个参数都是this要指向的对象,
    // 也就是想改变this的指向。

    // 2.call和apply参数不一样,apply方法接受两个参数,
    // 第一个参数是this要指向的对象,
    // 第二个参数是一个参数数组(从数组的第二项开始是传入
    // sayName方法的参数)。
    // call方法接受的是一个参数列表,第一个参数是this要指向的对象,后面的参数是传入sayName方法的参数。

    // 3.call和apply的作用是一样的,只是接受参数的方式不同。

    // 4.bind方法不会立即调用函数,而是返回一个改变了this后的新函数。

    // 5.bind方法支持break和continue

    // 6.bind方法是ES5新增的方法,IE8及以下不支持。

3.let、var、const的区别

var变量提升后会默认赋值undefined,let和const会存在暂时性死区在声明前调用会报错

var具有函数作用域会污染全局变量,let和const具有块级作用域,只在当前{}作用域内有效在{}外无效

var,let定义的变量可以修改赋值,const不可以修改,在相同作用域中let和const不能重复声明同一个变量名,var可以

vue

1. vue的双向数据绑定使用及实现原理

v-moudle可以将数据绑定,主要运用在表单处理

原理

通过数据劫持+发布者-订阅模式的方式来实现,
通过 ES5 的 Object.defineProperty() 方法来劫持(监听)各属性的 getter、setter
并在监听的属性发生变动时通知订阅者,是否需要更新,需要更新
就会执行对应的更新函数
常见的 基于数据劫持双向绑定 有两种实现

vue2 Object.defineProperty
vue3 使用ES2015新增的 Proxy


2.父子组件之间的通讯方式

vue2

父向子(包含祖孙):

  • 父组件在标签绑定值,子组件通过props接收
  • 通过依赖注入 Provide() 定义要传递的名与值 与 Inject 接收定义的名与值,
    如果未接收到父组件的值则这里设置的值就是默认值 ,对于其所有的后代组件都可使用无论有多深都可以注入由父组件提供给整条链路的依赖
  • 父组件可以通过 $children 可以调用子组件里定义的方法,通过这个方法的形参进行传参
  • 子组件设置 ref ,父组件可以使用 this.$refs.你设置的ref获取子组件的实例,就可以使用子组件的方法等
  • 通过 **$attrs** 可以获取数据 (注意子组件未用prpos接收的数据在这个容器里) (因为子组件使用props接受的数据存放在不同地方,所以将$attrs使用v-bind传递给孙组件就可以使用props接收到爷的其他数据了),//inheritAttrs: true,通过这个控制是否能够继,承默认是继承的,详情看官方文档

子向父(包含孙向爷):

  • 子组件使用 $emit 向父组件传递事件,父组件使用 v-on 监听事件 简写可用@代替
  • 子组件通过 $parent 可以调用父组件里定义的方法,通过这个方法的形参进行传参
  • root 当前组件的根组件 同上
  • 通过 **$listerns** 触发爷组件的方法,记得要用$emit (同父向子最后一条,也是v-bind绑定)

兄弟传参:

  • 通过事件总线 Event Bus 传参,一个组件先定义好传参要用到的函数并使用 bus.$emit('某函数名',value),
    另一个组件通过 bus.$on('某函数名',()=>{})接收并使用 (最好在生命周期创建的created钩子函数中进行监听,EventBus.$off('某函数名', {})可以移除掉该函数的事件监听)

3.vue的响应式数据是怎么工作的

数据双向绑定(响应式)是数据的变化引起视图的变化,视图的变化引起数据的变化。

实现的思路:

  • 侦测数据的变化(数据劫持/数据代理)
  • 收集视图依赖了哪些数据(依赖收集)
  • 数据变化时,自动通知更新视图(发布订阅模式)

4.vue的混入和拓展

混入:mixin 也就是把不同组件用到的相同的方法数据等放在一个文件里,使用的时候只需要去导入对应的混入对象就可以使用,减少重复定义相同的方法数据。

//全局混入:
Vue.mixin({
  created: function () {
      console.log("全局混入")
    }
})
//封装混入
//创建一个mixins.js 写入以下内容
export default {
//这里你们自己定义内容就行 data、watch、computed、methods等等都可以写
//该示例只是写了computed并不代表写了这个的同时不能写其他的,与组件里的写法是一样的各司其职
    computed: {
        filteredBlogs() {
            return this.blogs.filter(blog => {
                return blog.title.match(this.search);
            },)
        }
    }

}
//使用
import searchMixin from '../mixins/mixins.js'
mixins: [searchMixin] //导入的名字是searchMixin 所以后面就写searchMixin可自己定义 ,前面的mixins是固定写法
//当这句写入组件时,该组件用到filteredBlogs的地方就会自己在searchMixin寻找了无需单独再次定义

拓展:我的理解应该是 slot插槽吧
具体看文档吧,这里简单列举一下

//写一个slots.vue
<template>
    <div>
        <h1>这里是插槽</h1>
        <slot></slot>
        <slot name="jieshao">
            <h1>这里是具名插槽的默认内容</h1>
        </slot>

        <slot name="main"></slot>

    </div>
</template>

//再随意写一个你的组件来使用插槽
import slots from './views/slots.vue';//根据你的目录导入你的插槽组件
//在这个组件使用slots组件,并在当前slots里写入内容
<slots>
 <p>这句会写入匿名的插槽</p>
 <p>这句也会写入那个匿名的插槽</p>//你可以试下写了两个匿名插槽会发生什么^_^
 <p slot="main">在这里写的东西会在slots组件里显示出来,sloats组件里用name属性来对应这里传入的是谁</p>
 <p slot="jieshao">及时你的书写顺序和slots里不一样但它也会按照对应的name去渲染</p>
</slots>

//作用域插槽,顾名思义插槽和使用的组件之间是有作用域隔阂的
//这里演示如何将数组传递给插槽后进行遍历,并在原组件使用插槽遍历用到的值和index
//原组件data里定义好要用的数组
names:['张三','李四', '王五', '赵六', '孙七', '周八', '吴九', '郑十']

//使用插槽的父组件
//在标签传参
<slots :names='names'>//将names传递给插槽,这里涉及到父子传参自行回顾
//使用插槽时用template 包裹是标准写法上面萎了方便演示所以没写
 <template v-slot:toergodic="slotProps">//v-slot:可以简写为# 后面是插槽名默认是default,这里能获取到slot的props,这个slotProps可以写其他的名字
        <p>{{ slotProps.item }}---{{ slotProps.index }}</p>//使用
      </template>
</slots>

//插槽组件
<template>
 <div v-for="(item, index) in names" :key="index">
   <slot name="toergodic" :item="item" :index="index"></slot>//这里将数据item,index通过v-bind绑定不然父组件无法获取slotProps
 </div>
</template>
<script >
export default {
    name: 'slots',

    props: {
        names: {//父组件传递的数据
            type: Array,
            default: () => []
        }
    },
    created() {
        console.log(this.names);
    }
}
</script>

5.如何在vue中使用动态路由

参数匹配: 在路由后面写一个要匹配的参数 path: '/users/:id' 此时/users/johnny 和 /users/jolyne 这样的 URL 都会映射到同一个路由。

添加:router.addRoutes

移除:router.removeRoute()

6.如何配合路由守卫实现权限管理

首先你需要一分静态的路由表就像你自己配的那些不需要权限访问的路由一样

然后定义一个功能函数:是将你的路由表和用户权限里的路由对比把用户数据里有的路由放在新的数组里,使用router.addRoutes添加到某个你指定的原来的公用路由的子路由去(以后详细写一篇这里仅作参考)

从后端获取到用户的权限数据,存储到vuex中

数据方法都有了就只需要在路由前置守卫beforeEach里使用你定义的这个方法就可以了,当然了这是用户登录后才做的事

最后你动态添加的路由需要next(to.fullPath)去跳转,否则会白屏

7.vuex的核心概念

State:存储数据,数据是响应式的

Getter:可以理解为vue的计算属性

Mutation:如果要修改state的值那这里就要定义相应的方法,这是唯一的能够修改state的手段,必须是同步函数

Action:变异的mutation,定义的方法是是用来触发mutation里的方法的,支持异步函数

Module:定义一个独立的模块,其内部可以拥有独立的State、Getter、Mutation、Action和新的Moudle,新的Moudle就是该Module的子模块

uniapp

1.uniapp的生命周期

组件的生命周期与vue一样,不多写了

其他生命周期戳这里

发表评论

电子邮件地址不会被公开。 必填项已用*标注