必备-17.突击

必备-17.突击

W3C

  • W3C:万维网联盟,制定了一系列标准。
  • 网页主要由三部分组成:结构、样式、行为
  • 对应的三方面的规范
    • 结构:HTML和XML
    • 样式:CSS
    • 行为:ECMAScript2015(6)

HTML/CSS

HTML5新增标签元素

  • 用于绘画的canvas元素
  • 用于媒介回放的videoaudio元素
  • 新的结构标签:header、section、footer、aside、nav、article
  • 新的表单控件:type=number、email、color、date、time、url、tel、calendar、range、search

css新增伪类/伪元素

  • :before:在元素内部最前添加内容
  • :after:在元素内部最后添加内容
  • li:first-child:第一个子li标签
  • li:last-child:最后一个子li标签
  • li:nth-child(n):匹配当前父标签中所有子标签排序的第n个标签,如果不是li则没有匹配的
  • li:nth-last-child(n):从后往前排序
  • li:nth-of-type(n):匹配当前父标签中同类型子标签排序的第n个标签
  • li:nth-last-of-type(n):从后往前排序
  • li:first-of-type
  • li:last-of-type
  • li:only-of-type
  • :first-line:获取文本第一行
  • :first-letter:获取文本第一个字
  • :empty:选择空标签
  • :focus:选择当前获取焦点的表单元素
  • :enabled:选择可用的表单元素
  • :disabled:选择禁用的表单元素
  • :checked:选择选中的单选框或复选框
  • :root:获取根元素元素

JS

JS六座大山

  • 堆栈内存:闭包作用域
  • 面向对象:原型原型链、this、继承、数据类型检测
  • DOM操作:DOM方法、DOM事件
  • Ajax:HTTP网络协议、跨域
  • 同步异步编程
  • ES6新规范

函数防抖节流(柯里化)

防抖和节流的区别:如果持续触发事件,防抖是最后触发事件n秒后才执行一次,节流的函数是每n秒后可执行一次

  • 防抖:防止"老年帕金森",如果事件在n秒内被触发了多次,就重新计时,事件绑定的函数只能在最后一次触发的n秒后执行

    • **思路:**每次触发函数之前,都清除之前的定时器,即使没有定时器,clearTimeout(timer)也不会报错,以input为例

        function debounce(fn,...args1) {//利用闭包保存timeout属性
          let timeout = null; // 创建一个标记用来存放定时器的返回值
          return function (...args2) {
            clearTimeout(timeout); // 清除之前的定时器,不管有没有
              let args=args2.concat(args1)
            timeout = setTimeout(()=>{ //必须用箭头函数,让函数内的this指向
                
              fn.apply(this, args);//修改执行函数的this指向,并且把参数全部传给它
            }, 500);
          };
        }
        function sayHi() {
          console.log('防抖成功');
        }
    
        var inp = document.getElementById('inp');
        inp.addEventListener('input', debounce(sayHi,1,2,3)); // 防抖
    
    
  • 节流:让高频率触发的事件,在n秒内只执行一次绑定的函数,节流的作用就是稀释高频事件绑定函数的执行次数:以window.onscroll为例

    • **思路:**每次触发事件时,都先判断当前函数有没有被锁住,如果有就不再执行,如果没有就执行,并在执行完成后解锁

    •         function throttle(fn,...args1) {//throttle的作用是形成闭包
                  let lock = true;//俗称节流锁,true表示可以执行绑定的函数,false表示不可以执行
                  return function (...args2) {
                      if (!lock) return;//timer=false时,表示上次触发事件的函数还没执行
                      lock = false;//走到这里,先锁上,不让0.5秒内多次执行函数
                      let args=args2.concat(args1)
                      setTimeout(() => {//必须用箭头函数,让this指向DOM元素
                          fn.apply(this, args);//修改fn中的this指向并传参
                      }, 500)
                  }
              }
              function sayHello() {
                  console.log("haha")
              }
             let input=document.getElementById("inp");
           input.addEventListener("input",throttle(sayHello,2,3,4))
          
      

