您现在的位置:首页 >> 前端 >> 内容

小米前端实习面试题和一些面试经验浅谈

时间:2017/9/27 10:21:00 点击:

  核心提示:前两天接到了小米的面试邀请,在此之前有个电面,自认为已经初步对小米对前端实习生的要求了解了大概,然后没做过多心里建设准备就去了。(画外音:到了小米,正好赶上当天有许多新员工入职,大厅看起来比较忙碌,拿...

前两天接到了小米的面试邀请,在此之前有个电面,自认为已经初步对小米对前端实习生的要求了解了大概,然后没做过多心里建设准备就去了。(画外音:到了小米,正好赶上当天有许多新员工入职,大厅看起来比较忙碌,拿到码号等了会就有一个很漂亮的小姐姐下来把我带上去了,工作区,不得不说小米的环境布置的超级温馨,对员工的待遇也是很好的很人性化的,不自觉对小米更加了一点向往)

咳咳,言归正传,初试的笔试题已经有些模糊了,(最近忘性有点大)先码了再说

第一题考了对css选择器优先级以及权重的了解

1、输出下列四个选择器的优先级顺序

<p class="red" id="red">
    <span class="red" id="red">color</span>
</p>
<style type="text/css">
    .red #red{
        color:yellow;
    }
    #red .red{
        color:red;
    }
    p .red{
        color:block;
    }
    body .red{
        color:orange;
    }
</style>

这题首先考察了选择器的优先级:HTML标签属性>id选择器>类选择器>元素选择器

而选择器的权重对应下就是:

第一等:代表内联样式,如: style=””,权值为1000。

第二等:代表ID选择器,如:#content,权值为100。

第三等:代表类,伪类和属性选择器,如.content,权值为10。

第四等:代表类型选择器和伪元素选择器,如p p,权值为1。

在选择器类型相同的前提下,如以上代码前两个选择器权重其实是一样的,是110此时后面的样式就覆盖前面的样式,所以按①②③④来排序的话首先②>①

再看后两个选择器的权重也是相同的 为11 但后面的样式覆盖之前的所以④>③

综合起来是②>①>④>③

2、实现一个clearfix(清除浮动)代码:

.clearfix:after{
    content:".";
    display:block;
    height:0;
    clear:both;
    visiblity:hidden;
}
.clearfix{
    zoom:1;
}

面试官会就这种题进行深层发问,看你平时是否有爱思考。

比如:content是用来干嘛的,这就看你对伪类了解的是不是够深了,:after一般加在父元素上,为期内容添加后续,content你写个小点或者让他为空都可以,相当于添加一个空的内容

display:block;让他能为块级元素,这样可以设置宽和高,接下来把它的高度height设置为0,visiblity:hidden;内容隐藏掉,再clear:both掉,把前几个兄弟元素加注在父元素上的浮动影响清除掉

最后面试官小姐姐还问我这个zoom是干什么的 让他为1和为100有什么不一样吗?这个说实话之前只知道zoom是ie的专有属性 主要针对ie6 7有效的修复兼容性问题,可以在清除浮动时起到一定作用其余的确实没多加思考,所以这个没答上来有写尴尬,后来回来网上查了一下,大概是这样的:(下面这部分内容是引用网络资源)

zoom这个属性是ie专有属性,除了设置或者检索对象的缩放比例之外,它还有可以触发ie的haslayout属性,清除浮动,清除margin重叠等作用。
不过值得注意的一点就是火狐浏览器不支持zoom属性,但是在webkit内核浏览器中zoom这个属性也是可以被支持的。

(1)下面我们来看下zoom在非IE浏览器中的作用:看下面的例子,我是在谷歌浏览器下访问的,在该例子中zoom的作用是放大为原来的2倍(读者可以自己尝试缩小操作)

zoom:1时

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8" />  
<style>  
    p{  
        width: 100px;  
        height: 100px;  
        border: 3px solid red;   
        zoom: 1;  
}  
</style>  
    </head>  
    <body>  
        <p>hello</p>  
    </body>  
<html>

