1、讲一下let、var、const的区别

【答案】

  • var 没有块级作用域,支持变量提升。
  • let 有块级作用域,不支持变量提升。不允许重复声明,暂存性死区。不能通过window.变量名进行访问.
  • const 有块级作用域,不支持变量提升,不允许重复声明,暂存性死区。声明一个变量一旦声明就不能改变,改变报错。

2、面向对象中new都做了哪些事情(面试可让其实现一个new的伪代码)

【答案】

  • 做了哪些事情
  1. 1、现在内存中创建一个新的空对象
  2. 2new会让this指向这个新的对象
  3. 3、执行构造函数, 给这个对象添加属性和方法
  4. 4、返回这个心的对象
  • 实现伪代码
    function _new(){
      let obj = new Object();
      let Con = [].shift.call(arguments);
      obj.__proto__ = Con.prototype;
      let result = Con.apply(obj,arguments);
      return typeof result === 'object' ? result : obj
    }

3、什么是闭包, 闭包的应用场景有哪些

【答案】语雀-闭包

概念:

  • 闭包实际上是一种函数,所以闭包技术也是函数技术的一种,闭包能做的事情函数几乎都能做
  • 闭包技术花式比较多,用法也是比较灵活的,一般开发人员在学习闭包的时候都会遇到瓶颈,主要是因为闭包技术的分界线并不明显,几乎无法用一个特点去区分
  • 当一个内部函数被外部函数的变量引用时,就形成了一个闭包

应用场景:

  • 函数节流
  • 自定义js模块
  • 封装全局变量
  • 高级排它

4、简述一下关于数组方法中 forEach/map/filter()区别和用法

【答案】:

    // forEach -> 用来遍历数组中的每一项
    // 1.数组中有几项,那么我们传递进去的匿名回调函数就执行几次
    // 2.每一次执行匿名函数的时候,还给其传递了三个参数值:数组中的当前项item,当前项的索引index,原始的数组arry
    // 3.理论上forEach执行时没有返回值的,对原来的数组没有任何影响
    // 4.forEach方法中的this是ary,但是回调函数中的this默认是window
    ary.forEach(function(item, index, arry) {
      console.log(item, index, arry);
    }, obj); // 5.不管forEach,还是map都支持第二个参数值,作用是:把匿名函数中的this进行修改
    // map -> 和forEach非常相似,都是用来遍历数组的
    // 与forEach区别
    // 1.map的回调函数中是支持return的返回值的,return是什么就相当于把数组中的那一项变为什么,但是并不会影响原数组,只是把克隆的这一份改变了
  // 2.
    let a = ary.map(function(item, index, arry) {
      return item * 10;
    });
    console.log(a);
    // filter 是否操作原数组 -> No 
    // 返回结果 -> 过滤后的新数组 
    // 回调函数的返回结果 -> 如果返回true,表示这一项放到新数组中
    var arr = [1, 2, 3, 4, 5];
    var a = arr.filter((item, index) => {
      return item>2 && item<5;
    });
    console.log(a);//[3,4]

5、说一下基础数据类型和引用数据类型都包含哪些

    // 基础数据类型
    number
    string 字符串
    boolean 布尔
    null 空对象指针
    undefined 未定义

    // 引用数据类型
    object 对象数据类型
        {} 普通对象
        [] 数组
        /^$/ 正则
        Math 数学方法
        Date 日期
    function 函数数据类型

6、其它类型值转换为boolean类型时, 哪五个值为false, 其余都为true

【答案】

  • 0, NaN, “”, null, undefined

7、vue中组件的通信方式有哪些

【答案】:ref、 前端面试题 - 图2前端面试题 - 图3

8、vue中computed和watch的区别是什么

【答案】

    computed属性名自定义,可以监听一个或者多个它所依赖的数据项; watch一次只能监听一个属性,而这个属性接收两个参数,一个是新值一个是旧值
    computed里的自定义属性不能与data里的属性重复,否则会报错
    watch里监听的属性必须是已经存在的,其要么是data里的,要么是computed里计算出来的
    watch是允许异步操作的,并在我们得到最终结果前,设置中间状态,这些都是计算属性无法做到的
    computed是一个对象时,有哪些选项? - get, set
    computed和methods有什么区别? - methods是方法,可以接受参数,而computed不能;computed是可以缓存的,而methods不会;一般在v-for里,需要动态绑定值时,只能使用methods而不能使用computed,因为computed不能传参
    computed是否能够依赖其它组件的数据? - computed可以依赖其它computed,甚至是其它组件的data
    watch是一个对象时,有哪些选项? - handler(执行的函数),deep(是否深度监听),immediate(是否立即执行)

