前端知识点

更新于 2月27日  约 38 分钟

关于排序

  • js中sort函数的底层实现机制?

js中sort内置多种排序算法,是根据要排序数的乱序程度来决定使用哪一种排序方法。V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,长度小于20的使用InsertionSort(插入排序),大于20的数组则使用 QuickSort(快速排序)

快速排序

    function quickSort(arr){
        if(arr.length<=1){ //如果数组中只有一位数,返回数组
            return arr;
        }
        var mNumIndex = Math.floor(arr.length/2); //取基准值的下标
        var mNum = arr.splice(mNumIndex,1)[0];  //取基准值
        var left = [];  //左边数组 
        var right = []; //右边数组
        for(var i=0;i<arr.length;i++){
            if(arr[i]<mNum){  //如果数组小于基准值,放在左边数组
                left.push(arr[i]);
            }else{            ///否则
                right.push(arr[i]);
            }
        }        
        return quickSort(left).concat([mNum],quickSort(right));
        //返回左边数组+基准值+右边数组
    }

插入排序

function insertSort(arr) {
  let length = arr.length;
  for(let i = 1; i < length; i++) {
    let temp = arr[i];
    for(let j = i; j > 0; j--) {
      if(arr[j] >= arr[j-1]) {
        break;      // 当前考察的数大于前一个数,证明有序,退出循环
      }
      arr[j] = arr[j-1]; // 将前一个数复制到后一个数上
    }
    arr[j] = temp;  // 找到考察的数应处于的位置
  }
  return arr;
}

选择排序(实现思路跟冒泡排序差不多, 可以说是冒泡排序的衍生版本)

    function selectionSort(arr) {
      var len = arr.length
      var minIndex, temp
      for (i = 0; i < len - 1; i++) {
        minIndex = i
        for (j = i + 1; j < len; j++) {
          if (arr[j] < arr[minIndex]) {
            minIndex = j
          }
        }
        temp = arr[i]
        arr[i] = arr[minIndex]
        arr[minIndex] = temp
      }
      return arr
    }

数组去重

ES6

    function unique(arr) {
      return Array.from(new Set(arr))
    }
    new Set本身是一个构造函数,用来生成Set数据结构,类数组,成员的值都是唯一的,
    没有重复的值。
    Array.from()将一个类数组对象或者可遍历对象转换成一个真正的数组。

ES5


    function unique(arr) {
      var newArr = []
      for (var i = 0; i < arr.length; i++) {
        if (newArr.indexOf(arr[i]) === -1) {
          newArr.push(arr[i])
        }
      }
     return newArr
   }
    var result = [];    
    var obj = {};    
    for(var i =0; i<arr.length; i++){
       if(!obj[arr[i].key]){
          result.push(arr[i]);
          obj[arr[i].key] = true;
       }
    }

如何统计网页里出现多少种html标签

ES5

    var doms = document.getElementsByTagName('*')
    var obj = {}
    var ret = []
    for (let i = 0; i < doms.length; i++) {
      var name = doms[i].nodeName
      if(!obj[name]){
        ret.push(name)
        obj[name] = true
      }
    }
    console.log(ret.length)

ES6

   let names = [...document.getElementsByTagName('*')].map(v=>v.nodeName)
   console.log(new Set(names).size)

获取dom下的所有节点

    function getDom(dom) {
      var list = []
      var domChildren = dom.childNodes
      for (var i = 0; i < domChildren.length; i++) {
        // nodeType === 1 是元素节点,2是属性节点。
        if (domChildren[i].nodeType === 1) {
          list.push(domChildren[i])
          var retArr = getDom(domChildren[i])
          list = list.concat(retArr)
        }
      }
      return list
    }
    getDom(document.querySelector('body'))

实现bind方法:

Function.prototype.bind= function(obj){
    var _self = this, args = arguments;
    return function() {
        _self.apply(obj, Array.prototype.slice.call(args, 1));
    }
}

防抖功能函数,接受传参

定义:多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行。