zoom为2时:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8" />  
<style>  
    p{  
        width: 100px;  
        height: 100px;  
        border: 3px solid red;   
        zoom: 1;  
}  
</style>  
    </head>  
    <body>  
        <p>hello</p>  
    </body>  
<html>

注:zoom在非IE浏览器中表现为支持放大或者缩小,但是由于这个属性是一个不标准的css属性,因此一般在非IE浏览器中不用zoom来实现p 的缩放效果,现在要放大或者缩小直接用css3的transform属性。

(2)看完了zoom在非IE浏览器中的表现之后,我们就该看看这个属性在IE浏览器中的作用了。

Zoom的使用方法:

zoom :? normal | number

normal :  默认值。使用对象的实际尺寸

number :  百分数 | 无符号浮点实数。浮点实数值为1.0或百分数为100%时相当于此属性的 normal 值用白话讲解就是zoom:后面的数字即放大的倍数,可以是数值,也可以是百分比。如:zoom:1,zoom:120%。而这个属性只要在IE中才起作用,所以很少用到它的实际用途,而最经常用到作用是清除浮动等,如:

.border{

border:1px solid #CCC;

padding:2px;

overflow:hidden;

_zoom:1;

}

_ zoom是CSS hack中专对IE6起作用的部分。IE6浏览器会执行zoom:1表示对象的缩放比例,但这里

overflow:hidden;和_zoom:1;是连起来用的,作用是清除border内部浮动。

同理,还可以使用同样方法清除margin属性在IE浏览器中的重叠问题:这就要提到zoom属性在IE中的第二个作用了,即

兼容IE6、IE7、IE8浏览器,经常会遇到一些问题,可以使用zoom:1来解决,有如下作用:

触发IE浏览器的haslayout ,解决ie下的浮动,margin重叠等一些问题。

下面是zoom属性在IE浏览器中常见作用总结,希望对今后在使用这个属性时有所帮助:

1、检查页面的标签是否闭合

不要小看这条,也许折腾了你两天都没有解决的 CSS BUG 问题,却仅仅源于这里。毕竟页面的模板一般都是由开发来嵌套的,而他们很容易犯此类问题。

快捷提示:可以用 Dreamweaver 打开文件检查,一般没有闭合的标签,会黄色背景高亮。

2、样式排除法

有些复杂的页面也许加载了 N 个外链 CSS 文件,那么逐个删除 CSS 文件,找到 BUG 触发的具体 CSS 文件,缩小锁定的范围。

对于刚才锁定的问题 CSS 样式文件,逐行删除具体的样式定义,定位到具体的触发样式定义,甚至是具体的触发样式属性。

3、模块确认法

有时候我们也可以从页面的 HTML 元素出发。删除页面中不同的 HTML 模块,寻找到触发问题的 HTML 模块。

4、检查是否清除浮动

其实有不少的 CSS BUG 问题是因为没有清除浮动造成的。养成良好的清除浮动的习惯是必要的,推荐使用 无额外 HTML 标签的清除浮动的方法(尽量避免使用 overflow:hidden;zoom:1 的类似方法来清除浮动,会有太多的限制性)。

5、检查 IE 下是否触发 haslayout

很多的 IE 下复杂 CSS BUG 都与 IE 特有的 haslayout 息息相关。熟悉和理解 haslayout 对于处理复杂的 CSS BUG 会事半功倍。推荐阅读 old9 翻译的 《On having layout》(如果无法翻越穿越伟大的 GFW,可阅读 蓝色上的转帖 )

快捷提示:如果触发了 haslayout,IE 的调试工具 IE Developer Toolbar 中的属性中将会显示 haslayout 值为 -1。

6、边框背景调试法

故名思议就是给元素设置显眼的边框或者背景(一般黑色或红色),进行调试。此方法是最常用的调试 CSS BUG 的方法之一,对于复杂 BUG 依旧适用。经济实惠还环保^^

最后想强调一点的是,养成良好的书写习惯,减少额外标签,尽量语义,符合标准,其实可以为我们减少很多额外的复杂 CSS BUG,更多的时候其实是我们自己给自己制造了麻烦。

