美文网首页web前端面试题汇总web前端面试
2018前端常见面试题(精炼版)

2018前端常见面试题(精炼版)

作者: XKolento | 来源:发表于2018-09-19 21:42 被阅读35次

    本章内容都是作者手动整理并且尽量缩减内容,
    目的就是可以让开发者短时间内回顾知识碎片和面试问题。

    常规

    1.js如何查找元素以及他们的区别
    document.querySelector('#id'); //静态获取节点(js新增的节点无法获取)
    document.getElementById('#Id'); //性能更快更高效,动态获取节点
    $('#id'); //使用了第一种原生js封装而成的
    
    2.html新特性

    ①添加了新元素 footer header section nav progress audio video
    ②新增了 canvas 画布功能,该标签是图形容器,需通过js实现图画
    ③新增元素拖曳功能

    首先设置元素可拖曳
    然后可以调用相关事件:
    拖动什么 - ondragstart 和 setData()
    放到何处 - ondragover
    进行放置 - ondrop
    <img draggable="true" />
    

    ④HTML5地理定位功能(需要用户同意)
    ⑤新增 input的类型:date week month time email tel
    ⑥新增web存储:sessionStorage、localStorage、webStorge
    扩展:session local webStorge cookie的区别
    cookie:存储空间只有4kb,在用户设定的期限后过期。
    session:用户关闭当前标签页后失效,不同的标签页不共享session
    local:除非浏览器中删除文件或者js删除否则一直存在
    webStorge:大小扩展到 5mb,接口功能多,使用较复杂
    ⑦html5使用了离线存储功能,利用缓存离线访问,连网后再更新
    ⑧webStock:是一种浏览器和服务器可以相互通讯的协议,即服务器也可以主动发消息给浏览器。
    ⑨一些css3动画等

    3.最常用的 Number 方法

    数字转字符串

    常用4中方法
    let kolento = 1000+'';
    kolento.toString();
    kolento.toLocalString();
    String(kolento);
    区别扩展:
    toString与toLocalString转化数字和时间的格式不同
    String与上面2种相比,可以转化 null 与 undefine,
    其他2种会报错,也就是他的使用范围更大
    
    4.最常用 String 方法

    ①获取字符串长度

    let kolento = 'hello';
    kolento.length; 返回5
    

    ②返回指定位字符

    let kolento = 'hello'
    kolento.charAt(0) //返回第一个字符 h
    

    ③连接2个或者多个字符串

    var a = 'aaa';
    var b = 'bbb';
    var c= 'ccc';
    a.contact(b); //返回aaabbb
    a.contact(b,c); //返回aaabbbccc
    

    ④检索字符串中的某个值

    var kolento = 'hello';
    kolento.indexOf('h'); //返回0,如果找不到返回 -1
    kolento.match('h'); //返回值h,和相关对象信息
    

    ⑤替换字符串中的字符

    let kolento = 'hello kolento kolento';
    document.write(kolento.replace('kolento','Amiee'));
     //返回 hello Amiee kolento
    document.write(kolento.replace(/kolento/g,'Amiee'));
     //返回 hello Amiee Amiee
    

    ⑥提取字符串的局部,并且返回一个新字符串
    string.slice(start,end),返回内容不包括start,但是包括end(指定end情况下)

    string.substr(start,length)
    start:必须,开始下标,负数代表从尾部计算
    length:返回长度

    substring() 方法用于提取字符串中介于两个指定下标之间的字符
    string.substring(start,stop) ,返回内容不包括start和stop,并且不接受负数参数

    var str = 'hello happy world';
    str.slice(1); //返回 ello happy world! ,开始1没有end
    str.slice(4,9); //返回 lo hap 开始4,9结束
    str.slice(-2)); 返回 d!,从末尾开始截取 开始-1,结尾-2
    
    str.substr(0,7) //返回hello p
    他们的区别在于第二个参数的意思不同
    
    str.substring(2,5); //返回llo 
    返回内容不包括start和stop,并且不接受负数参数
    

    ⑦split方法将字符串分割成字符
    string.split(separator,howmany)
    separator : 分割符号
    howmany:返回的数组长度

    "2:3:4:5".split(":")    //将返回["2", "3", "4", "5"]
    "|a|b|c".split("|") //将返回["", "a", "b", "c"]
    "hello".split("", 3)    //可返回 ["h", "e", "l"]
    

    注意:js的字符串都是不可变的,所有的转化修改都是返回一个新的字符串。而不是修改原始的字符串。

    5.最常用 arr 方法

    ①返回数组长度其中的个数

    let kolento = [1,2,3,4,5]
    kolento.length //返回5 
    

    ②连接2个或更多数组

    var arr = new Array(3)
    arr[0] = "George"
    arr[1] = "John"
    arr[2] = "Thomas"
    
    var arr2 = new Array(3)
    arr2[0] = "James"
    arr2[1] = "Adrew"
    arr2[2] = "Martin"
    arr.concat(arr2)
    //返回 George,John,Thomas,James,Adrew,Martin
    

    ③数组组合成字符串

    var arr = new Array(3);
    arr[0] = "George"
    arr[1] = "John"
    arr[2] = "Thomas"
    arr.join();// George,John,Thomas 不添加分割符号,默认逗号
    arr.join(".");// George.John.Thomas
    

    ④数组的添加与删除
    pop(a) 删除返回数组的最后一项
    push(a,b) 在数组最后添加一项或者多项,用逗号隔开
    shift(a) 删除数组的第一项
    unshift(a,b,c) 添加数组的第一项

    splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
    arrayObject.splice(index,howmany,item1,.....,itemX)
    index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
    howmany:必需。要删除的项目数量。如果设置为 0,则不会删除项目。
    item1, ..., itemX:可选。向数组添加的新项目。

    var arr = new Array(6)
    arr[0] = "George"
    arr[1] = "John"
    arr[2] = "Thomas"
    arr[3] = "James"
    arr[4] = "Adrew"
    arr[5] = "Martin"
    arr.splice(2,3,"William")
    document.write(arr) //George,John,William,Martin
    

    ⑤颠倒数组的顺序

    var a = 'hello';
    s.reserve(); //返回 olleh
    

    ⑥slice方法截取数组中选定的部分
    arrayObject.slice(start,end)截取内容不包括start,但是包括end,end是可选参数

    let arr = [1,2,3,4,5]
    arr.slice(1,3); //[2,3]
    arr.slice(3); //[4,5]
    

    ⑦sort方法用于对数组进行排序
    ⑧toString() / toLocaleString()方法 ,数组转化成字符串

    6.js各种循环之间的区别

    ①for循环:适合数组循环,性能一般
    ②for key in和for value of,适合json对象的循环
    前者村换键,后者循环值,后者是es6新增

    for(var key in obj){
      // key 键,obj代表每个对象,obj[key]代表相对应的值
    } 
    for(var value of obj){
      // value代表键,obj代表每个对象
    } 
    
    let aArray = ['a',123,{a:'1',b:'2'}]
    for(let key in aArray){
        console.log(key); //返回 0 1 2
    }
    for(var value of aArray){
        console.log(value); a 123 {a:'1',b:'2'}
    }
    

    ③forEach与map

    let array = ['a','b','c']
    array.forEach((currentValue, index, arr)=>{
      console.log(currentValue); // a b c  必选参数
      console.log(index); //0 1 2 可选参数
      console.log(arr); // abc abc abc 可选参数
    })
    
    array.map(function(currentValue,index,arr){
       console.log(currentValue); // a b c  必选参数
      console.log(index); //0 1 2 可选参数
      console.log(arr); // abc abc abc 可选参数
    })
    

    以上2者区别:
    1.forEach是允许修改原始数组数据的,map的循环则是返回一个新数组。
    2.forEach适合你不需要改变数据的时候使用,map可以在你改变数据的情况下使用。
    3.map的性能高于forEach,快百分之70%左右

    ④jquery中的each

    $(selector).each(function(index,element){
      //都是必须的参数
      //index - 选择器的 index 位置
      //element - 当前的元素(也可使用 "this" 选择器)
    })
    

    ⑤filter过滤
    filter为“过滤”、“筛选”之意。指数组filter后,返回过滤后的新数组。用法跟map极为相似:

    var arr = [
      {"name":"apple", "count": 2},
      {"name":"orange", "count": 5},
      {"name":"pear", "count": 3},
      {"name":"orange", "count": 16},
    ];
    var newArr = arr.filter((value,index,arr)=>{
      // value 当前元素的值 例如 {"name":"apple", "count": 2}
      // index 0 1 2 等
      // arr 整个数组
      return value.name === "orange"; //返回2个name是orange的数组
    });
    
    7.window.location , window.location.href , window,location.replace的区别

    有3个页面 a,b,c

    如果当前页面是c页面,并且c页面是这样跳转过来的:a->b->c
    1:b->c 是通过window.location.replace("..xx/c") 此时b页面的url会被c页面代替,并且点击后退按钮时会回退到a页面(最开始的页面)

    2:b->c是通过window.location.href("..xx/c") 此时b页面的路径会被c页面代替,但是点击回按钮后页面回退的是b页面

    两者的区别: 两者后退时所回退的页面不一样

    8.js各种初始化之间的区别

    此处列举了4种常见的初始化方式

    <!DOCTYPE html>
    <html>
    <head>
    <title>首页</title>
    <script type="text/javascript" src="js/jquery-1.8.0.min.js"></script>
    <script>
    
    $(function(){
        alert("A");
    });
    
    $(document).ready(function(){
        alert("B");
    });
    
    function load(){
        alert("D");
    };
    
    window.onload=function(){
        alert("E");
    };
    
    </script>
    <body onload="load();">
    </body>
    </html>
    

    运行下面代码。弹出A、B、D、E的顺序:A=B>D=E。
    其中A和B都是在页面加载完,但是不包括图片多媒体数据加载完的情况。
    C和D是在页面所有资源加载完后运行的。

    9.css如何清除浮动
    .clearfix:after{
       content:".";/*加一段内容*/
       display:block;/*让生成的元素以块级元素显示,占满剩余空间*/
       height:0;/*避免生成的内容破坏原有布局高度*/
       clear:both;/*清除浮动*/
       visibility:hidden;/*让生成的内容不可见*/
      }
    .clearfix{zoom:1;/*为IE6,7的兼容性设置*/}
    
    10.css中有哪些办法让一个 div 水平垂直居中

    ①flex布局
    首先是他的兼容性,用在手机端问题不大


    flex布局兼容性
    里面内容会自定居中
    .parents {display:flex;justify-content:center;align-items: center;}
    

    ②定义table、table-cell

    .parents {display:table;width:300px;height:300px;}
    .child {display:table-cell;verital:middle;text-align:center;}
    

    ③定位窗体居中,常用在弹窗上

    .child {position:fixed;width:300px;height:300px;top:50%;left:50%;
    margin:-150px 0 0 -150px;}
    
    11.谈谈对ES6的认识

    具体解释:https://www.jianshu.com/p/287e0bb867ae
    ①新增变量声明,新增变量块作用域
    const:声明常量
    let:声明变量
    ②字符串模版,代码与变量拼接的时候特别好用。
    ③箭头函数
    继承当前上下文的 this 指向
    代码省略
    ④拓展的对象功能
    ES5我们对于对象都是以键值对的形式书写,是有可能出现键值对重名的。例如:

        function people(name, age) {
            return {
                name: name,
                age: age
            };
        }
    

    键值对重名,ES6可以简写如下:

        function people(name, age) {
            return {
                name,
                age
            };
        }
    

    ⑤解构赋值
    ⑥import(导入模块) 和 export(导出模块)

    12.对json的了解

    是一种轻量级的数据交换格式
    格式简单,容易读写
    把json字符串转化为json对象

    var obj =eval('('+ str +')');
    var obj = str.parseJSON();
    var obj = JSON.parse(str);
    

    json对象转化json字符串

    var last = obj.toJSONstring();
    var last = JSON.stringify(obj);
    
    13.jq怎么添加删除元素

    添加元素:
    append():在末尾添加元素
    prepend():在开头添加元素
    after():在某个指定元素后添加元素
    before():在某个指定元素前添加元素
    删除元素:
    a.remove(): 删除a
    a.empty():删除a的子元素

    14.数组去重

    ①js双层循环
    首先定义一个新数组,数组中存放老数组的第一个元素。
    然后循环老数组与新数组对比,如果值不相同就插入进新数组,最后形成一个没有重复的数组。

    function unique(arr){
     var res = [arr[0]];
     for(var i=1; i<arr.length; i++){
      var repeat = false;
      for(var j=0; j<res.length; j++){
       if(arr[i] === res[j]){
        repeat = true;
        break;
       }
      }
      if(!repeat){
       res.push(arr[i]);
      }
     }
     return res;
    }
    

    ②ES6数组去重

     let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
     let set = new Set(array);
     console.log(set);
     // => Set {1, 2, 3, 4, 5}
    
    15.浅拷贝与深拷贝的区别

    简单的来说,如果b复制了a,但是在修改b的同时,a也改变了,则是浅拷贝。因为还是指向了原来的地方,如果只改变了自身,说明是深拷贝。
    案例:

    let a=[0,1,2,3,4],
        b=a;
    a[0]=1;
    console.log(a,b);
    
    demo

    这里明明只是修改了a数组,但是b也跟着发生了变化。

    这里就要说一下基本数据类型和引用数据类型了。
    基本数据类型:number,string,boolean,undefine,null
    引用数据类型:obj,arr,function等

    我们的基本数据类型是存储在栈内存中,let a = 1

    image.png
    当你b=a复制时,栈内存会新开辟一个内存,例如这样:
    image.png
    他们各自有自己的值,所以当你修改a=2的时候,并不会对b造成影响,因为他们有自己对应的值。当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。

    引用数据类型,他们的属性名存在栈内存中,但是属性值存储在 堆内存中


    image.png

    当b=a复制的时候,其实只是复制了他的地址,而不是里面的值


    image.png
    而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了

    如果我们在堆内存中开辟一个新的地址给b,b单独引用地址就算是深拷贝了。


    demo

    这样修改a是不会影响b的

    16.如何进行深拷贝

    对象:
    ①首先判断类型,声明一个新对象保持继承链
    ②循环对象的key,输出key对应的值
    ③在循环中js动态添加新对象的属性和值完成深拷贝

    var clone = function (obj) { 
        if(obj === null) return null 
        if(typeof obj !== 'object') return obj;
        if(obj.constructor===Date) return new Date(obj); 
        var newObj = new obj.constructor ();  //保持继承链
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {   //不遍历其原型链上的属性
                var val = obj[key];
                newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
            }
        }  
        return newObj;  
    }; 
    
    17.添加删除一个js对象的属性和值
    添加:
    var x = {a:1,b:2}
    x.c=3
    //返回 {a:1,b:2,c:3}
    
    删除:
    delete x.c //返回 {a:1,b:2}
    
    18.vue的生命周期

    具体可以看我的这篇文章
    https://www.jianshu.com/p/3afd8785b7f5
    主要是这几个生命周期钩子
    beforeCreated()
    created()
    beforeMounted()
    mounted()
    beforeUpdate()
    updated()
    activated()
    deactivated()
    beforeDestory()
    destoryed()

    1.在created钩子中可以对data数据进行操作,这个时候可以进行ajax请求将返回的数据赋给data
    2.在mounted钩子对挂载的dom进行操作
    3.在使用vue-router时有时需要使用<keep-alive></keep-alive>来缓存组件状态,这个时候created钩子就不会被重复调用了,如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发。
    4.beforeDestroy,页面组件内容销毁前使用,所有对象数据都会清空。

    19.计算属性与监听的区别

    当我们需要根据一个值来做一些操作的时候,可以使用计算属性,但是这个操作复杂,还牵扯到异步交互的时候,推荐使用监听属性更加合适。

    20.v-if 与 v-show的区别

    v-if:返回false时,元素节点都是删除的,返回true,重新渲染节点。
    v-show:返回false时,元素节点为 display:none,true时 display:block

    一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if较好

    21.父子组件如何通信

    具体可以看我的这篇文章
    https://www.jianshu.com/p/00b7c4428574

    父组件通信子组件
    父组件在引用的子组件标签上添加动态的属性值
    子组件中通过props获取父组件中的属性名,能够得到他的属性值

    父组件
      <div id="app">
        <test :msg="childmsg" @parentevent="pevent"></test>
      </div>
    
    <script>
    import test from './components/test'
    export default {
      name: 'App',
      data(){
        return{
          msg:'',
          childmsg:'这是传递给子组件的内容'
        }
      },
    
      methods:{
        pevent(){
          console.log('这是子组件沟通父组件调用的父组件事件')
        }
      },
      computed:{
    
      },
      components:{
        test
      }
    }
    </script>
    
    子组件
    <template>
        <div class="test">
            {{msg}}
            <p @click="tryclick">点击试试</p>
        </div>
    </template>
    
    <script>
        export default{
            props:['msg'],
            data(){
                return{
    
                }
            },
            created(){
    
            },
            methods:{
                tryclick(){
                    this.$emit('parentevent');
                }
            }
        }
    </script>
    

    子组件通信父组件
    由子组件向父组件沟通的事件为:emit
    我们可以调用内建的 $emit 方法并传入事件的名字,来向父级组件触发一个事件
    还是以之前的代码的例子作解释:
    子组件p标签绑定点击事件,点击事件中触发

    22.vue双向绑定的原理

    当一个js对象传给vue实例的时候,vue会遍历他的所有属性值,并且通过Object.defineProperty把他转化成getter/setter,这个方法只有高级浏览器可用,所以ie9以下的浏览器不兼容。
    其中的每个组件都有watch对象监听,当里面的值发生改变的时候,会调用setter,然后导致相关联的组件更新,并且更新视图。

    23.vue的状态管理 vuex如何使用

    具体可以看我的这篇文章
    https://www.jianshu.com/p/bc25eebcf15d

    为什么要使用vuex
    父组件:app
    子组件:a,b
    app与a,b之间的通讯没问题,但是a,b之间的通讯比较麻烦。
    这里我们就可以使用vuex解决

    我们可以在目录中新建一个js文件,store.js

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import Vuex from 'vuex'
    
    Vue.use(Vuex);
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    
    
    const store = new Vuex.Store({
      state:{
        products:[
          {name: '鼠标', price: 20},
          {name: '键盘', price: 40},
          {name: '耳机', price: 60},
          {name: '显示屏', price: 80}
        ]
      }
    })
    
    new Vue({
      el: '#app',
      router,
      store,
      components: { App },
      template: '<App/>'
    })
    

    核心概念:
    state,可以看成所有组件公共的data
    可以通过 this.$store.state.products来引用

    getter,可以看成是所有组件的 computed 计算属性
    获取方法

    export default {
        data () {
            return {
                products : this.$store.getters.saleProducts 
            }
        }
    }
    

    Mutations,可以理解为 store 中的methods

    store.js中的内容
      mutations:{
        minusPrice(state,payload){
          let newPrice=state.products.forEach(product=>{
            product.price-=payload
          })
        }
      }
    vue文件中通过commit调用vuex中的方法
    this.$store.commit('minusPrice',2);
    

    action,类似于上者,是用来存放方法的,不同的时,他可以加入异步方法。
    触发方法

    this.$store.dispatch('minusPriceAsync',5)
    

    store中属性作用总结
    目前我们已经在store中使用了四种属性了,回顾一下
    1.state:用于存储状态,相当于公共data,可以让所有引入的组件获取。
    2.getters:相当于计算属性,可以以data中的数据源为基础,返回一个新的data,而且这个data是随着 state中数据的改变而改变的。
    3.mutations:相当于methods,在这个属性中可以再添加方法,可以传递 state作为第一个参数,和第二个payload作为自定义参数。其他组件可以通过 commit 来调用这里的方法,来改变视图
    4.action:大致和mutations类似,但可以用于异步操作。

    modules
    当状态很多难以管理的时候,可以通过 modules 进行分类管理,划分为modules (a,b,c)等。

    const moduleA = {
      state: { ... },
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: { ... },
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
    store.state.a // -> moduleA 的状态
    store.state.b // -> moduleB 的状态
    
    25.如何进行响应式布局

    大致思路如下
    ①首先需要添加meta标签

    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    

    设置设备按照一比一的尺寸进行显示,并且禁止用户缩放页面

    ②使用媒介查询 @meida 在不同的分辨率下设置css
    ③单位尽量使用rem,宽度使用百分比

    相关文章

      网友评论

      本文标题:2018前端常见面试题(精炼版)

      本文链接:https://www.haomeiwen.com/subject/cgxinftx.html