web小结

作者: 来了啊小老弟 | 来源:发表于2020-10-14 17:35 被阅读0次

    DOM操作部分

    DOM的数据结构是一种树

    attribute和property的区别

    节点的property

    const pList document.querySelectorAll('p')
    const p = pList[0]
    console.log(p.style.width) //获取样式
    p.style.width = '100px' //修改样式
    console.log(p.className) //获取class
    p.className = 'p1' //修改class
    

    节点的attribute

    const pList document.querySelectorAll('p')
    const p = pList[0]
    p.getAttribute('data-name')
    p.setAttribute('data-name','p1')
    p.getAttribute('style')
    p.setAttribute('style','font-size:50px;')
    

    区别

    property:修改对象属性,不会体现到html结构中(推荐,不会重新渲染DOM)
    attribute:修改html属性,会改变html结构。

    DOM性能

    1.DOM操作非常昂贵,应该避免频繁操作DOM

    2.对DOM查询做缓存

    3.将频繁操作改为一次性操作

    //不缓存DOM查询结果
    for(let i=0;i<document.getElementsByTagName('p').length;i++){}
    //缓存DOM查询结果
    const pList = document.getElementsByTagName('p')
    const length =  pList.length
    for(let i=0;i<length;i++){}
    
    //将频繁操作改为一次性操作
    const listNode = document.getrElementById('list')
    //创建一个文档片段,此时还没有插入到DOM树中
    const frag = document.createDocumentFragment()
    //执行插入
    for(let x = 0;x<10;x++){
      const li = document.createElement('li')
      li.innerHTML = 'list"+x
      frag.appendChild(li)
    }
    //都完成之后,再插入到DOM树中
    listNode.appendChild(frag)
    

    客户端存储

    cookie localstorage 和sessionstorage

    cookie

    H5之前用来客户端存储数据
    document.cookie = 'a:100'
    缺点:1.存储大小,最大4KB.
    2.http请求时需要发送到服务端,增加请求数据量
    3.只能用document.cookie = ’‘来修改,太过简陋

    localstorage 和sessionstorage

    1.H5新增,专门为了存储而设计,最大可存5M
    2.api简单易用:getItem,setItem
    3.不会随着http请求发送出去。

    区别

    localstorage数据会永远存储,除非代码或者手动删除
    sessionstorage数据只存储在当前会话,浏览器关闭则清空
    一般用localstorage会更多一些

    性能优化之防抖与节流

    防抖debounce

    监听一个input输入框的keyup,但是不能一松开键盘就触发事件,因为可能会频繁调用接口,应该松开若干毫秒后不再按下,才开始调用接口

    const input1 = document.getElementById('input')
    var timer = null
    input1.addEventListener('keyup',function(){
      //假如输入123,首先输入1的时候,time是null,触发定时器,
      // 500毫秒之后打印,但是在500毫秒之内再次输入了2,触发keyup,此时time!=null
      // 就清空了原来应该打印1的定时器,重新执行一个新的定时器打印12,关键在于清空原来的定时器
      if(timer){   
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        console.log(input1.value)
        timer = null
      }, 500);
    })
    // 封装一个方法
    function debounce(fn,delay = 500){
      // timer在闭包中
      let timer =null
      return function(){
        if(timer){   
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          fn()
          // fn.apply(this,arguments) 用这种更完美
          timer = null
        }, delay);
      }
    }
    input1.addEventListener('keyup',debounce(()=>{
      console.log(input1.value)
    },1000))
    

    节流throttle

    拖拽一个元素时,要随时拿到该元素被拖拽的位置,直接用drag事件,则会频繁触发,很容易导致卡顿。 节流:无论拖拽速度多快,都会每隔100ms触发一次

    const div1 = document.getElementById('div1')
    var timer = null
    div1.addEventListener('drag',function(e){
      //存在timer则说明前一次还没执行完,必须前一次执行完,才能执行下一次操作,确保规定时间只执行一次,
      // 和防抖的区别在于,防抖是清空原来执行新的,节流是执行原来的,正好相反
      if(timer){ 
        return
      }
      timer = setTimeout(() => {
        console.log(e.offsetX,e.offsetY)
        timer = null
      }, 500);
      
    })
    
    // 封装一个方法
    function throttle(fn,delay = 500){
      // timer在闭包中
      let timer =null
      return function(){
        if(timer){   
          return
        }
        timer = setTimeout(() => {
          console.log(this) //this是div1,箭头函数承接上文,就是return的方法
          fn.apply(this,arguments) //只是为了绑定事件的参数,fn.apply({},arguments)也可以起到效果
          timer = null
        }, delay);
      }
    }
    div1.addEventListener('drag',throttle((e)=>{
      console.log(this) //this是window 箭头函数承接上文,就是window
      console.log(e.offsetX,e.offsetY)
    },1000))
    div1.addEventListener('drag',throttle(function(e){
      console.log(this) //this是div
      console.log(e.offsetX,e.offsetY)
    },1000))
    

    WEB安全

    XSS跨站请求攻击

    XSRF跨站请求伪造

    1.XSS跨站请求攻击

    <body>
      <div>123<div>
      <script>alert(document.cookie)</script>
    </body>
    

    往网页中恶意插入代码,获取用户信息,这样很轻松就获取到了用户的cookie信息,这是很不安全的。

    预防

    替换特殊字符,如< 变为< >变成>
    <script>变为<script>直接显示,不会作为脚本执行
    也可以用xss插件 Npm install xss

    2.XSRF跨站请求伪造


    微信截图_20201014173257.png 微信截图_20201014173325.png
    微信截图_20201014173517.png

    箭头函数

    1.语法编写简洁,但是可读性不是很好。
    2.没有自己的this,继承上级的this。
    3.没有arguments
    4.没有prototype,也就没有Constructor,也就是不能被new.

    Flex布局

    一、基本概念

    采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。


    微信截图_20201015151059.png 微信截图_20201015151132.png 微信截图_20201015161833.png

    1C:对象以数字123做健名时,会覆盖原来的字符串'123'的健名
    2B:symbol是唯一属性
    3C:以对象为属性时,会转为字符串'[object,object]',多个对象健名重复,取最后一个。

    判断输入的是一个正确的URL

    let str = 'https://www.sdafsdfdsf.cn/index.html?name=admin&age=22#video'
    let reg = /(^(http|https|ftp):\/\/)?(([\w-]+\.)+[a-z0-9]+)((\/[^/?#]*)+)?(\?[^#]+)?(#.+)?$/i
    console.log(reg.exec(str))
    console.log(reg.test(str))
    

    正向预查和负向预查

    正向预查: ?= 表示一定要匹配, ?=[0-9] 必须匹配数字
    负向预查: ?! 表示不能匹配, ?![0-9] 后面不能是数字

    正则验证6-16位必须包含大写小写数字的字符串

    let reg = /(?!^[a-zA-Z]+$)(?!^[0-9]+$)(?!^[a-z0-9]+$)(?!^[A-Z0-9]+$)(^[a-zA-Z0-9]{6,16}$)/
    

    小括号代表一个组,一个小括号返回一个正则的结果true或者false.


    微信截图_20201015172125.png 微信截图_20201016103821.png

    答案:


    微信截图_20201016104121.png

    let obj = new Foo()时,调用了Foo(),当成普通函数执行,就改写了里面的Foo.a的指向,所以最后一个输出是1,new操作之后this就是obj,所以obj.a()输出2,私有属性有a(),就不去共有属性prototype上找了。

    手写一个new 函数

    function Dog(name){
      this.name = name
    }
    Dog.prototype.bark = function(){
      console.log('wangwang'+this.name)
    }
    function _new(fn, ...arg){
      // let obj ={}
      // obj.__proto__ = fn.prototype
      let obj = Object.create(fn.prototype) 
      // 上面两句等于Object.create(fn.prototype) 
      //Object.create(a对象) 等于创建一个空对象,并且让空对象作为a对象所属构造函数的实例,即obj.__proto__ =a对象
      fn.call(obj,...arg)
      return obj
    }
    var newDog = _new(Dog,'tom')
    newDog.bark()
    

    数据类型转换题

    a=?的情况下
    满足if(a==1&&a==2&&a==3){}

    a= {
      n:0,
      toString:function(){
        ++this.n
      }
    }
    

    非常巧妙,因为当比较的两边数据类型不一样时,都是先转化为数字,对象转换为数字之前,先调用toString()方法转换成字符串,再转换为数字。所以重写它的toString()方法,覆盖原型链上的toString(),连续调用就可以实现以上效果。
    再介绍两种方式:

    //1
    let a= [1,2,3]
    a.toString = a.shift
    //2
    Object.defineProperty(window,'a',{
      get:function(){
        this.value?this.value++:this.value = 1
        return this.value
      }
    })
    

    各位应该能看懂。

    再出一题

    微信截图_20201016164545.png

    接下来三大经典算法

    冒泡排序

     // 冒泡排序:当前项和后一项比较,如果当前项比后一项大,则让大的靠后
    let arr =[12,8,24,16,1,88,66,34,67,99,1]
     function getArr(arr){
      let count = arr.length
      for(let i = 0;i<count-1;i++){
        for(let j = 0;j<count-i-1;j++){
          if(arr[j]>arr[j+1]){
            [arr[j],arr[j+1]] = [arr[j+1],arr[j]]
          }
        }
      }
      return arr
     }
     getArr(arr)
    

    插入排序

    //插入排序:打扑克牌,抓一张牌,插到手里,进行排序的过程。
    function insert(arr){
      let count = arr.length
      let newArr = [arr[0]] // 存储第一张牌
      for(let i = 1;i<count;i++){ //依次抓牌 i从1开始,因为0已经存进去了
        let A = arr[i] //新抓的牌
         for(let j= newArr.length-1;j>=0;j--){
           let B = newArr[j] //每一次比较手里的牌
           if(A>B){
            newArr.push(A)
            // newArr.splice(j+1,0,A) //如果当前新牌比手里要比较的牌大,就插在后面
            break
           }
           if(j===0){
            newArr.unshift(A) //比到第一张了,就插在第一张
           }
         }
      }
      console.log(newArr)
      return newArr
    }
    insert(arr)
    

    快速排序

    let arr =[12,8,24,16,1,88,66,34,67,99,1]
     //快速排序,找到中间项,把他从原来的数组中移除,获取这一项的结果
     function quick(arg){
       //4.结束递归(ary种小于等于一项)
      if(arg.length<=1){
        return arg
      }
      //1.找到数组的中间项,在原有数据中删除
      let middleIndex =  Math.floor(arg.length/2)
      let middleValue =  arg.splice(middleIndex,1)[0]
      //2.准备左右两个数组,循环剩下数组中的每一项,小的放左大的方右
      let arrleft=[],arrRight = [];
      for(let i=0;i<arg.length;i++){
        if(arg[i]<middleValue){
          arrleft.push(arg[i])
        } else {
          arrRight.push(arg[i])
        }
      }
      //3 递归操作
      return quick(arrleft).concat(middleValue,quick(arrRight))
    
     }
     let a = quick(arr)
     console.log(a)
    

    计算斐波那契

    1。普通递归:代码优美逻辑清晰。但是有重复计算的问题,如:当n为5的时候要计算fibonacci(4) + fibonacci(3),当n为4的要计算fibonacci(3) + fibonacci(2) ,这时fibonacci(3)就是重复计算了。运行 fibonacci(50) 会出现浏览器假死现象,毕竟递归需要堆栈,数字过大内存不够。

    function fibonacci(n) {
        if (n == 1 || n == 2) {
            return 1
        };
        return fibonacci(n - 2) + fibonacci(n - 1);
    }
    fibonacci(30)
    

    2.方法二:改进递归-把前两位数字做成参数避免重复计算

    function fibonacci(n) {
        function fib(n, v1, v2) {
            if (n == 1)
                return v1;
            if (n == 2)
                return v2;
            else
                return fib(n - 1, v2, v1 + v2)
        }
        return fib(n, 1, 1)
    }
    fibonacci(30)
    

    3.普通for循环

    function fibonacci(n) {
        var n1 = 1, n2 = 1, sum;
        for (let i = 2; i < n; i++) {
            sum = n1 + n2
            n1 = n2
            n2 = sum
        }
        return sum
    }
    fibonacci(30)
    

    4.for循环+解构赋值

    var fibonacci = function (n) {
        let n1 = 1; n2 = 1;
        for (let i = 2; i < n; i++) {
            [n1, n2] = [n2, n1 + n2]
        }
        return n2
    }
    fibonacci(30)
    

    正向代理和反向代理

    QQ截图20201023162443.png

    相关文章

      网友评论

          本文标题:web小结

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