3、下面以下代码的执行结果:

①***JavaScript
var a = new Object();
a.value = 1;
b = a;
b.value = 2;
console.log(a.value);
***

执行结果2

解析:在 JS 中,有值类型和引用类型两种区别:

1、值类型:数值、布尔值、null、undefined。

2、引用类型:对象、数组、函数。

值类型指的是保存在栈内存中的简单数据段。按值访问,操作的是他们实际保存的值。

引用类型指的是那些保存在堆内存中的对象,变量中保存的实际上只是一个指针,这个指针执行内存中的另一个位置,由该位置保存对象。按引用访问,先从栈中读取内存地址,然后再由此找到保存在堆内存中的值。

题主这段代码里 a 也好、b 也罢,都只是一个变量别名,实际上存储的是一个指针,指向的是内存中的同一段地址,所以无论对 a 还是 b 进行操作,都会对内存中的这同一个对象造成影响搜索。

②***javascript
var x = 1;
(function(){
    console.log(x);
    var x = 2;  
})();
***

这是一个作用于闭包的问题,匿名自执行函数内部取不到外部的值,而x的赋值在打印之后也就相当于

***javascript
var x = 1;
(function(){
    var x;
    console.log(x);
    x = 2;  
})();
***

所以结果为undefined。

③***
var User = {
    count:1,
    getCount: function(){
        return this.count;
    }
};
console.log(User.getCount());
var func = User.getCount;
console.log(func());
***

正确的答案是1和undefined

答案1就不用解释了,undefined是因为函数赋给了一个新的全局变量,这个变量是在winodw的上下文中被执行,而window中没有count这个属性,所以是undefined

this的问题 :

有对象就指向调用对象

没调用对象就指向全局对象

用new构造就指向新对象

通过 apply 或 call 或 bind 来改变 this 的所指。

4、用js实现以下请求:

1.一个动物类Animal,拥有wow方法:console.log(this.name + ‘:wow’);

2.小狗Dog继承Animal,并且拥有run方法:console.log(this.name + ‘:run’);

function Animal () {
     this.name="Animal";
     this.age=3;
     this.wow=function() {
     console.log(this.name+'wow:name');
     }
   }
 function Dog () {
     this.name='a dog';
 }
    //Animal的原型上增加run方法,所有继承Animal类的子类都可以调用
    Animal.prototype.run = function() {
       alert(this.name+' is running!')
     };

     animal=new Animal();
     animal.run();

    Dog.prototype=new Animal();
     dog=new Dog();
     dog.run();

5、输出今天的日期,以YYYY-MM-DD的方式,比如今天是2015年9月26日,则输出2015-09-26

var d = new Date();
// 获取年,getFullYear()返回4位的数字
var year = d.getFullYear();
// 获取月,月份比较特殊,0是1月,11是12月
var month = d.getMonth() + 1;
// 变成两位
month = month < 10 ? '0' + month : month;
// 获取日
var day = d.getDate();
day = day < 10 ? '0' + day : day;
alert(year + '-' + month + '-' + day);

6、实现一个闭包的例子:

闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。

闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配

当在一个函数内定义另外一个函数就会产生闭包