9、vue中数组更新检测中的变异方法有哪些

【答案】:push(), pop(), shift(), unshift(), splice(), sort(), reverse() 【会改变调用了这些方法的原始数组】

10、简述一下vue-router中前端面试题 - 图4route的区别

【答案】:我们可以在任何组件内通过this.$router访问路由器(方法,编程式导航),也可以通过this.$route访问当前路由(属性参数)

11、在vue-router中怎样实现路由的懒加载

【答案】:当在打包构建的时候,javascript包会变得非常大,影响加载的速度;结合vue的异步组件和webpack的代码分割功能,可以轻松的实现路由的懒加载

    // -> 一般情况下我们都会使用webpack动态import语法来定义代码分块点
    // -> 如果不成功的话,可以先安装babel-plugin-syntax-dynamic-import插件,修改.babelrc文件,加入"plugins": ["syntax-dynamic-import"]
    component: ()=>import('../components/Home.vue'),

12、描述一下vuex的原理及其中几个核心概念的作用

【答案】

vuex原理

    1、State --> State是单一的状态树,作为'唯一的数据源 ' 存在, 当一个组件需要多个状态的时候,我们可以使用mapState辅助函数来帮助我们生成计算属性

    2、Getter --> 有时候我们需要从store中的state中派生出一些状态,如果有多个组件用到此属性,我们要复制很多遍这个函数,但是并不理想,vuex则提供了getter的概念; getter像计算属性一样可以根据依赖被缓存起来,并且只有当他的依赖发生改变的时候才会被重新计算

    3、Mutation --> 更改Vuex的store中状态的唯一方法是提交mutation; mutation类似于事件:每一个mutation都有一个字符串类型的type(事件类型)和handler(回调函数,而这个函数就是我们实际进行状态更改的地方,并且会接收state作为其第一个参数); 我们不能直接地调用mutation回调函数,需要一个类似于事件注册的过程

    4、Action --> action类似于mutation,但是和其又有很多不同点; Action提交的是mutation,而不是直接变更状态;Action支持任意的异步操作; 分发action,可以通过store.dispatch('xxx')

13、对比一下前端三大框架, 各自的优势是什么

【答案】
三大框架的对比

14、开放题:我们怎样去选择一款JavaScript框架

【答案】

    1. 根据项目需求
    2. 是否支持A级浏览器
    3. 是否有利于团队协作开发
    4. 是否成熟(包括文档是否健全、社区是否支持充足、扩展性)
    5. 代码的执行速度、性能如何
    6. 代码是否能模块化
    7. 代码的可重用性怎么样

15、什么算作跨域,解决前端跨域问题的方案有哪些

【答案】:简单的理解就是:因为javascript同源策略的限制; 当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域【不同域之间相互请求资源,就算作’跨域’】

前端跨域

方案:jsonp、cors、websocket、postMessage、nginx

16、减少http请求次数或者请求文件大小的方法

【答案】

    采用CSS雪碧图技术
    合并压缩(css/js)
    采用图片懒加载技术
    采用json格式进行数据传输
    音频处理
    304缓存处理(主要由服务器端进行处理)
    使用字体图标代替icon图标
    采用CDN加速

17、css中水平居中的方式有哪些

【答案】

    1、父级text-align:center, 子级display:inline-block;
    2、display:table方式
    3、定位方式
    4、flex布局
    5、display:grid网格布局

18、css中怎样实现单行文本省略和多行文本省略

【答案】

    /*文本-省略号 截断*/
        .ellipsis{
            word-wrap: normal;/* for IE */
            -webkit-text-overflow: ellipsis;
            -moz-text-overflow: ellipsis;
            -o-text-overflow: ellipsis;
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
        }
        /*多行位置截断*/
        .line-clamp {
            overflow : hidden;
            -webkit-text-overflow: ellipsis;
            -moz-text-overflow: ellipsis;
            -o-text-overflow: ellipsis;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 2;/*行数*/
            -webkit-box-orient: vertical;
        }

