web前端面试宝典-基础篇(一)

其实感觉前端的面试就是一些零碎的知识点,最后上升为架构而已。这篇博客我也会将架构设计融入其中。大致通过以下几个分类进行分析:

  • HTML/CSS部分
  • JavaScript部分
  • 数据结构部分(算法)
  • 框架之Angular.js
  • 框架之Vue.js
  • 框架之React.js
  • 移动端
  • HTTP
  • WEB安全
  • WEB渗透之Kali Linux
  • 前端性能
  • 设计模式
  • 正则表达式
  • 前端架构设计

一、HTML/CSS部分

1.1 什么是盒子模型?

在网页中,一个元素占有空间的大小由几个部分构成,其中包括元素的内容(content),元素的内边距(padding),元素的边框(border),元素的外边距(margin)四个部分。这四个部分占有的空间中,有的部分可以显示相应的内容,而有的部分只用来分隔相邻的区域或区域。4个部分一起构成了css中元素的盒模型。

1.2 什么是css Hack?

一般来说,css Hack就是针对不同的浏览器写不同css样式。css hack一般来说是为了兼容IE浏览器的。
IE浏览器Hack一般又分为三种: 条件Hack属性Hack选择器Hack

// 1.条件Hack


// 2.属性Hack
.test {
    color: #090\9; /* For IE8+ */
    *color: #f00;  /* For IE7 and earlier */
    _color: #ff0;  /* For IE6 and earlier */
}

// 3.选择器Hack
*html .test{
    color: #090; /* For IE6 and earlier */
}

*+html .test {
    color: #ff0; /* For IE7 */
}

1.3 什么是同步?异步?

当我在学习前端的时候,看到这个问题的时候,让不不禁想起了学习Linux时,一直想的一个问题。什么是异步,同步?什么是阻塞,非阻塞当时这个问题困惑了我很久,一直搞不清楚有什么区别,异步不就是非阻塞吗,同步不就是阻塞吗?下面咱们就唠唠嗑来说说这里面的玄机。在这里咱们主要来以网络I/O为例:

1.3.1 同步和异步

实际上同步与异步是针对***应用程序***与***内核***的交互而言的。同步过程中进程触发IO操作并等待或者轮询的去查看IO操作是否完成。异步过程中进程触发IO操作后,直接返回,做自己的事情,IO交给内核来处理,完成后内核通知进程IO完成。

(这里大家不禁要问了,什么是IO,如果你是做后端开发的,IO对于你来说很容易理解,但是纯做前端的话就不容易理解了。IO就是input/output,输入输出。比如文件的读写操作,网络的请求和得到数据的操作等等。不懂的童鞋要去学习操作系统了)
直接上图吧:

web前端面试宝典-基础篇(一)_第1张图片
同步与异步.png

1.3.2 阻塞与非阻塞

简单理解为需要做一件事情能不能立即得到返回应答。如果不能立刻获得返回,需要等待,那就阻塞了,否则就可以理解为非阻塞。详细区别如下图所示:
web前端面试宝典-基础篇(一)_第2张图片
阻塞与非阻塞.png

需要注意了, 这里的立即返回,是针对内核来说的。就是说这个应用程序的某些代码会不会阻塞内核(CPU)的执行。而同步异步是针对于应用程序来说的,到底某些代码会不会影响下面代码的执行。

1.4 px和rem和em的区别?

px和rem和em都是长度单位, 区别是px的值是固定的,指定是多少就是多少,计算比较容易。em的值不是固定的,它的值是继承父级元素的字体大小(其实也不能说是父级,应该说是自身,如果自身没有设置font-size, 则去找父级,然后父级没设置,在往上找,如果没有找到,就是浏览器默认的字体)。下面举例说明:

    /* 1. em的案列 */
    .test {
      font-size: 20px;  /* 这句话声明之后就代表了1em = 20px;但是后面的代码与这个没有关系 */
      text-indent: 2em;  /* 单位转换之后2em = 40px */
    }
    
    /* 2.rem的案列 -- rem是根据html的font-size大小定的,一般用于移动端网站开发时使用,进行屏幕适配。*/
    html {
        font-size: 40px;
    }
    
    .test {
        font-size: 1rem; /* 单位转换后为40px */
    }

1.5 浏览器的内核分别是什么?

IE: trident内核
Firefox: gecko内核
Safari: webkit内核
Opera: 以前是presto内核,Opera现已改用Google Chrome的Blink内核。
Chrome: Blink(基于Webkit内核,google和Opera公同开发)

1.6 清除浮动的影响的方法?