手写call/apply/bind

  • 区别

    • call:让函数中的this指向我们指定的对象,并把后面的参数以散列的形式传给函数
    • apply:与call的区别就是,后面的参数是以数组的形式传给函数的
    • bind:生成一个改变了this指向的新函数,不立即执行,调用新函数才执行,底层用的call实现的
  • call

    • **思路:**利用obj.fn(),fn中的this指向obj的原理实现

    •  Function.prototype.mycall=function(obj,...args){
           if(obj) obj=window;//如果this是undefined或null,this指向window
           if( !/^(object|function)$/.test(typeof obj)) Object(obj);//将不是对象类型的函数转为对象
           
           obj[Symbol("myfn")]=this;//将函数放到对象中,为了避免与原生方法属性名冲突,用Symbol
           let result=obj[Symbol("myfn")](...args);//这时的myfn里面的this指向obj
           delete obj[Symbol("myfn")];//不留痕迹
           return result;
       
       }
        	//调用
        		let obj={}
        		fn.mycall(obj,1,2,3)
      
  • apply:

    • 思路:与call唯一的区别就是,传实参时用数组

    •  Function.prototype.myapply=function(obj,params){
           if(obj) obj=window;//如果this是undefined或null,this指向window
           if( !/^(object|function)$/.test(typeof obj)) Object(obj);//将不是对象类型的函数转为对象
           
           obj[Symbol("myfn")]=this;//将函数放到对象中,为了避免与原生方法属性名冲突,用Symbol
           let result=obj[Symbol("myfn")](...params);//这时的myfn里面的this指向obj
           delete obj[Symbol("myfn")];//不留痕迹
           return result;
       }
        	//调用
        		let obj={}
        		fn.mycall(obj,1,2,3)
      
  • bind:

    • **思路:**基于call实现,通过return返回一个新的函数

    • Function.prototype.mybind=function(obj,...args){
          return function(...arg){
              this.call(obj,...arg,...args);
          }
      }
      

图片懒加载

  • 原生js:使用原生的new IntersectionObserver()

    • **思路:**interstectionObserver()用来监控DOM元素与浏览器窗口的交集,通过isIntersecting属性来决定图片的加载

    • let observer = new IntersectionObserver(function(changes){//changes是监控的DOM节点集合
          
          changes.forEach((item,index)=>{
              if(item.isIntersectng){
                  //修改img的src路径,实现加载
                  item.src=item.getAttribute("index");
              	//删除多余的项
                  this.unobserve(item);
              }
          })
          
      }, options);//创建交叉监听器
      //监听img元素
      let target=Array.from(document.querySelectorAll("img")) 
      //循环添加监控
      target.forEach((item)=>{
        observer.observe(item);  
      })
      
      
  • vue的图片懒加载:vue有插件能够实现图片懒加载vue-lazyload

    • 借鉴地址:https://www.cnblogs.com/xieli26/p/10057763.html

    • **第一步:**安装插件

      • npm install vue-lazyload --save-dev
        
    • **第二步:**在main.js中引入使用

      • import VueLazyload from 'vue-lazyload'
        //直接使用:
        Vue.use(VueLazyload)
        
        //或者配置使用
        Vue.use(VueLazyload, {
        preLoad: 1.3,
        error: 'dist/error.png',
        loading: 'dist/loading.gif',
        attempt: 1
        })
        
    • **第三步:**修改img标签为懒加载(将 :src 属性直接改为v-lazy)

      • <a href="javascript:;"><img v-lazy="'/static/img/' + item.productImage"></a>
        