19、分别阐述一下vue和react中常用的生命周期

【答案】

    vue
    • beforeCreate - 组件实例刚刚创建,还未进行数据观测和事件的配置
    • created - 组件实例创建完成,并且进行数据的观测和事件的配置
    • beforeMount - 模板编译之前,还没有挂载
    • mounted - 模板编译之后,已经挂载了,此时才会渲染完成,才能看到页面上数据的展示
    • beforeUpdate - 组件更新之前
    • updated - 组件更新完成
    • beforeDestroy - 组件销毁之前
    • destroyed - 组件销毁之后
    react
    1.组件加载时触发的函数
       constructor componentWillMount render componentDidMount
    2.组件数据更新的时候触发的生命周期函数
       shouldComponentUpdate componentWillUpdate render componentDidUpdate
    3.在父组件里改变props传值的时候触发的
       componentWillReceiveProps
    4.组件销毁的时候触发的
      componentWillUnmount

20、说明一下null和undefined的区别

【答案】:null一般都是暂时没有,预期中以后会有的(可能以后也没有达到预期):在Js中null一般都是手动先赋值为null,后期再给其具体赋值; undefined完全没在预料之内的

21、传输协议的分类有哪些

【答案】

    • http - 超文本传输协议(client+server传输的内容,除了文本以外,还可以传输图片、音视频等文件流)
    • https - 比http更加安全,因为数据内容的传输通道是经过ssl加密的
    • ftp - 资源文件传输协议,一般用于客户端把资源文件上传到服务器端

22、Get和POST请求的区别

【答案】

    get    参数长度有限制  
    get会把请求的数据附加在url上     
    get是明文传输     
    get请求能缓存    

    post 参数长度无限制
    post请求会把数据附加在请求体中
    post不是明文传输
    post不能缓存

23、怎样把JQ对象转换为JS对象

【答案】

    var $body = $("body");
    $body[0] // -> 在集合中获取指定索引的内容(获取的内容就是原生JS对象)

    $body.get(索引) // -> 等价于上面的,获取指定索引的对象,获取到的是JS对象
    $body.eq(索引) // -> 获取指定索引位置的对象,但是获取的结果还是JQ对象

24、简述一下JQ的插件机制

【答案】

    • $.extend() -> 把方法扩展到JQ对象上,这个操作一般是用来完善类库的
    • $.fn.extend() -> 把方法扩展到JQ原型上,供JQ实例(DOM集合)使用,这个一般是用来写JQ插件的

25、localStorage vs sessionStorage 区别

【答案】

    • localStorage属于永久存储到本地,不管是刷新页面还是关掉页面或者关掉浏览器,存储的内容不会消失,只有我们自己手动的去删除才会消失(不管是杀毒软件还是浏览器自带的清除历史记录功能都不能把localStorage存储的内容移除掉)
    • sessionStorage属于临时的会话存储,只要当前的页面不关闭,信息就可以存储下来,但是页面一旦关闭,存储的信息就会自动清除(F5刷新页面只是把当前的DOM结构等进行重新的渲染,会话并没有关闭)

26、localStorage vs cookie 区别

【答案】

    • cookie
        • 兼容所有浏览器
        • 存储内容的大小是有限制的,一般同源下只能存储4KB的内容
        • 存储的内容是有过期时间的
    • localStorage
        • 不兼容IE6-8
        • 存储的内容也有大小限制,一般同源下只能存储5MB的内容
        • 存储的内容是永久存储到本地

27、x 和 x 的区别

【答案】

    // 后缀表达式 x++ x-- ==> 先用x的当前值作为表达式的值,再进行自增自减操作 ==> 先用后变【先用变量的值参与运算,变量的值再进行自增自减运算】
    var a, b ;
    a = 20;
    b = 30;
    var res = (a++) + (b++);
    console.log(res, a, b); // 50 21 31
    // 前缀表达式 ++x --X ==> 先完成变量的自增自减运算, 再用x的值作为表达式的值 ==> 先变后用【变量的值先变, 再用变量的值参与运算】
    var a, b ;
    a = 20;
    b = 30;
    var res = (++a) + (++b);
    console.log(res, a, b); // 52 21 31