这个问题之前一直说一直说,没有时间去总结。咱们从下面几种方式进行探究。

  • 1.空的div方式(不是特别好,如果清除浮动地方多了,就会有多个空标签)
    /* 在要清除浮动的地方加上
*/ .clearfix { clear: both; height: 0px; }
  • 2.使用overflow方式进行清除浮动带来的影响。
    /* 想要使用overflow清除浮动带来的影响,一般分为两步骤:
        1. 给它加一个父元素
        2. 在父元素上写overflow: hidden;
    */
    
  • 3.使用伪元素的方式进行清除浮动带来的影响。
    /* 这里说明一下,一般使用这种方式也是需要给它加一个父元素的... */
    .test::after {
         content:"";        
         display:block;        
         height:0;        
         clear:both;        
         /* visibility:hidden;    */
    }

1.7 margin折叠现象?

要想解释这个现象,还是要了解BFC的。这里的我就不啰嗦了,就直接说一下解决方案了。(我经常使用的方案, 其他方案自行百度)

    /* 这个很简单和上一个题目一样
    */
    在父层加上overflow:hidden;

其他CSS的问题待续吧(CSS3动画、渐进、Canvas、SVG等等先忽略),特别是less、sass、stylus等预处理语言,面试中也是常问的重点。

二、JavaScript部分

这一部分涉及的内容就多了,那么我会从ES5和ES6都会讲解到。
先列个目录

2.1 如何快速合并两个数组?

这个问题看起来很简单,实则没有那么简单。给大家提几个问题,合并数组什么方式最好也就是说性能最佳?合并之后用不用去重?等等都需要考虑。网上给出了push和concat的对比。(温馨提示:如果不懂call和apply的童鞋,自行学习。)

2.1.1. 先使用代码来对比(concat函数与Array.prototype.push.apply)

    function testClass() {
            var testArray1 = [];
            var testArray2 = [];
            this.resetArray = function() {
                for (var i = 0; i < 100000; i++) {
                    testArray1.push(i);
                    testArray2.push(i + 100000);
                }
            }
            this.applyTest = function() {
                var startTime = 0,
                    endTime = 0;
                console.log('开始合并的时间是:' + (startTime = new Date().getTime()));
                var aa = Array.prototype.push.apply(testArray1, testArray2);
                console.log(aa);
                console.log('合并完成的时间是:' + (endTime = new Date().getTime()));
                console.log('合并数组所用的时间是:' + (endTime - startTime));
            }
            this.concatTest = function() {
                var startTime = 0,
                    endTime = 0;
                console.log('开始合并的时间是:' + (startTime = new Date().getTime()));
                var aa = testArray1.concat(testArray2);
                console.log(aa.length);
                console.log('合并完成的时间是:' + (endTime = new Date().getTime()));
                console.log('合并数组所用的时间是:' + (endTime - startTime));
            }
        }


        var apply = new testClass();
        apply.resetArray();
        apply.applyTest();


        var concat = new testClass();
        concat.resetArray();
        concat.concatTest();  

通过测试结果可知:
使用Array.prototype.push.apply 方式性能非常低下,在20w的数据下,执行时间将近是concat方法的11-17倍左右。

2.1.2 Array.prototype.concat.apply 和 concat对比

很多程序员喜欢直接使用Array构造函数下的原型对象上的方法,以此缩短搜索函数的时间,这种方式是否能够带来性能的提升?
在这里只需要将第一个函数中的代码改成

    var aa = Array.prototype.concat.apply(testArray1, testArray2);

根据测试的结果,使用Array.prototype.concat.apply的方式还是比直接使用concat函数慢些。之前我也没有注意过,这次测试,我感觉问题大了。难道之前想错了,经过又一轮的测试,证明了我的猜测。下一轮的对比您应该想到了,就是call和apply的性能。(这个对比大家自行研究,结论就是call的性能在chrome浏览上明显比apply性能要高很多)

2.1.3 Array.prototype.concat.call 和 concat对比

将第一个函数中的代码改成如下:

    var aa = Array.prototype.concat.call(testArray1, testArray2);

最后的测试结果验证使用Array.prototype.concat.call比直接使用concat方法性能提升了一倍。因此最后的得出的结论就是数据合并性能最高的方式就是使用Array.prototype.concat.call
各位兄弟姐妹,这只是我自己的测试结果,如果有出入,欢迎指正。至于去重的算法及其性能优化放到算法一节说吧。

2.2 说说你对原型链的理解?

这个问题也是相当有点难度的问题。首先要搞清楚什么是原型对象、什么是构造函数、什么是实例化对象。如果和我一样都清楚的童鞋,您只需看图即可。

web前端面试宝典-基础篇(一)_第3张图片
数组对象原型链 .png

这张图表明了如果数组实例化之后,它原型的整个指向。还有一种情况就是当实例化对象时,采用var a = {}或者new Object();它的指向如下:

web前端面试宝典-基础篇(一)_第4张图片
Obj.png

2.3 说说你对js中面向对象编程的理解?