原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。
    function debounce(fn) {
      // 4、创建一个标记用来存放定时器的返回值
      let timeout = null;
      return function() {
        // 5、每次当用户点击/输入的时候,把前一个定时器清除
        clearTimeout(timeout);
        // 6、然后创建一个新的 setTimeout,
        // 这样就能保证点击按钮后的 interval 间隔内
        // 如果用户还点击了的话,就不会执行 fn 函数
        var args = arguments;
        timeout = setTimeout(() => {
          fn.apply(this, args);
        }, 1000);
      };
    }
    sayDebounce(){
     console.log("防抖成功!");
    }
    btn.addEventListener("click", debounce(sayDebounce));
    

函数节流

定义:触发函数事件后,短时间间隔内无法连续调用,只有上一次函数执行后,过了规定的时间间隔,才能进行下一次的函数调用。

原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。

JavaScript中常用的设计模式

详细链接: https://juejin.im/post/5afe64...

  • 1.单例模式
特点:一个类只能构造出唯一实例 
案例:创建菜单对象或者弹出框
const singleton = function(name) {
  this.name = name
  this.instance = null
}

singleton.prototype.getName = function() {
  console.log(this.name)
}

singleton.getInstance = function(name) {
  if (!this.instance) { // 关键语句
    this.instance = new singleton(name)
  }
  return this.instance
}

// test
const a = singleton.getInstance('a') // 通过 getInstance 来获取实例
const b = singleton.getInstance('b')
console.log(a === b)
  • 2.工厂模式

创建一个对象常常需要复杂的过程,所以不适合在一个复杂的对象中。创建对象可能会导致大量的重复代码,也可能提供不了足够级别的抽象。工厂方法模式通过定义一个单独的创建对象的方法来解决这些问题,由子类实现这个方法来创建具体类型的对象。

function Animal(opts){
    var obj = new Object();
    obj.name = opts.name;
    obj.color = opts.color;
    obj.getInfo = function(){
        return '名称:'+obj.name +', 颜色:'+ obj.color;
    }
    return obj;
}
var cat = Animal({name: '波斯猫', color: '白色'});
cat.getInfo();
  • 3. 订阅/发布者模式

发布订阅模式,基于一个主题/事件通道,希望接收通知的对象(称为subscriber)通过自定义事件订阅主题,被激活事件的对象(称为publisher)通过发布主题事件的方式被通知。

就和用户订阅微信公众号道理一样,一个公众号可以被多个用户同时订阅,当公众号有新增内容时候,只要发布就好了,用户就能接收到最新的内容

订阅/发布者模式和观察者模式的区别:

在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。发布订阅模式相比观察者模式多了个事件通道,订阅者和发布者不是直接关联的。
1、Observer模式要求观察者必须订阅内容改变的事件,定义了一个一对多的依赖关系;
2、Publish/Subscribe模式使用了一个主题/事件通道,这个通道介于订阅着与发布者之间;
3、观察者模式里面观察者「被迫」执行内容改变事件(subject内容事件);发布/订阅模式中,订阅着可以自定义事件处理程序;
4、观察者模式两个对象之间有很强的依赖关系;发布/订阅模式两个对象之间的耦合读底

      function Public(){
        //存放订阅者信息
        this.subscribers = [];
        // 添加订阅者
        this.addSubscriber = function(subscriber) {
          //保证一个订阅者只能订阅一次
          let flag = this.subscribers.some(function(item){
            return item == subscriber;
          })
          if(!flag){
            this.subscribers.push(subscriber);
          }
          return this;
        }
        // 发布消息
        this.publish = function(data) {
          let arr = arguments; 
          this.subscribers.forEach(function(fn){
            fn(...arr)
          })
          return this;
        }
      }
      let publisher = new Public() //初始化
      let fanfan = function(data){
        console.log(`订阅者吴亦凡收到订阅信息:${[...arguments]}`)
      }
      let luhan = function(data){
        console.log(`订阅者鹿晗收到订阅信息:${[...arguments]}`)
      }
      publisher.addSubscriber(fanfan).addSubscriber(luhan); // 添加订阅者fanfan
      publisher.publish('NBA头条快讯', '蔡徐坤签约掘金');
  1. 3. 观察者模式

