2023年 4月 22日 来源: 新华社微博 字号:默认 超大

一:JavaScript 

1、闭包是什么?利弊?如何解决弊端?

闭包是什么:JS中内层函数可以访问外层函数的变量,外层函数无法操作内存函数的变量的特性。我们把这个特性称作闭包。

闭包的好处

闭包的弊端:内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。

解决办法:无法自动销户,就及时手动回收,使用后将函数的引用赋null。

2、深度拷贝

1、深拷贝与浅拷贝的区别?

拷贝的层级不同,深拷贝是指每一层数据的改动都不会影响原对象和新对象,浅拷贝只有第一层的属性变动不互相影响,深层的数据变动还会互相影响。

2、JSON的stringify和parse处理的缺点?

2023年前端面试题汇总

2023年前端面试题汇总

3、$.extend()

使用jquey的extend方法不仅能实现深度拷贝,还能实现深度合并。具体用法

深度拷贝:$.extend({},targetObject) // targetObject是需要复制的对象

深度合并:$.extend(true,{},targetObject1,targetObject2) // 可以将两个对象深度合并后再返回出一个新对象

3、如何判断空对象?如何区分数据类型?

    判断空对象

   区分数据类型

let a = [1,2]
Object.prototype.toString.call(a)  // '[object Array]'   

4、如何改变this指向?区别?

let a = {
    name: 'sunq',
    fn:function(action){
        console.log(this.name + ' love ' + action);
    }
}
let b = {name:'sunLi'}
// 正常的this指向
a.fn('basketball');   // sunq love basketball
// 改变this指向,并体现call与apply的区别
a.fn.apply(b,['football']); // sunLi love football
a.fn.call(b,'football'); // sunLi love football
// call 和 apply 区别: call 和 apply 都是可以改变this 指向的问题, call 方法中传递参数要求一
// 个 一个传递参数。 但是apply 方法要求传递参数是一个数组形式。
// 还是上面的示例,bind也可以实现call和apply的效果。
// bind的不同之处在于bind会返回一个新的函数。如果需要传参,需要再调用该函数并传参
a.fn.bind(b)('piano'); // sunLi love piano

5、沙箱隔离怎么做?

使用iframe可以实现,变量隔离

6、浏览器存储,他们的区别?

localStorage/sessionStorage是window的属性,cookie是document的方法

7、常用的数组方法有哪些?

    slice和splice的区别?

数组如何滤重?

8、Dom事件流的顺序?什么是事件委托?

当页面上的一个元素被点击时,先从document向下一层层捕获到该元素。然后再向上冒泡,一层层触发。

事件委托是将事件写在父级元素上,e.target是事件捕获时那个最小的元素,即选中的元素。所以可以根据e.target操作选中的元素。这样不需要给每个子元素绑定事件,代码更加简约。

9、对原型链的认识?

js通过原型链模拟实现面向对象,比如通过实例化一个构造函数可以给每个对象挂载自己专属的属性,通过给类的prototype上赋方法是所有对象所共有的方法。每次实例化不再赋值原型链上的方法。

10、防抖/节流的区别?

区别:防抖只会在最后一次事件后执行触发函数,节流不管事件多么的频繁,都会保证在规定时间段内触发事件函数。

原理是维护一个定时器,将很多个相同的操作合并成一个。规定在delay后触发函数,如果在此之前触发函数,则取消之前的计时重新计时,只有最后一次操作能被触发。例如:实时搜索的input,一直输入就不发送。

let input = document.querySelector("input");
let time = null;//time用来控制事件的触发
input.addEventListener('input',function(){
   //防抖语句,把以前的定时删除,只执行最后一次
   if(time !== null){
     clearTimeout(time);
   }
   time = setTimeout(() => {
     console.log(this.value);//业务实现语句,这里的this指向的是input
   },500)
 })

