Skip to content

小米面试总结 #12

@junfeisu

Description

@junfeisu

这个面试一共有俩轮,开始面试之前内心还是比较紧张的。俩个面试官开始都是给了我一套题,让我开始先做题,然后根据做题的结果来进行提问或者说讨论,面试官还是比较和蔼的。

然后介绍一下里面几个比较有意思的题目。

CSS

1.经典的三栏布局问题,左右宽度固定,中间宽度自适应,但是重点来了三列高度都不固定

首先看一下我写的布局吧,反正是漏洞百出,最致命的漏洞有三个

div.left
div.middle
div.right


.left {
    float: left;
    width: 200px;
}

.right {
    float: right;
    width: 200px
}

.middle {
    margin: 0 -200px //第一遍的写法
    padding: 0 -200px //第二遍的写法
}
  • 不讨论是margin还是padding是正确的,为负值就是不正确的,更为尴尬的是padding就没有负值这个说法,margin负值是向俩边扩张,而不是向中间挤压。所以为负值的话肯定就会超出父元素的宽度,从而导致出现横向滚动条。

  • 还有这种方式要实现的话,middle和right的位置需要交换,因为浮动的元素的前面一个节点不是浮动元素而且不是第一个子节点的时候就会以前一个元素的底边为起点进行浮动。假如上面说的那些问题都解决了的话,就是下面这样的效果。

  • 讨论是应该用margin还是padding的问题,我们先说一下盒模型中margin和padding的区别。这里再回顾一下chrome中的经典盒模型图

    盒模型图

    这里只讨论W3C盒模型,IE盒模型就不考虑了。

    margin是用来让自身和同级的其他盒子或者父盒子产生距离,padding是为了让自身的子盒子和自身产生距离,从某种意义来说,自身的padding就是子盒子的margin,这是理解层次的区别

    表现层次的区别就是这个盒子的宽高因此就不一样了。使用margin的时候自身的宽高是会被更改的,但是padding是不会更改自身的宽高,更改的是子元素的宽高,所以这里又可以复习一个知识就是一个元素的宽高100%是按照父元素的content的宽高来计算的,也就是不包括border和padding的部分就是上图的蓝色部分

    为了更好的展示区别,所以先把左边的一列去掉再来看效果

    margin:

    padding:

    屏幕的宽度是1920,所以margin形式的middle的宽度是1520,padding形式的middle宽度是1920,所以就本题,视觉上来说效果是一样的,但是就真正的自适应来说,还是margin的用法更符合题意,当然涉及到了浮动,还要消除浮动,但是这里就不涉及了。

  • 但是重点来了就是说这题的思想是三列的高度都不固定,所以当我们采用的上面的方法来实现的时候,就会出现下面的这种问题。

    所以这样就不是严格上的三列了。所以传统的方法就不能满足这种三列高度自适应的布局了。还有以前的那种绝对布局的方式也不行了。

    所以我还写了方法二:flex布局

    div.parent
        div.left
        div.middle
        div.right
    
    .parent {
        display: flex
    }
    
    .left,.right {
        width: 200px;
        flex: 0 0 auto;
        align-self: flex-start;
    }
    
    .middle {
        flex: 1 1 auto;
        align-self: flex-start
    }
    

JavaScript

  1. 将字符串'1234567'转换成'1,234,567',其实就是一个金额数的转换,从低位开始,每隔三位一个逗号分隔一下,当金额少于或者等于三位时就不用加逗号

    我一开始的思路:(其实是最简单也是正确的思路,但是因为忘记数组转回字符串的方法了,其实是脑子糊了,后面都知道用join(','))

    首先将字符串变成数组然后进行倒序,然后在变回字符串,然后在用spilt(/\w{3}/)切割成数组(纠正:这里不能用split,而是应该使用replace和他的第二个参数),最后在用join(',')实现变成字符串并且实现(纠正:这一步其实已经不需要)

    所以代码可以这样写

     Array.from(str).reverse().join('').replace(/(\w{3})(?=\w)/g, (match, result) => {
         return result + ','
     })
    

    其实主要的有俩点:

    1. 通过join('')将数组无伤转换成字符串,这里说一下,当时我写的是toString()方法。但是toString()的结果是'7,6,5,4,3,2,1',是的连数组的分隔符逗号也保留下来了。

    2. replace(reg, () => {}),然后说一下那个正则/(\w{3})(?=\w)/g,第一个括号里面的就是后面的函数里面的第二个参数result匹配到的内容,至于后面的(?=\w)是考虑到当字符串位数等于三位或者三位倍数的情况,详细的可以去看一下正则表达式。

    后来换的常规思路:首先将字符串的长度除以3获取余数,也就是字符串从第几位开始加,然后后面每3位加一个逗号

         const translate = (str) => {
             let strLen = str.length
             if (strLen < 4) {
                 return str
             }
    
             let newStr = ''
             let startNum = strLen % 3
             let addTimes = Math.floor(strLen / 3) - 1
    
             if (startNum !== 0) {
                 newStr += str.substring(0, startNum) + ','
             }
    
             for (let i = 0; i < addTimes; i++) {
                 newStr += str.substring(startNum + 3 * i, startNum + 3 * (i + 1)) + ','
             }
    
             newStr += str.substring(strLen - 3)
    
             return newStr
         }
    

    后面面试官提到的是另一种思路:就是将字符串转换成数字然后每次整除1000,用逗号加上这个数整除1000的余数,然后再用这个整除得到的数继续整除1000,重复加逗号和余数的操作,直到得到的数的长度小于4,最后再加上这个整除的小于1000的数

         const translate = (str) => {
             let num = +str
             if (num < 1000 || isNaN(num)) {
                 return str
             }
    
             let newStr = ''
    
             while (num > 999) {
                 let temp = parseInt(num / 1000)
                 newStr = ',' + num % 1000 + newStr
                 num = temp
             }
    
             newStr = num + newStr
    
             return newStr
         }
    

    所以相比来说还是第一种方法最简单,但是当时没写出来就是一大败笔呀。

  2. 问下面的代码打印出来的是什么。

     var a = 100
     (function(a){
         console.log(a)
         var a = 1
         function a() {}
     }(10))
    

    我写的是function a() {}(其实是正确答案,但是面试官给我解释的扯了什么编译阶段,我就虚了,也就没去争论了)

    实际上是这样的,就是当一个函数执行的时候,遇到同名的时候,规则如下:

    当同名的里面出现了函数声明的(注意不是函数表达式)时候,其他的都会被函数声明覆盖。当没有函数声明的时候,就看形参和变量以及函数表达式的值到底是哪个起作用了,这三个同名的后面的会覆盖前面的值。当这三个都没找到的时候就去父作用域找,如果一直到全局作用域也没找到就是undefined。
  3. 俩个里面都出现的Jquery.extends的实现

    这里介绍一下extends的作用,就是拷贝一个对象的属性,如果自身也有这个属性会被覆盖,支持深拷贝

     const isArray = (arr) => arr instanceof Array
    
     const isStaticObject = (obj) => obj instanceof Object
    
     const extend = (dest, source, isDeep) => {
         if (!isArray(source) && !isStaticObject(source)) {
             return dest
         }
    
         for (let prop in source) {
             let temp = source[prop]
             if (isDeep) {
                 if (isArray(temp) || isStaticObject(temp)) {
                     let copySource = isArray(temp) ? [] : {}
                     dest[prop] = extend(copySource, temp, true)
                 } else {
                     dest[prop] = temp
                 }
             } else {
                 dest[prop] = temp
             }
         }
    
         return dest
     }
    

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions