zoukankan      html  css  js  c++  java
  • 四月份前端面试指北

    前言


     由于个人的一些原因最近也参加了几家公司的面试,发现有很多基础性的东西掌握程度还是不够,故此想总结一下最近面试遇到的问题,希望能为在准备面试的的小伙伴尽一些绵薄之力,主要说的是一些我面试当中问到的一些问题,说的不对的地方请小伙伴们即使指正出来,或者有其他的看法也可以一起探讨。


    一、HTML/CSS


    1.html5新增标签

    • 新增了一些语义化的标签例如:header,fotter,nav,main
    • 新增图像:canvas svg
    • 新增媒体标签: audio video

    2.什么是盒模型

    • W3C标准盒模型,属性width,height包含content,不包含border和padding
    • IE盒模型,属性width,height包含content,border,padding

    3.css居中元素

    说一下我比较常用的的几种

    • 使用position布局
    postion: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
    • 使用flex布局
    display: flex;
    align-items: center;
    justify-content: center;
    • 知道宽高的情况下使用position布局
    postion: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;

    4.移动端1px边框问题


    主要是不同手机的dpi不同会导致这个问题。解决办法:

    • 看手机是否支持0.5px的边框,并且dpi大于等于2,会用到js判断比较复杂,这里不做实现。
    • 使用背景图,修改颜色麻烦,需要换图。圆角需要特殊处理。
    • 伪类元素加transform实现,个人经常使用。
    .border {
        position: relative;
        border:none;
    }
    .border:after {
        content: '';
        position: absolute;
        bottom: 0;
        background: #000;
         100%;
        height: 1px;
        transform: scaleY(0.5);
        
    }

     5.css3有哪些新特性

    • border新增border-img, border-raius
    • 新增盒阴影box-shadow,文字阴影 text-shadow
    • background新增background-image,background-size background-repeat
    • 媒体查询

    6.rem em px区别

    • px相对长度单位,相对于显示器屏幕分辨率来的
    • em相对长度单位,相对于当前对象内文本的字体尺寸来的,值不固定,会继承父级元素字体的大小,未经调整浏览器:16px = 1em。假如父元素为2em,子元素为2em字体实际大小为4em
    • rem是css3新增的相对单位,使用rem为元素设置大小时,是相对大小,相对的是html根元素,修改根元素就可以调整所有字体大小,还可以避免字体大小逐层复合的连锁反应,未经调整浏览器:16px = 1rem。

    7.详细说下BFC

    • BFC块级格式上下文,是页面上一个独立的容器,容器内的子元素不会影响到外边的元素,垂直方向边距重叠,计算高度是浮动元素也会计算
    • BFC触发:根元素(html),浮动元素(float不为none),绝对定位元素(position为absolute和fixed),行内块元素(display为inline-block),overflow值不为visible,弹性元素(display为flex)
    • 应用场景:设置元素为BFC防止浮动高度塌陷,避免外边距重叠

     8.重绘和回流

    • 简单的一句话就是:回流必引起重绘,重绘不会引起回流,回流比重绘更耗性能。
    • 回流:当元素的尺寸结构和某个属性发生改变时,浏览器重新渲染部分或全部文档的过程。
    • 会发生回流的操作:浏览器窗口发生改变,元素位置和大小发生改变,元素内容发生改变,对可见的dom进行添加或者删除,查询某些属性或调用某些方法(clientWidth,offsetWidth, scrollWidth,scrollTo等等)
    • 重绘:改变元素的样式不影响在文档流的位置(color,background-color)浏览器把新的样式重新赋给元素并绘制
    • css避免:避免设置多层内联样式,避免使用css表达式(ealc),将动画放在position的属性上(absolute, fixed),避免使用table布局。
    • js避免:避免重复操作样式(定义一个class并一次修改class属性),避免频繁操作dom(创建一个documentFragment,在它上边操作dom,最后添加的文档中),避免频繁读取引起重绘/回流的值(可以使用变量缓存)。

    二、JS

    1.js基本类型和引用类型

    • 基本类型:Boolean Null Number String Undefined Symbol BigInt
    • 引用类型:Object

    2.作用域

    • 分为:全局作用域(定义在函数外部的变量)和局部作用域(定义在函数内部的变量),每个函数在被调用时都会创建一个新的域。ECMAScript 6引入了let和const关键字,利用let和const可以形成块级作用域。
    • 块语句(if, switch, for, while)不会创建新的作用域,定义的变量保存在已经存在的作用域中,let和const支持在局部作用域块语句中声明
    if (true) {
        var a = 1;
        let b = 2;
        const c =3;
    }
    console.log(a) // 1
    console.log(b) // Uncaught ReferenceError: b is not defined
    console.log(c) // Uncaught ReferenceError: c is not defined
    • 作用域链:作用域链是在变量对象之后创建的,作用域链用于解析变量,当变量被解析时,javascript先从代码嵌套的最内层开始找,如果内层没有找到,会转到上一层父级作用域查找,直到找到该变量

    3.闭包

    • 它允许函数访问局部作用域之外的数据,闭包有自己的作用域链,父级作用域链和全局作用域链。
     function a() {
            var b = 3;
            return function() {
                console.log(b)
            }
        }
        a()() // 3

    4.前端存储cookie,localStorage,sessionStorage

    • localStorage有效期为永久,sessionStorage有效期为窗口关闭
    • 同源文档可以修改并读取localStorage的值,sessionStorage只允许同一个窗口下的文档访问
    • 用法:
    localStorage.setItem('a', 1); // storge a->1
    localStorage.getItem('a'); // return value of a
    localStorage.removeItem('a'); // remove a
    localStorage.clear(); // remove all data
    • cookie是浏览器储存少量数据,cookie会自动在浏览器和服务器之间传输,可以通过path和domain配置,页面同目录和子目录都可以访问
    document.cookie = 'a=1; path=/'; // 创建当前页面的cookie
    var a = document.cookie; // 读取cookie 返回格式key=value; key1=value1;
    document.cookie = 'a=2; path=/'; // 修改值,会把以前的值覆盖
    document.cookie = "a=; expires=Thu, 01 Jan 1970 00:00:00 GMT"; //删除cookie

     5.promise实现原理

    9k字 | Promise/async/Generator实现原理解析

    可以看看蔡徐抻大佬总结的文章,非常详细

    6.宏任务和微任务

    • 首先你要知道Javascript是单线程语言,Event Loop是JavaScript的执行机制
    • 微任务和宏任务都属于异步任务,属于同一个队列,主要区别是他们执行的顺序,开始执行宏任务的时候,宏任务执行完毕之后会看有没有微任务,如果有的话执行微任务,没有接着下一个执行宏任务。在当前微任务没有执行完毕,是不会执行下一个宏任务的,而微任务又在宏任务之前执行
    console.log(1)
    setTimeout(() => {
        console.log(5)
    },0)
    new Promise(resolve => {
        resolve()
        console.log(2)
    }).then(() => {
        console.log(4)
    })
    console.log(3)
    // 打印出1,2,3,4,5
    • 宏任务:setTimeout, setInterval, requestAnimationFrame
    • 微任务 Promise.then catch finally

    7.节流和防抖

    • 节流:高频事件触发n秒内执行一次,如果这个时间点内触发多次函数,只有一次生效。
    function throttle(fn) {
            var flag = true
            return function() {
                if (!flag) return;
                flag = false;
                setTimeout(function () {
                    fn()
                    flag = true
                }, 1000)
            }
        }
    • 防抖:高频事件触发n秒之后执行,如果n秒之内再次被触发,重新记时。
    function debounce(fn) {
        var timeout = null;
        return function() {
            clearTimeout(timeout)
            timeout = setTimeout(function (){
                fn()
            }, 1000)
        }
    }

    8.get和post区别

    • 最直观的区别get把参数包含在URL中,post通过request body传递参数,相对于get比较安全
    • get请求URL传参有长度限制,post没有
    • get在浏览器回退是无害的,post会再次提交请求
    • get请求会被浏览器主动缓存,post不会
    • get和post报文格式不同
    • get请求是幂等性的,而post请求不是(新增和删除数据一般不用post请求就是这个原因)

    9.Js的事件委托是什么,原理是什么

    • 通俗点说将元素事件委托给他的父级或者更外级来处理
    • 事件委托是利用冒泡机制来实现的(事件冒泡:事件由具体的元素接受,然后逐渐向上传播到不具体的节点)
    // 每个列表点击弹出内容
    // 不使用事件委托需要给每个列表添加点击事件(消耗内存,不灵活,添加动态元素时需要重新绑定事件)这里不做介绍
    <ul id="myLink">
      <li id="1">aaa</li>
      <li id="2">bbb</li>
      <li id="3">ccc</li>
    </ul>
    // 使用事件委托(减少内存占用,提升性能,动态添加元素无需重新绑定事件)
    var myLink = document.getElementById('myLink');
    
    myLink.onclick = function(e) {
      var e = event || window.event;
      var target = e.target || e.srcElement;
      if(target.nodeName.toLowerCase() == 'li') {
        alert(target.id + ':' + target.innerText);
      }
    };


     10.什么是原型链

    • 原型:每个javascript创建的时候都会关联另一个对象,这个对象就是原型,对象会从原型继承属性
    • 构造函数可以通过prototype去寻找他关联的原型,A.prototype就是它关联的原型对象,原型对象可以通过构造器constructor来寻找与自身关联的构造函数 
    function A () {
    }
    A.prototype.constructor === A //true
    • 原型链:原型链是由原型对象组成,每个对象都有__proto__属性,指向该构造函数的原型,__proto__将对象连接起来组成了原型链
    • 原型链查找机制:当查找对象的属性时,如果实例对象不存在该属性,沿着原型链向上一级查找,直到找到object.prototype(也就是对象原型object.prototype为null),停止查找到返回undefined
    function A () {
    }
    new A().__proto__ === A.prototype //true
    • 原型上的属性和方法被实例共享
    function A () {
    }
    A.prototype.name = 'a'
    var a = new A()
    var b = new A()
    a.name === b.name // true
    a.__proto__.name === b.__proto__.name // true
    • instanceOf原理:instamceOf可以判断实例对象的__proto__属性与构造函数的prototype是不是同一地址(如果网页中有多个全局环境就会不准确)
    function _instanceOf(obj, type) {
        var obj = obj.__proto__
        var type = type.prototype
        while(true) {
            if (obj == null) {
                return false
            }
            if (obj == type) {
                return true
            }
            obj = obj.__proto__
        }
    }
    var a = [1, 2, 3]
    _instanceOf(a, Array)


    11.深拷贝和浅拷贝

    • 浅拷贝只是复制引用,新旧对象共享一块内存,一般把第一层拷贝到一个对象上,改变其中一个另一个也会改变
    var obj = {
        name: 'a',
        age: 18,
        arr: [1, 2]
    }
    function shallowCopy(obj) {
      var newObj = {};
      for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) { // 过滤掉原型上的属性
          newObj[prop] = obj[prop];
        }
      }
      return newObj;
    }
    var obj1 = shallowCopy(obj)
    var obj2 = obj
    obj1.name = 'b'
    obj2.age = 20
    obj2.arr[0] = 3
    obj1.arr[0] = 4
    console.log(obj) // {name: "a", age: 20, arr: [4, 2]}
    console.log(obj1) // {name: "b", age: 18, arr: [4, 2]}
    console.log(obj2) // {name: "a", age: 20, arr: [4, 2]}

    我们通过浅拷贝得到obj1,改变obj1的name,obj不会发生改变,通过赋值得到obj2,obj2改变age值obj的值也会被改变。说明赋值得到的对象只是改变了指针,浅拷贝是创建了新对象。

    我们通过obj2和obj1都改变的值,发现obj,ob1,obj2都发生了改变,所以无论是浅拷贝还是赋值都会改变原始数据(浅拷贝只拷贝了一层对象的属性)

    • 深拷贝:复制并创建一个一摸一样的对象(递归复制了所有层级),不共享内存,改变其中一个另一个不会改变
    var obj = {
        name: 'a',
        age: 18,
        arr: [1, 2]
    }
    function copy (obj) {
        var newobj = Array.isArray(obj) ? [] : {};
        if(typeof obj !== 'object'){
            return;
        }
        for(var i in obj){
           if (obj.hasOwnProperty(i)) {
              newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
           }
        }
        return newobj
    }
    var copyObj = copy(obj)
    copyObj.arr[0] = 3
    console.log(copyObj) //{name: "a", age: 20, arr: [3, 2]}
    console.log(obj) //{name: "a", age: 20, arr: [1, 2]}

    12.实现继承

    • 原型链继承(在实例化一个类时,新创建的对象复制了父类构造函数的属性和方法,并将__proto__指向父类的原型对象,当在子类上找不到对应的属性和方法时,将会在父类实例上去找。)
    function Big () {
        this.age = [1, 2, 3]
    }
    Big.prototype.getAge = function () {
        return this.age
    }
    function Small() {}
    Small.prototype = new Big()
    var small = new Small()
    console.log(small.age) // [1, 2, 3]
    console.log(small.getAge()) // [1, 2, 3]

    缺点:无法为不同的实例初始化继承来的属性

    function Big (name) {
        this.age = [1, 2, 3]
        this.name = name
    }
    Big.prototype.getAge = function () {
        return this.age
    }
    function Small() {}
    Small.prototype = new Big('small')
    var small = new Small()
    var small1 = new Small()
    console.log(small1.name) // small
    console.log(small.name) // small
    • 构造函数继承(在子类的构造函数中执行父类的构造函数,并为其绑定子类的this,让父类的构造函数把成员属性和方法都挂到子类的this上)
    function Big (name) {
        this.age = [1, 2, 3]
        this.name = name
    }
    Big.prototype.getAge = function () {
        return this.age
    }
    function Small(name) {
        Big.apply(this, arguments)
    }
    var small = new Small('small')
    var small1 = new Small('small1')
    console.log(small.name) // small
    console.log(small1.name) // small1
    small.age[0] = 12
    console.log(small.age) // [12, 2, 3]
    console.log(small1.age) // [1, 2, 3]

    缺点:无法访问原型上的方法

    small.getAge() //small.getAge is not a function
    • 组合式继承(将原型链继承和构造函数继承组合到一起, 综合了原型链继承和构造函数继承的优点)
    function Big (name) {
        this.age = [1, 2, 3]
        this.name = name
    }
    Big.prototype.getAge = function () {
        return this.age
    }
    function Small(name) {
        Big.apply(this, arguments)
    }
    Small.prototype = new Big()
    var small = new Small('small')
    var small1 = new Small('small1')
    console.log(small.name) // small
    console.log(small1.name) // small1
    small.age[0] = 12
    console.log(small.age) // [12, 2, 3]
    console.log(small1.age) // [1, 2, 3]
    console.log(small.getAge()) // [12, 2, 3]
    console.log(small1.getAge()) // [1, 2, 3]

    小缺点:调用了两次父类构造函数

    • 寄生组合式继承(在组合继承的基础上减少一次多余的调用父类构造函数)
    function Big (name) {
        this.age = [1, 2, 3]
        this.name = name
    }
    Big.prototype.getAge = function () {
        return this.age
    }
    function Small(name) {
        Big.apply(this, arguments)
    }
    // 对父类原型进行拷贝,否则子类原型和父类原型指向同一个对象,修改子类原型会影响父类
    Small.prototype = Object.create(Big.prototype) 
    var small = new Small('small')
    var small1 = new Small('small1')
    console.log(small.name) // small
    console.log(small1.name) // small1
    small.age[0] = 12
    console.log(small.age) // [12, 2, 3]
    console.log(small1.age) // [1, 2, 3]
    console.log(small.getAge()) // [12, 2, 3]
    console.log(small1.getAge()) // [1, 2, 3]

    注意:对父类原型进行拷贝后赋值给子类原型,因此Small上的constructor属性被重写,需要修复Small.prototype.constructor = Dog;

    • extends继承(class和extends是es6新增的,class创建一个类,extends实现继承)
    class Big {
      constructor(age) {   
        this.age = age;
      }
      getAge () {
        return this.age
      }
    }
    class Small extends Big {
      constructor(age) {   
       super(age)
      }
    }
    var small = new Small([1, 2, 3])
    var small1 = new Small([12, 2, 3])
    small.age[0] = 13
    console.log(small.age) // [13, 2, 3]
    console.log(small.getAge()) // [13, 2, 3]
    console.log(small1.age) // [12, 2, 3]
    console.log(small1.getAge()) // [12, 2, 3]

    浏览器网络


    1.常用http状态码

    • 200 成功状态码
    • 301 永久重定向,302 临时重定向
    • 400 请求语法错误, 401 请求需要http认证,403 不允许访问资源,404 资源未找到
    • 500 服务器内部错误,502 访问时出错,503 服务器忙,无法响应

    2.https原理

    • http协议:客户端浏览器与web服务器之间的应用层通讯协议
    • https协议:HTTP+SSL/TLS,http下加入SSL层,https安全基础时SSL,用于安全的HTTP数据传输
    • https优势:内容经过对称加密,每个连接会生成唯一的加密密钥,内容经过完整性校验,第三方无法伪造身份
    • 使用对称加密(加密和解密使用的是同一个密钥)被中间人拦截,中间人可以获取密钥,就可以对传输的信息进行窥视和篡改
    • 使用非对称密钥(双方必须协商一对密钥,一个私钥和一个公钥)用私钥加密的数据,只有对应的公钥才能解密,用公钥加密的数据,只有对应的私钥才能解密,弊端:RSA算法很慢
    • 非对称密钥+对称密钥(结合两者优点)
    • 客户端获取公钥确认服务器身份通过SSL证书,客户端接受到服务端发来的SSL证书给客户端。

    3.前端安全

    • XSS攻击:注入恶意代码来攻击。攻击者在目标网站上注入恶意代码,被攻击者登陆网站执行这些恶意代码,这些脚本可以读取 cookie,session tokens,或者其它敏感的网站信息,对用户进行钓鱼欺诈(打开新标签跳转,新标签存在恶意代码,跳转到伪造的页面),网页植入广告等。
    • XSS攻击防御手段:禁止JavaScript读取某些敏感cookie,限制输入内容和长度控制,检测是否有恶意代码注入
    • CSRF攻击:诱导用户进入第三方,获取到登录凭证,冒充用户对被攻击的站点执行操作,导致账号被劫持
    • 防御CSRF攻击:验证token(请求服务器时,返回token,每个请求需要加上token)

    5.浏览器缓存

    • 浏览器每次发起请求,都会在浏览器缓存中查找请求结果和缓存标识,浏览器每次拿到的数据会将结果和标识存入浏览器中
    • 强制缓存:当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。
    • 协商缓存:强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。(协商缓存生效,服务器返回304,资源未更新,协商缓存失效,服务器返回200,资源更新重新缓存)
    • 详情可以看这篇文章彻底理解浏览器的缓存机制

    框架


    1.什么是vue生命周期

    • 每个vue实例在被创建之前都要经过一系列初始化过程,这个过程就是vue生命周期。(开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程)
    • beforeCreate: 完成实例初始化,this指向被创建的实例,data,computed,watch,mothods方法和数据都不可以访问
    • created: 实例创建完成,data,computed,watch,methods可被访问,未挂载dom,可对data进行操作,操作dom需放到nextTick中
    • beforeMount: 有了el,找到对应的template编译成render函数
    • mounted: 完成挂载dom和渲染,可以对dom进行操作,并获取到dom节点,可以发起后端请求拿到数据
    • beforeUpdate: 数据更新之前访问现有dom,可以手动移除已添加事件的监听
    • updated: 组件dom已完成更新,可执行依赖的dom 操作,不要操作数据会陷入死循环
    • activated: keep-alive缓存组件激活时调用
    • deactivated keep-alive移除时调用
    • deactivated keep-alive移除时调用
    • beforeDestroy: 实例销毁之前调用,可以销毁定时器
    • destroyed: 组件已经被销毁

    2.第一次页面加载会触发哪几个钩子

    • 会触发beforeCreate, created, beforeMount, mounted 

    3.created和mounted的区别

    • created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
    • mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

    4.hash模式和history模式

    • 在vue的路由配置中有mode选项 最直观的区别就是在url中 hash 带了一个很丑的 # 而history是没有#的。
    • hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
    • history利用了HTML5中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
    • history模式需要后台配置支持

    5.computed和watch区别

    • computed: 依赖的属性值发生改变才会重新计算,得出最后的值
    • watch: 当依赖的data的数据变化,执行回调(可以观察数据的某些变化,来做一些事情)

    6.vue中key的作用

    • key是给每一个vnode的唯一id,可以依靠key,更准确, 更快的拿到oldVnode中对应的vnode节点。

    7.vue的两个核心点

    • 数据驱动:ViewModel,保证视图的一致性
    • 组件系统:组件化,封装可复用的组件,页面上每个独立的区域都可以看成一个组件,组件可以自由组合成页面

    8.SPA首屏加载慢如何解决

    • 使用路由懒加载
    • 使用SSR渲染
    • 优化webpack打包体积
    • 图片使用CDN加速

    9.vue禁止弹窗后的屏幕滚动

    • 主要是是写一个点击出现弹窗禁止屏幕滚动的方法,关闭弹窗屏幕可以正常滚动
    methods : {
        //禁止滚动
        stop(){
            var mo=function(e){e.preventDefault();};
            document.body.style.overflow='hidden';
            document.addEventListener("touchmove",mo,false);//禁止页面滑动
        },
        /取消滑动限制/
        move(){
            var mo=function(e){e.preventDefault();};
            document.body.style.overflow='';//出现滚动条
            document.removeEventListener("touchmove",mo,false);
        }
    }

    10.Vuex中actions和mutations的区别

    • action主要处理的是异步的操作,mutation必须同步执行,而action就不受这样的限制,也就是说action中我们既可以处理同步,也可以处理异步的操作
    • action改变状态,最后是通过提交mutation

    结尾


    如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎点赞收藏关注三连,对作者也是一种鼓励。一起在码农的道路上越走越远。

  • 相关阅读:
    neutron-server Connection pool is full, discarding connection 连接池过满
    C#中值类型与引用类型
    抽象类与接口的比较
    XML解析与文件存取
    Json序列化与反序列化
    关于Stream系列实战
    CTS,CLS,CLR
    冒泡排序——算法
    Js 调用 webservice
    使用WebService的优点
  • 原文地址:https://www.cnblogs.com/Limengbo/p/12785447.html
Copyright © 2011-2022 走看看