最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

必备

IT圈 admin 54浏览 0评论

必备

文章目录

  • 必备-17.突击
    • W3C
    • HTML/CSS
      • HTML5新增标签元素
      • css新增伪类/伪元素
    • JS
      • JS六座大山
      • 函数防抖节流(柯里化)
      • 手写call/apply/bind
      • 图片懒加载
      • 冒泡排序
      • 快速排序
      • 数组深浅拷贝
      • 对象深浅拷贝
      • ES6新增
      • 封装-判断标准普通对象
      • 封装-时间字符串格式化
    • vue
      • 路由懒加载
      • 动态路由表
      • vue.use()
    • DOM
      • 虚拟DOM
      • vue/react的DOM渲染
      • DOM DIFF
    • 跨域身份验证方法
    • axios
      • axios二次封装
    • http
      • http请求分类

必备-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指向windowif( !/^(object|function)$/.test(typeof obj)) Object(obj);//将不是对象类型的函数转为对象obj[Symbol("myfn")]=this;//将函数放到对象中,为了避免与原生方法属性名冲突,用Symbollet result=obj[Symbol("myfn")](...args);//这时的myfn里面的this指向objdelete 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指向windowif( !/^(object|function)$/.test(typeof obj)) Object(obj);//将不是对象类型的函数转为对象obj[Symbol("myfn")]=this;//将函数放到对象中,为了避免与原生方法属性名冲突,用Symbollet result=obj[Symbol("myfn")](...params);//这时的myfn里面的this指向objdelete 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

    • 借鉴地址:.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=[];
      //方法一:slicenewArr=oldArr.slice(0);
      //方法二:concatnewArr=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);//falseconsole.log(nobj.arr==oldObj.arr);//false//方式三:工作中使用lodashnpm 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指向的构造函数,否则返回falseCtor=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. 添加全局方法或 propertyVue.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="";//所有请求的前缀
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:只更新修改了的数据

必备

文章目录

  • 必备-17.突击
    • W3C
    • HTML/CSS
      • HTML5新增标签元素
      • css新增伪类/伪元素
    • JS
      • JS六座大山
      • 函数防抖节流(柯里化)
      • 手写call/apply/bind
      • 图片懒加载
      • 冒泡排序
      • 快速排序
      • 数组深浅拷贝
      • 对象深浅拷贝
      • ES6新增
      • 封装-判断标准普通对象
      • 封装-时间字符串格式化
    • vue
      • 路由懒加载
      • 动态路由表
      • vue.use()
    • DOM
      • 虚拟DOM
      • vue/react的DOM渲染
      • DOM DIFF
    • 跨域身份验证方法
    • axios
      • axios二次封装
    • http
      • http请求分类

必备-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指向windowif( !/^(object|function)$/.test(typeof obj)) Object(obj);//将不是对象类型的函数转为对象obj[Symbol("myfn")]=this;//将函数放到对象中,为了避免与原生方法属性名冲突,用Symbollet result=obj[Symbol("myfn")](...args);//这时的myfn里面的this指向objdelete 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指向windowif( !/^(object|function)$/.test(typeof obj)) Object(obj);//将不是对象类型的函数转为对象obj[Symbol("myfn")]=this;//将函数放到对象中,为了避免与原生方法属性名冲突,用Symbollet result=obj[Symbol("myfn")](...params);//这时的myfn里面的this指向objdelete 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

    • 借鉴地址:.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=[];
      //方法一:slicenewArr=oldArr.slice(0);
      //方法二:concatnewArr=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);//falseconsole.log(nobj.arr==oldObj.arr);//false//方式三:工作中使用lodashnpm 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指向的构造函数,否则返回falseCtor=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. 添加全局方法或 propertyVue.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="";//所有请求的前缀
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)

  1. 暂无评论