在没有学习面向对象编程的时候,大家更多的是函数(就是一些功能性代码的集合),而面向对象的思想是从另外一个角度出发来解决函数的局限性。它把对象作为程序的基本单元,其实对象就是对事物的一种抽象描述。
人们发现,现实世界中的事物,都可以用「数据」和「能力」来描述。比如我要描述一个人,「数据」就是他的年龄、性别、身高体重,「能力」就是他能做什么工作,承担什么样的责任。描述一台电视,「数据」就是它的屏幕尺寸、亮度,「能力」就是播放《葫芦娃》。

面向对象的世界里,到处都是对象。对象不光有「数据」和「能力」,还可以接受命令。例如你可以让「狗」这个对象「吃狗粮」,就可以把「吃狗粮」的命令发给「狗」让其执行,然后我们就实现了「狗吃狗粮」的需求。

现在对象有了,如何进行面向对象的编程呢?很简单,依次向不同的对象发送命令就可以了。比如产品经理说要把大象装进冰箱里,用面向对象来实现,我们会先定义一个「冰箱」对象,它的「数据」就是当前的冷冻温度,或者该冰箱已经有了多少头大象,「能力」就是开门、关门。还有一个「大象」对象,它的「数据」可以是大象的智商、体积,「能力」就是「自己跑到冰箱里去」。然后我们依次:

向冰箱下达「开门」的命令。

向大象下达「进冰箱」的命令。

向冰箱下达「关门」的命令。

大家好好理解一下就可以整明白,当然还有其他的编程思想。比如面向接口、面向切面都是常用编程思想。(具体到后面再讲解吧)

2.4 JavaScript(ES5)中封装、多态、继承的实现方式?

2.4.1 JavaScript实现封装

→原始模式

var cat = {
    name: '',
    color: ''
}

这种方式缺点是: 1.如果生成的对象很多就会很麻烦。2.不能看出实例之间的联系

→简单工厂模式

function Cat(name, color) {
    var obj = {};
    obj.name = name;
    obj.color = color;
    return obj;
}

var cat1 = Cat("金毛", "黄色");
var cat2 = Cat("泰迪", "棕色");

这种方式可以解决代码重复的问题,但是依然不能反映出实例对象之间的关系。

→构造函数模式

    function Cat(name, color) {
        this.name = name;
        this.color = color;
    }
    var cat1 = new Cat("金毛", "黄色");
    var cat2 = new Cat("泰迪", "棕色");

构造函数的方式确实好用,但是存在一个浪费内存的问题。所以就要使用原型模式。

→原型模式

    function Cat(name, color){
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "猫科动物";
  Cat.prototype.eat = function(){alert("吃老鼠")};

还有等等一些模式进行封装,大家自行去查询即可。

2.4.2 JavaScript中继承的实现

→原型继承

    // 基类
    var Person = function() {
        this.name = '张三';
        this.age = 20;
    };
    
    Person.prototype = {
        say: function() {
            console.log('Person.prototype');
        }
    };
    
    // 子类
    var Student = function() {
        
    };
    
    // 这样就可以继承了Person里面的所有的方法。
    Student.prototype = new Person();
    Student.prototype.getTeacher = function(){
        console.log('Student.prototype.getTeacher');
    };
    

这种继承存在一些问题,就是无法通过参数定义对象。

→构造函数实现继承

    function Person(){
        this.race = '人类';
    }
    Person.prototype={
        eat:function(){
            alert('123')
        }
    };

    /*学生对象*/
    function Student(name, skin) {
        // People(), 
        Person.apply(this, arguments);
        this.name = name;
        this.skin = skin;
    }

构造函数这种方式的继承利用apply这个方法改变this的指针来完成继承的。

→组合模式实现继承

组合模式就是将上述的原型继承和构造函数继承的组合。

    function Person(name, age) {}
    Person.prototype.say = function() {}


    function Student(name, age, no) {
        /*会自动调用Person的方法,同时将name age传递过去*/
        Person.call(this, name, age);
        //自己的属性
        this.no = no;
    }
    Student.prototype = new Person();

通过这种方式进行继承会更加完善。至于其他的继承方式在这就不一一列举了,比如寄生虫组合继承、拷贝继承等等。

2.4.3 JavaScript中重载的实现

这里只说一种方式实现重载(根据arguments个数)

    function add() {
        var sum = 0 ;
        for ( var i = 0 ; i < arguments.length; i ++ ) {
            sum += arguments[i];
        }
        return sum;
    }
    alert(add());
    alert(add( 1 , 2 ));
    alert(add( 1 , 2 , 3 ));

内容太多了,请移步web前端面试宝典-基础篇(二)
有问题请联系QQ:136062834,一直感觉良好的攻城狮。

你可能感兴趣的:(web前端面试宝典-基础篇(一))