• 前端面试题(亲身面试经验)


      最近面试了一些公司,趁着疫情期间,总结一波。大家可以看看  会有用的。

    webpack

    1、webpack中entry和output的作用

    webpack中的entry标记入口文件,它可以是一个字符串(单入口)或者一个数组(多入口),output用来标记输出,主要有两个属性 path和 filename。其次就是publicPath 和chunkFileName

    2、webpack中loader和plugin的作用

    loader 用于加载某些资源文件。 因为webpack 本身只能打包commonjs规范的js文件,对于其他资源例如 css,图片,或者其他的语法集,比如 jsx, coffee,是没有办法加载的。 这就需要对应的loader将资源转化,加载进来。loader是用于加载的,它作用于一个个文件上。
    plugin 用于扩展webpack的功能。它直接作用于 webpack,扩展了它的功能。当然loader也是变相的扩展了 webpack ,但是它只专注于转化文件。而plugin的功能更加的丰富,而不仅局限于资源的加载。

    3、webpack中可以有哪些优化

    1、优化Loader的文件搜索范围,指定include、exclude
    2、把Babel编译过的文件缓存起来  loader: 'babel-loader?cacheDirectory=ture'
    3、懒加载、按需加载、分包、压缩
    4、提取公共代码与第三方代码
    5、使用HappyPack插件开启多进程Loader转换
    6、区分dev、product环境,减少不必要
    7、减少不必要的编译,提升开发时构建速度(devServer.watchOptions.aggregateTimeout)

    4、你是依据什么来确定你要优化哪个模块

    我会使用一个webpack-bundle-analyzer的插件,这个插件(plugin常规使用,需要在package.json 中写npm_config_report=true)可以查看打包后文件包的大小,进而可以找出比较大的包,对他进行优化

    5、webpack构建流程

    1、初始化参数,从配置文件和shell语句中读到的参数合并,得到最后的参数
    2、开始编译:用合并得到的参数初始化complier对象,加载是所有配置的插件,执行run方法开始编译
    3、确定入口,通过entry找到入口文件
    4、编译模块,从入口文件出发,调用所有配置的loader对模块进行解析翻译,在找到该模块依赖的模块进行处理
    5、 完成模块编译,得到每个模块被翻译之后的最终的内容和依赖关系
    6、输出资源,根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,在把每个chunk转换成一个单独的文件加载到输出列表、
    7、输出完成,确定输出的路径和文件名,把内容写到文件系统中
    
    简单版
    初始化参数 -> 编译开始 -> 找到入口 -> 编译入口及其依赖 -> 完成编译确定关系 -> 输出资源 -> 输出完成

    6、webpack热更新原理

    首先可以这么理解 webpack是一个基于node的小服务器,这么就好理解了,简单来说:
    1、watch 编译过程、devServer 推送更新消息到浏览器
    2、浏览器接收到服务端消息做出响应
    3、对模块进行热更新或刷新页面

    7、webpack-dev-server和http服务器如nginx有什么区别?

    webpack-dev-server使用内存来存储webpack开发环境下的打包文件,并且可以使用模块热更新,他比传统的http服务对开发更加简单高效。

    8、happypack使用方法

    module:{
        rules:[{
                test:/.js$/,
                use:['happypack/loader?id=babel']
                exclude:path.resolve(__dirname, 'node_modules')
            },{
                test:/.css/,
                use:['happypack/loader?id=css']
            }],
        plugins:[
            new HappyPack({
                id:'babel',
                loaders:['babel-loader?cacheDirectory']
            }),
            new HappyPack({
                id:'css',
                loaders:['css-loader']
            })
        ]
    }

    js

    1、array map和foreach的区别

    map有返回值而且必须return返回一个数组才行,而forEach没有返回值可直接打印结果。

    2、array some和filter的区别

    some方法返回的是boolean值,可用于检察数组中是否有某对象
    filter方法返回的是一个新数组,可用于过滤数组中的对象

    3、new一个对象过程发生了什么

    1、创建一个新对象,如:var person = {};
    2、新对象的_proto_属性指向构造函数的原型对象。
    3、将构造函数的作用域赋值给新对象。(也所以this对象指向新对象)
    4、执行构造函数内部的代码,将属性添加给person中的this对象。
    5、返回新对象person。

    4、数组排序的方式

    主要有像快速排序、插入排序、冒泡排序

    插入排序

    方法:将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
    从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。
    
    代码:
    var insertSort = function(arr) {
        var len = arr.length;
        var preIndex, current;
        for(var i = 1; i < len; i++){
            preIndex = i - 1;
            current = arr[i];
            while(preIndex >= 0 && arr[preIndex] > current){
                arr[preIndex + 1] = arr[preIndex];
                preIndex--;
            }
            arr[preIndex + 1] = current;
        }
        return arr;
    }

    快速排序

    方法 :基本原理是将数组内的数分成三组,取数组中间的数为基准,将较小数放在左边,较大数放在右边,分别将三类数存放在一个数组内,最后递归进行排序。
    
    代码:
    
    function quickSort(arr) {
    
        if(arr.length<=1) {
            return arr;
        }
    
        let leftArr = [];
        let rightArr = [];
        let q = arr[0];
        for(let i = 1,l=arr.length; i<l; i++) {
            if(arr[i]>q) {
                rightArr.push(arr[i]);
            }else{
                leftArr.push(arr[i]);
            }
        }
        return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
    }

    冒泡排序

    方法:比较两个相邻的元素,如果后一个比前一个大,则交换位置,然后类推,每次比较完成后可以少比较最后一个
    
    代码:
    function bubbleSort(arr) {  
        for(let i = 0,l=arr.length;i<l-1;i++) {
            for(let j = i+1;j<l;j++) { 
              if(arr[i]>arr[j]) {
                    let tem = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tem;
                }
            }
        }
        return arr;
    }

    5、数组去重的几种方式

    1、利用ES6 Set去重
    代码:
    Array.from(new Set(arr))或者[...new Set(arr)]
    2、健值去重法
    原理:
    利用对象的属性不能相同的特点进行去重
    
    function unique(arr) {
        var arrry= [];
         var  obj = {};
        for (var i = 0; i < arr.length; i++) {
            if (!obj[arr[i]]) {
                arrry.push(arr[i])
                obj[arr[i]] = 1
            } else {
                obj[arr[i]]++
            }
        }
        return arrry;
    }
    3、includes
    
    
    function unique(arr) {
        var array =[];
        for(var i = 0; i < arr.length; i++) {
                if( !array.includes( arr[i]) ) {
                        array.push(arr[i]);
                  }
        }
        return array
    }
    高大上写法:(reduce+includes)
    function unique(arr){
        return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
    }
    4、排序后相邻去除法
    原理:sort之后会把所有元素排序,只需要比较当前和下一个元素是否完全一致,不完全一致则增加
    function uniq(array){
        array.sort();
        var temp=[array[0]];
        for(var i = 1; i < array.length; i++){
            if( array[i] !== temp[temp.length-1]){
                temp.push(array[i]);
            }
        }
        return temp;
    }
    5、indexof
    原理:如果当前数组的第i项在当前数组中第一次出现的位置不是i,那么表示第i项是重复的,忽略掉。否则存入结果数组。
    function uniq(array){
        var temp = [];
        for(var i = 0; i < array.length; i++) {
            //如果当前数组的第i项在当前数组中第一次出现的位置是i,才存入数组;否则代表是重复的
            if(array.indexOf(array[i]) == i){
                temp.push(array[i])
            }
        }
        return temp;
    }

    6、字符串出现最多的次数

    1、对象法
    原理:利用js健值对唯一的特性,如果包含key则value++
    function countStr(str){
        var obj = {};
        for(var i = 0, l = str.length,k; i < l ;i++){
            k = str.charAt(i);
            if(obj[k]){
                obj[k]++;
            }else{
                obj[k] = 1;
            }
        }
        var m = 0,i=null;
        for(var k in obj){
                if(obj[k] > m){
                    m = obj[k];
                    i = k;
                }
        }
    return i + ':' + m;
    }

    7、reduce两个参数分别是什么?

    第一个参数是一个函数,函数包含4个参数分别是:total(本次总和、初始值)、currentValue(当前元素)、currentIndex(当前index)、arr(原数组)
    第二个参数是一个初始值

    8、怎么可以将一个包含id的数组变成一个用id做key的json

    arr.reduce((total,currentItem) => {
        total[currentItem.id] = currentItem;
        return total
    },{})

    9、下面这个代码段输出什么

     console.log(1);
            setTimeout(function(){
                console.log(2)
            })
            new Promise(function(res,rej){
                console.log(3)
                res()
            })
            .then(function(){
                console.log(4)
            })
            .catch(function(){
                console.log(5)
            })
            async function a(){
                console.log(6);
                await console.log(7);
                console.log(8)
            }
            a();
            console.log(9)
    
    1,3,6,7,9,4,8,2
    本题花式比较多,遵循 同步>异步 微任务>宏任务

    10、promise有几种状态?

    三种,分别是pending、fulfilled、rejected(未决定,履行,拒绝),resolve时候会由padding->fulfilled,reject会由padding->rejected

    11、聊聊原型链

    这个。。。不知道怎么描述,也不给个题目,大致说下我是怎么说的吧
    js是一个万物皆对象的语言,每一个对象都会包含一个原型对象,对象以原型为模版,从他的原型继承方法和属性。当然对象也是可以由原型的,一层一层类似于一个链条,我们叫做原型链。比如举个例子,man->person->object

    12、判断是不是array

    1、instanceof (检测构造函数的 prototype 属性是否出现在某个实例对象的原型链)
    a instanceof Array
    2、原型prototype + toString +  call()
    Object.prototype.toString.call(a) === "[object Array]"
    Object.getPrototypeOf(a) ===  "[object Array]"
    3、原型prototype + isPrototypeOf()
    Array.prototype.isPrototypeOf(variable)
    4、 Array.isArray

    13、判断是不是对象

    1、原型prototype + toString +  call()
    Object.prototype.toString.call(obj) === '[object Object]'
    Object.getPrototypeOf(b).toString() === "[object Object]"
    2、instanceof(需要处理array)
    a instanceof Object && !(a instanceof Array)

    14、闭包是什么?怎么写?

    主要是为了从函数外部访问函数内部的变量,且这个变量永远不会被内存回收(会回收闭包内函数的变量不会回收闭包的变量),写法为函数内reuturn一个function

    15、null和undefined的区别?

    1、null是一个表示”无”的对象,转为数值时为0;undefined是一个表示”无”的原始值,转为数值时为NaN。
    2、undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。
    3、null表示”没有对象”,即该处不应该有值。

    16、前端几种数据存储方式

    localstorage、sessionstorage、cookie、websql等

    17、localstorage和sessionStorage以及cookie的区别

    首先是cookie他的存储大小对比local和session会小很多,local他会一直存在于浏览器内存,即使窗口关闭或者是浏览器关闭哪怕是新开一个页面local都是共享的。而session他是属于会话级的,我们哪怕打开相同的页面,session也不会共享过来。

    18、解决跨域的方式

    1、通过jsonp跨域(callback)
    2、CORS (服务端配置Access-Control-Allow-Origin)
    3、domain+iframe(做法:页面写一个iframe,将sec设置为要跨域的域名,通过document.getElementById('iframe').contentWindow.$,来取到对应的$,再通过$.ajax调用即可,不过仅限于主域名相同)
    4、nginx
    5、iframe

    19、eventloop介绍

    js是一个单线程的东西(为什么单线程自己百度),
    在JavaScript中,存在一个执行栈,当我们调用一个函数时,JavaScript引擎会将函数push到执行栈(Stack)中,
    执行完毕之后pop出去(先进先出),当遇到异步任务时,就将任务放进任务队列中。异步任务有两种,一种叫做宏任务一种叫做微任务,
    当主线程执行完毕,也就是执行栈为空时,会先确认微任务队列是否为空,不为空就取出微任务队列中所有微任务然后顺序执行,微任务执行完毕后
    会在宏任务队列中取出下一个任务添加到执行栈,如果这时候再遇到微任务继续加到微任务队列,如此循环。

    20、对象深拷贝方式

    1、最简单的方式 JSON.parse(JSON.stringify(Obj)) 这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。
    2、jQuery深拷贝 var copiedObject = $.extend(true, {}, originalObject)
    3、手动写递归方式
    function deepClone (obj) {
            var newobj = obj.constructor === Array ? [] : {};
            if(typeof obj !== 'object'){
                return;
            }
            for(var i in obj){
               newobj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];
            }
            return newobj
    }

    21、object.assign 是深拷贝还是浅拷贝

    Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
    也就是说,如果对象的属性值为简单类型(如string, number),通过Object.assign({},srcObj);
    得到的新对象为深拷贝;如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。

    22、聊聊平时你都会做哪些优化

    这个比较多了,我之前总结过。可以移步https://www.cnblogs.com/jinzhenzong/p/11777065.html,可以整理一下剪短的话语。
    我们可以分为几块去说,首先是缓存方面,我们可以借助如:localstorage、sessionStorage、cookie、浏览器自带缓存、h5 minifest等对数据、ajax接口、页面等进行缓存,
    在请求时,我们应该尽量减少重定向,做一些dns预解析、提高浏览器并发数等。之后就是页面解析和渲染时候,可以做关键路径的渲染,避免一下复杂的布局,用flex替换原始的布局模型
    还有一些像长列表的尾优化啊、图片懒加载啊,尽量的减少js的占用时间,比如使用jq时候我们经常会遇到一些操作样式或者设置内容等,这个时候我们可以对dom进行缓存和批量处理,来减少操作dom和回流重绘等。比如我们
    需要监听用户输入时候都要做一下事件的防抖节流,还有一些如减少闭包(为了变量能够被内存回收机制回收)、使用性能好的api如requestanimationframe代替settimeout、setinterval等。还有一块比较大的就是
    资源的处理,更多还是借助webpack,比如像分包、代码合并、文件的缓存、小图片base64等,还有像综合考虑是否使用框架、图片选质量比较低的,视频的预加载等。
    ps:强烈建议去看我原文章至少吧xmind下下来注释都看一编,不然深入问一下你就废了

    23、前端打开页面的过程

    同样之前总结过https://www.cnblogs.com/jinzhenzong/p/11753559.html
    大概过程如下:
    1、输入网址
    2、浏览器解析ip
    3、通过ip发起链接
    4、服务器接受请求
    5、处理请求并返回
    6、页面渲染 -> 
        解析HTML文件,创建DOM树(解析执行JS脚本时,会停止解析后续HTML) 
        解析CSS,形成CSS对象模型
        将CSS与DOM合并,构建渲染树
        布局渲染树
        绘制渲染树(可能触发回流和重绘)
    7、渲染完毕
    
    经过几个公司问题总结出,一般多会问第六步。前面就是听听而已。

    24、promise,async函数区别与理解

    promise 将原来的用 回调函数 的 异步编程 方法转成用relsove和reject触发事件, 用 then 和 catch 捕获成功或者失败的状态
    async函数就相当于自执行的Generator函数,async函数就相当于自执行的Generator函数,较于Promise,async 的优越性就是把每次异步返回的结果从 then 中拿到最外层的方法中,不需要链式调用,只要用同步的写法就可以了。
    比 promise 直观,但是 async 必须以一个 Promise 对象开始 ,所以 async 通常是和Promise 结合使用的。

    js的比较多,我只是选了一部分比较常问的,其他还有像设计模式、mvc、mvvm、jq组件、还有各种数组对象的方法的作用参数等就不总结了

    vue篇

    1、watch和计算属性区别

    watch适合处理的场景是,侦听一个数的变化,当该数据变化,来处理其他与之相关数据的变化(该数据影响别的多个数据)
    computed适合处理的场景是,获得一个值或者结果,该结果受其他的依赖的影响。(一个数据受多个数据影响)

    2、watch也可以实现监听为什么还要计算属性这个?

    1、首先vue官方推荐的是如果可以用计算属性就不要用watch,为什么呢?因为计算属性是有缓存的,对性能来说是更好的。
    2、其次两种功能是不一样的,computed针对于一个值依赖于多个,watch虽然也可以,但是代码会很臃肿

    3、data为什么是一个函数?

    组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

    4、双向数据绑定原理?

    利用了 Object.defineProperty() ,让数据变得可观测,结合订阅者发布者模式,当数据变化时候会由发布者来通知订阅者更新数据和view
    vue3利用proxy来让数据可观测。

    5、组件怎么实现双向数据绑定

    组件 内部props写上value,watch value进行数据的初始化,data中声明一个newvalue,监听这个newvalue,如果页面发生变化使用emit/on向上抛出input事件

    6、生命周期(8+3)官网去看

    7、extend和混合

    extend用于创建vue实例
        mixins可以混入多个mixin,extends只能继承一个
        mixins类似于面向切面的编程(AOP),extends类似于面向对象的编程

    8、什么是单向数据流

    数据流向是单向的,即父能给子,子不能修改父,写组件时我都会声明一个类似box的东西,当我子组件发生变化我都会通知到box再由box进行分发,这么做可以保证出现bug时候的调试

    9、事件通信

    父子:props
    子父:emit/on
    同级:新声明一个vue作为中间,来转发。或者原生写一个eventbus
    跨层级:provide 和 inject(不建议日常开发使用,主要在开发高阶插件/组件库时使用。)、同级的在这里也可以使用 

    10、vue中 key 值的作用

    1、不加key的话,更新机制的进行diff的时候是会全部比较的,对性能不好。
    2、不加可能会出现数据变化而没有渲染视图
    3、根据diff算法,同一层级的一组节点,他们需要通过唯一的id进行区分。

    11、vue中如果数据更新了视图没有渲染怎么办

    一般这种都会出现在jsonjsonArray中,需要使用$set(目标,要修改的key,值),如果是层级特别多可能set还是不会生效,需要this.$forceUpdate();

    12、如果我需要在视图更新后进行一些操作可以怎么做

    $nextTick

    13、vue v-show/v-if

    v-show控制display,适用于切换频繁的场景
    v-if如果为false,页面都不会渲染这个dom,适用于不经常切换的场景。

    14、vue-router有哪几种导航钩子

    1、全局的 beforeEach、afterEach
    2、单个路由独享的beforeEnter
    3、页面级的 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

    15、hash和history路由

    hash 值变化不会导致浏览器向服务器发出请求
    hash改变会触发 hashchange 事件(hashchange只能改变 # 后面的url片段)
    hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了
    
    history 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。但是history路由需要后端配置
    如果路由找不到,就始终返回一个html,不然会由刷新404的问题

    更多的会慢慢完善,我过滤掉了很多比如根据自己理解去解答的,代码题也过滤了不少,因为最近电话面试居多,程序题几乎没有。过滤了很多基础的,基本用过的人都会的。只取其中最经典,最常问,最有误区的,可能会由一些疏漏我之后想起来或者遇到再补上

  • 相关阅读:
    [atAGC049E]Increment Decrement
    [atARC099F]Eating Symbols Hard
    [atARC099E]Independence
    [Codeforces] Codeforces Round #456
    Treap
    Splay树
    [Offer收割]编程练习赛42
    [Codeforces]Good Bye 2017
    Codeforces Round #455
    Educational Codeforces Round 35
  • 原文地址:https://www.cnblogs.com/jinzhenzong/p/12345249.html
Copyright © 2020-2023  润新知