今天发的文章是先行者计划成员-james的亲身经历,里面涉及很多前端开发及js的基础知识,相信对于新人面试会很有帮助。所以在取得原作者同意之后在公众号发出来,希望让更多的人看到。
下面是正文:
// ------------------------ //
之前找工作总是“没心没肺”,总觉得自己可以的,除了简历,基本没准备过面试题。这次真是被面的体无完肤,深刻认识到基础的重要性,大体总结一下经历过的面试题,简单梳理一下。
首先,如果面试的是前端,需要深刻认识的一点是,考察范围为html|css|js(es6),所以node,java等后台语言不必过多纠结,如果有相关项目那当然是甚好。接下来就基础方向总结一下。
首先css方向
1.盒模型
盒模型有两种,标准模型(box-sizing:content-box)和IE模型(box-sizing:border-box)。假设页面有一个div,其样式如下:
div {
width: 50px;
height: 50px;
background: #ff00ff;
border: 1px solid #ffff00;
padding: 20px 10px;
margin: 20px;
}
标准模型下,盒子的宽度为50px+(1*2)px+(10*2)px=72px,高度为50px+(1*2)px+(20*2)px=92px,也就是默认把border和padding算在了盒子最后的宽高内。
IE模型下,盒子的宽度为50px,高度为50px,也就是在某一方向的(padding+border)*2小于盒子的width时,最后的宽高依然等于盒子原始的width和height,如果某一方向的(padding+border)*2大于盒子的width时,此时宽高计算方式为(padding+border)*2。
除了模型本身外需要关注的一点就是margin。如果盒子是上下结构,则之间的间距为拥有较大margin的盒子,如果是左右结构,则间距为两个盒子的margin值相加。
2.BFC
BFC(Block formatting context)直译为"块级格式化上下文"。最大的特点为它是一个独立的渲染区域,并且与这个区域外部毫不相干。这个话题可以由盒模型中上下margin重叠的问题引出。触发BFC的方式如下
float
的值不为none
position
的值不为static
或者relative
display
的值为 table-cell
, table-caption
, inline-block
, flex
, inline-flex
overflow
的值不为visible
通常我喜欢的方式为浮动,display:inline-block和overflow:hidden。可以通过一个例子来理解BFC的独立性,如下所示
<style>
p{
margin: 30px;
height: 20px;
line-height: 20px;
background: red;
}
</style>
<p>我是p1</p>
<p>我是p2</p>
两个块级元素p设置了margin为30px,此时可能我们的需求为上下间距(30px+30px),这种情况就可以通过创建BFC解决,代码如下:
<style>
div{
overflow: hidden;
}
p{
margin: 30px;
height: 20px;
line-height: 20px;
background: red;
}
</style>
<div>
<p>我是p1</p>
</div>
<p>我是p2</p>
我们为第一个p标签外层加了一个div,并且样式为overflow:hidden;也就创建了一个BFC盒子,此时两个p标签的间距就变为了第一个p标签在所属的BFC内的底部margin值30px加上第二个p标签距离顶部的margin值30px,最终也就变为了60px。
3.水平垂直居中(盒子宽高可以随意更改,也就是盒子宽高不固定的居中方式)
方法1:(通过定位,外层和内层盒子宽高都不固定)
<style>
div{
position: relative;
}
.inner{
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
<div>
<div class="inner">
</div>
</div>
方法2:(table-cell)
<style>
div{
display: table-cell;
vertical-align: middle;
text-align: center;
}
.inner{
vertical-align: middle;
display: inline-block;
}
</style>
<div>
<div class="inner">
</div>
</div>
方法3:(flex)
<style>
div{
display: flex;
align-items: center;
justify-content: center;
}
</style>
<div>
<div class="inner">
</div>
</div>
方法4:(translate)
<style>
div{
position: relative;
}
.inner{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<div>
<div class="inner">
</div>
</div>
4.移动端rem
首先1rem的值为页面根节点html的font-size值。移动端为了适配不同机型,所以通常会采用rem来定义页面节点尺寸。定义方式通常有两种,第一种:通过js来控制;第二种:通过css媒体查询来控制。两种方式各有优缺点。通过js来控制,会把浏览器宽度分割成制定的份数,每份定义为1rem,这样做的好处是能比较精确的控制元素尺寸,但是也有缺点,就是需要提前加载这段适配js,不可避免会导致白屏时间。第二种方式首先是直接加载的,但是媒体查询的精确度就不是很高了,通常我们都是通过比例给出一个合适的值。
再来看看js的
1.闭包
最常见的例子为下列两个,代码如下:
for(var i=0,l=5;i<l;i++){
setTimeout(function(i){
console.log(i); // 5,5,5,5,5
},0)
}
// 改写成正常输出
for(var i=0,l=5;i<l;i++){
(function a(i){
setTimeout(function(){
console.log(i); // 0,1,2,3,4
},0)
})(i)
}
2.this指针
var name = 1;
var util = {
name: 2,
getName: function(){
return this.name;
},
sub: {
name: 3,
getName: function(){
console.log(this);
return this.name
}
}
}
var a = util.getName;
var b = util.sub;
console.log(a()); // 1 a的this为window,所以为window.name
console.log(util.getName()); // 2 util.name为2
console.log(b.getName()); // 3 // b的this为sub,sub.name = 3
console.log(util.sub.getName()); // 3 getName为sub的直接调用,所以也为sub.name
通常意义上this指针指向为最后调用它的对象。这里需要注意的一点就是如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例,例子如下:
function getName(){
this.name = 1;
return {}; // 返回对象
}
var a = new getName;
console.log(a.name); //undefined
function getName(){
this.name = 1;
return 2; // 返回非对象
}
var d = new getName;
console.log(d.name); //1
3.事件
事件分为捕获和冒泡,阻止默认和阻止冒泡都是有兼容性的,兼容代码如下:
function preventDefa(e){
if(window.event){
//IE中阻止函数器默认动作的方式
window.event.returnValue = false;
}else{
//阻止默认浏览器动作(W3C)
e.preventDefault();
}
}
function stopBubble(e) {
if(e && e.stopPropagation) { //非IE
e.stopPropagation();
} else { //IE
window.event.cancelBubble = true;
}
}
其次为原生事件的兼容,代码如下:
function addEvent(){
var arg0 = arguments[0],
arg1 = arguments[1],
arg2 = arguments[2];
if (arg0.addEventListener) {
arg0.addEventListener(arg1, arg2, false);
}else if(arg0.attachEvent) { // ie
arg0.attachEvent('on' + arg1, arg2);
}else{
arg0['on' + arg1] = arg2;
}
};
5.原型链
function Parent(){
this.name = 'james';
}
Parent.prototype.getName = function () {
console.log(this.name);
}
var newPeople = new Parent();
function Son(){
// 继承静态方法
Parent.call(this);
}
// 继承原型
Son.prototype = new Parent().__proto__;
// 修复指向
Son.prototype.constructor = Son;
// 下列4个均为true
console.log( newPeople.__proto__ === Parent.prototype );
console.log( Parent.prototype.__proto__ === Object.prototype );
console.log( newPeople instanceof Parent );
console.log( newPeople instanceof Object );
首先子类继承父类需要从静态方法和原型链两层上实现继承。通过call引用了父类的this,在原型挂载时需要注意指向应该是一个父类的实例,而不是直接Parent.prototype,存在引用问题。当指向实例后需要注意prototype上默认挂载的constructor属性需要修正到子类构造函数上。
在例子的下方有4句console,前两句展示了原型链的含义,最终指向为Object对象。后两句阐述了instanceof的含义,即判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上。
6.call,apply,bind
首先这3个都是用来改变this指针用的,call和apply是立即执行,bind只是为绑定,需要调用才会执行,call和apply的参数形式也有所不同call的第二个参数为列举的一个个参数,apply为数组。面试中有问怎么实现一个bind方法,也就是只绑定this指向,简单实现代码如下:
Function.prototype.bind = function(that){
var _this = this,
args = Array.prototype.slice.call(arguments,1);
return function(){
return _this.apply(that,args)
}
}
思路:首先认识到bind为Function下的方法,其次拿到参数,最后由于需要执行才有效,所以返回一个函数。
除了这些外接下来基本考察的就是数组了(对象考察较少),这里列举几个
1.数组去重
new Set([...arr])
es5实现方式为
function unique(arr) {
var n = [];
for (var i = 0; i < arr.length; i++) {
if (n.indexOf(arr[i]) == -1) n.push(arr[i]);
}
return n;
}
2.判断是否为数组
Object.prototype.toString.call(arr)=='[object Array]'
3.取出两个数组的相同项
let arr1 = [1,3,4,5];
let arr2 = [1,4,6];
let getSome = arr1.filter(item=>{
return arr2.includes(item)
});
console.log(getSome); // [1,4]
以上为基础部分考察最多的地方。
除此之外还有一些其它问题,这里简单罗列下:
1.css3实现一个三角形,实现原理是什么;
2.css3透明opacity,以及避免影响子元素的rgba表示方式,还有ie的hack方式;
3.js帧动画,如果为定时器,则为每秒60帧动画最流畅;
4.求一个数组的最大连续子集,求一个数组的和最大的子集,二维数组遍历如果采用从边上环形绕圈的方式,也就是m*n数列arr,从arr[0][0]-arr[0][m]-a[n][m]-a[n][0]-a[1][0]...这样等,所以数组这块的内容需要多练习;
5.跨域原理和常见跨域方式;
6.函数柯粒化及其实现;
7.性能优化;
到这里基本遍是基础部分大多数的问题了。接下来说一下除了基础外的部分内容
1.AMD,CMD,COMMON的区别和一些原理(例如怎么实现的按需加载);
2.vue的原理以及生命周期,父子组件通信,vuex的原理;
3.react生命周期,父子组件通信;
4.vue双向绑定原理;
5.对于构建工具的了解和常用的npm包有哪些;
6.对于es6的掌握程度,尤其如promise的理解等;
7.对于后台语言的接触;
8.个人职业规划或者最近几年的规划;
9.参照你的项目做一些提问,例如亮点,用到的技术栈等;
谨记基础对于一场面试成功与否的重要性~