2024年前端面试题汇总
一:JavaScript
1、闭包是什么?利弊?如何解决弊端?
闭包是什么:JS中内层函数可以访问外层函数的变量,外层函数无法操作内存函数的变量的特性。我们把这个特性称作闭包。
闭包的好处:
- 隔离作用域,保护私有变量;有了闭包才有局部变量,要不然都是全局变量了。
- 让我们可以使用回调,操作其他函数内部;
- 变量长期驻扎在内存中,不会被内存回收机制回收,即延长变量的生命周期;
闭包的弊端:内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。
解决办法:无法自动销户,就及时手动回收,使用后将函数的引用赋null。
2、深度拷贝
1、深拷贝与浅拷贝的区别?
拷贝的层级不同,深拷贝是指每一层数据的改动都不会影响原对象和新对象,浅拷贝只有第一层的属性变动不互相影响,深层的数据变动还会互相影响。
2、实现拷贝的方法有哪些?
浅拷贝:数组可以用拓展运算符[...arr],或者slice().浅拷贝对象可以用Object.assign({},obj)
深拷贝:JSON.parse(JSON.stringify(obj)),或封装递归方法,或使用第三方库的方法,比如 JQuery的$.extend({},obj),或者lodash 的cloneDeep
3、JSON.parse(JSON.stringify(obj))处理的缺点?
- 如果对象中有属性是function或者undefined,处理后会被过滤掉;
- 如果属性值是对象,且由构造函数生成的实例对象,会丢弃对象的
constructor
;
3、如何判断空对象?如何区分数据类型?
判断空对象
- 1、用JSON的stringify转成字符串后,跟'{}'对比;
- 2、用ES6,判断Object.keys(targetObject)返回值数组的长度是否为0;
- 3、用ES5,判断Object.getOwnPropertyNames(targetObject)返回的数组长度是否为0;
区分数据类型
let a = [1,2]
Object.prototype.toString.call(a) // '[object Array]'
4、如何改变this指向?区别?
- call/apply
call 和 apply 都是可以改变this 指向的问题, call 方法中传递参数要求一个 一个传递参数。 但是apply 方法要求传递参数是一个数组形式。
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
- bind
还是上面的示例,bind也可以实现call和apply的效果。bind的不同之处在于bind会返回一个新的函数。如果需要传参,需要再调用该函数并传参
a.fn.bind(b)('piano'); // sunLi love piano
5、沙箱隔离怎么做?
使用iframe可以实现,变量隔离
6、浏览器存储,他们的区别?
- localStorage:永久保存,以键值对保存,存储空间5M
- sessionStorage:关闭页签/浏览器时清空
- cookie:随着请求发送,通过设置过期时间删除
- session:保存在服务端
localStorage/sessionStorage是window的属性,cookie是document的方法
7、常用的数组方法有哪些?
- 改变原数组:push、pop、shift、unshift、sort、splice、reverse
- 不改变原属组:concat、join、map、forEach、filter、slice
slice和splice的区别?
- slice切片的意思,根据传入的起始和终止下标,获取该范围数组。
- splice可根据传入参数个数不同实现删除、插入操作,直接操作原数组。第1个参数为起始下标,第2个为删除个数,第3个为要增加的数据。
数组如何滤重?
[...new Set(arr)]
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时间)
-
使用场景
两者主要区别在于时间间隔的处理方式和触发函数的执行时机。防抖是等待一段时间后执行事件,如果在这段时间内再次触发事件,会重新等待;而节流是每隔一定时间执行一次事件,不管这段时间内触发了多少次事件。
防抖适合处理连续触发的事件,例如输入框的实时搜索;而节流适合限制高频触发的事件,例如滚动事件、鼠标移动事件。
11、ES5,ES6如何实现继承?
在 JavaScript 中,可以使用原型继承、构造函数继承和组合继承等方式来实现继承。下面分别介绍这些方式的实现方法:
1. 原型继承
原型继承是 JavaScript 中最基本的继承方式,通过将一个对象的原型设置为另一个对象来实现继承。
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello from Parent');
};
function Child() {
this.name = 'Child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child = new Child();
child.sayHello(); // 输出:Hello from Parent
2. 构造函数继承
构造函数继承通过在子类构造函数中调用父类构造函数来实现继承。这种方式可以实现实例属性的继承,但无法继承父类原型上的方法。
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
const child = new Child('Child', 10);
console.log(child.name); // 输出:Child
3. 组合继承
组合继承结合了原型继承和构造函数继承的优点,通过在子类构造函数中调用父类构造函数并设置子类原型为父类实例来实现继承。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayHello = function() {
console.log('Hello from Parent');
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const child = new Child('Child', 10);
child.sayHello(); // 输出:Hello from Parent
4. ES6 Class 继承
ES6 引入了 Class 语法,提供了更加简洁和易读的继承语法。
class Parent {
constructor(name) {
this.name = name;
}
sayHello() {
console.log('Hello from Parent');
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
const child = new Child('Child', 10);
child.sayHello(); // 输出:Hello from Parent
12、讲讲什么是作用域?
13、常用操作字符串方法有哪些?是否更改自身?
14、break/continue/return的使用场景?
- break:用于结束整个循环(for、while、do-while)
- continue:用于结束循环中的当前层循环,进入下一层循环
- return:用户结束整个函数
二:Html
1、重绘和重排(回流/重构/重载)是什么?如何优化?
- 样式的调整会引起重绘,比如字体颜色、背景色调整等
- Dom的变动会引起重排,比如定位改动、元素宽高调整
避免循环插入dom,比如table的行。可以js循环生成多个dom后,一次性插入。
2、html5有哪些新特性?
- 本地存储,比如localStorage、sessionStorage
- 语义化标签,如header、footer、nav等,使代码结构清晰,利于seo
- canvas
- svg
- web worker,在主线程外再创建一个线程,可与主线程交互
- 拖放功能
三: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、伪类和伪元素区别?
- 伪类本质上用于弥补常规css选择器的不足,因为如果没有我们可能需要多写一个class,所以叫伪类
.class:last-child{}
.class:first-child{}
a:link {color:green;}
a:visited {color:green;}
a:hover {color:red;}
a:active {color:yellow;}
- 伪元素本质上是创建了一个有内容的虚拟元素,如::before ::after。因为相当于多了一个元素/节点,所以叫为元素
// :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' })
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>
其实就是通过在父组件声明方法,并绑定在子组件上。以子组件内部触发方法的形式,向父组件传参,实现子向父传值的效果。如下
父组件中声明方法,并绑定在子组件上
<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
- 通过Vuex传递
1、通过共同父组件传递
比如兄弟组件A和B,有一个共同父组件P。A要向B发送数据,可以先发消息给P(this.$emit),P接收到消息(P绑定在A上的方法接收方法)。P再发消息给B(P修改B组件的自定义属性的值),B接收数据(Props变量接收)。
2、使用
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、通过Vuex实现交互
比如兄弟组件A和B,A为发送方。
则A改动Vuex state中的变量(this.$store.commit('set',{name:xx})),
B组件中通过computed和watch配合使用(this.$store.state.name),监听name变动即可。
3、v-if和v-show区别?
v-if控制Dom是否存在,v-show控制样式
4、vuex是什么?使用步骤大概说下
vuex是一个状态管理工具,集中式的管理所有组件的状态数据。统一的去管理组件。
比如,组件A、B、C需要交互变量name、sex。可以将name、sex放在Vuex的state属性中。
限制A组件通过commit方法触发mutation里的函数来改变组件状态。组件B、C使用computed、watch监听数据的变化进行操作(接收数据)。
如下举个loading组件的栗子,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组件交互的组件中,直接去操作Vuex的状态即可
import Store from '../../store'
Store.commit("ChangeLoading",true);
5、vuex中 mutation和action的区别和使用?
- Mutation用于执行简单的同步操作,比如直接修改state的值。
- Action用于执行更复杂的异步操作,比如发起API请求或批量的异步操作,并可以在异步操作完成后再触发mutation来修改状态。
Mutation用于修改state中的状态,通过执行mutation来实现对状态的同步修改。只能执行同步操作,一般用于处理简单的状态修改。在组件中通过commit()
方法来触发mutation的执行。接收一个参数state,即当前的state对象,以及可选的payload参数作为载荷传入。在mutation中直接修改state的值,将其作为一个原子操作。
Action用于执行异步操作,可以包含任意的异步操作逻辑(例如发起API请求、异步数据处理等)。可以用于处理复杂的业务逻辑和流程控制。在组件中通过dispatch()
方法来触发action的执行。接收一个参数context,即一个和store实例具有相同方法和属性的context对象,可以通过context去调用其他的action、mutation等。通过commit方法触发mutation来修改state的值,实现对状态的同步修改。
6、vue watch和computed区别?
computed
computed中的属性本质上是一个变量。一个特殊的变量,它由其他变量计算或者说赋值而来。它依赖于其他变量的变化,无需也无法像普通变量一样直接赋值修改。
watch
watch用来监听某个变量,当被监听的值发生变化时,执行相关操作。
与computed的区别是,computed是一个变量,watch用来监听变量。computed监听构成它的变量的变化,导致自己这个变量变化。watch单纯监听某个变量变化,触发事件。computed方法体内只能返回构成它的变量构成,而watch里可以进行任何操作。
data(){
return{
'first':2
}
},
watch:{
first(){
console.log(this.first)
}
},
7、Vue的虚拟Dom是什么?谈一谈对vue diff算法的认识?key的作用?
8、谈谈对vue的双向绑定原理的理解?
双向绑定主要指修改数据时,无须操作DOM,视图会自动刷新。操作视图时绑定的数据也会跟随变动。vue数据双向绑定是通过数据劫持结合订阅者,发布者的方式来实现的,通过Object.defineProperty()来劫持各个属性的setter,getter,当数据变动时发布消息给订阅者,触发相应的监听回调
数据 => 视图
vue在初始化实例时,会用Object.defineProperty方法,给所有的数据添加setter函数,实现对数据变更的监听。当数据被修改时,生成新的虚拟DOM树,跟老的虚拟DOM对比,根据对比结果找出需要更新的节点进行更新。
视图 => 数据
从视图到数据较为简单,视图变化后触发监听如oninput等,在绑定的方法中修改数据。
9、vue首屏优化怎么做?
- 使用较轻量的组件,比如echart对应有vue-chart
- vue-cli开启打包压缩 和后台配合 gzip访问;
- 路由懒加载,分包;
- 打包时配置删掉log日志
- 资源过大可以使用cdn模式引入,不再打包到本地
10、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),或者作者重写的那些方法来操作;
11、异步操作放在created还是mouted?
如果有些数据需要在初始化时就渲染的,比如select下拉框的下拉内容,在mouted中请求。好处如下
- 页面初始化速度更快,减少用户等待时间
- 放在 created 中有助于一致性,因为ssr 不支持 beforeMount 、mounted 钩子函数
12、vue-router的钩子函数(生命周期)有哪些?
路由守卫可以在全局范围内设置,也可以在单个路由配置中设置。
在 Vue Router 中,路由守卫可以分为以下几种类型:
-
全局前置守卫(Global Before Guards):
router.beforeEach(to, from, next)
:在路由导航触发之前调用。可以用于全局的权限验证、导航控制等。
-
全局解析守卫(Global Resolve Guards):
router.beforeResolve(to, from, next)
:在导航被确认之前调用,也就是在beforeEach
之后调用,但在路由组件渲染之前。只在导航被确认之前调用一次。
-
全局后置钩子(Global After Hooks):
router.afterEach(to, from)
:在每次路由导航完成之后调用,不关心导航的成功与否。
-
路由独享守卫(Per-Route Guard):
beforeEnter(to, from, next)
:在单个路由配置中使用,用于对特定路由进行拦截。
-
组件内的导航守卫(Component’s Navigation Guards):
beforeRouteEnter(to, from, next)
:在路由进入前,在组件实例化之前调用。beforeRouteUpdate(to, from, next)
:在当前路由改变,但是该组件被复用时调用。beforeRouteLeave(to, from, next)
:在路由离开前调用,导航离开该组件的对应路由时调用。
13、页面如何跳转?如何跨页面传参数?
- router-link标签跳转
- 路由如下跳转,顺便把参数传了。如下
this.$router.push({
path: '/url',
query: {
par:parid
}
})
接受参数
var parid = this.$route.query.par;
14、vue子组件的生命周期?子元素在什么时候挂载?
- 父:beforeCreate 首先初始化父原素
- 父:created 父原素挂载数据
- 父:beforeMounte 父原素开始挂载dom,tpl里遇到子组件
- 子:beforeCeate 子组件开始挂载数据
- 子:created 子元素数据挂载成功
- 子:beforeMount 子元素开始挂载dom
- 子:mounted 子元素dom挂载结束
- 父:mounted 父原素dom挂载结束
- 父:beforeUpdate 下面开始类似于dom事件流
- 子:beforeUpdate
- 子:updated
- 父:updated
- 父:beforeDestory
- 子:beforeDestory
- 子:destroyed
- 父:destoryed
子元素在父元素挂载dom时,开始加载。子元素一直到加载完毕dom后,父原素结束dom挂载。后面就类似于dom事件流了。
15、vue的import和node的require区别?
JS支持两种模块化方式,commonjs和ES6。
commonjs用于nodejs,同步加载模块。ES6的import为了不卡顿,异步加载模块。
新版Nodejs也支持使用import,但是需要修改文件后缀名为.mjs,或者在package.json中,制定type字段为module。
17、vuex如何解决数据丢失?
Vuex 存储的状态是在内存中保存的,当页面刷新或者跳转时,状态会被重置。为了避免数据丢失,你可以考虑使用持久化存储(如 localStorage 或 sessionStorage)来保存状态,在页面加载时从持久化存储中恢复状态。
18、爷爷组件跟孙子组件如何交互?
Provide 和 Inject可以将父组件传给所有后代组件
在 Vue 3 中,provide
和 inject
是响应式的,但在 Vue 2 中它们不是响应式的。
1)Vue 3 中的响应式
在 Vue 3 中,使用
provide
和inject
提供的数据是响应式的,这意味着如果提供的数据发生变化,所有依赖这些数据的组件都会自动更新。这种响应式是通过 Vue 3 中的响应式系统来实现的。2)Vue 2 中的非响应式
在 Vue 2 中,
provide
和inject
提供的数据不是响应式的,这意味着如果提供的数据发生变化,依赖这些数据的组件不会自动更新。这是因为 Vue 2 中的provide
和inject
是基于原型链来实现的,而不是响应式系统。
19、说说vue的生命周期?
vue的生命周期是vue实例从创建到销毁的一系列过程。每个过程都有相对应的钩子函数,分为 beforeCreate(创建前),created(创建后),beforeMount(加载前),mounted(加载后),beforeUpdate(更新前),updated(更新后),beforeDestroy(销毁前),destroyed(销毁后)。
当使用keep-alive时,有activated和 deactivated两个钩子函数。当组件用keep-alive包裹时切换文件时不会进行销毁,而是进行缓存并执行deactivated 钩子函数,组件被激活时执行actived 钩子函数。
20. data为什么是一个函数?
为保证组件的可重用性和独立性。如果直接使用一个对象作为data,则不同的组件将共享同一个data对象,如果某个组件修改了data,则会影响到其他组件,导致数据混乱。所以data必须是一个函数,每个组件实例都有自己的独立的data数据。这样每次创建组件都会返回一个新的data对象就避免了组件间共享问题。
21.vue中常见指令
- v-text
- v-html
- v-model
- v-bind
- v-on
- v-for
- v-if v-else
- v-show
- v-slot
22.vue常用修饰符
- .prevent:阻止默认事件
- .stop:阻止冒泡事件
- .self:只在自己本身触发
- .once:只触发一次
- .capture:捕获模式
- .lazy:懒加载模式
- .trim:去除首尾空格
- .number:格式化为数字类型
五:ES6
1、箭头函数与es5函数区别?
- 箭头函数的this指向是固定的,普通的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指向
a.fn.apply(b,['football']); // sunLi love football
// 如果将a对象的fn函数改成箭头函数,this.name会是undefined
// 箭头函数的this指向不会改变,且总是指向函数定义生效时所在的对象。
- 不可以当作构造函数,不可以对箭头函数使用
new
命令,否则会抛出一个错误。
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
- 无
arguments
对象 - 不可以使用
yield
命令,因此箭头函数不能用作 Generator 函数。
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的区别
- for in适合遍历对象,遍历数组拿到的是下标
- for of适合遍历数组,遍历数组直接拿到数组的项。for of只能遍历具备iterator接口的类型。
8、多个数据请求,如何顺序执行?
使用promise的then方法,或者写多个promise,async中使用await顺序执行。
9、proxy的理解,与defineProperty的区别?
- proxy直接给所有属性设置拦截,defineProperty只给指定的设置
- proxy拦截后要用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、如果需要修改样式怎么做?
- 再写一个样式表引入,!important覆盖
- 样式穿透
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()的区别?执行先后顺序?
window.onload
必须等到页面内包括图片的所有元素加载完毕后才能执行。$(document).ready()
是DOM结构绘制完毕后就执行,不必等到加载完毕。通常简写为$()
总结: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如何使用?
- git stash -m 'xxx'
- git stash pop
2、版本回退语句?soft和hard区别?
git reset --soft 版本号
git reset --hard 版本号
soft会退后,代码改动在本地还保存的有。hard会删除本地改动,彻底抹去该版本的痕迹。详情
3、合并分支注意事项?
将自己分支合到目标分支前,最好先将目标分支合到自己分支上处理完冲突,再将自己的分支合回目标分支。
4、如何进行分支管理?
九:敏捷开发
1、什么是敏捷开发?
个人理解,敏捷开发就是把一个大需求拆为多个独立的小需求。每个小需求可独立开发、测试、上线,循序渐进的完成整个系统。每个版本的周期可能比较短,比如2周,或者4周。
比如某公司需要开发维护一个巨大的平台,可能把平台外包给多个公司干。如果用如上方法,并行开发,可显著缩减工期。
如果想要保证每个版本又快又顺利的上线,需要有完善的角色支持和流程规范。
2、敏捷开发的好处?
个人理解,当团队稍大,工期很紧时,如何有条不紊的保证版本质量就需要一套有效的流程了。
比如一个团队同时收到3个需求,每个需求分发给多个前后端开发。作为版本负责人或者项目负责人,如何把控每个人的代码质量、完成进度、过程留痕、风险规避,其实是有难度的。
一个需求如果经过,需求澄清、方案设计、设计评审、需求传递、版本排期/评审、开发划分、版本开发、测试用例评审、内部测试、代码评审、灰度试用&测试、版本发布、业务验收等完整的流程,可以有效地降低犯错的几率。也方便后期的查找责任人,总结各环节的问题,提升团队的工作效率,使交付越来越平稳。
十:加分项
1、开源项目
部分公司面试要求中有写,维护有开源项目且具有50个star者优先。
我业余时间有开发维护一个具备管理后台/用户端/服务端的个人网站:sunq's blog。(目前因不舍得给服务器续费已停止运营,需下载源码自己在本地跑起来)
该个人网站的页面UI/功能设计,用户端/管理后台/服务端代码开发,SEO解决/首屏优化,服务器部署维护等,都由本人独立完成。目前github获得300多个赞。
面试过程中无话可说时,可以拿出来聊聊,缓解尴尬。在面试过程中还真起了点作用,有部分面试官现场看了网站效果,评价网站整体风格简洁唯美。觉得我有独立学习的能力和摸索尝试的习惯,知识面较宽,得到过一些鼓励。
博客的代码全部开源:源码Github地址
2、不止前端
软件行业越来越不景气,为了多一些竞争力,哥们在找到稳定工作后,开始对比着js学习java,计划用一年时间做到全栈能力。
本次的学习路线是直接从公司的现网产品源码开始看,倒推着学习。看的过程中去了解需要掌握的知识,再针对性的学习。
我设了一个专栏,会把装备JAVA能力的过程一点点的总结下来,方便自己也方便大家:JAVA从工作到入门
十一:计算机网络
1、http和https的区别?
- HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
- 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
- HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
- http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
- HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
2、常见状态码
- 200:成功返回
- 304:网页未更改有缓存,未重新请求
- 404:请求资源不在
- 500:服务器内部错误
- 503:服务器超时
3、tcp与udp的区别?
-
TCP面向连接(如打电话要先拨号建立连接)提供可靠的服务,UDP是无连接的,即发送数据之前不需要建立连接,UDP尽最大努力交付,即不保证可靠交付。
-
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
-
每一条TCP连接只能是一对一的,UDP支持一对一,一对多,多对一和多对多的交互通信。
-
UDP分组首部开销小,TCP首部开销20字节,UDP的首部开销小,只有8个字节。
-
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流,UDP是面向报文的一次交付一个完整的报文,报文不可分割,报文是UDP数据报处理的最小单位。
-
UDP适合一次性传输较小数据的网络应用,如DNS,SNMP等。
十二:webpack
1、dependencies和devDependencies区别
安装时 --save -dev会放在devDependencies中,--save放在dependencies
devDependencies中安装的是开发环境使用的包,比如eslint、vue-cli-serve;
dependencies中安装的是生产和开发环境下都需要使用的包,比如vue、element、vant等
devDependencies中的依赖模块,在生产环境下不会被打入包内
2、使用过哪些loader?
3、webpack具备哪些功能?
十三:页面优化
1、某个页面加载较慢,从哪些方向分析、解决问题?
- 传统页面
首先判断是接口慢,还是页面慢。如果接口慢,后端优化。
如果前端页面加载慢,看是否是因为图片等资源过大,尝试替换不同格式体积的图片。定位是否是某些数据处理的函数,比较耗时。或者是否循环操作DOM,js生成dom后再批量插入。
如果页面直接卡死,就需要分析是否内存泄漏。比如大屏展示的定时刷新卡死,排查思路可如下:
使用chrome的任务管理器,操作页面观察的内存占用的变化。定位到是哪些操作,哪块代码导致内存占用飙升。
因为js并没有直接释放缓存的语法,只有靠浏览器的垃圾回收机制自动清理。我们需要做的是及时给不需要的变量赋空。
特别注意大数据的循环实例化后,变量是否及时赋空。定时器等闭包方法中是否存在内存泄漏,循环渲染地图时,是否先将之前地图数据清空等。
- 单页面应用
单页面一般不会某个页面加载慢,一般都集中在首屏加载时白屏较久。处理方法可参考上文中。
2、使用缓存
- 有些接口没必要每次打开页面都请求,可用cookie计时。某个时间段内不重新获取。
- 某些数据初始化时加载一次即可,可通过cookie计时,某个时间段内不用请求。
- 某些数据可用localstorage存储,默认填充input,更新时才重写缓存。这个只是用户体验好些
十四:Node.js
1、用过哪些插件(express中间件)?
- cors 解决express的跨域问题
- body-parser 解析post数据
- mongoClient mongodb官方提供的Node.js驱动
- crypto 用来加密,添加签名
- nodemailer 用来发邮件
2、mongodb和mysql的区别?
- mongodb是文档型非关系型数据库,mysql是关系型数据库
- mongodb以json形式存储数据,字段可以随意添加,mysql的字段需要提前确定好
- mysql多表之间只能通过外键建立关系,mysql可以多层嵌套也可拆表并关联
3、是否了解nvm?
十五:Linux
1、查找文件的命令是什么?
find:find
命令用于在指定目录下递归搜索文件。
find [路径] [选项] [表达式]
find /home/user -name "*.txt"
locate:locate
命令通过搜索数据库来快速定位文件。注意,locate
命令搜索的是预先建立的数据库,因此可能会比find
命令快速,但可能不会返回最新创建或修改的文件。
locate [文件名]
locate myfile.txt
grep:grep
命令用于在文件中搜索特定的文本模式。
grep [选项] 模式 [文件]
grep "pattern" file.txt
whereis:whereis
命令用于定位二进制、源代码和帮助文件。
whereis [选项] 文件名
whereis python
which:which
命令用于查找命令的执行路径。
which [命令]
which python
2、解压/压缩命令是什么?
在Linux中,常见的解压文件的命令通常取决于文件的压缩格式。以下是一些常用的解压命令:
解压 .tar
文件:使用 tar
命令。
tar -xvf 文件名.tar
解压 gzip 文件:
gzip -d 文件名.gz
解压 zip 文件:
unzip 文件名.zip
解压 rar 文件:
unrar x 文件名.rar
十六:计算机原理
1、进程和线程的关系?
进程(Process)和线程(Thread)是操作系统中两个重要的概念,它们之间存在密切的关系。
进程:
- 进程是程序执行的实例,是操作系统资源分配的基本单位。
- 每个进程都有独立的地址空间、代码、数据和系统资源。
- 进程之间相互独立,彼此不受影响。
线程:
- 线程是进程内的执行单元,是操作系统调度的基本单位。
- 同一进程内的多个线程共享相同的地址空间和系统资源。
- 线程之间可以方便地进行通信和数据共享。
关系:
- 一个进程可以包含多个线程,这些线程共享进程的资源,包括内存、文件句柄等。
- 同一进程内的线程之间可以通过共享的内存空间进行通信和数据交换,这使得线程间的协作变得更加高效。
总的来说,进程和线程之间是包含关系,一个进程可以拥有多个线程,而线程则是在进程内执行的单元。通过线程的使用,可以更加高效地利用系统资源,提高程序的并发性和响应性。
十七:Android
为什么要使用superWebview?
十八:数据可视化
1、大屏类的项目怎么做屏幕自适应?
-
使用响应式布局(Responsive Layout):
- 使用相对单位(如百分比)来定义元素的尺寸和位置,以便它们可以根据屏幕大小进行调整。
- 使用 CSS 媒体查询(Media Queries)来根据不同的屏幕尺寸应用不同的样式,以确保页面在不同的设备上具有良好的布局和显示效果。
-
弹性布局(Flexbox)和网格布局(Grid Layout):
- 使用 Flexbox 和 Grid Layout 可以轻松地创建灵活的布局,以适应不同尺寸的屏幕。
- 这些布局技术提供了更直观、更有效的方式来管理项目的布局,并且能够自适应屏幕的大小和方向。
-
Viewport(视口)设置:
- 使用
<meta>
标签来设置视口的宽度和缩放选项,以确保页面在移动设备上正确显示并且具有良好的可读性和可操作性。 - 例如,可以使用以下代码来设置视口:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- 使用
-
流式布局(Fluid Layout):
- 使用流式布局设计页面,使得页面元素能够根据浏览器窗口大小的变化而自动调整布局。
- 通过设置百分比宽度或最大宽度等方式来实现流式布局。
-
测试和调试:
- 在不同尺寸和分辨率的设备上进行测试和调试,确保页面在各种设备上都有良好的显示效果。
- 使用浏览器开发者工具或者第三方的模拟工具来模拟不同设备的显示效果,并进行布局的优化和调整。
2、大屏类项目如何实现定时刷新?
-
JavaScript 定时器:
- 使用 JavaScript 的
setInterval()
函数可以创建定时器,定期拉取数据;
- 使用 JavaScript 的
-
WebSocket 实时更新:
- 使用 WebSocket 技术实现实时通信,服务器端有新数据时即时推送给客户端,从而实现数据的实时更新。
- 当服务器端有新数据时,使用 WebSocket 发送更新通知给客户端,客户端接收到通知后更新页面内容。
-
使用框架或库的自动刷新功能:
- 许多前端框架或库(如React、Vue、Angular等)都提供了自动刷新页面的功能,可以在开发环境中实现热重载,也可以在生产环境中定期更新页面。
十九:算法
有空刷刷Leetcode,很多好公司一面前先让写两道算法题。
这种情况下,如果没有一点算法积累,上面的问题咱都没机会被问。
二十:最近面,还未来及整理的
1、除了ajax还有哪些请求?await、aysic、table
2、url输入框后发生什么事
3、加密方式有哪些?对称、非对称
4、mongodb好处?
5、如何辨别ie8是否兼容?
更多推荐
所有评论(0)