冒泡排序

  • 思路:两个for循环,内层的每次循环都会在右边产生一个最大的数

    • 注意:每次外层循环都会出现一个最大的,所以内层循环的长度应该-i次
  • 	let arr=[1,2,3,5,3,1,2,4]
    	
    	let bubbleSort=function bubbleSort(arr){
    		for(let i=0;i<arr.length-1;i++){
    			let midarr;
    			for(let j=0;j<arr.length-1-i;j++){
    				if(arr[j]>arr[j+1]) {
    				midarr=arr[j];
    				arr[j]=arr[j+1];
    				arr[j+1]=midarr;
    					
    				}
    			}
                
    		}
            return arr;
    	}
    
    

快速排序

  • **思路:**找到数组最中间一项,然后按这个值将数组拆分为两个大小数组,两个数组再近一次拆分,最终再拼接在一起

    •     const _quickSort = array => {
              if(array.length<=1){
                  return array
              }
              // 创建变量:获取中间值,并创建两个左右数组
              let newArr=array.slice(0);
              let mid=Math.floor(newArr.length/2)
              let leftArr=[];
              let rightArr=[];
              let midNum=newArr.splice(mid,1)[0];
              //分大小放到左右数组中
              for(let i=0;i<newArr.length;i++){
                  if(newArr[i]>midNum){
                      rightArr.push(newArr[i])
                  }else{
                      leftArr.push(newArr[i]);
                  }
              }
              //对分组的数组进行深层次排序
              let leftSort=_quickSort(leftArr);
              let rightSort=_quickSort(rightArr);
              //重新拼接数组
              return leftSort.concat(midNum,rightSort)
              
              
          }
      

数组深浅拷贝

  • 指向同一个引用地址的两个数组不叫拷贝

    • 效果:arr1===arr2
  • 浅拷贝生成新内存空间,只拷贝旧数组中的第一层,数组中如果用引用数据类型,会与旧数组中的引用类型同时指向一个内存空间,藕断丝连

    • 效果:arr1!=arr2,arr1.obj===arr2.obj

    • 思路:重点是生成新数组

    • let oldArr=[1,2,3,4,[5,6]],newArr=[];
      //方法一:slice
      	newArr=oldArr.slice(0);
      //方法二:concat
      	newArr=oldArr.concat([]);
      //方法三:展开运算符
      	newArr=[...oldArr]
      //方法四:for循环
      	oldArr.forEach((item)=>{
              newArr.push(item);
          })	
      
  • 深拷贝:生成的新数组里面,如果仍然有引用数据类型,对这一项再进一步拷贝,循环加递归

    • //:for循环加递归
      let oldArr=[1,2,3,4,[5,6]];
      
      //深克隆方法
      let deepClone=function(oldarr){
          let newArr=[]
          oldarr.forEach((item,index)=>{
            if(item!=null&&/^(object|function)$/.test(item)){
              newArr.push(deepClone(item));
          }        
          	newArr.push(item);
         })
         return newArr;
      }
      
      

对象深浅拷贝

  • 对象的拷贝与数组相似,但是由于对象不能遍历,所以实现的方法有些不同

  • 浅拷贝:与数组浅拷贝方式相似

    let oldObj={
        name:"lili",
        arr:[1,2]
    }
    //方式一:展开运算符
    	let newObj={...oldObj}
    
    //方式二:Object.assign()
        let newObj=Object.assign({},obj);
    
    
  • 深拷贝:可以用JSON的方法实现,但是有缺陷,所以我们一般还是用循环加递归

    • let oldObj={
          name:"lili",
          arr:[1,2]
      }
      //方式一:JSON方法实现(有缺陷)
      	//四大缺陷:
      		//遇到bigInt对象值会报错,
      		//遇到undefined/null/function的值会过滤掉
      		//遇到Date对象值会转为字符串
      		//遇到Error对象值会转为空对象
      	let newObj=JSON.parse(JSON.stringify(oldObj));
      //方式二:for循环加递归实现
              function deepClone(oldObj) {
                  let newObj;
                  //如果是数组就用数组的方式循环
                  if (Array.isArray(oldObj)) {
                      newObj = [];
                      oldObj.forEach((item) => {
                           newObj.push(deepClone(item));
                      })
                      return newObj;
                  }
                  //如果是对象,就用对象的方式循环
                  if (typeof oldObj == "object" && oldObj!= null) {
                      newObj = {};
                      //获取对象的所有可迭代和唯一的属性
            let keys = Object.getOwnPropertyNames(oldObj).concat(Object.getOwnPropertySymbols(oldObj));
                      keys.forEach((item) => {
                           newObj[item] = deepClone(oldObj[item]);//对每一项做深克隆检测
                      })
                      return newObj;
                  }
                  return oldObj;
              }
              // 
              let nobj=deepClone(oldObj);
              console.log(nobj);
              console.log(nobj==oldObj);//false
              console.log(nobj.arr==oldObj.arr);//false
      	
      
      
      //方式三:工作中使用lodash
      		npm i lodash --save//安装lodash包
              import _ from "lodash"//在main.js中导入
           	let newObj=_.cloneDeep(oldObj);//实现克隆
      

