前端面试题(一)

1.Vue的生命周期

vue的生命周期分为8个阶段:

beforeCrete( 创建前 ):在实例初始化之后,数据观测和事件配置之前被调用,此时无法访问methods, data, computed等上的方法和数据。
created( 创建后 ):创建实例之后执行,此时可以访问到methods, data, computed等上的方法和数据。

beforeMount:模版已经编译完成,但是尚未挂在到真实DOM上
mounted阶段:vue实例挂载到真实DOM上,此时可以操作Dom

beforeUpdate:数据更新时调用,发生在虚拟DOM打补丁之前,适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
updated:虚拟DOM重新渲染和打补丁之后调用,组成新的DOM已经更新

beforeDestroy:实例销毁前调用,实例还可以用,this能获取到实例,常用于销毁定时器,解绑事件
destroyed:实例销毁后调用,调用后所有事件监听器会被移除,所有的子实例都会被销毁

2.vue中 key 值的作用

使用key来给每个节点做一个唯一标识,主要是为了高效的更新虚拟DOM。

3.$route和$router的区别

$router是路由器的意思,通常用来进行路由跳转,例如:
this.$router.push()、replace()/back()/go()

$route是记录当前路由信息的一个对象,例如:
$route.path/params/query/meta

4.vue修饰符

vue修饰符一般用来控制事件的冒泡和默认行为
.stop:阻止事件冒泡
.prevent:阻止默认事件
.once:只触发一次

5.axios是什么?怎么使用

(1)安装与引入:通过npm i axios -S将axios安卓到本地,然后通过import axios from 'axios'引入到项目中
(2)默认配置和封装:创建实例,设置baseurl,添加请求拦截器,为每个请求的请求头添加token,添加 loading 的UI效果。添加响应拦截器,当请求回来后关闭 loading 的UI效果,并根据状态码判断token是否过期(401 未登录 403 登录过期),以此决定后一步的操作
(3)配置代理解决跨域问题:打开config/index.js文件,在proxyTable节点中进行设置

proxyTable: {
    '/api': {  // 匹配以/api开头的所有路径
        target: 'http://localhost:4000', // 代理的后端api基础路径
        changeOrigin: true, // 支持跨域
        pathRewrite: { // 重写路径,去掉路径中开头的'/api'
            '^/api': ''
            // '^/api': 'api' 
        }
    }
}

6.vue数据存储

vue存储有三种:cookie/localstorage/vuex

cookie一般由服务器生成,可以设置过期时间,大小限制为4K,每次同源请求都会携带在 header 中,参与到服务端的通信。前端通过document.cookie对cookie进行操作

localstorage除非被清理,否则一直存在,通过专门的方法setItem()/getItem()可以很容易的对数据进行操作,大小限制在5M,不参与服务端通信

vuex通过将$store挂载到Vue原型链中,使得所有的vue实例都能访问到它,便于各个组件间的通信。但是他存储在内存中,页面刷新或者关闭则数据失效

7.vue的双向绑定原理

先看原理图

(1)对data对象中所有层次的属性进行遍历,通过Object.defineProperty()实现数据劫持,同时为每个属性创建一个Dep,用来存放订阅者
(2)对模版进行编译,如果模版中的某个节点中存在数据绑定的指令,例如v-model,v-html,v-bind,双括号语法,除了将模版中变量替换成数据,还会为此创建一个watcher,然后添加订阅,将这个watcher添加到对应的dep中。如果是v-model指令,还会为此表单元素添加事件监听,当表单元素的值发生改变时,则触发其回调,将最新的值同步到data中
(3)当data中的数据发生改变时,会触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。

8.闭包的概念?优缺点?

闭包的概念:闭包就是能读取其他函数内部变量的函数。

优点:能够缓存函数内部的变量,避免全局变量污染

缺点:增加内存使用量

实现方式:
在函数A内部定义一个函数B,在函数B内部对函数A的变量C进行操作,然后将函数B return出去,将会形成一个闭包。
因为函数B在A的内部,所以它能够对函数A内的变量进行操作,函数B返回之后,函数A内部和函数外部搭建起一座桥梁,使得函数A的内存无法被释放,里面的变量得以缓存下来。

9.节流和防抖

节流:当一个事件被频繁触发时(滚动条滚动,鼠标移动事件),为了节省性能,我们希望某个固定的时间段内只触发一次
具体做法就是:设定一个时间间隔,当事件发生时,对比当前时间和上次回调执行时间作对比,如果时间差超过了规定的时间间隔,则触发回调,并更新回调执行时间

