常见面试题整理(码字ing)

·  阅读 149

for in 和 for of 的区别

通过以下例子助你理解

    //1. 循环对象
    let obj = {
        name: 'hello',
        age: 25
    }
    for (let i in obj){
        console.log(i)
        // name age
    }
    
    for(let i of obj){
        console.log(i)
        // Uncaught TypeError: obj is not iterable 报错了,因为对象没有部署Symbol.iterator 属性。
    }
    //2. 循环数组
    let arr  = ['a','b','c'];
    for(let i in arr){
        console.log(i) // 0,1,2
    }
    for(let i of arr){
        console.log(i) // a,b,c
    }
复制代码

for in 的特点

  • for in 比较适合遍历对象。
  • 遍历对象返回的都是key值,数组也是,在数组中叫(下标)

for of 的特点

  • for of 循环的对象必须部署symbol.iterator属性,否则就会报错。普通对象是没有symbol.iterator属性
    • 拥有symbol.iterator属性的对象如下
      • 数组Array
      • Map
      • Set
      • String
      • argument对象等等
  • for of获取的值时键值对的值

vue-router里两个模式的区别,用了哪些原生的API

hash 模式

  • hash模式的原理是onhashchange事件,可以在window监听hash的变化
    window.onhashchange = function(event){
    consoel.log(event)// 返回一个
    }
复制代码
  • hash模式实现的路由,链接后面添加#+路由

history 模式

  • history模式用到的原生API有window.history.popState('传参','标题','跳转的路由地址')windown.history.pushState()、以及window.history.replaceState('传参','标题','跳转的路由地址')
    • window.history.pushState只是改变了后面的路由,页面并没有跳转,也没有重新加载。repalceState也是一样。
    • window.onpopState只有在点击浏览器的前进或者后退按钮才会出触发,或者调用history.back()forward()等方法
  • history模式实现是链接后面直接加路由,网址更好看。
  • history的使用需要后端配合使用,如果后端没有设置,生产环境下会404。

共有的特点 更新视图但是不重新加载页面

  • hash虽然出现在url地址中,但它不被包含在HTTP请求里,他是用来指示浏览器的,对服务器端没有任何作用,改变hash不会重新加载页面,咱们可以通过设置onhashchange实现更新视图但是不重新加载页面
  • history中pushStatereplaceState这两个方法虽然说修改了浏览器的历史记录栈,虽然当前的url路由改变了,但是不会重新加载页面。这就为了更新视图但不重新加载页面提供了基础。

使用vue-lazyload 插件实现图片懒加载

  • 使用npm下载lazyload插件npm i vue-lazyload
  • 在main.js中引用文件
    import vueLazy form 'vue-lazyload'
    Vue.use(vueLazy,{
        preLoad: 1, //提前加载的高度,数字1代表一屏的高度
        error: imgUrl, //图片加载失败时显示的图片
        loading: imgUrl, //图片加载成功显示的图片
        attempt: 2 //图片加载错误后,最大的尝试次数
    })
    复制代码

vuex

  • 为什么使用vuex,可以简单理解为,组件中的data属性需要共享时,可以使用vuex进行统一的状态管理。

vuex的使用

vuex中默认的五种基本对象

state

  • 存储变量,相当于组件中的data
export default new Store({
    state:{
        count:2
    }
})
复制代码