一个或多个观察者对目标的状态感兴趣,通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,会发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标状态感兴趣时,他们可以简单将自己从中分离。

首先是目标的构造函数,他有个数组,用于添加观察者。还有个广播方法,遍历观察者数组后调用他们的update方法:
class Subject{
  constructor(){
    this.subs = [];
  }
  addSub(sub){
    this.subs.push(sub);
  }
  notify(){
    this.subs.forEach(sub=> {
      sub.update();
    });
  }
}
那么观察者就得有个update方法:
class Observer{
  update(){
    console.log('update');
  }
}
let subject = new Subject();
let ob = new Observer();
//目标添加观察者了
subject.addSub(ob);
//目标发布消息调用观察者的更新方法了
subject.notify();   //update

4. 策略模式
特征:根据不同参数可以命中不同的策略;
案例:动画库里的算法函数
5. 代理模式
特征:代理对象和本体对象具有一致的接口;
案例:图片预加载
5. 迭代器模式
特征:能获取聚合对象的顺序和元素;
案例:each([1, 2, 3], cb);
5. 命令模式
特征:不同对象间约定好相应的接口;
案例:按钮和命令的分离;
6. 组合模式
特征:组合模式在对象间形成一致对待的树形结构;
案例: 扫描文件夹;
7. 组合模式
特征:组合模式在对象间形成一致对待的树形结构;
案例: 扫描文件夹;
6. 组合模式
特征:组合模式在对象间形成一致对

  1. 列表项目

待的树形结构;
案例: 扫描文件夹;

判断数组

Object.prototype.toString.call(arr) === "[object Array]" ;
Array.isArray(arr);
arr.constructor === Array;

This的指向

  1. 全局范围:指向winodow(严格模式下不存在全局变量,指向undedfiend);
  2. 普通函数调用:指向window
  3. 对象方法调用:指向最后调用它的对象
  4. 构造函数调用:指向new出来的对象
  5. 显示设置this: call apply 方法显示将this指向第一个参数指明的对象
  6. 箭头函数没有prototype(原型),所以箭头函数本身没有this, 箭头函数的this指向在定义的时候继承自外层第一个普通函数的this,跟使用位置没有关系。被继承的普通函数的this指向改变,箭头函数的this指向会跟着改变,不能直接修改箭头函数的this指向(call、aaply、bind)会默认忽略第一个参数,但是可以正常传参。

什么是原型链?

创建一个函数就会为其创建一个prototype属性,指向这个函数的原型对象,原型对象会自动获得constructor属性,指向prototype属性所在函数。

Function.prototype.a = "a";   
Object.prototype.b = "b";   
function Person(){}   
console.log(Person);    //function Person()   
let p = new Person();   
console.log(p);         //Person {} 对象   
console.log(p.a);       //undefined   
console.log(p.b);       //b
p.__proto__ === Person.prototype;Person.prototype.constructor === Person;

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1

new操作符具体干了什么呢?

  1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
  2. 属性和方法被加入到 this 引用的对象中。
  3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。

页面加载过程

  1. 浏览器根据 DNS 服务器得到域名的 IP 地址
  2. 向这个 IP 的机器发送 HTTP 请求
  3. 服务器收到、处理并返回 HTTP 请求
  4. 浏览器得到返回内容

Vue实现数据双向绑定的原理:Object.defineProperty()

vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。

<body>
    <div id="app">
    <input type="text" id="txt">
    <p id="show"></p>
</div>
</body>
<script type="text/javascript">
    var obj = {}
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj
        },
        set: function (newValue) {
            document.getElementById('txt').value = newValue
            document.getElementById('show').innerHTML = newValue
        }
    })
    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
    })
</script>

对vue的keep-alive 的了解

keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。

Vue.js的template编译的理解?

答:简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)

详情步骤:

首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

