前端面试题

1、js(es6)部分

1.1、es6中的遍历方式

for(let i=0 ; i < x; i++){}:在es6中循环的变量用let声明。
for(let n of data):es6实现了迭代器,实现了iterator 的数据对象都可以使用上面方式进行遍历
forEach():arr.forEach((item, index, arr)),用来遍历数组,item:数组元素,index:当前元素的下标, arr:被遍历的对象本身
map():我觉得它还算是遍历方式,用于遍历数据时能对数组做出操作
好,下面我个人认为不应该算作遍历方式
filter():我觉得它不应该算是遍历方式,它主要的作用是过滤,不是遍历了整个整个数组就是遍历方式吧。
some():同样不觉得是遍历方式,返回的是布尔值,查找数组中是否存在满足条件的的元素,找到即打断,不会继续执行。
every():作用和some相似,。返回布尔值,但是是查找不满足条件的方法,同样只要有一个不满足就打断

1.2、原生中如何修改this指向

可使用call,bind,apply来改变。

1.3、箭头函数和function的区别

首先是语法形式不一样(特么这是废话),
其次就是内部this指向不一样,箭头函数this指向就是该函数所在的作用域指向的对象 bind、call、apply都无法改变this。而function则是谁调用就指向谁
再者就是es6构造对象使用class而非function,所以箭头函数不能作为构造函数使用,function依旧可以使用es5语法去构造函数。
还有就是生成器,箭头函数同样不可以构造生成器,function 可以使用function * generatorName 来创建生成器。
再着就是箭头函数是匿名函数,不被变量名承接就会消失,而function可以作为匿名函数也可以声明函数名。
最后就是他们的函数参数不同,function的参数是arguments参数,是个数组,而箭头函数的参数则是rust参数,是一个列表。

1.4、什么是闭包

闭包是为了应对函数内部变量随着函数执行完毕而消失,外部无法使用其变量,所以就在函数内部在定义一个新函数来使用这个变量,从而占用这个变量,延长它的生命周期。

1.5、前端页面的渲染过程

这个不算是js问题,但是先放这里吧,这个我确实没有回答出来。
首先浏览器会先访问我们加载HTML文件,通过css连接去下载css,然后并行加载dom树和css规则树,所以css连接要放在head里面有利于快速加载。
然后再去并行下载js脚本,通过脚本来对已经加载完html的dom进行修改。
最后完成之后就开始绘制和布局显现出来。

1.6、宏任务和微任务

去他么的宏任务,有人问你宏任务,你就让他穿越会去自己研究宏任务吧。
微任务队列的优先级最高。

1.7、let、const和var区别

老生常谈,我真没想到这个会被问到,不过这个倒是能回答。。。
let:满足代码块作用域,不可变量提升、不能在相同代码块作用域中重名;
const:常量变量,不可变量提升、定义就要初始化、不可修改变量指向,但是可有修改内部属性或元素

1.8、 原型和原型链

这个我确实也没想到还有人问。
原型和原型链都与继承有关系,原型是当前对象继承的父对象,而原型链则是对象继承的记录,最上面的源头是null,一旦调用一个对象的属性或者方法,会顺着原型链上溯上去直到找到这个方法(属性),没有就会报错undefined。

1.9、es5和es6继承的区别

感谢我朋友面试前一晚随口问了我一个这个,否则我要是被问到,还真不好组织语言(社恐面试,简直煎熬,一紧张就张不开嘴)
es5是通过构造函数去先创建子类,然后再讲父类的属性和方法添加到子类的this上。
es6则是通过class去构造对象,通过extends继承父类,在子类中constructor使用super继承父类的this之后在付给它新的属性和方法。

1.10、 如何对一个数组去重

我面试的这个是笔试,我特么写了一整张纸(字大),说实话方法真不少
set:

let arr = ['1', 'n', 'haha', 'n']
let set = new Set(arr)
let result = [...arr]

for:

let arr = ['1', 'n', 'haha', 'n']
for(let i = 0; i < arr.length; i++){
	for(let j = i+1; j < arr.length; j++){
		if(arr[i] === arr[j]) 	
			arr.splice(j,1)
			 if(j === i+1) i--
	}
}

filter、map、includes都可以的

1.11、promise对象

说实话,这个我就真知道一个then -_-||
then就不说了
all:用于多个promise对象一起使用的使用

promise.all([promise1, promise2 ……]).then(value => {
	//获取的value是所有的单个promise的resolve的值组成的数组
	console.log(value)
}, reason => {
	//抓取的是第一个错误
	console.log(reason)
})

allsetttled:返回一个在所有给定的 promise 都已经fulfilled或rejected后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。

Promise.allSettled(promises).
  then((results) => 
  // 对于每个结果对象,都有一个 status 字符串。如果它的值为 fulfilled,则结果对象上存在一个 value 。
  // 如果值为 rejected,则存在一个 reason 。value(或 reason )反映了每个 promise 决议(或拒绝)的值。
  results.forEach((result) => console.log(result.status)));

