zoukankan      html  css  js  c++  java
  • 前端面试题(亲身面试经验)

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

    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的问题

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

  • 相关阅读:
    搭建非域AlwaysOn win2016+SQL2016
    从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点)
    从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群)
    从0开始搭建SQL Server AlwaysOn 第三篇(配置AlwaysOn)
    从0开始搭建SQL Server AlwaysOn 第一篇(配置域控)
    四、基于Windows 2012配置SQL Server 2014 AlwaysOn
    三、安装SQLserver 2014(For AlwaysOn)
    二、 Windows 2012配置故障转移(For SQLServer 2014 AlwaysOn)
    Mybatis-SQL语句构建器类及日志
    Mybatis-JavaAPI
  • 原文地址:https://www.cnblogs.com/jinzhenzong/p/12345249.html
Copyright © 2011-2022 走看看