原理是判断是否达到一定的时间来触发事件。某个时间段内只能触发一次函数。例如:在指定的时间内多次触发无效

    //节流
    function throttle(fn, time) {//连续触发事件  规定的时间
        let flag = false;
        return function () {
            //使用标识判断是否在规定的时间内重复触发了函数,没有就触发,有就不触发
            if (!flag) {//不为假时 执行以下
                fn();//触发事件
                flag = true;//为真
                setTimeout(() => {//超时调用(在规定的时间内只执行一次)
                    flag = false;
                }, time);
            }
        }
    }
    mybtn.onclick = throttle(btn, 3000);//单击事件   节流(btn,3s时间)

二:Html

1、重绘和重排(回流/重构/重载)是什么?如何优化?

避免循环插入dom,比如table的行。可以js循环生成多个dom后,一次性插入。

2、html5有哪些新特性?

三:CSS

1、如何实现一个宽度不固定的上下左右居中的弹框?

方法一:
.pop{
    width: 300px;
    height: 300px;
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto;
    border: 1px solid red;
} 
方法二:
.chartLengend {   // 父元素
    width: 60px;
    height: 40px;
    position: relative;
    .line {       // 子元素
      width: 100%; 
      height: 3px;
      background-color: #DEA182;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border-radius: 2px;
    }
}

2、伪类和伪元素区别?

.class:last-child{}
.class:first-child{}
a:link    {color:green;}
a:visited {color:green;}
a:hover   {color:red;}
a:active  {color:yellow;}
//  :before用于在某个元素之前插入某些内容。
//  :after用于在某个元素之后插入某些内容。
css
p:before{
    content:"Read this: ";
}
html:
<p>I live in Ducksburg</p>
页面展示:
Read this: I live in Ducksburg
F12看dom中:
before
Read this: I live in Ducksburg

四:Vue

1、单页面应用是什么?优缺点?如何弥补缺点

单页面对一个入口DOM通过路由去更改内容,整个应用只有一个html页面

SPA优点:用户体验好,没有页面切换就没有白屏情况;

SPA缺点:首屏加载慢,不利于SEO

SPA弥补:通过压缩、路由懒加载缓解首屏慢;通过SSR 服务器端渲染解决SEO问题;

2、组件及通信方式有哪些?

2.1、什么是组件?

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

声明组件

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

使用组件(把组件当作自定义元素)

<div id="components-demo">
  <button-counter></button-counter>
</div>

引入组件

new Vue({ el: '#components-demo' })

2.2、父向子传值

Prop 是你可以在组件上注册的一些自定义 attribute当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:

组件内部声明prop

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

父组件里调用,并给prop赋值,传递到组件内部

<blog-post title="My journey with Vue"></blog-post>

2.3、父组件监听子组件事件

其实就是通过在父组件声明方法,并绑定在子组件上。以子组件内部触发方法的形式,向父组件传参,实现子向父传值的效果。如下

父组件中声明方法,并绑定在子组件上

<template>
<lineChart v-on:getQuotaVal="getQuotaVal"></lineChart>
</template>
<script>
 methods: {
    // 本事件用来监听折线图子组件,从子组件拿到指标数据
    getQuotaVal:function(obj){
      this.lineDateType = obj.lineDateType; // 这样父组件就拿到了,子组件的obj数据
    }
  },
</script>

子组件触发方法

that.val = {};
that.$emit('getQuotaVal',that.val); // 将子组件的数据发送过去;

 2.4、兄弟组件间交互

使用EventBus(事件总线),vue.$bus.on和emit方法。

初始化——全局定义,可以将eventBus绑定到vue实例的原型上,也可以直接绑定到window对象上.

//main.js
Vue.prototype.$EventBus = new Vue();

触发事件

this.$EventBus.$emit('eventName', param1,param2,...)

监听事件

this.$EventBus.$on('eventName', (param1,param2,...)=>{
    //需要执行的代码
})

移除监听事件

      为了避免在监听时,事件被反复触发,通常需要在页面销毁时移除事件监听。或者在开发过程中,由于热更新,事件可能会被多次绑定监听,这时也需要移除事件监听。