28、谈一谈对async、await的理解

【答案】async

    async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
    进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

29、说一下冒泡排序的原理

【答案】

    //首先明确冒泡排序的思想:当前项和后一项进行比较,如果当前项大于后一项,两者交换位置
    //明晰冒泡排序的规律:
    (1)轮数:arry.length-1
    (2)每一轮比较的次数:arry.lengh-1-i
    (3)交换位置的方法:
        1) 拿一个第三者
        var a=12;
        var b=13;
        var c=null;
        c=a;
        a=b;
        b=c;

        2) 倒在一块,然后再分开
        a=a+b;
        b=a-b;
        a=a-b;
    //方法
    function bubbleSort(arry){
        var temp=null;
        for(var i=0;i<arry.length-1;i++){
            for(var j=0;j<arry.length-1-i;j++){
              if(arry[j]>arry[j+1]){
                  temp=arry[j];
                  arry[j]=arry[j+1];
                  arry[j+1]=temp;
              }
            }
        }
        return arry;
    }

    如果数组简单一轮就可以达到呢?例如:var arry=[2,1,3,5,4];
    优化方法:
    1) 在第一轮设置一个变量var flag=false;
    2) 要是有需要交换位置的,我们就让var flag=true;继续执行下一轮
    3) 在下一轮开始之前又把var flag=false;

    //优化后的方法如下:
    function bubbleSort(arry){
        var flag=false;
        for(var i=0;i<arry.length-1;i++){
            for(var j=0;j<arry.length-1-i;j++){
                if(arry[j]>arry[j+1]){
                    arry[j]=arry[j]+arry[j+1];
                    arry[j+1]=arry[j]-arry[j+1];
                    arry[j]=arry[j]-arry[j+1];
                    var flag=true;
                }
            }
            if(flag=true){
                flag=false;
            }else{
                break;//已经排好序了,直接结束循环即可
            }
        }
    }

30、数组去重

【答案】

    var arry = [1,2,2,2,3,3,5,5,7,7,9,8,8,9];

    function deleteReapeat(arry){
      var obj={};
      for(var i=0;i<arry.length;i++){
          var cur=arry[i];//数组当前项
          if(obj[cur]==cur){//说明已经存在这一项了,cur重复了,需要进行删除
            //把数组末尾的那一项拿过来替换当前项,再把数组末尾的那一项删除掉
            arry[i]=arry[arry.length-1];
            arry.length--;
            //如果用splice删除的话,索引会发生改变,所以用i--来进行处理解决
            i--;
            continue;
          }
          obj[cur]=cur;     
      }
      return arry; 
    }
    function unique(array) {
      return Array.from(new Set(array));
    }

    let w = unique([1, 1, 2, 3, 3]);
    console.log(w) // => [1, 2, 3]

31、说一下知道的JavaScript的设计模式, 并简要说明一下其中的2种

【答案】:单例模式、工厂模式、适配器模式、装饰器模式、代理模式、外观模式
设计模式

    // 工厂模式
    // 理解: • 你去购买汉堡,直接点餐、取餐,并不会自己亲手做
                 //• 商店要封装做汉堡的工作,做好直接给买者
    class Product {
            constructor(name) {
                this.name = name;
            }
            init() {
                console.log('init')
            }
            fn1() {
                console.log('fn1')
            }
        }

        // Creator就是一个工厂,里面有一个加工函数create
        class Creator {
            create(name) {
                return new Product(name)
            }
        }

        let creator = new Creator();
        let p = creator.create('p1');
        p.init();
        p.fn1();
    // 单例模式
    // • 系统中被唯一使用的
    // • 一个类只有一个实例
    class SingleObject {
            login() {
                console.log('login');
            }
        }

        SingleObject.getInstance = (function() {
            let instance;
            return function() {
                if (!instance) {
                    instance = new SingleObject();
                }
                return instance;
            }
        })();

        let obj1 = SingleObject.getInstance();
        obj1.login();

32、实现一个对象的深拷贝

    function deepClone(obj) {
        if (obj.isArray) {
            var result = [];
        } else {
            var result = {};
        }
        for (let key in obj) {
            if (typeof (obj[key]) == 'object') {
                result[key] = deepClone(obj[key]);
            } else {
                result[key] = obj[key];
            }
        }
        return result;
      }