any: 只要其中的一个 promise 成功,就返回那个已经成功的 promise ,且不再往下面执行,和all完全相反。
race:一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝,就是谁速度快就返回谁的结果。

1.12、异步改成同步

在同一个作用域中的多个异步请求如何让他们一个执行完成后再执行另一个。
方法有很多:
return

let c = function (){
    setTimeout(function() {
        return console.log('执行了3')
    },1000)
}
let b = function (){
    setTimeout(function() {
        console.log('执行了2')
        return c()
    },1000)
}
let a = function(){
    //发送异步,这里用定时器
    setTimeout(function() {
        console.log('执行了1')
        return b()
    },1000)
}
a()

generator:

  function getUser(){
    setTimeout(()=>{
      console.log('我是用户')
      generator.next('用户')
    },1000)
  }
  function getOrder(user){
    setTimeout(()=>{
      if(user !== null && user !== undefined){
        console.log('我是订单');
        generator.next('订单');
      }
    },1000)
  }
  function getGoods(order){
    setTimeout(()=>{
      if(order !== null && order !== undefined){
        generator.next('商品');
      }
    },1000)
  }
  function * gen(){
    let user = yield getUser();
    let order = yield getOrder(user);
    let good = yield getGoods(order);
    console.log(good)
  }
  let generator = gen();
  generator.next()

async 和 await

let a = () => {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log('1')
            resolve();
        },1000)
    })
}
let b = () => {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log('2')
            resolve();
        },1000)
    })
}
let c = () => {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log('3')
            resolve();
        },1000)
    })
}

let abc = async function() {
    await a();
    await b();
    await c();
}
abc()

1.13、防抖和节流

两者都是为了防止多次触发事件导致多次加载造成性能消耗;
防抖就是在事件第一次出发之后一定间隔内多次在被触发,那么就会打断上一次的方法,从新处理,即保证最后一次触发生效;

方法可以采用定时器,先定义一个定时器对象,在闭包中去定义定时器的方法,把此闭包函数返回,那么只需每次先去检查这个定时器对象是否为空,如果为空那就是闭包函数执行完毕释放了这个对象,可以再次触发,如果不为空,那就是时间未到,所以清理当前定时器对象在新建一个从而保证始终是最后一次触发生效。

节流就是在固定时间段内多次触发,只能生效一次。

方法可以先定一个数字对象记录上一次触发的时间,然后在闭包中去比较当前时间-上一次触发的时间-间隔时间是否小于0,若小于0即是时间已过可以再次出发,大于0就是还在当前时间段,跳过执行。

2、css部分

我平时写前端css都是百度的,然后一问我,不说全部傻眼吧,但是大多数我不知道,知道的还背不过关键词

2.1 如何使内部元素垂直居中,至少写出四种

1.flex

display: flex;
justify-content: center;
align-items: center;

2.使用span套住子元素

 #div1{/* 外部父元素*/
   text-align: center;
    border: 1px solid #000000;
    width: 500px;
    height: 500px
}
span{
	height: 500px;
	line-height: 500px
}

3.relative 和 absolute 以及 margin 配合

#div1 {
/* 外部父元素*/
     border: 1px solid #000000;
     width: 500px;
     height: 500px;
     position: relative;
 }

 #div2 {
 /* 内部元素*/
     border: 1px solid #000000;
     width: 100px;
     height: 100px;
     margin: auto;
     position: absolute;
     left: 0;
     top: 0;
     right: 0;
     bottom: 0;
 }

4.relative 和 absolute 以及 transform 配合

 #div1 {
 /* 外部父元素*/
     border: 1px solid #000000;
     width: 500px;
     height: 500px;
     position: relative;
 }
 #div2 {
  /* 内部元素*/
     border: 1px solid #000000;
     width: 100px;
     height: 100px;
     margin: auto;
     position: absolute;
     left: 50%;
     top: 50%;
     transform: translate(-50%, -50%);
 }

2.2、css实现两栏布局,左侧固定宽,右侧自适应

dom如下:

<div class="box" id="box">
    <div class="left">左侧定宽</div>
    <div class="right">右侧自适应</div>
</div>

1.利用 calc 计算宽度的方法

.box{overflow: hidden;height: 100px;margin: 10px 0;}
.box>div{height: 100%;}
#box>div{float: left;}
.left{width: 100px;}
.right{width:calc(100% - 100px);}

2.利用 float 配合 margin 实现

.box{overflow: hidden;height: 100px;margin: 10px 0;}
.box>div{height: 100%;}
.left{float: left;width: 100px;}
.right{margin-left: 100px;}

3.利用网格布局

.box>div {
    display: grid;
    grid-template-columns: 200px auto;
    background-color: #2196F3;
    padding: 10px;
}
.left,.right{
    background-color: rgba(255, 255, 255, 0.8);
    border: 1px solid rgba(0, 0, 0, 0.8);
    padding: 20px;
    font-size: 30px;
    text-align: center;
}

2.3、如何清除浮动