this.$EventBus.$off('eventName');

3、v-if和v-show区别?

v-if控制Dom是否存在,v-show控制样式

4、vuex是什么?使用步骤大概说下

vuex是一个状态管理工具,集中式的管理所有组件的状态数据。统一的去管理组件,将组件的状态抽象为一个store文件,通过commit方法触发mutation里的函数来改变组件属性。

组件中可以使用computed属性监听数据的变化控制组件显隐等。如下举个loading组件的栗子

loading组件中根据Loading数据,控制DOM显隐

<template>
    <div class="cover" v-show="Loading">
        <div>加载中</div>
    </div>        
</template>
<script>
  import Store from '../../store'
  export default {
    name: "Loading",
    computed:{
      Loading(){
        return Store.state.Loading; 
      }
    }
  }
</script>

vuex集中管理状态,创建一个叫store的js文件

import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    // Loading组件
    Loading:false,
  },
  mutations: {
    // Loading组件
    ChangeLoading:function (State,Value) {
      State.Loading = Value;
    }
  },
});

 使用loading的组件中,这样操作

import Store from '../../store'
Store.commit("changeFooter",true);

vuex中 mutation和action的区别和使用?

5、vue watch和computed区别?

computed

        计算结果并返回,只有当被计算的属性发生改变时才会触发(即:计算属性的结果会被缓存,除非依赖的响应属性变化才会重新及孙)。

        如上loading组件也有使用到computed属性。

watch

        监听某一个值,当被监听的值发生变化时,执行相关操作。

与computed的区别是,watch更加适用于监听某一个值得变化,并做对应操作,比如请求后台接口等。而computed适用于计算已有的值并返回结果。 监听简单数据类型:

data(){
    return{        
        'first':2     
    }   
},   
 watch:{      
     first(){        
         console.log(this.first)    
    }   
 },

6、Vue的虚拟Dom是什么?谈一谈对vue diff算法的认识?key的作用?

7、谈谈对vue的双向绑定原理的理解?

双向绑定主要指修改数据时,无须操作DOM,视图会自动刷新。操作视图时绑定的数据也会跟随变动。

数据 => 视图

vue在初始化实例时,会用Object.defineProperty方法,给所有的数据添加setter函数,实现对数据变更的监听。当数据被修改时,生成新的虚拟DOM树,跟老的虚拟DOM对比,根据对比结果找出需要更新的节点进行更新。

视图 => 数据

从视图到数据较为简单,视图变化后触发监听如oninput等,在绑定的方法中修改数据。

8、vue首屏优化怎么做?

9、vue2的缺陷是什么?如何解决vue2.0数组中某一项改变,页面不改变的情况?

缺陷:数据如果为对象直接新增属性,如果为数组通过下标操作数组项,页面无法触发更新。

原因: Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。关于数组作者通过重写push/pop/shift/unshift/splice/reverse/sort这些方法来实现数据的相应绑定,其余的操作无法触发页面更新;

对策:关于对象可以通过Vue.$set(obj,key,value),组件中通过this.$set(obj,key,value)实现新增,修改属性vue可以相应更新视图。关于数组也可以通过Vue.$set(obj,key,value),或者作者重写的那些方法来操作;

10、异步操作放在created还是mouted?

如果有些数据需要在初始化时就渲染的,比如select下拉框的下拉内容,在mouted中请求。好处如下

11、vue-router的钩子函数有哪些?

组件内部钩子:beforeRouterEnter()、beforeRouterLeave、beforeRouterUpdate

12、页面如何跳转?如何跨页面传参数?

this.$router.push({
    path: '/url',
    query: {
        par:parid
    }
})

接受参数

var parid = this.$route.query.par; 