ES6新增

  • ES6(ECMAScript 2015):第六次修订
  • 新增元素
    • let/const:防止变量提升
    • 块儿作用域:只针对于ES6修饰符
    • 箭头函数:没有arguments、没有this
    • 模板字符串:代替普通字符串
    • 解构赋值:从数组中提取值变为局部变量
    • **for…of…😗*可以遍历数组、Set、Map、类数组对象、对象、字符串【原理:iterator迭代器】
    • 展开运算符:…arr
    • 剩余运算符:…args
    • class类的继承
    • async、await:Promise的语法糖
    • promise:解决回调炼狱的
    • Symbol:基本数据类型,唯一值
    • Proxy:新增的类,vue3用这个做的状态值监控加

封装-判断标准普通对象

  • 问题:document对象、

  • 思路:

    • 先用检测数据类型的方法判断结果是不是[Object object],如果不是,直接返回false
    • 进一步判断,这个对象的构造函数是不是function类型
    • 最后判断,这个对象的构造函数是不是Object
    • 全都符合,那它就是普通标准对象
let hasOwn=Object.prototype.hasOwnProperty;//获取检查私有属性方法
let isPlainObject=function isPlainObject(obj){
	let proto,Ctor;
    //如果obj是null或undefined,或用检测方法检查出来的不是object,则直接不是纯对象
	if(!obj||Object.prototype.toString.call(obj)!="[Object object]") return false;
    proto=Object.getPrototypeOf(obj);//获取obj的原型
    //如果有proto中有constructor就返回constructor指向的构造函数,否则返回false
    Ctor=hasOwn.call(proto,"constructor")&&proto.constructor;
	//如果Ctor是构造函数类型,并且指向Object类,则说明是普通对象     
    return typeof Ctor="function"&& Ctor=Object

}

封装-时间字符串格式化

  • 问题:工作中经常遇到获取的时间字符串不是我们想要的格式,所以需要一个格式化字符串的方法

  • 思路:传进去一个时间字符串,传进去我们想要的格式,最终返回对应格式的时间字符串

  • 	let time="2021-7-8" 
    let formatTime=function formatTime(oldTime,temp="{0}年{1}月{2}日"){
     	if(typeof oldTime!="string") throw new TypeError("转换的时间必须是字符串");
     	if(typeof temp!="string") throw new TypeError("模板必须是字符串类型");
         let arr=oldTime.match(/\d+/g);//["2021","7","8"]
        return temp.replace(/\{(\d+)\}/g,(_,$1){
            //_是被捕获的整个元素,$1是第一个小分组(\d+)捕获的值
              let item=arr[$1]||"00";
        		item=item.length<2?"0"+item:item;
        		return item;
           })
         
     }
    

vue

路由懒加载

//  import(路径)    :实现懒加载
// /*webpackChunkName:文件名*/ :分组打包
component:()=>import(/*webpackChunkName:ranking*/"@/views/findMusic/Ranking.vue")