Vue.nextTick

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
原理:事件循环机制(Event Loop)
以下事件属于宏任务:

setInterval()
setTimeout()
以下事件属于微任务

new Promise()
new MutaionObserver()

  1. MutationObserver监听DOM更新完毕的。
  2. vue用异步队列的方式来控制DOM更新和nextTick回调先后执行
  3. microtask(微任务)因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
  4. 因为兼容性问题,vue不得不做了microtask向macrotask(Promise)的降级方案
  • vue2.5后改成setImmediate>MessageChannel>setTimeout.

VUE的watch与computed的区别

  • 触发条件不同

1.computed计算属性会依赖于使用它的data属性,只要是依赖的data属性值有变动,则自定义重新调用计算属性执行一次。
2.watch则是在监控的data属性值发生变动时,其会自动调用watch回调函数。

  • 应用场景不同

1.执行异步操作,开销较大的操作,避免堵塞主线程,使用watch。
2.简单且串行返回的,使用computed

请解释闭包

《JavaScript权威指南》中的概念:有权访问另一个作用域中变量的函数
闭包特性:可实现函数外访问函数内变量,外层变量可以不被垃圾回收机制回收。
缺点:

  1. 在闭包中使用this对象可能会导致一些问题,因为匿名函数的执行具有全局性,因此其this对象通常指window.
  2. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除.
function outer() {
  var a = 2;
  function inner() {
    console.log(a);
  }
  return inner;
}
var neal = outer();
neal();//2

JS数据类型

基础类型:Undefined, Null, Boolean, Number, String, Symbol(ES6新增,表示独一无二的值))一共6种
引用类型:Object

JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问 数据在栈内存中的存储与使用方式类似于数据结构中的堆栈数据结构,遵循后进先出的原则。

JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。
clipboard.png

数据结构:

数据结构就是关系,没错,就是数据元素相互之间存在的一种或多种特定关系的集合。
逻辑结构:是指数据对象中数据元素之间的相互关系,也是我们今后最需要关注和讨论的问题。
物理结构:是指数据的逻辑结构在计算机中的存储形式。

常见的数据结构:
数组,队列(queue),堆(heap),栈(stack),链表(linked list ),树(tree),图(graph)和散列表(hash)

参照:https://www.cnblogs.com/slly/...

  • 栈:

一种遵循后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都接近栈顶,旧元素都接近栈底。

  • 队列(queue):

是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。与栈相反,队列是一种先进先出(First In First Out, FIFO)的线性表。与栈相同的是,队列也是一种重要的线性结构,实现一个队列同样需要顺序表或链表作为基础。

  • 链表:

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本事的节点和一个指向下一个元素的引用组成。相对于传统的数组,链表的一个好处在于,添加或者删除元素的时候不需要移动其他元素。 使用链表结构可以克服数组需要预先知道数据大小的缺点(C语言的数组需要预先定义长度),链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。 数组和链表的一个不同在于数组可以直接访问任何位置的元素,而想要访问链表中的一个元素,需要从起点开始迭代列表。

click在ios上有300ms延迟,原因及如何解决?

  1. 粗暴型,禁用缩放
 <meta name="viewport" content="width=device-width, user-scalable=no"> 
  1. 利用FastClick,其原理是:
检测到touchend事件后,立刻出发模拟click事件,并且把浏览器300毫秒之后真正出发的事件给阻断掉

css盒模型

1.IE盒子模型(怪异盒子)box-sizing :border-box;
宽度=margin+width
2.W3C标准盒模型box-sizing : content-box;

宽度=margin+border+padding+width

选择器权重

!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符* > 继承 > 浏览器默认属性

有几种方法清楚浮动

1. 父元素 overflow: hidden;
2.浮动元素后面的元素: clear: both;
3.伪元素选择器:.clear::after{ content:’’;display:block; clear:both;};
4.父元素设置高度;
5.父级元素:display: table;

如何做水平垂直居中?