function sayAlice() {
    var sayAlert = function() { alert(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后,局部变量仍然可以被访问到。

以上是记得的初试笔试题

面试的时候面试官就以上问题问了一些思路以后还有一些拓展题:

比如:

实现一个三列布局,左右两块是固定宽度比如都为100像素,中间一列宽度自适应,且外面套一个父元素边框,当这三列布局里面内容填充的话高度会增加,而父元素的高度会随着最高的子元素被撑开,这个布局怎么写,有哪几种方法?

一 首先,使用浮动布局来实现一下

1左侧元素与右侧元素优先渲染,分别向左和向右浮动

2中间元素在文档流的最后渲染,并将 width 设为 100%,则会自动插入到左右两列元素的中间,随后设置 margin 左右边距分别为左右两列的宽度,将中间元素调整到正确的位置。

这是一种比较便利的实现方式,无需额外的元素辅助定位,同时兼容性也比较优秀。但有一个缺点就是该布局方式只能实现左右两列宽度固定,中间自适应这一种三列布局,灵活性不强。

二 可以试试利用 BFC

深入理解BFC和Margin Collapse

1同样的左右两列元素优先渲染,并分别左右浮动。

2接下来将中间元素设置 overflow: hidden; 成为 BFC 元素块,不与两侧浮动元素叠加,则自然能够插入自己的位置啦。

三 双飞翼布局

双飞翼是由淘宝玉伯等前端大牛提出的一种多列布局方法,主要利用了浮动、负边距、相对定位三个布局属性,使三列布局就像小鸟一样,拥有中间的身体和两侧的翅膀。

<p class="grid">
    <p id="p-middle-02"><span>p-middle</span></p>
    <p id="p-left-02"><span>p-left</span></p>
    <p id="p-right-02"><span>p-right</span></p>
</p>
#p-middle-02 {
    float: left;
    background-color: #fff9ca;
    width: 100%;
    height: 50px;
}
#p-left-02 {
    float: left;
    background-color: red;
    width: 150px;
    margin-left: -100%;
    height: 50px;

}

#p-right-02 {
    float: left;
    background-color: yellow;
    width: 200px;
    margin-left: -200px;
    height: 50px;
}

因为通过负边距调整浮动元素位置时,会产生层叠的效果,上面的布局其实只是左右两列元素分别定位在自己的位置上并覆盖中间元素的那部分而已,中间元素的定位并未成功。中间元素要怎样定位在自己的位置上呢?小鸟的身体不是还缺少骨架嘛,那么我们在小鸟体内加上骨架吧:

<p id="p-middle-02">
        <p id="middle-wrap-02"><span>p-middle</span></p>
</p>

#middle-wrap-02 {
    margin: 0 200px 0 150px;
}

 

双飞翼能够兼容到 IE6

总结整个过程,就是先放好身体,再加上翅膀,然后让身体包裹一层骨架,通过骨架将身体定位到正确的位置。这就是双飞翼布局的完全体吗?当然不是,接下来我们要请出大杀器相对布局啦,就像小鸟可以通过各种不同的姿势飞翔一般,通过 position: relative; 双飞翼可以实现任意的三列或双列布局。本实例加上相对定位,便成为了这样的完全体:

#p-middle-02 {
    float: left;
    background-color: #fff9ca;
    width: 100%;
    height: 50px;
}

#middle-wrap-02 {
    margin: 0 200px 0 150px;
}

#p-left-02 {
    float: left;
    position: relative;***
    background-color: red;
    width: 150px;
    margin-left: -100%;
    height: 50px;

}

#p-right-02 {
    float: left;
    position: relative;***
    background-color: yellow;
    width: 200px;
    margin-left: -200px;
    height: 50px;
}

四 flex

设计一个弹性容器包裹需定位的三个元素,然后将该弹性容器的排列属性设为水平排列(flex-flow: row)

现在三个元素已经是三列布局了,再将三列元素分别设定一下宽度就行了,左右元素设定为定宽,自适应的中间元素设定为 100%。那么该如何设置中间元素的宽度呢,flex: 1; 即可

那么 flex 有什么缺点呢?对,就是兼容性!

小米前端实习面试题和一些面试经验浅谈

所以在使用 flex 的时候还请注意是否兼容当前浏览器,是否需要 -webkit- 标签。

rem 和 em有什么区别?

PX为单位

在Web页面初期制作中,我们都是使用“px”来设置我们的文本,因为他比较稳定和精确。但是这种方法存在一个问题,当用户在浏览器中浏览我们制作的Web页面时,他改变了浏览器的字体大小,这时会使用我们的Web页面布局被打破。这样对于那些关心自己网站可用性的用户来说,就是一个大问题了。因此,这时就提出了使用“em”来定义Web页面的字体。

em为单位

前面也说了,使用是“px”为单位是比较方便,而又一致,但在浏览器中放大或缩放浏览页面时会存在一个问题,要解决这个问题,我们可以使用“em”单位。

