提示:这是一次字节跳动前端二面的失败总结,希望能帮助小伙伴们理解一些知识点。
本文将分享一下字节跳动公司前端二面(没过)经验,首先会问一些前端的学习路径以及简历中项目的相关模块,然后再会问一下JS,CSS的内容,最后会编程题,包括一个JS代码题和一个算法题。
当听到面试官问到这个问题,当时心情非常激动,觉得这么简单,但是之后却是麻烦不断。
重绘:就是当界面中某个DOM的样式需要发生变化,例如字体颜色的改变等等,但是并没有影响整个界面的布局变化,我们只需要重新对这个颜色进行修改即可;
重排:就是当整个界面布局发生变化,例如尺寸等等,或者一些DOM元素的消失、新增等等,那么浏览器需要重新渲染,就会发生重排;
总结:就是发生重排一定会发生重绘,但是发生重绘不一定发生重排。
面试官:按照你说的,那么重绘不一定重排,举个例子如果发生了重绘什么情况下会发生重排呢?
我:emmmm,那既然重绘都发生了重排,那就是说其实这本质是一个重排了,也就是说要么这次改变是单独发生了重绘,要么这次改变发生的是重排+重绘。
面试官:那举个例子什么情况下重排,什么情况下重绘?
我:就是元素的字体颜色,大小,这种简单变化就重绘,但是页面布局变化了,例如盒子大小变化了就重排,或者按钮的消失和新增也重排。
面试官:那要是按钮的消失,用CSS的属性消失的,而不是删除,那这是重排还是重绘?
我:重绘叭。
面试官:那display的和visibility和opacity呢都是重排重绘?
我:重排叭。(已经开始出错了)
面试官:那换你说的就是只要位置改变是不是就要重排,重新进行计算呢,那我的界面只有一个盒子,来回拖动呢,横着拖动,或者竖着拖动,无限任何距离呢,是重排重绘啊?
我:(脑子炸了,心里想,我就是知道大概,具体没了解这么透彻,八股文真的不够啊,还得具体到情况)emmmmm,就是我觉得叭位置变了就是重排了,因为一般盒子拖拽我就用那个JS写的,每次都要计算嘛,所以位置一直改变,所以发生的都是重排。
面试官:如果不用JS呢?用动画呢?
我:transform,那就是重绘?(哎,错误总是在无知的时候脱口而出)
面试官:你可以课下在了解一下。
我:好的(心里一万句:我是xx,懂得都懂,省略了)。
任何改变元素的位置和元素尺寸的大小都会发生重排,例如:
更新了元素的绘制属性,但是没有改变整个布局,而只是将元素的外观重新绘制,这就是重绘,例如:
面试官:了解过promise吗?
我:(woc,终于问到了,太熟悉了)emmmm,了解过的
面试官:那说说你的项目中都哪里用到了promise,以及怎么用的?
我:(??????思索了好久)emmmm,我没用过啊(不过有个axios用到了then()很多)
面试官:啊?那你的项目中怎么用的异步操作?
我:我忘了,emmmm,但是我了解promise。
面试官:你了解啥?
我:(woc,这么大的范围,你问我一句这么笼统的,我怎么答啊?)那个可以给点提示吗?
面试官:算了,我们下一个环节吧,来个代码题叭。
我:(有苦说不出,这不凉了吗,还代码啥啊,开摆了)
在讲解promise之前先总结一下之前的翻车对话,其实我当时就是不会读axios,不然我自信点说出来就有后续了,请大家跟我读:艾克斯伊欧姿,艾克斯伊欧姿,艾克斯伊欧姿!!!
ajax的全名是async JavaScript and XML(异步JavaScript和XML),起着串联前后台交互的作用,并且默认异步执行机制,同时也可以同步(async = false),但是默认异步(async = true),有以下优点:
var xhr = new XMLHttpRequest();
var xhr = new XMLHttpRequest();
xhr.open('get','./data.php');
//括号中的是方法(get/post,url,true)最后一项默认是true为异步可以不用写,如果是同步写成false
var xhr = new XMLHttpRequest();
xhr.open('get','./data.php');
//括号中的是方法(get/post,url,true)最后一项默认是true为异步可以不用写,如果是同步写成false
xhr.setRequestHeader();
var xhr = new XMLHttpRequest();
xhr.open('get','./data.php');
//括号中的是方法(get/post,url,true)最后一项默认是true为异步可以不用写,如果是同步写成false
xhr.setRequestHeader();
xhr.send();
//如果方法是get直接xhr.send(),参数为空,
//如果方法是post,需要带参数xhr.send(参数);
var xhr = new XMLHttpRequest();
xhr.open('get','./data.php');
//括号中的是方法(get/post,url,true)最后一项默认是true为异步可以不用写,如果是同步写成false
xhr.setRequestHeader();
xhr.send();
//如果方法是get直接xhr.send(),参数为空,
//如果方法是post,需要带参数xhr.send(参数);
xhr.onreadyStateChange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.responseText);
}
}
readyState === 0 : 表示未初始化完成,也就是 open 方法还没有执行
readyState === 1 : 表示配置信息已经完成,也就是执行完 open 之后
readyState === 2 : 表示 send 方法已经执行完成
readyState === 3 : 表示正在解析响应内容
readyState === 4 : 表示响应内容已经解析完毕,可以在客户端使用了
首先axios是一个基于promise的HTTP库,可以用于浏览器端和node.js端。本质上是对小黄人(XHR)的封装,但是却是Promise的实现版本,符合最新ES规范,有以下特点:
具体axios内容可以跳转到 Vue axios 详细介绍(核心使用、封装、个性化配置,破万字)进行学习。
ajax不是不流行了,只是它存在回调地狱的问题,如果要连环发送请求,就得一直嵌套,而现在用的流行的axios库,其实也是ajax,只是用promise对它进行了封装,可以链式调用发送请求,所以现在项目普遍使用的是axios。
最开始使用的是callback回调函数到Promise,再到后来的Generator,最后是async/await。
Promise有三种状态,分别是 pending 、 resolved 、rejected,其中pending可以转换为另外两种状态,如下:
const p1 = new Promise((resolve,rejecte)=>{
})
console.log('p1',p1);
//此时结果为Pending,默认的
const p2 = new Promise((resolve,reject)=>{
resolve();
})
console.log('p2',p2);
//此时结果为fulfilled,也就是resovled
const p3 = new Promise((resolve,reject)=>{
reject();
})
console.log('p3',p3);
//此时结果为rejected
Promise是一个构造函数,使用new关键字进行创建实例对象。Promise函数中自带executor执行器,executor执行器中有两个默认函数参数resolve,reject,其中then是Promise原型上的一个方法,实例在调用该方法的时候,可以通过原型来调用,即Promise.prototype.then(),好处是不需要每次实例化都创建一个then(),节省内存。
Promise的手撕代码,如下:
function Promise(executor){
this.status = 'pending'
this.value = null //value就是resovel
this.reason = null //reason就是reject
//定义两个队列数组,使得可以达到异步的操作
this.onFulfilledArray = [] //定义成功的队列数组
this.onRejectedArray = [] //定义失败的队列数组
const resolve = value =>{
if(value instanceof Promise){
return value.then(resolve,reject)
}
setTimeout(()=>{
if(this.status === 'pending'){
this.value = value
this.status = 'fulfilled'
this.onFulfilledArray.foreach(func=>{
func(value)
})
}
})
}
const reject = reason =>{
setTimeout(()=>{
if(this.status === 'pending'){
this.reason = reason
this.status = rejected
this.onRejectedArray.foreach(func =>{
func(reason)
})
}
})
}
executor(resolve,reject)
}
Promise.protptype.then = function(onfulfilled,onrejected){
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {throw error}
if(this.status === 'fulfilled'){
onfulfilled(this.value)
}
if(this.status === 'rejected'){
onrejected(this.reason)
}
if(this.status === 'pending'){
this.onFulfilledArray.push(onfulfilled)
this.onRejectedArray.push(onrejected)
}
}
Promise实例的基本代码结构,如下:
//ES6 箭头函数写法
let promise = new Promise((resolve,reject)=>{
if(/判断条件/){
resolve()//承诺实现
}else{
reject()//承诺实效
}
})
promise.then(res=>{
//处理承诺实现方法
},err=>{
//处理承诺失效方法
})
结论
console.log('步骤1');
new Promise((resolve,reject)=>{
console.log('步骤2');
})
console.log('步骤3')
//执行结果
### 步骤1
### 步骤2
### 步骤3
console.log('步骤1');
new Promise((resolve,reject)=>{
console.log('步骤2');
resolve()
}).then(res=>{
console.log('步骤3');
})
console.log('步骤4')
//执行结果
### 步骤1
### 步骤2
### 步骤4
### 步骤3
then()的第二个参数可以是抛出错误,当然catch()也可以抛出错误,其区别在于前者只能处理当前Promise异步失败的回调,而后者可以处理整个Promise链上发生的异步失败的回调,准确说就是整体错误。
Promise的链式调用
链式调用的最主要作用就是异步事件同步化,例如:
let promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
},2000)
}).then(res=>{
console.log('步骤1')
})
let promise2 = new Promise((resolve,reject)=>{
resolve()
}).then(res=>{
console.log('步骤2')
})
//执行结果
###步骤2
###步骤1
显然是异步事件,并不是按照顺序结构执行的,所以用链式调用即可达到效果,如下:
let promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
},2000)
}).then(res=>{
console.log('步骤1')
return new Promise((resolve,reject)=>{
resolve()
})
}).then(res=>{
console.log('步骤2')
})
//执行结果
###步骤1
###步骤2
Promise链式调用的规则
Promise.all()和Promise.race()
Promise.all()一般用于将多个实例组装成一个实例,简单来说就是多个Promise,如果都是res的话(成功),那就返回一个数组,包括所有成功的值,但是当有rej(失败)的时候,则返回最先rej的值即可。
let promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('The shy')
},2000)
})
let promise2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('The shy')
},2000)
})
Promise.all([promise1,promise2]).then(data =>{
console.log(data)
})
//执行结果
["The shy","The shy"]
Promise.race()意思为赛跑的意思,一般和setTimeout连用,输出最快的那个结果,无论是res还是rej。
let promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('The shy')
},2000)
})
let promise2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('Rookie')
},4000)
})
Promise.race([promise1,promise2]).then(data =>{
console.log(data)
})
//2s后执行结果
The shy
Promise的应用场景
Promise的缺点
本文大致就讲这些吧,首先讲一下浏览器的重排和重绘的区别,以及一些优化方法,其次重点讲一下ajax和axios的使用,以及Promise的入门到熟练应用,本文更是对一次面试的总结,因为只有总结面试的不足,并且通过这种方式强迫自己挖深知识,最终才能成功。
感谢大家的关注,我将持续更新一些前端面试的常考和重难点内容,预祝大家都可以面试成功,拿下offer!!!