一、总结建议
1.平时的积累很重要:工作中遇到的一些问题及其解决方式,我们可以记录下来,抽空做个总结。最好就是找个平台写博客,掘金、GitHub、博客园、CSDN都可以,好记性不如烂笔头,自己写一遍比看一遍好使的多。在线博客不进方便自己查阅,面试玩意碰到记不太清的知识点还能说——我博客上有总结过。
2.简历不要给自己挖坑:会啥就写啥,写啥就会啥。正常的面试官都会根据简历来提问,所以简历上不要出现自己不懂的名词——不懂就去查,去背,哪怕不特别理解到时候也能说个一二三来。不然一问三不知真的尴尬。
3.面试之前要做准备:面试就是考试,考试我们就得复习。前端的知识体系还是很明朗的——页面、样式、JS、框架、项目打包及构建,先掌握自己拿手的,然后扫除盲点。
别在一个坑掉进去两次:和面试官切磋,为了了解目前的就业形势。了解并正视自己的不足;当这个面试有些问题你答不上来时,没关系,回去查资料,看视频,做复盘,一定要记住它,下次你就可以游刃有余了。怕忘的话可以录音。
4.不要裸辞!不要裸辞!不要裸辞!:重要事情说三遍,如果有房贷车贷的话,裸辞找工作压力真的很大——手里有粮,心里不慌。当然,土豪随意……
5.尽量不要把时间线拖太久:一边上班一般找工作是蛮辛苦的,要想请假理由,下班要准备面试。所以要一鼓作气,尽量突击半个月多拿几家offer,时间线拉太长可能会懈怠(我就是拖得有些久了)。尽量有选择则的、找匹配(薪资、能力、通勤之类)的公司去面试,决不能接到面试邀请就去,不然只是浪费自己的时间。
6.端正心态,坚持学习:技术日新月异,框架层出不穷。既然选择了这个行业,就要端正心态,努力学习,提升技术,熟悉业务,深入行业,别让后浪拍死。
二、经典前端面试题附加
1、三栏布局的实现及优缺点
布局方案 | 实现 | 优点 | 缺点 |
---|---|---|---|
Float布局 | 左右中三列,左列左浮动,右列右浮动,中间列设置左右margin | 比较简单,兼容性也比较好 | 浮动元素脱离文档流,使用的时候只需要注意一定要清除浮动。 |
Position布局 | 左中右三列(无顺序),根据定位属性去直接设置各个子元素位置 | 快捷,设置很方便 | 元素脱离了文档流,后代元素也脱离了文档流,高度未知的时候,会有问题,有效性和可使用性比较差 |
Table布局 | 左中右三列,父元素display: table;子元素display: table-cell;居中子元素不设宽度 | 使用起来方便,兼容性也不存在问题 | ①无法设置栏边距;②对seo不友好;③当其中一个单元格高度超出的时候,两侧的单元格也是会跟着一起变高的 |
Flex布局 | 左中右三列,父元素display: flex;两侧元素设宽;居中子元素flex: 1; | 比较完美 | 存在IE上兼容性问题,只能支持到IE9以上 |
Grid布局 | 左中右三列,父元素display: grid;利用网格实现 | 最强大和最简单 | 兼容性不好,IE10+上支持,而且也仅支持部分属性 |
2. 文字单行显示/三行显示
单行文本溢出隐藏变为…
p {
/* 隐藏元素溢出内容 */
overflow: hidden;
/* 单行显示 */
white-space: nowrap;
/* 溢出显示省略号 */
text-overflow: ellipsis;
}
多行文本溢出隐藏变为…
p {
overflow: hidden;
/* 将对象作为弹性伸缩盒子模型显示 。 */
display: -webkit-box;
/* 限制在一个块元素显示的文本的行数,即行数设置 */
-webkit-line-clamp: 3;
/* 规定框从上向下垂直排列子元素 */
-webkit-box-orient: vertical;
}
3. 重绘和回流
回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(其实我觉得叫重新布局更简单明了些)。每个页面至少需要一次回流,就是在页面第一次加载的时候。
重绘:当render tree中的一些元素需要更新属性,这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
区别:
- 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
- 当页面布局和几何属性改变时就需要回流
比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变
- 回流往往伴随着布局的变化,代价较大:
- 重绘只是样式的变化,结构不会变化
4. 手写斐波那契数列及其优化
什么是斐波那契数列: 斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=1,F(1)=1, F(n)=F(n-1)+F(n-2)(n>2,n∈N*), 这个数列从第3项开始,每一项都等于前两项之和
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
这个方法算是代码最少也容易理解,但是当n较大时很快产生栈溢出,引发原因是“调用帧”过多,出现浏览器假死现象。详情参阅函数扩展之尾调用优化——阮一峰。
2.递归(优化版)
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
此方式是函数尾调用优化之后的写法(默认参数ES6及以后版本支持,ES5请使用常规默认值写法),理解上稍微复杂,但是不会发生栈溢出,推荐使用。
3.普通循环版
function Fibonacci3(n) {
if (n===1 || n===2) {
return 1;
}
var n1 = 1, n2 = 1, sum;
for (let i = 2; i < n; i++) {
sum = n1 + n2
n1 = n2
n2 = sum
}
return sum
}
循环版本最好理解,就是给初始值,然后不断的累加即可
4.解构赋值版
var Fibonacci4 = function (n) {
if (n===1 || n===2) {
return 1;
}
let n1 = 1; n2 = 1;
for (let i = 2; i < n; i++) {
[n1, n2] = [n2, n1 + n2]
}
return n2
}
5.编写javascript深度克隆函数deepClone
// 深拷贝
/**
* 深拷贝对象,可以正确序列化日期
* @param {*} obj
*/
export const DEEP_CLONE = function (obj) {
let objClone = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === 'object') {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 判断ojb子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === 'object') {
// 深拷贝日期类型
if (obj[key] instanceof Date) {
objClone[key] = new Date(obj[key].valueOf())
// console.log('deepClone', objClone[key])
} else {
objClone[key] = DEEP_CLONE(obj[key])
}
} else {
// 如果不是,简单复制
objClone[key] = obj[key]
}
}
}
}
return objClone
}
7. 手写去重函数
const arr = [1, '1', '1', 'NaN',NaN,'NaN', {a: 1}, '{a: 1}', {a: 1}]
// 利用for嵌套for,然后splice去重(ES5中最常用)
function unique2(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) { //第一个等同于第二个,splice方法删除第二个
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
// 利用hasOwnProperty
function unique7(arr) {
var obj = {};
return arr.filter(function (item, index, arr) {
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
// 利用filter
function unique4(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
8.手写ajax
// 手写ajax
function get(){
var req = new XMLHTTPRequest();
if(req){
req.open("GET", "http://test.com/?keywords=手机", true);
req.onreadystatechange = function(){
if(req.readyState == 4){
if(req.status == 200){
alert("success");
}else{
alert("error");
}
}
}
req.send();
}
}
9.Pomise实例
// Promise
function fn(){
var promise = new Promise(function(resolve,reject){
// ... some code
if(/*异步操作成功*/) {
resolve(value) // 成功调用resolve 往下传递参数 且只接受一个参数
}else {
reject(error) // 失败调用reject 往下传递参数 且只接受一个参数
}
});
return promise
}
fn.then(function (r) {
console.log('成功: ' + r);
}).catch(function (reason) {
console.log('失败: ' + reason);
});
// 全部成功调用
Promise.all([promise1,promise2]).then(sucess1,fail1)
// 有一个成功就调用
Promise.race([promise1,promise2]).then(sucess1,fail1)
10.Vue路由传参--------params和query的区别
背景:项目中需要跨页面传值,如试题id,遇到了刷新后,传的值消失,所以研究了以下两者的区别
1.params只能用name来引入路由,query用path来引入
2.params类似于post,query更加类似于我们ajax中get传参,说的再简单一点,前者在浏览器地址栏中不显示参数,后者显示,所以params传值相对安全一些。
3.取值用法类似分别是this.route.params.name和this.route.params.name和this.route.params.name和this.route.query.name。
4.params传值一刷新就没了,query传值刷新还存在
this.$router.push({
path:"/detail",
params:{
name:'nameValue',
code:10011
}
});
this.$router.push({
path:'/xxx'
query:{
id:id
}
})