动态路由表

  • 动态路由有两种方法:
    • 前端控制路由:前端把路由写好,用户登录的时候根据用户的角色权限来用v-if动态展示路由
    • **后端控制路由:**后台传来当前用户对应权限的路由表(JSON格式),我们前端根据JSON文件,转为路由表再实现动态渲染

vue.use()

  • vue.use和vue.prototype.$xxx的区别?

    • vue.use主要用于挂载Plugin插件,它的主要目的是实现Vue功能的扩展
    • vue.prototype.$xxx:是体现了vue框架渐进式的特点,我们初期不需要安装所有的组件,比如vuex,vue-router,或者自己封装的函数模块,而是当我们需要什么时,只需要插入到Vue类的原型上,就能供所有vue实例使用
  • vue.use()的作用?

    • 不是为vue写的插件不支持Vue.use()加载方法

    • 非vue官方库不支持new Vue()加载方法

    • Vue.use的作用就是加载哪些可供Vue使用的插件:

      • 这些供Vue使用的插件都需要有一个默认的方法MyPlugin.install()

        • Vue.use(MyPlugin, { someOption: true })//它会自动去找MyPlugin中的install方法
          
          //-----------------------------插件可基于install方法给Vue扩展属性方法
          MyPlugin.install = function (Vue, options) {//第一个Vue类,第二个是配置项
            // 1. 添加全局方法或 property
            Vue.myGlobalMethod = function () {
              // 逻辑...
            }
            // 2. 添加全局资源
            Vue.directive('my-directive', {
              bind (el, binding, vnode, oldVnode) {
                // 逻辑...
              }
              ...
            })
          
            // 3. 注入组件选项
            Vue.mixin({
              created: function () {
                // 逻辑...
              }
              ...
            })
          
            // 4. 添加实例方法
            Vue.prototype.$myMethod = function (methodOptions) {
              // 逻辑...
            }
          }
          
        • Vue.myGlobalMethod=fn:扩展全局方法或属性

        • Vue.directive():添加全局资源

        • Vue.mixin():注入组件选项

        • Vue.prototype.$myMethod():添加实例方法

DOM

虚拟DOM

  • 什么是虚拟DOM?为什么用虚拟DOM?
    • 虚拟DOM是faceBook提出的,解决真实DOM渲染带来性能问题的一种优化方案。
    • 我之前也对这个虚拟DOM也产生过好奇,所以专门去看过一些虚拟DOM的文章,那我就说说我对虚拟DOM的理解把:
    • 首先,我们知道,浏览器的内核是分为两部分:渲染引擎JS引擎,浏览器通过DPR渲染出标签节点构成的真实DOM树是放在渲染引擎里的,而JS引擎本身是不能直接操作DOM内存中的DOM树的,所以为了能让他操作,浏览器在JS全局内置window对象中封装了document对象,然后对该对象添加了大量DOM操作接口,这些接口都是由c++语言实现的。
      • 渲染引擎:取得网页的内容(HTML/XML/图像)、整理讯息(CSS),以及计算网页的显示方式,再输出到显示器。
      • ==JS引擎:==解析Javascript语言,执行javascript语言来实现网页的动态效果。
      • 在这里插入图片描述
    • 所以,js操作真实DOM,每次都需要经过webkit这个中介来实现,JS操作DOM的次数越多,所导致的性能消耗就会越大,这也是我们将减少JS操作DOM作为性能优化的重点的原因。(而且操作DOM会导致重排和重绘)
    • 虚拟DOM的出现,就是为减少JS操作DOM次数,(react和vue都利用了这个概念):它会根据js绑定的HTML节点去生成虚拟DOM对象(vnode),以后js对DOM的操作,实质上是操作这个对象的,当所有的操作处理完成之后,会生成一个新的虚拟DOM对象(new_vnode),再通过DOMDIFF算法,计算出差异,再通知webkit去渲染差异化的部分,这样才能够达到我们想要的性能优化效果
    • 这就是我对虚拟DOM的理解,可能我理解的某些地方还不是很透彻,也希望老师们能给我指出来。
    • JS操作真实DOM
      • 在这里插入图片描述
    • JS操作虚拟DOM:
      • 在这里插入图片描述
    • React/vue操作虚拟DOM:
      • 在这里插入图片描述