这种技术需要一个参考点,一般都是以的“font-size”为基准。比如说我们使用“1em”等于“10px”来改变默认值“1em=16px”,这样一来,我们设置字体大小相当于“14px”时,只需要将其值设置为“1.4em”。

          body {
                font-size: 62.5%;/*10 &pide; 16 × 100% = 62.5%*/
            }
            h1 {
                font-size: 2.4em; /*2.4em × 10 = 24px */
            }
            p   {
                font-size: 1.4em; /*1.4em × 10 = 14px */
            }
            li {
                font-size: 1.4em; /*1.4 × ? = 14px ? */
            }

在使用“em”作单位时,一定需要知道其父元素的设置,因为“em”就是一个相对值,而且是一个相对于父元素的值,其真正的计算公式是:

1 &pide; 父元素的font-size × 需要转换的像素值 = em值

Rem为单位

前面说了“em”是相对于其父元素来设置字体大小的,这样就会存在一个问题,进行任何元素设置,都有可能需要知道他父元素的大小,在我们多次使用时,就会带来无法预知的错误风险。而rem是相对于根元素,这样就意味着,我们只需要在根元素确定一个参考值,,在根元素中设置多大的字体,这完全可以根据您自己的需,大家也可以参考下图:

小米前端实习面试题和一些面试经验浅谈

我在根元素中定义了一个基本字体大小为62.5%(也就是10px。设置这个值主要方便计算,如果没有设置,将是以“16px”为基准 )。从上面的计算结果,我们使用“rem”就像使用“px”一样的方便,而且同时解决了“px”和“em”两者不同之处。

以及浏览器的兼容问题 关于这个问题借鉴于 css3字体单位rem

以上是分割线*******

此外还有几个面其他公司时候的杂七杂八的问题,之前没有总结,现在把能想起来的都在这一一列举了吧:

1、ie9之前的事件模型都有哪些,与现在的有何区别

attachEvent() or detachEvent() 方法的工作原理与 addEventListener() or removeEventlistener() 类似:

其特点如下:

a:IE9之前的版本IE事件模型不支持事件捕获,所以attachEvent() or detachEvent()要求只有两个参数;

attachEvent(type,handler) or detachEvent(type,handler) – type:事件类型, handler:处理程序函数;

b:IE的这个方法 第一个参数使用了带“on”前缀的事件处理程序属性名;例如:attachEvent(“onclick”,handler);

c:attachEvent()也允许相同的事件处理程序函数 注册多次;当特定的事件类型发生时,注册函数的调用次数和注册次数一样,

但不是所有的都按照注册的顺序调用,所以代码不应该依赖于调用顺序~~

function addEvent(el, type, callback, useCapture ){
//el:是事件对象, type:事件类型, callback:注册的事件处理程序,useCapture:布尔值;
        if(el.dispatchEvent){//w3c方式优先
                el.addEventListener( type, callback, !!useCapture );
                }else {
                el.attachEvent( "on"+type, callback );
                }
        return callback;//返回callback方便卸载时用
        }

addEvent 可传入4个值,第四个useCapture 布尔值

javascript 事件模型 事件处理机制 整理

2、已知一个二维数组[[2,5,123,63,76],[23,6,79,10],[10,74,888,102],[9999,1042,3]],请用js写一个方法返回这个二维数组中的最大值

    function largestOfFour(arr) {
var results = []; // 创建一个results变量来存储
// 创建一个外层循环,遍历外层数组
for (var n = 0; n < arr.length; n++) {
var largestNumber = 0; // 创建第二个变量,存储最大的数
// 创建另一个循环,遍历子数组
for (var sb = 0; sb < arr[n].length; sb++) {
//检查子数组的元素是否大于当前存储的最大值
if (arr[n][sb] > largestNumber) {
// 如果为真,将这个值赋予给变量largestNumber
largestNumber = arr[n][sb];
}
}
// 内部循环后,将每个子数组中的值保存到数组results中
results[n] = largestNumber;
}
// 返回数组
return results;
}
largestOfFour([[2,5,123,63,76],[23,6,79,10],[10,74,888,102],[9999,1042,3]])