clear 属性:
none - 允许两侧都有浮动元素。默认值
left - 左侧不允许浮动元素
right- 右侧不允许浮动元素
both - 左侧或右侧均不允许浮动元素
inherit - 元素继承其父级的 clear 值

3、vue部分

3.1、生命周期

在vue中,如果是非SSR模式下,生命周期分为8部分;
beforCreate:在这个生命周期只是初始化了vue实例,除了一个组件自带的方法和属性外,数据观察和方法都没被装配到vue上面。

created:此时vue实例的createApp方法执行,数据和方法都被装配到了vue上,在此刻就可以访问到data数据和方法,此时组件和dom节点还没绑定。

beforeMount:vue3中前面两个生命周期没有回调钩子,统一在setup方法中完成,同时以下生命周期的钩子函数都可以在setup中被调用,beforeMount其实就是render函数被执行之前,此时dom节点和组件已经关联,但是因为还没有执行render,patch等方法,所以还未渲染出实际的dom元素。

mounted:此时render方法已经执行完毕,真实的dom已经渲染到宿主元素上面,数据监听也已经就位,可以在mounted hook中获取数据进行页面更改。

beforeUpdate:无论是在mounted hook里面还是在其他绑定方法里面修改响应数据,都会被proxy拦截,beforeUpdate这个生命周期就是在数据已经被改变但是相应的render还没执行的时候,这时候还是可以获取原来的dom对象,但是数据已经发生了变化。

updated:此时render方法执行完毕,更新的真实dom替换了原来的元素,dom对象更新。

beforeDestroy:此时已经触发了组件注销,但是data和方法还没销毁,依旧可以获取数据、方法和dom,如果需要数据传递的可在这个名为 hook里面进行,vue3此钩子改名为onBeforeUnmount 。

destroyed:此时组件已经注销,data和方法也随之消失,vue3里面hook的名字为unmounted。

3.2、vue3中已有的自定义组件在纯js文件中调用

我实在没有听懂是什么意思,当时也刚睡醒就面试,迷迷糊糊的就张嘴答了一个use方法,对面也没说话,时候一想不对,那是引用插件的,我自定义的组件给我自己项目用肯定不会完成install,所以感觉还是函数组件,但是函数式组件怎么可能满足已有的的自定义组件呢?所以还是不明白什么意思。

3.3、动态组件和异步组件

动态组件就是当前组件是根据场景模式而使用不同的组件,在编码阶段无法确定固定组件,所以需要<component :is=‘’>进行占位,通过is在数据中动态赋予来实现调用,is可绑定引入组件的名称,或者组件实例对象,还有原生html代码
异步组件就是根据业务场景或者减少加载消耗而采用异步方式加载组件,使用defineAsyncComponent API,参数是一个promise对象,在promise中resolve一个组件返回并加载;当然我们常用的() => import(‘./components/.vue’)也是返回一个promise对象。

3.4、vue3的通信方式

父组件向子组件传输数据可使用子组件的属性传输、provide和inject搭配使用、vuex(pinia)
子组件向父组件通讯可使用自定义事件,通过emit调用父组件绑定的事件,通过参数进行传值,或者使用vuex(pinia)
兄弟组件通讯,在原生vue3里面就只用用父组件中转或者vuex(pinia),vue2可使用事件总线eventBus,但是vue3已经移除,只能使用mitt等第三方插件。

3.5、 vue2和vue3的区别

1.vue2和vue3双向数据绑定原理发生了改变,vue2是通过object.definedproperty()将data里面的数据进行遍历之后劫持他们的setter和getter,生成watcher监听,一旦发生改变就会进行重新渲染,但是这种监听不是深层的,例如改变数组元素无法生效。vue3则是使用proxy对数据进行代理,handler参数方法对set、get进行拦截,一旦触发就会重新渲染。
2.Vue3支持碎片,即多个组件内部多个根元素
3.vue3实现了Composition API,即方法,hook都可以在setup中调用以及新的语法糖
4.data数据的不同, vue3数据采用响应式放在setup函数里面,然后return出去,而vue2则是data函数
5.vue 实例发生变化,原来的全局属性是直接装配到vue实例上面,现在是vue.config.globalProperties上面
6.setup函数的props、context参数,props是自定义的属性,context里面是emit等方法
7.vue与模板一起使用时,需要声明一个对象

4、ts部分

4.1、 ts与js的不同

ts是js的超集,比之js在代码编写阅读上更加规范,对js的语法有了一些修改。
1.数据类型,比js来说多了any;never;
2.多了interface 接口,可以用来声明规范对象类型或者用于对象继承
3.多了泛型,可以规定方法的返回类型
4.多了枚举和元组
5.多了抽象类
6.多了断言和声明,断言就是在编码是解释当前变量时什么类型,并不能排除编译或者运行时的错误;声明就是给当前变量归属一个数据类型,但是同样是在编码和编译阶段,并不是ts就是一个强数据类型的语言

4.2、ts的基本数据类型

布尔值、数值、字符串、 null 、 undefined 以及 ES6 中的新类型 Symbol

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值