美文网首页让前端飞Web前端之路JavaScript 进阶营
前端常见面试题(二十二)@郝晨光

前端常见面试题(二十二)@郝晨光

作者: 郝晨光 | 来源:发表于2019-08-15 20:16 被阅读47次

    vw、vh、rem、em、px的区别

    • px
      px是绝对单位,1px就是一个像素点

    • em
      em是相对单位,是相对于父级的font-size

    • rem
      rem是相对单位,是相对于html的font-size

    • vw
      vw是相对单位,相对于视口宽度,1vw是视口宽度的 1%

    • vh
      vh是相对单位,相对于视口的高度,1vh是视口高度的 1%


    前端安全:xss攻击和CSRF攻击

    • xss攻击就是通过用户输入的信息,比如input、textarea等,输入一段html、css、js代码,接着提交到数据库,其他用户访问对应页面的时候,就会触发攻击。
      • 常见的攻击:css攻击,body { display: none !important; },js攻击,通过script标签嵌入js代码,html攻击,嵌入a标签超链接,指向攻击网站。
      • 防御方式:对用户的输入进行过滤,将用户输入的标签内容或者事件内容过滤掉,让它形成不了代码块,只能显示对应文本即可。
      • 一般是后端进行替换。
    • CSRF攻击,是跨站请求伪造,是用户先打开一个正常页面,接着完成登录等身份验证,在本地浏览器存储了cookie值,接着在钓鱼网站或者攻击网站上,点击a标签超链接,或者触发其他事件,而这个链接指向的就是那个正常网页的API接口,当用户点击之后,就可以通过之前正常网站存储的cookie访问到接口的操作。
      • 常见的攻击:新浪微博用户粉丝增加,在支付的时候通过CSRF攻击直接调用支付接口
      • 防御方式:使用token验证,每次向后台发送请求都携带token,这样不是由前端发送的请求就过滤掉。或者对网页来源进行验证,如果不是我们自己的网页,就过滤。
    • XSS是像你的页面注入JS脚本执行,在JS里面去做他想做的事情;无需做权限认证;
    • CSRF是用你API本身的漏洞,帮你自动执行;需要登录认证;

    http和https的区别

    • HTTPS相较于HTTP更加安全
    • HTTPS是密文传输,HTTP是明文传输
    • 所以HTTPS不会被劫持,而HTTP协议传输的内容可以被劫持
    • HTTPS默认使用443端口,而HTTP默认使用80端口

    优雅降级和逐渐增强

    • 优雅降级相当于向下兼容,就是先针对最主流、最高级、最完善的浏览器进行产品设计,接着在开发的最后阶段,通过测试以及兼容处理,保证低级浏览器可以提供不影响使用的功能即可。
    • 优雅降级根据公司不同,项目不同,所需要兼容的浏览器等级也不同,一般可以是IE8就可以了
    • 渐进增强相当于向上兼容,一开始就针对低级浏览器进行产品设计,然后在针对高级浏览器设计更好的用户体验和交互动效。

    什么是堆和栈

    • 在js中分为基本数据类型和引用数据类型,而基本数据类型(Number、String、null、undefined、Boolean)都是存放在栈内存中的,占据固定大小的空间,而引用类型(Object)是存放在堆内存中的,在栈内存中存放了对应的引用,按照引用访问堆内存的数据。
    • 对于引用类型,存放在栈内存中的就是一个引用指针,这个指针指向堆内存中的数据,而数据的大小也是不固定的。
    • 堆:先进先出,动态内存空间,不会自动释放
    • 栈:先进后出,自动分配内存空间,会自动释放

    什么是BFC

    • BFC是块级格式化上下文,BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
    • 它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
    • 具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。
    • 内部的Box会在垂直方向,一个接一个地放置
    • 在BFC垂直方向的边距会发生重叠
    • BFC的区域不会与float区域的box重叠
    • BFC是一个页面上的独立的容器,外面的元素不会影响BFC里的元素,反过来,里面的也不会影响外面的
    • 计算BFC高度的时候,浮动元素也会参与计算
    • 浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为“visiable”的块级盒子,都会为他们的内容创建新的BFC(块级格式上下文)。
    • 1、float的值不是none。
      2、position的值不是static或者relative。
      3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
      4、overflow的值不是visible
    • 什么时候使用BFC:上下边距重叠、高度塌陷

    什么是闭包,闭包的优缺点,内存泄露怎么处理,闭包的使用场景

    • 闭包就是一种可以重复使用变量而且不会造成全局变量污染的机制。
    • 因为:全局变量可以重复使用,但是容易造成变量污染。局部变量仅在局部作用域内有效,不可以重复使用,不会造成变量污染。闭包结合了全局变量和局部变量的优点。
    • 闭包的优点:可以重复使用变量,并且不会造成变量污染,可以用来定义私有属性和私有方法。
    • 闭包的缺点:比普通函数更占用内存,会导致网页性能变差,在IE下容易造成内存泄露。
    • 内存泄漏的解决方案:在退出函数之前,将不使用的局部变量全部删除。手动释放闭包内的变量。
    • 闭包的使用场景:封装功能时(需要使用私有的属性和方法),函数节流、函数防抖、函数柯里化、给元素伪数组添加事件需要使用元素的索引值。

    内存泄露

    • 造成内存泄露的原因:
      • 意外的全局变量(在函数内部没有使用var进行声明的变量)
      • console.log
      • 闭包
      • 对象的循环引用
      • 未清除的计时器
      • DOM泄露(获取到DOM节点之后,将DOM节点删除,但是没有手动释放变量,拿对应的DOM节点在变量中还可以访问到,就会造成泄露)
    • 解决方案:手动释放内存来避免内存泄露

    将多维数组转化为一维数组

    • ·通过apply拉平数组,但是只能拉平二维数组

    • let arr = [1, 2, [3, 4], 5];
      let result = Array.prototype.concat.apply([], arr);
      // [1, 2, 3, 4, 5]
      
    • 通过ES6新增的flat方法拉平数组,默认拉平二维数组,参数默认为1,可以写n,则拉平n+1维数组,对于未知维度的数组,可以通过Infinity关键字作为参数

    • let arr = [1, 2, [3, 4, [5, 6]], 7];
      let result = arr.flat();
      // [1, 2, 3, 4, [5, 6], 7];
      let result2 = arr.flat(2);
      // [1, 2, 3, 4, 5, 6, 7];
      let result3 = arr.flat(Infinity);
      
    • 通过递归拉平数组

    • let arr = [1, 2, [3, 4, [5, 6]], 7];
      function flatArray(arr) {
          let result = [];
          for(let i = 0; i < arr.length; i++) {
              if(Array.isArray(arr[i])) {
                  result = result.concat(arguments.callee(arr[i]));
              }else {
                  result.push(arr[i])
              }
          }
          return result;
      }
      flatArray(arr);
      // [1, 2, 3, 4, 5, 6, 7];
      

    JS继承的方法及优缺点

    1. 原型链继承

      • function Parent() {
            this.name = 'parent'
        }
        Parent.prototype.sayName = function() {
            alert(this.name)
        }
        
        function Child() {
            this.age = 20;
        }
        Child.prototype = new Parent(); // 改造子类构造函数的原型,使其指向父类的实例。
        let child = new Child();
        child.name; // parent
        child.sayName; // alert(parent);
        child.age;  // 20
        
      • 通过改造子类的原型prototype,使其指向父类的实例,达到继承父类所有属性和方法的效果。

      • 优点:父类的方法也可以达到复用

      • 缺点:父类的属性也达到了复用,子类实例没有属于自己的属性(如上例中的name属性)

    2. 构造函数借用继承

      • function Parent(name) {
            this.name = name;
        }
        Parent.prototype.sayName = function() {
            alert(this.name);
        }
        
        function Child(name, age) {
            Parent.call(this, name); // 通过call或者apply的特性实现将父类的属性复用
            this.age = age;
        }
        Child.prototype.sayAge = function() {
            alert(this.age);
        }
        
        let child = new Child('child', 24);
        child.name; // child
        child.age; // 24
        child.sayName(); // error
        child.sayAge(); // alert(child)
        
      • 通过call和apply改变this指向并且调用函数的特性,实现父类的属性复用。

      • 优点:可以达到父类的属性复用,并且可以定制子类自己的属性值。

      • 缺点:父类上的方法没有实现复用

    3. 组合继承

      • function Parent(name) {
            this.name = name;
        }
        Parent.prototype.sayName = function() {
            alert(this.name);
        }
        
        function Child(name, age) {
            Parent.call(this, name); // 通过call或者apply的特性实现将父类的属性复用
            this.age = age;
        }
        Child.prototype = new Parent(); // 改造子类构造函数的原型,使其指向父类的实例。
        Child.prototype.sayAge = function() {
            alert(this.age);
        }
        
        let child = new Child('child', 24);
        child.name; // child
        child.age; // 24
        child.sayName(); // alert(child)
        child.sayAge(); // alert(24)
        
      • 组合继承,其实就是将原型链继承和构造函数借用继承组合到一块,结合他们之间的优点,使得既可以继承父类的方法,又可以在子类上继承父类的属性并定制。

      • 优点:结合了原型链继承和构造函数借用继承的优点,既继承了父类的方法,又继承了父类的属性,并可以定制。

      • 缺点:父类的构造函数被调用了两次,在子类的构造函数内调用一次,在子类的prototype上调用一次,并且子类的prototype上的属性会被覆盖掉,造成内存浪费。

    4. 原型式继承

      • function Parent(name) {
            this.name = name;
        }
        Parent.prototype.sayName = function() {
            alert(this.name);
        }
        
        function createObject(obj) {
            function F() {};
            F.prototype = obj;
            return new F();
        }
        
        let parent = new Parent('parent');
        let child = createObject(parent);
        child.name; // parent;
        child.sayName(); // alert(parent)
        
      • 原型式继承是通过借助临时的构造函数,将原有的对象的属性和方法进行浅拷贝,然后赋予新的对象,实现继承。通过ES5新增的Objet.create也可以实现。

      • 优点:好像没什么优点

      • 缺点:缺点和原型链继承一样,只能实现属性和方法的复用,子类没有属于自己的属性,没有可定制性。

    5. 寄生式继承

      • function Parent(name) {
            this.name = name;
        }
        Parent.prototype.sayName = function() {
            alert(this.name);
        }
        
        function createObject(obj) {
            function F() {};
            F.prototype = obj;
            return new F();
        }
        
        function Child(name, age) {
            let parent = new Parent(name);
            let result = createObject(parent);
            result.age = age;
            return result;
        }
        
        let child = Child('child', 24);
        child.sayName(); // alert(child)
        
      • 寄生式继承是将原型式继承封装成为一个函数,并且进行参数传递,实现子类继承父类的属性和方法,并且可以定制属性。

      • 优点:子类可以继承父类的属性和方法,并且可以定制属性

      • 缺点:子类不是通过new关键字调用的,所以没有属于自己的方法,如果要创建子类的方法,那就是每一个子类都得重新创建一遍,不能达到子类的方法复用。

    6. 组合寄生式继承

      • function Parent(name) {
            this.name = name;
        }
        Parent.prototype.sayName = function() {
            alert(this.name);
        }
        
        function Child(name, age) {
            Parent.call(this, name);
            this.age = age;
        }
        Child.prototype = Object.create(Parent.prototype);
        Child.prototype.constructor = Child;
        Child.prototype.sayAge = function() {
            alert(this.age);
        }
        
        let child = new Child('child', 24);
        child.sayName(); // alert(child)
        child.sayAge(); // alert(24)
        
      • 组合寄生式继承,结合了组合继承的优点,并结合了寄生式继承的优点,目前为止完美的继承方式。

      • 优点:可以继承父类的属性和方法,并且可以定制,最主要的是对于父类的构造函数只需要调用一次。并且可以正常的使用intanceof和isPrototypeOf,原型链保持正常。

      • 缺点:好像没什么缺点


    结言
    感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照

    相关文章

      网友评论

        本文标题:前端常见面试题(二十二)@郝晨光

        本文链接:https://www.haomeiwen.com/subject/ggutsctx.html