13、vue子组件的生命周期?子元素在什么时候挂载?

  1. 父:beforeCreate    首先初始化父原素
  2. 父:created              父原素挂载数据
  3. 父:beforeMounte    父原素开始挂载dom,tpl里遇到子组件
  4. 子:beforeCeate      子组件开始挂载数据
  5. 子:created              子元素数据挂载成功
  6. 子:beforeMount      子元素开始挂载dom
  7. 子:mounted            子元素dom挂载结束
  8. 父:mounted            父原素dom挂载结束
  9. 父:beforeUpdate     下面开始类似于dom事件流
  10. 子:beforeUpdate
  11. 子:updated
  12. 父:updated
  13. 父:beforeDestory
  14. 子:beforeDestory
  15. 子:destroyed
  16. 父:destoryed

子元素在父元素挂载dom时,开始加载。子元素一直到加载完毕dom后,父原素结束dom挂载。后面就类似于dom事件流了。

14、vue的import和node的require区别?

JS支持两种模块化方式,commonjs和ES6。

commonjs用于nodejs,同步加载模块。ES6的import为了不卡顿,异步加载模块。

新版Nodejs也支持使用import,但是需要修改文件后缀名为.mjs,或者在package.json中,制定type字段为module。

五:ES6

1、箭头函数与es5函数区别?

let a = {
    name: 'sunq',
    fn:function(action){
        console.log(this.name + ' love ' + action);
    }
}
let b = {name:'sunLi'}
// 正常的this指向调用他的对象
a.fn('basketball');   // sunq love basketball
// 改变this指向
a.fn.apply(b,['football']); // sunLi love football
// 如果将a对象的fn函数改成箭头函数,this.name会是undefined
// 箭头函数的this指向不会改变,且总是指向函数定义生效时所在的对象。
var Person = function(name){
    this.name = name;
}
let sunq = new Person('sq'); // {name: 'sq'}
var Person = (name) => {
    this.name = name;
}
let sunq = new Person('sq'); // 报错 Person is not a constructor

2、ES6提供的解决异步交互的新方法?区别?

Promise、Genarate、async\await

3、宏任务和微任务有哪些?执行顺序?

4、先并行请求2个接口后,再请求第3个接口,如何处理?

使用Promise.all()方法,将两个promise传入all方法,拿到异步结果再请求第三个

明明知道的语法,面试官一问我偏偏就是跟实际场景联系不到一块,

5、js的数据类型

string number null undefined boolean object bigInt symbol

6、说几个ES6新增的数组的方法  详情

map 实例方法,类似于forEach,但是返回新数组

find和findIndex  实例方法,传入一个匿名函数,ruturn出符合条件的项或下标

... 拓展运算符 基本功能是将一个数组转为用逗号分隔的参数序列

7、for in和for of的区别

8、多个数据请求,如何顺序执行?

使用promise的then方法,或者写多个promise,async中使用await顺序执行。

9、proxy的理解,与defineProperty的区别?

// es6的proxy
let obj = {name:1,sex:2}
let p = new Proxy(obj,{
    get:(value,key)=>{
        return 'sq的'+obj[key]
    }
});
p.name // sq的1
p.sex // sq的2
// es5的代理
let newObj = {name:1,sex:2}
Object.defineProperty(newObj,'name',{
    get:function(val){  //defineProperty中get函数没有入参
        return 'sq的' + val
    }
})
newObj.name  // sq的undefined
newObj.sex // 2

10、谈谈promise的原理?

六:ElementUI

1、如果需要修改样式怎么做?

2、如何通过继承扩展 element-ui 组件的功能?

通过继承扩展 element-ui 组件的功能_elementui扩展组件_在厕所喝茶的博客-CSDN博客

七:Jquery

1、如何获取同一个cl下,最后一个li?

$("#id").children().eq(3).remove();
// 获取多个class中的某一个
$(".className").eq(n).attr("class") // 可行
$(".className")[n].attr("class") // 不可行

2、window.load和$(document).ready()的区别?执行先后顺序?

总结:ready事件在load事件加载之前完成。

3、如何绑定一个点击事件?