mutations

  • 修改state中的变量,并且是同步的,在组件中调用`this.$store.commit('方法名',传参)
    • 因为vuex中不建议通过this.$store.state直接修改变量。
export default new Store({
    state:{
        count:2
    },
    mutations:{
        changeCount(state,n){
            state.count += n
        }
    }
})

mounted(){
    this.$store.commit('changeCount',5)
}
//在html中用{{$store.state.count}}
复制代码

actions

  • 异步修改state变量,这个的原理通过actions中的方法去调用mutations中的方法去改变state。
  • actions内部会接收一个context对象,这个对象里有{dispatch, commit, getters, state, rootGetters...}等等方法,actions方法一般是用来调用mutations的,我们可以解构赋值{commit},直接使用。
export default new Store({
    state:{
        count:2
    },
    mutations:{
        changeCount(state,n){
            state.count += n
        }
    },
    actions:{
        yiCount(context,n){
            
        }
    }
    // 项目中一般都是这样写的
    actions:{
        yiCount({commit},n){
            commit('changeCount',5)
        }
    }
     
})

mounted(){
    this.$store.dispatch('yiCount',5)
}
复制代码

getters

  • 它可以根据state中的变量进行其他的计算操作,返回的是一个新的变量,我们可以通过this.$store.getters.filTodos调用。但是它不会改变state中的变量。
    export default new Store({
    state:{
        count:2,
        todos:todos: [
          { id: 1, text: "你好", done: true },
          { id: 2, text: "世界", done: false },
        ],
    },
    getters:{
        filTodos(state){
           state.todos.filter((item)=>{
               return item.done
           })
        }
    }
     
    })
    
    //组件中调用 
    mounted(){
        let a = this.$store.getters.filTodos
        //{ id: 1, text: "你好", done: true },
    }
    复制代码

flex布局

设置flex布局

  • 块级元素display:flex
  • 行内块元素:display:inline-flex

父级元素设置flex后也叫做flex容器,设置容器属性

设置主轴的方向flex-diretion

  • 水平方向
    • flex-direction:row:主轴水平方向,从左到右
    • flex-diretion:row-reverse:主轴方向,从右到左
  • 垂直方向
    • flex-diretion:column:垂直方向,从上到下

    • flex-diretion:column-reverse:主轴方向,从下往上

设置项目是否换行

  • flex-wrap:nowrap //默认,如果不换行,当项目宽度超过以后,它的宽度会失效,变成所有项目在一行内平分宽度。
  • flex-wrap:wrap //换行,第一行在上方
  • flex-wrap:wrap-reverse //换行第一行在下方

设置项目在主轴的对齐方式,以主轴为水平方向为例

  • justify-content:flex-start //默认左对齐
  • justify-content:center //居中对齐
  • justify-content:flex-end //右对齐
  • justify-content:space-between // 左右两个项目贴边对齐,中间的项目间隔相等。
  • justify-content:space-around //左右两个项目是中间项目之间间隔的一半。

设置交叉轴(垂直)的对齐方式,以垂直方向为例

  • align-item:flex-start //从上往下默认
  • align-item:center //居中
  • align-item:flex-end //从下往上
  • align-item:baseline //基线对齐,其实就是项目中所有文字的最底端对齐

设置子元素(项目)的属性

设置排列顺序

  • order属性必须是单个添加的才有用,假设ul>li{order:1},所有li项目都是1,它的排列顺序是不会有变化的。假设需要第二个li排在最前面,我们可以设置li:nth-child(2){order:-10},order的值越小排序越靠前。
    • order:2

设置项目的放大比例

  • flex-grow这个设置项目的比例
  • flex-grow:1

设置项目的缩小比例

  • 当空间不足的时候,缩小的比例
  • flex-shrink:0 // 默认是0

flex-basis默认为auto,项目本身的大小,项目中就用auto就行

flex属性

  • flex属性是flex-grow、flex-shrink、flex-basis的缩写。
    • 一般都是flex:1 0 auto。

注意

  • 使用flex布局的时候,最后对宽度进行一个设置反正就尼玛的设置宽度

垂直居中

   <div>
       <p></p>
   </div>
   
   <style>
   div{
       display:flex;
       justify-content:center;
       align-item:center;
   }
   </style>
复制代码

深拷贝和浅拷贝

浅拷贝

  • 浅拷贝就是共用一个地址,只要其中一个的属性值发生改变,这两个都会改变。最外层的堆内存地址是不一样的,但是里面的的地址是一样的,如下obj.b的地址是一样的,所以修改obj.b中的值,所以两个对象都是联动的。

实现一个浅拷贝

    let obj = {name:'你好',b:{age:25}}
    function shallowClone(obj){
        if(obj===null) return null
        if(obj instance Date) return new Date(obj)
        if(obj instance RegExp) return RegExp(obj)
        let newObj = {}
        for(var i in obj){
            newObj[i] = obj[i]
        }
        return newObj
    }
   let a = shallowColne(obj)
   a.b.age = 55
   console.log(obj)  //{name:'你好',b:{age:55}}
复制代码

深拷贝

  • 深拷贝开辟两个完全不一样的堆内存,对对象中的所有子对象进行递归拷贝,拷贝前后两个对象互不影响。

实现深拷贝

let obj = {name:'你好',b:{age:25}}
function deep(obj){
    let newObj = {}
    if(obj === null) return null
    if(obj === Date) return new Date(obj)
    if(obj === RegExp) return new RegExp(obj)
    if(typeof(obj) != 'object') return obj
    for (var i in obj){
        newObj[i] = deep(obj[i])
    }
    return newObj
}
复制代码

css画斜线

  • 旋转法
div{
    border: 1px solid red;
    transform: rotate(45deg)
}
复制代码
  • 画两个三角形,然后另一个三角形用定位去盖住三角形
div{
    width: 0
    height: 0
    border: 100px solid
    border-color: transparent yellow transparent transparent
}
复制代码

两列布局

用calc加浮动

  • html 部分
    <div class= 'main'>
        <div class= 'left'></div>
        <div class= 'right'></div>
    </div>
    复制代码
  • css部分
    .main{
        overflow:hidden
        width:100%
        height:100%
    }
    .left{
        height:100%
        width:250px
        float:left
        background:red
    }
    .right{
        height:100%
        width:calc(100% - 250px)
        float:left
        background:yellow
    }
    复制代码

右边margin-left,左边浮动

    .main{
       overflow:hidden
       width:100%
       height:100%
   }
   .left{
       height:100%
       width:250px
       float:left
       background:red
   }
   .right{
       width:100%
       height:100%
       margin-left:250px
       background:yellow
   }
复制代码

左边绝对定位+右边浮动

  .main{
        overflow:hidden
        width:100%
        height:100%
    }
    .left{
        height:100%
        width:250px
        position:absolute
        background:red
    }
    .right{
        width:100%
        height:100%
        float:right
        background:yellow
    }
复制代码

利用flex布局

  .main{
        display:flex
        width:100%
        height:100%
    }
    .left{
        height:100%
        width:250px
        background:red
    }
    .right{
        height:100%
        flex:1
        background:yellow
    }
复制代码

中间自适应,两边固定

圣杯布局

.main{
    position;relative;
    padding: 0 250px
}
.left{
        height:100%
        width:250px
        background:red
        position:absolute
        top:0
        left:0
}
.right{
        width:250px
        height:100%
        background:yellow
        position:absolute
        top:0
        right:0
}
复制代码

calc+浮动

.main{
        overflow:hidden
        width:100%
        height:100%
    }
.left{
        height:100%
        width:250px
        float:left
        background:red
     }
.right{
        height:100%
        width: 250px
        float:left
        background:yellow
      }
.center{
    height:100%
    float:left
    width:calc(100% - 500px)
    background:blue
}
复制代码

数组去重的几种方法

循环+indexOf

    let arr = [11, 2, 3, 4, 2, 1, 5, 2, 56, 5];
    let ary = [];
    arr.forEach((item) => {
      if (ary.indexOf(item) === -1) {
        ary.push(item);
      }
    });
    console.log(ary);//[11, 2, 3, 4, 1, 5, 56]
复制代码

利用对象属性名不重名的特性

 let arr = [11, 2, 3, 4, 2, 1, 5, 2, 56, 5];
    let ary = {};
    let a = [];
    arr.forEach((item) => {
      ary[item] = 1;
    });
    Object.keys(ary).forEach((item) => {
      a.push(Number(item));
    });
    console.log(a);//[11, 2, 3, 4, 1, 5, 56]
复制代码

利用set和Array.from

  • Set对象存储的对象值都是唯一的
  • Array.from将一个类数组,或者属性名是数字且有lenth属性的对象,浅拷贝生成一个新的数组
let arr = [11, 2, 3, 4, 2, 1, 5, 2, 56, 5];
let a = new Set(arr)
let b = Array.from(a)
console.log(b)//[11, 2, 3, 4, 1, 5, 56]
复制代码

常用的ES6

let const

let const var的区别

  • let const 声明不会产生变量提升
  • let const 不能重复声明
  • let const 存在块级作用域
  • let var 声明时可以不赋值

模板字符串

  • 名字:${name}

箭头函数

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

  • 箭头函数没有argument
  • 箭头函数没有this,如果在箭头函数内部写this,它是继承上下文的this
  • 箭头函数不能new执行
  • 箭头函数没有原型

函数形参默认值

Object.assign

  • 合并对象,浅拷贝
let obj = {name:45,age:56}
let a = Object.assign(obj)
a.name = 85
console.log(obj) // {name:85,age:56}
复制代码

Set对象

  • 将数组转为对象,而且对象的值是唯一的。通过用来数组去重

解构赋值

解析url,获取传参

let str = "www.baodu.com?name=12&age=13";
    let obj = {};
    function getData(a) {
      let ary = a.split("?");
      let b = ary[1].split("&");
      console.log(b);
      b.forEach((item) => {
        let c = item.split("=");
        obj[c[0]] = c[1];
      });
      console.log(obj);
    }
    getData(str);
复制代码

柯里化

  • 柯里化就是闭包的高级版
  • add(1)(2)==>3
  function add(...arg) {
      let a = [...arg];
      return function(b) {
        a.push(b);
        return a.reduce((total, item) => {
          return total + item;
        });
      };
    }
    console.log(add(1)(2));//3
复制代码

new 一个函数执行时执行步骤

  • 先创建一个空对象
  • 将this指向这个空对象
  • 函数执行,给这个新对象添加属性和方法
  • 返回这个新创建的对象(所以构造函数不需要return,如果有return,return的是一个对象,就返回这个对象,如果是个非对象,就返回刚创建的对象)
    function fn(obj){
        this.name = obj
        console.log(obj)
    }
    new fn()
    复制代码

call bind apply

bind

bind的作用

  • 是改变当前函数的this,返回一个新的函数,并将this进行改变,不立即执行。

手写bind

  • 因为bind必须是所有函数都可以调用,故myBind得加在函数的公共方法
Function.prototype.myBind = function(){
    //这里面的this肯定是需要更改的函数,因为fn.myBind(),this == fn
    let f = this
    return 
}
复制代码

连续bind的问题

  let obj = { name: 15 };
    let obj2 = { age: 5 };
    function a() {
      console.log(this);
    }
  
    let b = a.bind(obj).bind(obj2);
    b()// 返回的时第一次bind的this
复制代码
  • 因为将a.bind(obj)=a3
  • b() == a3.bind(obj2) //这样a3的this指向obj2
  • a3() == 将a的this指向obj

call

call的作用

  • call的作用是改变this指向,并立即执行函数。

手写call

  • 必须是所有函数都可以调用
    let obj = {name: 15, age: 16}
    Function.prototype.myCall(){
        let [context,...args] = arguments
        if(!context){
            context = window
        }
        context.fuc = this
        let res = context.fuc(...args)
        delete context.fuc
        return res
    }
    function fn(...args){
        return args.reduce((total,item)=>{
            return total+item
        })
    }
    fn.myCall(obj,1,2,3)
    复制代码

apply

apply的作用

  • 改变this的指向,并立即执行函数,参数为数组的形式

手写apply

Function.prototype.myApply = function (){
    let [contxt,...args] = arguments
    context.fuc = this || window
    let res = context.fuc(...args)
    delete context.fuc
    return res
}
复制代码

vue中$nextTick的原理和应用

vue.$nextTick原理,原因

  • vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,其实也就是事件循环(Event loop),可以将事件循环理解为一个圆圈,同一时间段内更新的Dom被观察到数据变化后,它会把这些改变Dom操作放到这个队列中(圆圈中),然后执行,实现DOM更新。
    • 下面例子直观的表达了vue是异步更新DOM的
      <div @click='fn' ref='str'>{{str}}</div>
      
      data(){
          return {
              str:35
          }
      }
      methods:{
          fn(){
              this.str = '你好'
              console.log(this.$refs[str].innerHTML)// 35     
          }
      }
      复制代码
  • 为了确定在DOM更新完成之后再去做某些事情,就可以使用Vue.$nextTick(callback),这样回调函数会在DOM更新完成之后执行。

vue.$nextTick(callback)应用

  • 最常用的应用是在created钩子函数中操作DOM,由于created钩子函数中,DOM还没有更新,所以需要用$nectTick操作DOM,否则就会报错。
    created(){
        this.$nextTick(()=>{
            console.log(this.$refs[st].innerHTML = '小丑竟是你自己')
        })
    }
    复制代码
  • 点击事件需要这个DOM做一些操作时
        <div @click='fn' ref='str'>{{str}}</div>
        
        data(){
            return {
                str:35
            }
        }
        methods:{
            fn(){
                this.str = '你好'
                this.$nextTick(()=>{
                     console.log(this.$refs[str].innerHTML)// 你好   
                })
               
            }
        }
    复制代码

proxy 和 defineProperty 的区别

proxy

proxy的优势

  • 直接监听对象而非属性
  • 直接监听数组的变化
  • Proxy的拦截方式更多
  • Proxy返回的是一个新的对象,修改值的时候我们可以直接操作这个对象,而Object.defineProperty只能通过遍历对象的属性就行修改。
  • 直接实现对象的增加、删除

Object.defineProperty的优势

  • 兼容性强

vue3.0.和2.0的区别

  • vue3.0重构响应式系统,用Proxy替换了Object.defineProperty,使用的优势如上
  • 新增了组合式API(Composition),更好的逻辑复用和组织代码。当然同样支持Options API(选项式API)
  • vue3.0的声明周期也发生了改变
    • 取消了beforCreat和created声明周期,用setup替换。
    • 所有的声明周期都写在setup中。
  • 自动按需引入
  • 重构了虚拟DOM的实现(跳过静态节点,只处理动态节点),性能得到了很大的提高。

MVVM的原理

image.png

  • 它的原理就是通过数据劫持(Observer)加发布订阅模式,主要有observer监听器,watcher订阅者,Compile解析器。
  • 首先Observer数据劫持,利用Object.defineProperty给每一个数据添加get和set属性,Compile解析器,给属性添加订阅者和绑定更新函数,由于订阅者很多,所以就将他们放到Dep中在属性变化后通知watcher订阅者,将这些订阅者放入Dep中统一管理,当数据发生变化时,通知订阅者执行更新函数触发视图更新。

节流、防抖

节流

  • 规定的时间断内函数只触发一次,通过用于页面滚动、发验证码。
    let flg = true
    function throttle(fn,delay=500){
        return (...args)=>{
            if(!flg) return 
            flg = false
            setTimeout(()=>{
                flg = truw
            },delay)
            fn.apply(this,args)
        }
    }
    复制代码

防抖

  • 每次时间触发后需要等待一段时间的执行,若这段时间内再次触发,就会重新计时,触发的时候只触发最后一次。
let timer = null
function debounce(fn,delay=500){
    return (...args)=>{
         if(timer){
            clearTimeout(timer)
        }
        setTimeout(()=>{
            fn.aplly(this,args)
        },delay)
    }
   
}
复制代码

Vue中自定义指令

  • 自定义指令有全局注册和局部注册两种

全局自定义指令

  • html部分
    <div v-fous = 'fn'></div>
    复制代码
  • js部分
    <script>
    Vue.diretive('fous'{
        inserted:function(el,fuc){
            console.log(el,fuc.value)
            //el dom元素;  fuc.value返回的是fn函数体
        }
    })
    
    methods:{
        fn(){
            console.log(11111)
        }
    }
    </script>
    复制代码

局部自定义指令

    export default = {
        directiveds:{
            fous:{
                inserted:function(el,fuc){
                    console.log(el,fuc.value)
                }
            }
        }
    }
复制代码

vue中data为什么一定是一个函数而不是一个对象

  • vue中的组件是可以被多次复用的,如果是一个对象,每一次复用都是复用的一个data,这样就会相互影响,如果是一个函数,每一次复用组件都会生成一个新的data实例,互不影响。

FormData和普通对象有什么区别

  • FormData一般用来处理表单数据,提交给后台。
  • FormData数据的增删改查和普通对象不一样
    • FormData增加属性formDate.append('属性名','属性值')
    • FormData获取属性值formdata.get('属性名')
    • FormData修改属性formData.set('属性名','属性值')
    • FormData判断是否有该属性formData.has('属性名')
    • FormData删除属性formData.delete('属性名')

vue多个页面传参

  • vuex 和 内存

异步函数都有哪些

  • 事件
  • 发布订阅
  • axjax
  • Promise
  • async await
  • 定时器

H5和C3的新增属性

H5新增属性

  • 语义化标签 header、nav、aside、footer
  • 音频video、音频audio
  • 画布canvas
  • 内存localStorage和sesstionStorage
  • 表单的控件,email、url、number、file...

C3新增特性

  • transform转换

  • transition过度

  • border-radius圆角

  • box-shadow 阴影

  • 伪类选择器

  • 伪元素选择器

  • calc函数

  • animations

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改