在javascript中可以通过内置的 Math.max() 的最大值

第二个解决方案:

function largestOfFour (arr) {
// 通过map()方法,并通过回调函数,将子数组中最大值组合在一起,得到一新数组
return arr.map(function (group) {
// 通过reduce方法,把每个子数组中最大值返回到group数组中
return group.reduce(function (prev, current) {
// 如果current 大于prev,返回current,否则返回prev
return (current > prev) ? current : prev;
});
});
}
largestOfFour([[2,5,123,63,76],[23,6,79,10],[10,74,888,102],[9999,1042,3]]);

在外层数组中使用 Array.prototype.map() 方法遍历数组。使用 map() 方法遍历数组,会调用一个回调函数,在这个回调函数中,使用 reduce() 方法对每个子数组 group 进行合并,将值返回到一个新数组中。而在使用 reduce() 方法时,同样会调用一个回调函数,这个回调函数只做了一件事情,就是子数组中的元素做为比较,如果 current 大于 prev ,将会返回 current ,否则返回 prev ,最终得到每个子数组中最大值。

和前面一样,通过 Math.max.apply() 最终得到最大值。

参考

js中apply()的用法以及结合Math.max()的妙用

最佳解决方案

function largestOfFour (arr) {
return arr.map(Function.apply.bind(Math.max, null));
}
largestOfFour([[1,34],[456,2,3,44,234],[4567,1,4,5,6],[34,78,23,1]]);

这个方案,使用 Function.bind 方法创建一个特殊的回调函数,就类似于 Math.max 方法一样,但其有一个 Function.prototype.apply 功能,将数组作为它的参数。

先对主数组中的每个元素做遍历,也就是数组内部的每个子数组

使用 map() 方法需要一个回调函数,用来找出内部每个数组中的最大值。需要创建一个函数,让 Math.max 能接受输入的数组工作。换句话说,这是非常简单而且这样工作也非常的好,如 Math.max([9,43,20,6]); 将会返回最大值 43

Function.prototype.apply 方法工作可以接受数组做为参数,但函数通过调用上下文,这事情就有点复杂。例如 Math.max.apply(null,[9,43,20,6]) 将调用一个 Max.max 方法,但这样的方法找起来不容易。

这里给 Function.prototype.apply 方法传递了一个 null 参数,告诉 Math.max 不需要任何上下文。

因为 arr.map() 需要一个回调函数,而不只是一个表达式,我们在 Function.bind 方法中提供了一个函数

因为 Function.prototype.apply 是一个静态方法,类似一个函数对象,我们可以称之为 Function.prototype.apply 上绑定了一个 Function.prototype.bind 。例如: Function.apply.bind

现在可以通过 Function.prototype.apply.bind 回调函数指定其上下文,比如在这个示例中的 Math.max 方法

由于是嵌入到 Function.prototype.apply 方法,需要一个上下文作为第一个参数,而且这个上下文还是一个虚假的。

所以我们将 null 作为第二个参数传递给 Function.prototype.apply.bind ,并且绑定一个上下文,这个上下文就是 Math.max 方法

由于 Math.max 是独立于任何上下文的,所以它会忽略 Function.prototype.apply 方法调用的虚假上下文

我们使用 Function.prototype.apply.bind(Math.max,null) 让一个新函数接受 arr.map 值,比如数组中的子数组

3、已知一个对象objA。请用代码实现一个拷贝方法,他能够生成一个跟objA一样的新对象。

var objA={
    lists:['3','4','5'],
    methods:function(){
        return this.lists;
    },
    children:{
        name:'xin';
        type;'fu',
        props:'she',
    }
}

extendCopy() 只是拷贝了基本类型的数据,我们把这种拷贝叫做“浅拷贝”。

function extendCopy(p) {
    var c = {};
    for (var i in p) { 
      c[i] = p[i];
    }
    c.uber = p;
    return c;
 }

这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

深拷贝

所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。

function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }

参考js对象浅拷贝和深拷贝详解

这个对象拷贝的知识点以及js中的继承问题,具体不一一列举具体的题目了

作者:网络 来源:李乐乐的博客