方案一:已知宽高,设置position: absolute;,然后left和top都设置为50%,再根据margin的负值来调正
方案二:类似方案一,最后一步用transform:translate(-50%,-50%);
方案三:绝对定位,top、bottom、left、right都设置为0,设置好宽高,然后margin: auto;
方案四:display:table-cell; + vertical-align:middle;
方案五:使用flex弹性盒模型

<div class=“box”>
    <div class=“item”></div>
</div>
box{
    display:flex;
    justifly-content:center;
    Align-item:center;
}
Box{
    Position:relative;
}
Item{
 Position:absolute;
 Top:50%;
 Transform:translateY(-50%);
}

选择器权重

!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符* > 继承 > 浏览器默认属性

webpack的一些原理和机制

webpack:把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载。

  1. 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
  2. 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
  3. 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
  4. 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
  5. 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
  6. 输出所有chunk到文件系统。

ES6模块与CommonJS模块的差异

  1. CommonJs 模块输出的是一个值的拷贝,ES6模块输出的是一个值的引用
  2. CommonJS 模块是运行时加载,ES6模块是编译时输出接口
  3. ES6输入的模块变量,只是一个符号链接,所以这个变量是只读的,对它进行重新赋值就会报错

模块加载AMD,CMD,CommonJS Modules/2.0 规范

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出(不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同))。
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

  1. 这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的
  2. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行
  3. CMD 推崇依赖就近,AMD 推崇依赖前置

继承方式

相关文案

  1. 原型链继承
  2. 借用构造函数 (类式继承)
  3. 组合式继承(原型+构造函数继承)
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承
父子类
function Animal(name) {
    this.name = name || "Mike";//实例属性
    this.love = function () {
      console.log(this.name + "爱吃骨头")//实例方法
    }
    this.other = [];//实例引用属性
  }
  Animal.prototype.sleep = function (place) {
    console.log(this.name + "在" + place + "睡觉")
  }
  • 原型链继承
  function Dog() {}
  Dog.prototype = new Animal();
  Dog.prototype.name = "Jack";
  var dog1 = new Dog("twoHa");
  var dog2 = new Dog("keJi");
  console.log(dog1.name);//Jack
  console.log(dog2.name);//Jack
  dog1.love();//Jack爱吃骨头
  dog2.love();//Jack爱吃骨头
  dog1.sleep("床上");//Jack在床上睡觉
  dog2.sleep("客厅");//Jack在客厅睡觉

优点:
 1. 实例是子类的实例,也是父类的实例
 2. 父类新增原型方法/原型属性,子类都能访问到
 3. 简单,易于实现

缺点:

 1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
 2. 无法实现多继承
 3. 来自原型对象的引用属性是所有实例共享的,改变实例会污染父类
 4. 创建子类实例时,无法向父类构造函数传参

XSS和CSRF

XSS跨站脚本攻击:
不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。

  • 编码:当恶意代码值被作为某一标签的内容显示:在不需要html输入的地方对html 标签及一些特殊字符( ” < > & 等等)做过滤,将其转化为不被浏览器解释执行的字符。
  • 在标签的 href、src 等属性中,包含 javascript: (伪协议)等可执行代码。
  • 过滤:移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除);
  • httpOnly: 在 cookie 中设置 HttpOnly 属性后,js脚本将无法读取到 cookie 信息。
  • jsonp引发的xss攻击
  • 就是前面说到的,Content-Type 不要乱用,严格按照标准协议来做。目前的框架默认肯定会检测一下内容类型,如果不是很必要,不要手动设置。因为有可能多转发几次 Content-Type 就被改了。
  • callback 做长度限制,这个比较 low。

检测 callback 里面的字符。一般 callback 里面都是字母和数字,别的符号都不能有。

  • callback 做一个编码,如果用 Go 语言做的话,如下。对所有 callback 都处理。callback = template.JSEscapeString(callback)
  • CSRF攻击:跨站请求伪造。

要完成一次CSRF攻击,受害者必须满足两个必要的条件:

  • 登录受信任网站A,并在本地生成Cookie。(如果用户没有登录网站A,那么网站B在诱导的时候,请求网站A的api接口时,会提示你登录)
  • 在不登出A的情况下,访问危险网站B(其实是利用了网站A的漏洞)。