vue/react的DOM渲染

  • vue的渲染机制:视图层基于template实现
    • 第一步:把template模板编译为render函数
    • 第二步:把Vue实例挂载到指定节点
    • 第三步:基于vue-template-compiler模块实现模板解析[生成虚拟DOM(_vnode)]
    • 第四步:监听data的变化,如果变化了,触发render函数重新生成vnode节点
    • 第五步:两个vnode节点做DOM DIFF算法计算出差异,最后通知这是DOM去重新渲染组件(组件与组件之间的隔离性,保证了真实DOM的局部重排)
  • React的渲染机制:视图层基于jsx实现
    • **第一步:**通过babel-preset-react-app将jsx转换为React.createElement()表达式
    • 第二步:调用render()函数,将每个React.createElement表达式渲染成一个虚拟节点element
    • 第三步:众多element组成虚拟DOM
    • 第四步:ReactDOMComponent将众多element转换为真实节点
    • 第五步:当数据发生改变时,触发render()函数生成新的虚拟DOM,通过DOMDIFF算法,算出差异化,再渲染需要更新的真实DOM

DOM DIFF

  • 两个节点做精细化比较,计算出不同的每小部分,再去渲染这些不同的小部分,其他相同的地方无需修改,实现最大可能的性能优化

跨域身份验证方法

  • 登录态校验:

    • **思路:**利用cookie获取用户登录状态

      • 第一步:客户端发送post请求让服务器做账号密码验证,如果服务器返回成功,客户端在本地cookie中手动设置:isLogin=true,之后再登录时,直接判断是否登陆即可
    • **缺点:**cookie很容易被修改,不安全

  • ==cookie验证:==利用cookie与服务器端之间的关系

    • **思路:**客户端发送请求,服务器端做账号密码校验,验证成功之后,在服务器的session池中存储一份connect.sid,并通过set-cookie:connect.sid传给客户端,客户端收到后直接放到浏览器cookie中,之后再访问页面时,客户端会默认向服务器端发送对应cookie,服务器会做校验,再返回是否可访问。
    • **缺点:**仍然可以被伪装获取到,不安全、多服务器的情况下不方便使用
  • token验证:(目前最流行的)

    • 客户端向服务器发送登录请求,服务器进行校验,如果成功,服务器基于JWT(json web token)算法生成一个Token信息【含有密钥、登陆者、过期时间等】,生成之后传给客户端,客户端可以存储到本地(localStorage/sessionStorage…),之后每次访问页面时,都基于axios的请求拦截器发送本地存储的token信息,服务器拿到之后做反解析,然后再去验证它的有效性

    • 案例:

      • 请求加载最新供应链消息

        客户端:请求接口+token

        服务器:验证是否能通过token找到用户,若不能——该token不正确

        验证token是否失效,若失效——凭证已失效
        到权限表查询是否在权限内,若没有——该用户未分配资源

axios

axios二次封装

import axios from "axios";
let default=axios.defaults;
default.baseUrl="http://www.baidu.com";//所有请求的前缀
default.timeout=1000;//请求超时时间
default.withCrident=true;//跨域是否携带资源凭证
default.transformRequest=data=>{//转换post请求参数的格式
	return data;
}

axios.interceptors.request.use((config)=>{},(reason)=>{})//config:配置的请求头信息
axios.interceptors.response.use((response)=>{},(reason)=>{})//response:响应主体信息

http

http请求分类

  • http分为两大类:getpost
    • get:
      • get:获取
      • /head:只获取响应头
      • /delete:删除
      • /opations:询问支持的方式
    • post:
      • /post:发送数据
      • /put:对全部数据更新
      • /patch:只更新修改了的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值