// 方式一
$("#id").on('click',function(){});
// 方式二
$("#id").click(function(){});
// 方式三
$("#id").bind("click",function(){});

4、Jquery常用动画?

八:Git的使用

1、常用哪些语句?

pull、commit、push、reset、merge、log、branch、stash

stash如何使用?

  1. git stash -m 'xxx'
  2. git stash pop

2、版本回退语句?soft和hard区别?

git reset --soft 版本号

git reset --hard 版本号

soft会退后,代码改动在本地还保存的有。hard会删除本地改动,彻底抹去该版本的痕迹。详情

3、合并分支注意事项?

将自己分支合到目标分支前,最好先将目标分支合到自己分支上处理完冲突,再将自己的分支合回目标分支。

4、如何进行分支管理?

2023年前端面试题汇总

九:敏捷开发

1、什么是敏捷开发?

个人理解,敏捷开发就是把一个大需求拆为多个独立的小需求。每个小需求可独立开发、测试、上线,循序渐进的完成整个系统。每个版本的周期可能比较短,比如2周,或者4周。

比如某公司需要开发维护一个巨大的平台,可能把平台外包给多个公司干。如果用如上方法,并行开发,可显著缩减工期。

如果想要保证每个版本又快又顺利的上线,需要有完善的角色支持和流程规范。

2、敏捷开发的好处?

个人理解,当团队稍大,工期很紧时,如何有条不紊的保证版本质量就需要一套有效的流程了。

比如一个团队同时收到3个需求,每个需求分发给多个前后端开发。作为版本负责人或者项目负责人,如何把控每个人的代码质量、完成进度、过程留痕、风险规避,其实是有难度的。

一个需求如果经过,需求澄清、方案设计、设计评审、需求传递、版本排期/评审、开发划分、版本开发、测试用例评审、内部测试、代码评审、灰度试用&测试、版本发布、业务验收等完整的流程,可以有效地降低犯错的几率。也方便后期的查找责任人,总结各环节的问题,提升团队的工作效率,使交付越来越平稳。

十:开源项目

部分公司面试要求中有写,维护有开源项目且具有50个star者优先。

我平时有维护一个具备管理端/用户端/服务端的个人网站:sunq's blog

面试过程中无话可说时,可以拿出来聊聊,缓解尴尬。

博客的代码全部开源:源码Github地址​​​​

十一:网络相关

1、http和https的区别?

2、常见状态码

十二:webpack

1、dependencies和devDependencies区别

安装时 --save -dev会放在devDependencies中,--save放在dependencies

devDependencies中安装的是开发环境使用的包,比如eslint、vue-cli-serve;

dependencies中安装的是生产和开发环境下都需要使用的包,比如vue、element、vant等

devDependencies中的依赖模块,在生产环境下不会被打入包内

十三:页面优化

1、某个页面加载较慢,从哪些方向分析、解决问题?

首先判断是接口慢,还是页面慢。如果接口慢,后端优化。

如果前端页面加载慢,看是否是因为图片等资源过大,尝试替换不同格式体积的图片。定位是否是某些数据处理的函数,比较耗时。或者是否循环操作DOM,js生成dom后再批量插入。

如果页面直接卡死,就需要分析是否内存泄漏。比如大屏展示的定时刷新卡死,排查思路可如下:

使用chrome的任务管理器,操作页面观察的内存占用的变化。定位到是哪些操作,哪块代码导致内存占用飙升。

因为js并没有直接释放缓存的语法,只有靠浏览器的垃圾回收机制自动清理。我们需要做的是及时给不需要的变量赋空。

特别注意大数据的循环实例化后,变量是否及时赋空。定时器等闭包方法中是否存在内存泄漏,循环渲染地图时,是否先将之前地图数据清空等。

单页面一般不会某个页面加载慢,一般都集中在首屏加载时白屏较久。处理方法可参考上文中。

2、使用缓存

十四:Node.js

1、用过哪些插件(express中间件)?

2、mongodb和mysql的区别?