阿里巴巴2014前端线上笔试题

来源:互联网 发布:java gbk转utf8 乱码 编辑:程序博客网 时间:2024/06/05 21:51

原文地址:http://www.ituring.com.cn/article/56881,Thanks


2014年阿里的前端线上笔试题目区分度十分的高,感觉十分有意义。所以在这里做一些分析,也希望能丰富一下自己。这里已经有一个帖子,但是因为其中作者有一部分也没有解释,而且文章的版式比较混乱,所以决定自己动手再分析一下。

No. 1

题目:

下图绿色区域的宽度为100%,其中有三个矩形,第一个矩形的宽度是200px,第二个和第三个矩形的宽度相等。请使用CSS3中的功能实现它们的布局。


已知HTML结构是:

<div class="box">    <div class="item">column 1</div>    <div class="item">column 2</div>    <div class="item">column 3</div></div>

解答:


【2013/10/16 更新】

十分感谢 @酷剑 的指正。回去仔细查了一下MDN,发现本题一开始的解答有误。回头查了查原帖,发现原帖正确的使用 box-flex 的属性,而我的解答并不规范。更新解答如下:

.box {    background-color: #619B99;    display: -webkit-box;    height: 40px;    padding: 10px;    padding-right: 0px;    width: 100%;}.box>div {    height: 40px;    background-color: #EEEEEE;    margin-right: 10px;    -webkit-box-flex: 1;}.box div:first-of-type {    width: 200px;    -webkit-box-flex: 0;}

这里其实 box-flex 的含义在于规定框是否具有可伸缩尺寸。我个人总结的规则如下:

  • 当box-flex: 0时,框的空间固定而不伸缩
  • 当 box-flex: n时,框的空间大约会按照剩余宽度的 n/m 伸缩,m为所有设定了box-flex值的和。
  • 当给有flex的box设定了大小时,该box将拥有设定的空间与flex分配的空间之和

例如父元素有500的空间,其中五个元素设定flex为1,则五个元素的宽度均为100;一个设置为flex:0、宽度180,则剩下的元素均分320的宽度,也即每个80;一个设置flex:6,则该元素分的6/10的宽度即300,剩余的元素各分1/10的宽度即50;一个元素设定为flex:1,同时设定宽度100,则该元素分得100+(500-100)/5=180的宽度,剩余的元素分的(500-100)/5=80的宽度。

(实测发现这里并不是很准确,准确的说当设置了flex的元素内部没有其他元素时,框的宽度会按照box-flex设置的那样按比例填充,但如果有元素和边距等情况,这个比例就仅仅是个大约的值了。)

实际上box-flex是个特别有用的工具,其一劳永逸的解决了当一个box中有元素有固定值,而又要求剩下的元素按比例浮动的情况。像这种问题之前都是使用绝对定位,将固定的元素采取 absolute 定位的方式,然后剩下的元素使用margin留出浮动元素留下的位子。但是当固定的元素过多且混杂出现时,使用绝对定位的方法将不能优雅的解决这个问题,那么flex模型就能展现它的力量了。

No.2

题目:

有两个盒子 A、B,B 在 A 盒子中,它们的 CSS 是这么定义的:

.A {    position: relative;    width: 500px;    height: 500px;    background-color: green;}.B {    position: absolute;    max-width: 300px;    max-height: 300px;    background-color: blue;}

如何实现 B 在 A 中水平方向和垂直方向居中?

解答:

作者在原文里表示可以使用CSS3的 box-align 和 box-pack 的方法(W3C链接如下box-align,box-pack),但是在实际测试中发现,如果B的定位不是 absolute (而是 relative 或者static)那么如下追加的代码是有效的:

.A {    display: -webkit-box;    -webkit-box-align: center;    -webkit-box-pack: center;} 

但是当B的定位是绝对定位时,使用CSS3的box进行定位就起不到应有的效果。这里我们可以简单的看以下CSS的四种定位的原理(可参见mdn的解释:css position 以及quicksmode的这篇博文:the position declaration)。

  • static :默认定位,不可使用 top right bottom left 四种属性(以下简称TRBL);
  • relative :相对定位,指的是针对默认定位的位置而产生偏移,可以使用TRBL调整定位的偏移量,但是元素本身还在文档流中占据位置;
  • absolute :绝对定位,指针对上一级非默认定位的位置(即上一级非 static 定位的父元素的位置)而产生的偏移,可用TRBL调整偏移量,与相对定位不同的是,元素本身漂出文档流,不再占据位置;
  • fix :固定定位,锚定浏览器窗口的一种定位方式,可用TRBL调整偏移量。

这里B元素使用了绝对定位,也就是其偏移是针对A元素而计算的,可以使用TRBL进行调整。所以说莫非这里出题人是想让我们采用原始的div居中方法?

在CSS2的时代,要将div水平垂直居中也算是个挺让人头疼的问题,其大致的方法有如下几种:

  1. 外层元素宽度不固定,内层元素宽度固定:使用自动外边距或负值外边距两种方式;

    • 自动外边距,这里要求内层元素不能采取绝对定位:

      .B {    width: 100px;    margin: 0 auto;}
    • 负值外边距,用到了TRBL属性,要求内层元素采取绝对定位:

      .B { width: 200px; position: absolute; left: 50%; margin-left: -100px; }

  2. 外层元素宽度固定,内层元素宽度不固定,采用表格法,要求内部元素不能采取绝对定位:

        A. {        display: table;    }    .B {        display: table-cell;        vertical-margin: center;    }
  3. 增添HTML元素、使用Hack、使用JS等,不详述之。

然而观察上面的方法,题目中的限定条件是:外部元素宽度固定、内部元素宽度变动且绝对定位,这样就导致上述的方法没有一个可以满足所需要求的。我认为如果不改变题目中的限定,可以使用负值外边距的方法,用JS设置内层元素的margin值;或者直接在后面的CSS中修改内层元素的定位(设置为非绝对定位),然后使用CSS3的 box-align 和 box-pack 来实现,而这也是目前我认为实现效果最好,实现代码最为简洁的实现方式。

其中如果附加JS,代码如下:

<script>    var b = document.getElementsByClassName('B')[0]    ,   l = b.offsetWidth    ,   h = b.offsetHeight    b.style.marginLeft = -l/2 + 'px';    b.style.marginTop = -h/2 + 'px';</script>

No.8

题目:

现有代码如下:

var foo = 1;function main(){    console.log(foo);    var foo = 2;    console.log(this.foo)    this.foo = 3;}
  1. 请给出以下两种方式调用函数时,输出的结果,并说明原因。

    var m1 = main();var m2 = new main();
  2. 如果想要 var m1 = main() 产生的m1和前面的m2完全一致,又该如何改造main函数?

解答:

看到全局变量与函数内局部变量重名时,一定要先想到 hoisting (变量声明提升规则)!

在第一种调用方式里,函数内部声明了局部变量 foo ,因此其声明被提升至最前。因此该段代码等价于:

function main(){    var foo;    console.log(foo);    foo = 2;    console.log(this.foo)    this.foo = 3;}

在调用的时候,第一次输出为undefined,因为局部变量 foo 还没有被赋值,第二次输出为1,因为此时在非严格模式下 this 指代 window 对象。this.foo 即为全局变量的 foo 。

在第二种调用方式里,使用了原型链继承的方法,可以参见我的这篇文章:JavaScript原型继承工作原理,其中 new 运算符的作用等价于如下函数:

function New (f) {    var n = { '__proto__': f.prototype };    return function () {        f.apply(n, arguments);        return n;    };}

则在进行 new 运算时,函数中的 this 实际上指代的是运算符后面所跟的构造函数的原型链。因为在 new 运算的时候,实际上执行构造函数的是这一步: f.apply(n, arguments); 也就是说在构造函数中的 this 实际上指向的是变量 n ,而 n 是一个除了原形链指向 f 的原形链之外一无所有的空对象,所以这里的 this 实际上是指向了构造函数 f 的原形链。

所以这里的输出也就不难理解:因为构造函数 main() 本身的原形链为空,所以输出 this.foo 的结果为 undefined。

然后是第二问:使m1与m2的结果完全一致。我们知道m2所得到的是类 main 的一个实例化对象,这里要使得达到题目中的要求,就只能手工编写原形链继承,使 main 函数的返回结果为一个继承自其本身的对象。这里可以参考上面代码中 new 运算符的具体实现,编写 main 函数如下:

function main(){    console.log(foo);    var foo = 2;    console.log(this.foo)    this.foo = 3;    //返回一个main的实例化对象    var n = {'__proto__': main.prototype};    //main.apply(n, arguments);    n.foo = 3;    return n;}

这里因为在 main 函数里有 this.foo = 3 这一项,为原型添加了成员变量,但如果在这里使用apply 的方式为n添加这一属性的话,将进行无限递归导致栈溢出。而采取其他的方式都不可避免的会带来一些问题,所以直接给返回值n的属性foo进行了赋值,也起到了同样的效果。


No.9

题目:

实现如下图所示的布局,要求:

  1. sidebar 固定宽度200px,content和header宽度自适应;
  2. 当window宽度小于600px时,变成三行布局。

默认如下


宽度小于600px时如下


下面是html结构:

<div class='header'>    <h1>header</h1></div><div class="sidebar>    <h1">sidebar"</h1></div><div class="content">    <h1>content</h1></div>

请写出其css代码:(提示,可以使用media query来检测浏览器窗口宽度)

解答:

这里涉及到CSS的media query检测,这里有篇文章详细介绍了其中的参数类型,而这篇文章则饶有性质的探讨了不同设备下应该如何编写media query,有兴趣的读者可以详细看一下。针对这个题来说,只要用到 screen and (min-width) 即可。代码如下:

h1 {    margin: 0;}section div{    height: 100px;}section div:nth-of-type(n+2) {    margin-top: 20px;}.header {    background-color: red;}.sidebar {    background-color: green;}.content {    background-color: blue;}@media screen and (min-width: 600px) {    .sidebar {        width: 200px;        position: absolute;    }    .content {        margin-left: 220px;    }}

这里还需要注意一个问题:sidebar的宽度固定,而content填充剩余的全部。针对这种一边固定而一边液态的情况,最好的解决办法就是将固定的部分浮动出来,然后将液态的部分使用margin来填充固态部分浮动而产生的空隙。这里使用了绝对定位,以满足题目中的要求。

No.10

题目:

写一段脚本,实现:当页面上任意一个链接被点击的时候,alert出这个链接在页面上的顺序号,如第一个链接则alert(1), 依次类推。

解答:

起初做这个题目的时候简单的认为所有链接应该指的就是 <a> ,所以很快写出了代码如下:

var body = document.getElementsByTagName("body")[0],   links = document.getElementsByTagName("a"),   len = links.length,   foo = function(e){    if (e.target.tagName.toLowerCase() !== 'a') //注意这里的tagName是大写        return    for (var i = 0; i < len; i++) {        if (links[i] === e.target)             alert(i + 1)    }}body.addEventListener('click', foo, false)

但是后来看到网上有一个本题的解答:最英俊的程序员 阿里巴巴2013年校园招聘前端工程师笔试题详解,解答中使用了 document.links 的方法,查了一下发现这个数组返回的是所有有 href 的和标签,才知道链接不止有一种,那么可以把代码改成如下的样式:

var body = document.getElementsByTagName("body")[0],   links = document.getElementsByTagName("a"),   len = links.length,   foo = function(e){    var tagName = e.target.tagName.toLowerCase()    if ((tagName === 'a' || tagName === 'area') && e.target.href) {        for (var i = 0; i < len; i++)            if (links[i] === e.target) {                alert(i + 1)            }    }}body.addEventListener('click', foo, false)

当然也可以采用网上最英俊的程序员的解答,在页面中每一个链接都绑定事件监听,只不过这样显得有些效率不高,特别是在页面链接过多的情况下。个人认为相同效果的事件监听还是在父级元素上绑定为好,要不就可惜了JS冒泡的监听机制啊。

No.14

题目:

(new Date).getTime()  +new Date() 都可以取到当前时间戳,它们的实现原理是什么,哪个效率更高?

解答:

这个题目挺有趣,起初我认为应该是第二个写法的效率更高……因为写起来更容易,但是经过实际的检验我意识到了我的错误,第一种写法所耗的时间基本上只有第二种写法的一半。后来查阅了MDN:Date.getTime 发现这个函数本身实际上等价于 valueOf ,那么结论也呼之欲出了。因为第一种方法实际上直接调用了 valueOf ,而第二种方法涉及到JS内部的类型转换(可以参见我的这篇文章:【WEB前端】百度前端面试经历小研究1——JavaScript 类型转换),尽管最终的结果也是调用了 valueOf 函数,但是毕竟有个转换的过程,所以效率理应比前者要来的低下吧。

No.16

题目:

请写一个 getParents 方法让它可以获取某一个 DOM 元素的所有父亲节点。

解答:

第一反应就是想到jQuery里面的 getParents() ,自己想了一下JS中可以获取 parentNode ,所以应该循环获取就可以了。解答如下:

function getParents(ele) {    var matched = []    // 防止获取 document    while ( (ele = ele.parentNode) && ele.nodeType !== 9) {        matched.push(ele)    }    return matched} 

No.20

题目:

有一个数组,其中保存的都是小写英文字符串,现在要把它按照除了第一个字母外的字符的字典顺序(字典顺序就是按首字母从a-z顺序排列,如果首字母相同则按第二个字母……)排序,请编写代码

例:["abd","cba","ba",],排序后["ba","cba","abd"]

解答:

这个题目是专门凸显JS函数式编程特点的题目,结果原作者居然用C语言解答了……捂脸。这里给出JS的写法,十分简洁明了。

var bySecLetter = function(x, y) {    if (typeof x === 'string' && typeof y === 'string') {        var a = x.slice(1)        ,   b = y.slice(1)        if (a > b) return 1        else if (a < b) return -1    }    return 0}var a = ["abd","cba","ba"]a.sort(bySecLetter)

这里只需要调用sort的比较函数,大于返回正数,小于返回负数,等于返回0即可。而作为字符串,本身的比较顺序就是字典顺序,所以只需要截取从第二个字符开始的字串进行比较,就可以得出比较结果。更多关于sort函数的信息可以参见MDN的链接。



0 0