温馨提示一下,cookie保证了用户可以处于登录状态,但网站B其实拿不到 cookie。
预防

  • Token 验证:(用的最多)

(1)服务器发送给客户端一个token;
(2)客户端提交的表单中带着这个token。
(3)如果这个 token 不合法,那么服务器拒绝这个请求。

  • 隐藏令牌:

把 token 隐藏在 http 的 head头中。
方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。

  • Referer 验证:Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。

XSS 与 CSRF异同

  • CSRF:需要用户先登录网站A,获取 cookie。
  • XSS:不需要登录。
  • CSRF:是利用网站A本身的漏洞,去请求网站A的api。
  • XSS:是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。

HTTP

HTTP是用于传输如HTML文件,图片文件,查询结果的应用层协议。它被设计于用于服务端和客户端之间的通信。

传统轮询(Traditional Polling)

当前Web应用中较常见的一种持续通信方式,通常采取 setInterval 或者 setTimeout 实现。例如如果我们想要定时获取并刷新页面上的数据,可以结合Ajax写出如下实现:

setInterval(function() {
    $.get("/path/to/server", function(data, status) {
        console.log(data);
    });
}, 10000);
缺陷:
程序在每次请求时都会新建一个HTTP请求,然而并不是每次都能返回所需的新数据。
当同时发起的请求达到一定数目时,会对服务器造成较大负担。
  • 长轮询(long poll)

客户端发送一个request后,服务器拿到这个连接,如果有消息,才返回response给客户端。没有消息,就一直不返回response。之后客户端再次发送request, 重复上次的动作。

http协议的特点是服务器不能主动联系客户端,只能由客户端发起。它的被动性预示了在完成双向通信时需要不停的连接或连接一直打开,这就需要服务器快速的处理速度或高并发的能力,是非常消耗资源的。

websocket 和http区别

 相同点:

  1. 都是应用层的协议
  2. 都是基于tcp,并且都是可靠的协议

不同点:

  1. websocket是持久连接的协议,而http是非持久连接的协议.
  2. websocket是双向通信协议,模拟socket协议,可以双向发送消息,而http是单向的.
  3. websocket的服务端可以主动向客服端发送信息,而http的服务端只有在客户端发起请求时才能发送数据,无法主动向客户端发送信息.

回流与重绘

  1. 当render tree(渲染树)中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
  2. 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

注意:回流必将引起重绘,而重绘不一定会引起回流。

    • 当页面布局和几何属性改变时就需要回流。下述情况会发生浏览器回流:
    1. 添加或者删除可见的DOM元素;
    2. 元素位置改变;
    3. 元素尺寸改变——边距、填充、边框、宽度和高度
    4. 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;
    5. 页面渲染初始化;
    6. 浏览器窗口尺寸改变——resize事件发生时;

    请详细说下你对vue生命周期的理解?

    总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后
    beforeCreate 创建前执行(vue实例的挂载元素$el和数据对象data都为undefined,还未初始化)

    1. created 完成创建 (完成了data数据初始化,el还未初始化)
    2. beforeMount 载入前(vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。)
    3. mounted 载入后html已经渲染(vue实例挂载完成,data.message成功渲染。)
    4. beforeUpdate 更新前状态(view层的数据变化前,不是data中的数据改变前)
    5. updated 更新状态后
    6. beforeDestroy 销毁前
    7. destroyed 销毁后 (在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在)

    说一下每一个阶段可以做的事情

    1. beforeCreate:可以在这里加一个loading事件,在加载实例时触发。
    2. created:初始化完成时的事件写这里,如果这里结束了loading事件,异步请求也在这里调用。
    3. mounted:挂在元素,获取到DOM节点
    4. updated:对数据进行处理的函数写这里。
    5. beforeDestroy:可以写一个确认停止事件的确认框。
    阅读 628更新于 2月27日

    推荐阅读
    目录