代码:

<script>
    function throttle(cb,delay){
        //记录上次回调执行时的时间戳
        var prev = 0
        //定义内部函数并返回,形成闭包,缓存prev
        return function(){
            //获取函数调用时的上下文
            var context = this
            //获取参数(事件对象)
            var event = arguments[0]
            //对比时间差 超过时间间隔则执行回调
           if(Date.now() - prev >= delay){
               //执行回调(绑定this,传递参数)
               cb && cb.apply(context,event)
               //更新prev
               prev = Date.now()
           }
        }

    }

    function handle(e){
        console.log(e)
        
    }

    document.addEventListener('scroll', throttle(handle,200))
</script>

防抖:当事件触发时,将回调延迟执行,如果在延迟时间内再次触发了事件,则重置回调执行的延迟时间。
效果:如果一个事件被频繁触发,只有最后一次触发事件后才会执行回调
实现方式:通过设置延时定时器来推迟回调执行,如果延迟期间再次触发回调,则清除延时定时器,并开启新的延时定时器

<script>
    function debounce(fn, wait) {    
        //默认情况下 没有延时定时器
        var timeout = null
        //定义内部函数并返回,形成闭包,缓存 timeout
        return function() {        
            //获取函数调用时的上下文
            var context = this
            //获取参数(事件对象)
            var event = arguments[0]

            //如果已经存在延时定时器 则尝试清除
            if(timeout !== null) {
                clearTimeout(timeout) 
            }
            //开启新的延时计时器      
            timeout = setTimeout(fn.bind(context,event), wait)    
        }
    }

    function handle(e){
        console.log(e)
    }

    document.addEventListener('scroll', debounce(handle,200))
</script>

10.数据类型?深拷贝和浅拷贝?

(1)数据类型分为:
字符串类型,数字类型,布尔类型,undefined,null,数组,对象
(2)如何区分数据类型:
使用typeof,根据输出的值类判断数据类型,字符串类型返回sting,数字类型返回number,布尔类型返回boolean,undefined返回undefined,null,数组,对象这三个则返回object,需要进行进一步的区分
要区分null容易,直接判断值是否为null即可
区分数组有多种方法:
Array.isArray(val)
val instanceof Array

<script>
    console.log(typeof 'hello world') //string
    console.log(typeof 100) //number
    console.log(typeof true) //boolean
    console.log(typeof undefined) //undefined
    console.log(typeof null) //object
    console.log(typeof []) //object
    console.log(typeof {}) //object

    function dataType(val){
        var type = (typeof val).toLocaleLowerCase()
        //如果不为object,则直接返回
        if(type === 'object'){
            //判断是否为null
            if(val === null){
                return null
            }else if(Array.isArray(val)){
                //val instanceof Array
                return 'array'
            }else{
                return 'object'
            }
        }else{
            //如果不为object,则直接返回
            return type
        }
    }
    console.log(dataType(null)) //null 
    console.log(dataType([])) //array
    console.log(dataType({})) //object
</script>

(3)浅拷贝:直接使用 '=' 赋值就是浅拷贝,想数组/对象使用浅拷贝赋值的话,因为他们共享同一个数据,所以会相互影响
(4)深拷贝就是给数组/对象赋值时让他们拥有自己独立的数据,互不影响。具体的方法有3种:

方法 代码 说明
JSON转换 JSON.parse(JSON.stringify(obj)) 将对象(数组)转换为字符串在转换成对象(数组)
E6的解构赋值 {...obj} 创建新的对象,使用解构赋值将键值对填入(注意:二级嵌套时此方法无效)
遍历 for() 遍历数组或对象,依次添加属性
<script>
    var obj = {name:'zhangsan',age:20}

    //浅拷贝
    var obj2 = obj

    //深拷贝1
    var obj3 = JSON.parse(JSON.stringify(obj))

    //深拷贝2
    var obj4 = {...obj}

    //深拷贝3
    var obj5 = {}
    for(key in obj){
        obj5[key] = obj[key]
    }

    //修改obj
    obj.age = 25

    console.log(obj2.age) //25 浅拷贝受到影响
    console.log(obj3.age) //20
    console.log(obj4.age) //20
    console.log(obj5.age) //20
</script>
posted @ 2020-12-07 23:20  ---空白---  阅读(229)  评论(0编辑  收藏  举报