[{"content":"跳转到脚本列表🚀\n什么是书签栏的脚本？ 用过油猴脚本自然不会对书签栏脚本感到陌生，与油猴不同的是，书签栏脚本的触发方式是点击书签，而不会自动触发。以下是演示GIF：\n\n使用方式 以Chrome浏览器为例，第一步：右键书签栏，选择添加网页\n\n第二步：在名称处填写脚本名称，网址处填写脚本，最后点击保存即可！\n\n脚本分享 1.页面自动刷新 功能： 点击之后输入秒数t（未作限制，请输入数字），页面会自动进行 t 秒一次的刷新，无限循环；如果需要停止，再次单击，点击取消或者输入0即可刷新一次自动取消。\njavascript: var t=prompt(\u0026#34;Time(s)=\u0026#34;); var current = location.href; if (t) {setTimeout(\u0026#39;reload()\u0026#39;, 1000 * t); function reload() { setTimeout(\u0026#39;reload()\u0026#39;, 1000 * t); var fr4me = \u0026#39;\u0026lt;frameset cols=\\\u0026#39;*\\\u0026#39;\u0026gt;\\n\u0026lt;frame src=\\\u0026#39;\u0026#39; + current + \u0026#39;\\\u0026#39; /\u0026gt;\u0026#39;; fr4me += \u0026#39;\u0026lt;/frameset\u0026gt;\u0026#39;; with (document) { write(fr4me); void (close()); }; };}else{location.replace(current);}; 2. Video自动播放 功能：点击之后输入速度t即可实现视频倍速播放，点击取消或者输入0即可恢复1倍速播放。\njavascript: var t=prompt(\u0026#34;speed = \u0026#34;); if(t){document.getElementsByTagName(\u0026#34;video\u0026#34;)[0].playbackRate = t*1;}else{document.getElementsByTagName(\u0026#34;video\u0026#34;)[0].playbackRate = 1;}; ","permalink":"https://blog.archai.space/p/%E5%88%86%E4%BA%AB%E4%B9%A6%E7%AD%BE%E6%A0%8F%E7%9A%84%E8%84%9A%E6%9C%AC/","summary":"跳转到脚本列表🚀\n什么是书签栏的脚本？ 用过油猴脚本自然不会对书签栏脚本感到陌生，与油猴不同的是，书签栏脚本的触发方式是点击书签，而不会自动触发。以下是演示GIF：\n\n使用方式 以Chrome浏览器为例，第一步：右键书签栏，选择添加网页\n\n第二步：在名称处填写脚本名称，网址处填写脚本，最后点击保存即可！\n\n脚本分享 1.页面自动刷新 功能： 点击之后输入秒数t（未作限制，请输入数字），页面会自动进行 t 秒一次的刷新，无限循环；如果需要停止，再次单击，点击取消或者输入0即可刷新一次自动取消。\njavascript: var t=prompt(\u0026#34;Time(s)=\u0026#34;); var current = location.href; if (t) {setTimeout(\u0026#39;reload()\u0026#39;, 1000 * t); function reload() { setTimeout(\u0026#39;reload()\u0026#39;, 1000 * t); var fr4me = \u0026#39;\u0026lt;frameset cols=\\\u0026#39;*\\\u0026#39;\u0026gt;\\n\u0026lt;frame src=\\\u0026#39;\u0026#39; + current + \u0026#39;\\\u0026#39; /\u0026gt;\u0026#39;; fr4me += \u0026#39;\u0026lt;/frameset\u0026gt;\u0026#39;; with (document) { write(fr4me); void (close()); }; };}else{location.replace(current);}; 2. Video自动播放 功能：点击之后输入速度t即可实现视频倍速播放，点击取消或者输入0即可恢复1倍速播放。\njavascript: var t=prompt(\u0026#34;speed = \u0026#34;); if(t){document.getElementsByTagName(\u0026#34;video\u0026#34;)[0].playbackRate = t*1;}else{document.getElementsByTagName(\u0026#34;video\u0026#34;)[0].playbackRate = 1;}; ","title":"分享书签栏的脚本"},{"content":"关于Vue和VueComponent   一个重要的内置关系:VueComponent.prototype.proto===vue.prototype\n\n  为什么要有这个关系？\n让组件实例对象(vc)可以访间到Vue原型上的属性、方法.\n  ref属性 1.被用来给元素或子组件注册引用信息(id的替代者)\n2.应用在htm1标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)\n3.使用方式: 打标识: \u0026lt;h1 ref=\u0026quot;xxx\u0026quot;\u0026gt;.....\u0026lt;/h1\u0026gt;或\u0026lt;School ref=\u0026quot;xxx\u0026quot;\u0026gt;\u0026lt;/School\u0026gt; 获取:this.$refs.xxx\n配置项props 功能: 让组件接收外部传过来的数据\n(1).传递数据:\n\u0026lt;Demo name=\u0026quot;xxx\u0026quot;/\u0026gt;\n(2).接收数据:\n第一种方式(只接收):\nprops:['name']\n第二种方式(限制类型):\nprops:{ name:String }\n第三种方式(限制类型、限制必要性、指定默认值):\nprops:{ name:{ type:String,//类型限制  required:true,//必要性  default:\u0026#34;JQF\u0026#34;//默认值  } } 备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告, 若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据.\nmixin(混入) 功能: 可以把多个组件共用的配置提取成一个混入对象\n使用方式:\n第一步：定义混合\nconst mixData={ data(){...} }, const mixMethods= { methods:{...} } 第二步：使用混入\n(1).全局混入:Vue.mixin(xxx)\n(2).局部混入:mixins:[xxx']\n插件 功能:用于增强vue\n本质:包含install方法的一个对象,install的第一个参数是vue,第二个以后的参数是插件使用者传递的数据。\n定义插件:\n对象.install = function (Vue, options) { //1.添加全局过滤器  Vue.filter(..) //2.添加全局指令  Vue.directive(....) //3.配置全局混入(合)  Vue.mixin(....) //4.添加实例方法  Vue.prototype.$myMethod = function () {...} Vue.prototype.$myProperty = xxxx } 使用插件:Vue.use()\nscoped样式 作用:让样式在局部生效,防止冲突.\n写法:\u0026lt;style scoped\u0026gt;\n此外，还可以通过lang指定css的语言，比如 css(默认),sass,less(推荐)等，但要注意用npm安装对应解析器\n总结TodoList案例  仅通过属性和props在组件间传递数据\n \n  组件化编码流:\n 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突. 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一 些组件在用:   一个组件在用:放在组件自身即可. 一些组件在用:放在他们共同的父组件上(状态提升). 实现交互:从绑定事件开始.    props适用于:\n 父组件 ==\u0026gt; 子组件通信 子组件 ==\u0026gt; 父组件通信(要求父先给子一个函数)    使用v-model时要切记: v-models绑定的值不能是props传过来的值,因为props是不可以修改的!\n  props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做.\n  组件的自定义事件   一种组件间通信方式，适用于： 子组件===\u0026gt;父组件\n  使用场景：A是父组件，B是子组件，B想给A传数据，那么就要在A中给B绑定自定义事件，B中通过$emit出发自定义事件并传递数据（事件的回调在A中）\n  具体使用方式：\n  绑定自定义事件：\n  第一种方式：在父组件中：\u0026lt;Demo @jqf=\u0026quot;test\u0026quot;/\u0026gt;或\u0026lt;Demo v-on:jqf=\u0026quot;test\u0026quot;/\u0026gt;\n  第二种方式，在父组件中：\n\u0026lt;Demo ref=\u0026#34;demo\u0026#34;/\u0026gt; ...... methods:{ test(){//do something} }, mounted(){ this.$refs.demo.$on(\u0026#39;jqf\u0026#39;,this.test) }   如果想让自定义事件只触发一次，可以通过使用once修饰符(第一种方式)，或者$once方法(第二种方式)\n    触发自定义事件：this.$emit('jqf',data...)\n  解绑自定义事件：this.$off('jqf')\n    组件上也可以绑定原生DOM事件，需要使用native修饰符(否则会默认为自定义事件)\n  注意，通过this.$refs.demo.$on('jqf',回调) 绑定自定义事件时，回调要么配置在methods中，要么使用箭头函数，否则this的指向会出现问题！\n  全局事件总线(GlobalEventBus)   一种组件间通信的方式，适用于任意组件间通信🧐\n  具体使用步骤：\n  安装全局事件总线\nnew Vue({ ...... beforeCreate(){ Vue.prototype.$bus=this//全局事件总线，$bus就是当前应用的vm \t}， ...... })   使用事件总线：\n  接收数据：A组件想接收数据，则在A组件中给$bus绑定自定义事件，事件的回调留在A组件自身。\nmethods(){ demo(data){......} } ...... mounted(){ this.$bus.$on(\u0026#39;xxx\u0026#39;,this.demo) }   提供数据：传递数据给指定指令\nthis.$bus.$emit(\u0026#39;xxx\u0026#39;,数据)       图示\n\n  注意点\n最好在beforeDestory钩子中用$off('xxx')解绑当前组件所用到的事件。\n父子之间互相通信还是用上面的方法！\n  消息订阅与发布(pubsub)   一种组件间通信的方式，适用于任意组件间通信。\n  使用步骤：\n  安装pubsub: npm i pubsub-js\n  引入pubsub:import pubsub from 'pubsub-js'\n  接收数据：A组件想接收数据，则在A组件中订阅消息，订阅的回调留在A组件自身。\n...... methods: { demo(msgName, data) {...} }, ...... mounted() { this.pubId = pubsub.subscribe(\u0026#39;xxx\u0026#39;, this.demo)//订阅消息  }, ...... beforeDestroy() { pubsub.unsubscribe(this.pubId) },   提供数据：pubsub.publish('schoolNameTrans',this.name)\n    注意点\n在beforeDestroy 钩子中，用unsubscribe(pubId) 取消订阅消息\n在Vue中，其实用全局事件总线更方便点🤓\n  nextTick  语法：this.$nextTick(回调函数) 作用：在下一次DOM更新结束后执行其回调 什么时候用：当改变数据后，要基于更新后的新DOM进行某些操作时，要在nextTick所指定的回调函数中执行。   在修改数据后不会立马重新解析模板，而是在本次所有语句执行完后再进行模板解析，所以有时候在更新数据后立即操作DOM（比如获取焦点操作），会不起作用，这时候就需要用到nextTick，或者setTimeout也可以\n Vue封装的过渡与动画   作用：在插入、更新或移除DOM元素时，在合适的时候给元素添加样式类名。\n  图示：\n\n 具体用法\n  第一种方式：自定义过渡或者样式：\n  准备好样式(CSS)：(默认为v-)\n 进入：[name]-enter,[name]-enter-active,[name]-enter-to 离开：[name]-leave,[name]-leave-active,[name]-leave-to    使用\u0026lt;transition\u0026gt;或\u0026lt;trasition-group\u0026gt;包裹过渡的元素，并设置name属性：\n\u0026lt;transition name=\u0026#34;hello\u0026#34;\u0026gt; \u0026lt;h1\u0026gt;HELLO\u0026lt;/h1\u0026gt; \u0026lt;/transition\u0026gt; \u0026lt;transition-group\u0026gt; \u0026lt;span key=\u0026#34;1\u0026#34;\u0026gt;1\u0026lt;/span\u0026gt; \u0026lt;span key=\u0026#34;2\u0026#34;\u0026gt;2\u0026lt;/span\u0026gt; \u0026lt;span key=\u0026#34;3\u0026#34;\u0026gt;3\u0026lt;/span\u0026gt; \u0026lt;/transition-group\u0026gt;     第二种方式：使用第三方动画库，比如Animate.css\n  安装并且引入\n#安装 npm i animate.css //引入 import \u0026#39;animate.css\u0026#39;   给\u0026lt;transition\u0026gt;或\u0026lt;trasition-group\u0026gt;添加类名animate__animated animate__bounce\n  添加相应动画，直接在\u0026lt;transition\u0026gt;或\u0026lt;trasition-group\u0026gt;上添加相应属性\n\u0026lt;transition-group appear class=\u0026#34;animate__animated animate__bounce\u0026#34; enter-active-class=\u0026#34;animate__bounceIn\u0026#34; leave-active-class=\u0026#34;animate__bounceOutLeft\u0026#34; \u0026gt; \u0026lt;h2 v-show=\u0026#34;a\u0026#34; key=\u0026#34;1\u0026#34;\u0026gt;嘿嘿额\u0026lt;/h2\u0026gt; \u0026lt;h2 v-show=\u0026#34;a\u0026#34; key=\u0026#34;5\u0026#34;\u0026gt;嘿嘿额\u0026lt;/h2\u0026gt; \u0026lt;/transition-group\u0026gt; 其中appear是让动画在初始进入的时候就生效\n        Vue脚手架配置代理 官网GO\n方式一 ​\t在vue.config.js中添加如下配置：\ndevServer:{ proxy:\u0026#34;http://localhost:5000\u0026#34; } 说明：\n 优点：配置简单，请求资源时直接发给前端（8080）即可。 缺点：不能配置多个代理，不能灵活的控制请求是否走代理。 工作方式：若按照上述配置代理，当请求了前端不存在的资源时，那么该请求会转发给服务器（优先匹配前端资源）  方式二 在Vue.config.js配置具体的代理规则\nconst { defineConfig } = require(\u0026#39;@vue/cli-service\u0026#39;) module.exports = defineConfig({ ...... devServer:{ proxy:{ //匹配所有以\u0026#39;/api1\u0026#39;开头的请求路径  \u0026#39;api1\u0026#39;:{ target:\u0026#39;http://localhost:5000/\u0026#39;,//代理目标的基础路径  changeOrigin:true, pathRewrite:{\u0026#39;^/api1\u0026#39;:\u0026#39;\u0026#39;}//请求路径重写  }, //匹配所有以\u0026#39;/api2\u0026#39;开头的请求路径  \u0026#39;api2\u0026#39;:{ target:\u0026#39;http://localhost:5001/\u0026#39;, changeOrigin:true, pathRewrite:{\u0026#39;^/api2\u0026#39;:\u0026#39;\u0026#39;} } } } }) // changeOrigin设置为true时,服务器收到的请求头中的host为:1oca1host:5000 // changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080 // changeOrigin默认值为true  说明：\n 优点：可以配置多个代理，且可以灵活的控制请求是否走代理 缺点：配置略微繁琐，请求资源时必须加前缀。  插槽   作用：让父组件可以向子组件指定位置插入htm结构，也是一种组件间通信的方式(这里通信的数据是html结构)，适用于父组件===\u0026gt;子组件。\n  分类：默认插槽、具名插槽、作用域插槽\n  具体使用方式：\n  默认插槽：\n\u0026lt;!--父组件中：--\u0026gt; \u0026lt;itemList\u0026gt; \u0026lt;span\u0026gt;我是插槽内容\u0026lt;/span\u0026gt; \u0026lt;/itemList\u0026gt; \u0026lt;!--子组件中：--\u0026gt; \u0026lt;slot\u0026gt;插槽中默认的内容，没有内容时显示\u0026lt;/slot\u0026gt;   具名插槽：\n\u0026lt;!--父组件中：--\u0026gt; \u0026lt;itemList\u0026gt; \u0026lt;span slot=\u0026#34;center\u0026#34;\u0026gt;我是中间\u0026lt;/span\u0026gt; \u0026lt;!--Vue2.6+新出的属性--\u0026gt; \u0026lt;template v-slot:footer\u0026gt; \u0026lt;h4\u0026gt;我是底部\u0026lt;/h4\u0026gt; \u0026lt;span\u0026gt;Lorem ipsum dolor sit amet...\u0026lt;/span\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;/itemList\u0026gt; \u0026lt;!--子组件中：--\u0026gt; \u0026lt;slot name=\u0026#34;center\u0026#34;/\u0026gt; \u0026lt;slot name=\u0026#34;footer\u0026#34;/\u0026gt;   作用域插槽🧐\n理解：数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在itemList组件中,但使用数据所遍历出来的结构由App组件决定)，其实实现了数据的 子组件===\u0026gt;父组件\n//父组件中(App)： \u0026lt;itemList\u0026gt; \u0026lt;template scope=\u0026#34;dataObj\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;无序列表\u0026lt;/h3\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li v-for=\u0026#34;(game,ind) in dataObj.games\u0026#34; :key=\u0026#34;ind\u0026#34;\u0026gt;{{ game }}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;br\u0026gt; \u0026lt;h3\u0026gt;有序列表\u0026lt;/h3\u0026gt; \u0026lt;ol\u0026gt; \u0026lt;li v-for=\u0026#34;(game,ind) in dataObj.games\u0026#34; :key=\u0026#34;ind\u0026#34;\u0026gt;{{ game }}\u0026lt;/li\u0026gt; \u0026lt;/ol\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;/itemList\u0026gt; //子组件(itemList)中： \u0026lt;slot :games=\u0026#34;games\u0026#34;\u0026gt;默认内容\u0026lt;/slot\u0026gt; ...... data() { return { games: [\u0026#34;红色警戒Ⅱ共和国之辉\u0026#34;, \u0026#34;红色警戒Ⅱ尤里的复仇\u0026#34;, \u0026#34;CS1.6\u0026#34;, \u0026#34;流星蝴蝶剑\u0026#34;] } } ......     Vuex 1. 概念： 在Vue中实现集中式态（数据）管理的一个Vue插件，对vue应用中多个组件的共享状态进行集中式的管理（读/写），也是一种组件间通信的方式，且适用于任意组件间通信。\n2. 何时使用： 多个组件需要共享数据时\n3.原理图： \n4. 搭建Vuex环境   创建文件：src/store/index.js(官方推荐方式)\n//引入Vue核心库 import Vue from \u0026#39;vue\u0026#39; //引入Vuex import Vuex from \u0026#34;vuex\u0026#34;; //应用Vuex插件 Vue.use(Vuex) //准备actions对象，相应组件中用户的动作 const actions = {} //准备mutations对象，修改state中的数据 const mutations = {} //准备state对象，保存具体数据 const state = {} //创建并暴露store export default new Vuex.Store({ actions,//对象中的简写形式  mutations, state })   在main.js中创建vm时传入store配置项\n...... import store from \u0026#39;./store\u0026#39; ...... new Vue({ render: h =\u0026gt; h(App), store//对象中的简写形式 }).$mount(\u0026#39;#app\u0026#39;)   5. 基本使用   初始化数据、配置actions、配置mutations，操作文件store.js\n//引入Vue核心库  import Vue from \u0026#39;vue\u0026#39; //引入Vuex  import Vuex from \u0026#34;vuex\u0026#34;; //应用Vuex插件  Vue.use(Vuex) //准备actions对象，相应组件中用户的动作  const actions = { //可以进行复杂逻辑处理  incOdd(context, value) { if (context.state.sum % 2) { context.commit(\u0026#39;INC\u0026#39;, value) } }, } //准备mutations对象，修改state中的数据  const mutations = { //mutations中的方法应该大写更规范  INC(state, value) { state.sum += value }, } //准备state对象，保存具体数据  const state = { sum: 1 } //创建并暴露store  export default new Vuex.Store({ actions, mutations, state })   组件中读取vuex中的数据：$store.state.sum\n  组件中修改vuex中的数据：$store.dispatch('actions中的方法名',数据)或$store.commit('mutations中的方法名',数据)\n   备注：若没有网络请求或其他业务逻辑，组件中也可以越过actions，即不写dispatch，直接编写commit\n 6. getters的使用   概念：当state中的元数据需要复杂的逻辑处理之后再暴露出去共享时，使用getters加工。\n  在store.js中追加getters配置\nconst getters = { handleSum(state){ return state.sum * 10//...模拟复杂的逻辑  } } //创建并暴露store export default new Vuex.Store({ ...... getters })   组件中读取数据：$store.getters.handleSum\n  7. 三个map方法的使用  先引入：import {mapState, mapActions, mapMutations, mapGetters} from 'vuex'\n   mapState：映射在state中的数据为计算属性。\ncomputed:{ //原始写法 /*sum(){ return this.$store.state.sum }, name(){ return this.$store.state.name }, address(){ return this.$store.state.address },*/ //借助mapState生成计算属性：sum、name、address（对象写法）  ...mapState({sum: \u0026#39;sum\u0026#39;,name:\u0026#39;name\u0026#39;,address:\u0026#39;address\u0026#39;}), //借助mapState生成计算属性：sum、name、address（数组写法）  ...mapState([\u0026#39;sum\u0026#39;,\u0026#39;name\u0026#39;,\u0026#39;address\u0026#39;]) }   mapGetters：映射在getters中的数据为计算属性。\ncomputed:{ //借助mapGetters生成计算属性：handleSum（对象写法）  ...mapGetters({handleSum:\u0026#39;handleSum\u0026#39;}), //借助mapState生成计算属性：handleSum（数组写法）  ...mapGetters([\u0026#39;handleSum\u0026#39;]) }   mapActions：映射在action中的方法，即包含$store.dispatch(xxx)的函数。\nmethods:{ //借助mapActions生成incOdd（对象写法）  ...mapActions({incOdd:\u0026#39;incOdd\u0026#39;}), //借助mapActions生成incOdd（数组写法）  ...mapActions([\u0026#39;incOdd\u0026#39;]) }   mapMutations：映射在mutations中的方法，即包含$store.commit(xxx)的函数。\nmethods:{ //借助mmapMutations生成INC（对象写法）  ...mapMutations({INC:\u0026#39;INC\u0026#39;}), //借助mapMutations生成INC（数组写法）  ...mapMutations([\u0026#39;INC\u0026#39;]) }   8. vuex模块化+命名空间   目的：让代码更好维护，让多种数据分类更加明确。\n  修改store.js\nconst countAbout = { namespaced: true,//开启命名空间  actions: {...}, mutations: {...}, state: {...}, getters: {...} } const personAbout = { namespaced: true,//开启命名空间  actions: {...}, mutations: {...}, state: {...}, getters: {...} } export default new Vuex.Store({ modules: { countAbout, personAbout } })  当一个命名空间中的内容过于繁琐时，可以通过Es6模块化的语法新建文件进行精简\n   开启命名空间后，组件中读取state数据：\n//方式一：直接读取 this.$store.state.personAbout.persons //方式一：借助mapState读取 ...mapState(\u0026#39;personAbout\u0026#39;, [\u0026#39;persons\u0026#39;]),   开启命名空间后，组件中读取getters数据：\n//方式一：直接读取 this.$store.getters[\u0026#39;countAbout/handleSum\u0026#39;] //方式一：借助mapState读取 ...mapState(\u0026#39;countAbout\u0026#39;, [\u0026#39;handleSum\u0026#39;]),   开启命名空间后，组件中调用dispatch，执行actions中的方法：\n//方式一：直接读取 this.$store.dispatch[\u0026#39;countAbout/incOdd\u0026#39;,num] this.$store.dispatch[\u0026#39;countAbout/incWait\u0026#39;,num] //方式一：借助mapState读取 ...mapActions(\u0026#39;countAbout\u0026#39;, {incrementOdd: \u0026#39;incOdd\u0026#39;, incrementWait: \u0026#39;incWait\u0026#39;})   开启命名空间后，组件中调用commit，执行mutations中的方法：\n//方式一：直接读取 this.$store.commit[\u0026#39;countAbout/INC\u0026#39;,num] this.$store.commit[\u0026#39;countAbout/DEC\u0026#39;,num] //方式一：借助mapState读取 ...mapMutations(\u0026#39;countAbout\u0026#39;, {increment: \u0026#39;INC\u0026#39;, decrement: \u0026#39;DEC\u0026#39;})   路由  理解：一个路由（route）就是一组映射关系（key-value），多个路由需要路由器（router）进行管理。 前端路由：key是路径，value是组件。  1.基本使用   安装vue-router：npm i vue-router\n  引入、应用插件并写好配置项：\n... import vueRouter from \u0026#39;vue-router\u0026#39; import router from \u0026#39;./router\u0026#39; Vue.use(vueRouter) new Vue({ render: h =\u0026gt; h(App), router }).$mount(\u0026#39;#app\u0026#39;)   编写在router/index.js的配置项\n//用于创建整个应用的路由 import vueRouter from \u0026#34;vue-router\u0026#34;; //引入相应组件 import About from \u0026#34;@/components/About\u0026#34;; import Home from \u0026#34;@/components/Home\u0026#34;; //创建并暴露路由器 export default new vueRouter({ routes: [ {path: \u0026#39;/about\u0026#39;, component: About}, {path: \u0026#39;/home\u0026#39;, component: Home} ] })   实现切换（active-class可配置高亮样式）\n\u0026lt;router-link active-class=\u0026#34;active\u0026#34; to=\u0026#34;/home\u0026#34;\u0026gt;Home\u0026lt;/router-link\u0026gt; \u0026lt;router-link active-class=\u0026#34;active\u0026#34; to=\u0026#34;/about\u0026#34;\u0026gt;About\u0026lt;/router-link\u0026gt;   指定展示位置\n\u0026lt;router-view\u0026gt;\u0026lt;/router-view\u0026gt;   2.几个注意点  路由组件通常存放在pages文件夹，一般组件通常存放在components文件夹。 通过切换，“隐藏”了的路由组件，默认是被销毁掉的，需要的时候再去挂载。 每个组件都有自己的$route属性，里面存储着自己的路由信息。 整个应用只有一个router，可以通过组件的$router属性获取到。  3.多级路由(嵌套路由)   配置路由规则，使用children配置项：\nroutes: [ {path: \u0026#39;/about\u0026#39;, component: About}, { path: \u0026#39;/home\u0026#39;, component: Home, //通过children配置子路由  children:[ //这里的path一定不带 /  {path:\u0026#39;homeMessage\u0026#39;,component:HomeMessage}, {path:\u0026#39;homeNews\u0026#39;,component:HomeNews} ] } ] 注意： children下的路由path一定不带/\n  跳转（要写完整路径）\n\u0026lt;router-link to=\u0026#34;/home/homeNews\u0026#34; \u0026gt;News\u0026lt;/router-link\u0026gt; \u0026lt;router-view/\u0026gt;   4.路由的query参数   传递参数\n\u0026lt;!--跳转路由并携带query参数，to的字符串写法--\u0026gt; \u0026lt;router-link :to=\u0026#34;`/home/homeMessage/detail?id=123\u0026amp;title=hello`\u0026#34;\u0026gt;\u0026lt;/router-link\u0026gt; \u0026lt;!--对象写法 --\u0026gt; \u0026lt;router-link :to=\u0026#34;{ path:\u0026#39;/home/homeMessage/detail\u0026#39;, query:{ id:123, title:hello } }\u0026#34;\u0026gt;\u0026lt;/router-link\u0026gt;   接收参数\n$route.query.id $route.query.title   5.命名路由   作用：路径过长时简化路由的跳转路径\n  具体使用：\n  在router/index.js中配置：\nroutes: [ { path: \u0026#39;/home\u0026#39;, component: Home, children: [ { path: \u0026#39;rt1\u0026#39;, component: Rt1, children: [ { name:\u0026#39;hello\u0026#39;//给路由命名  path: \u0026#39;rt2\u0026#39;, component: Rt2} ] }, ] } ]   跳转配置：\n\u0026lt;!--简化前，需要写完整路径--\u0026gt; \u0026lt;router-link to=\u0026#34;/home/rt1/rt2\u0026#34;\u0026gt;跳转\u0026lt;/router-link\u0026gt; \u0026lt;!--简化后，直接通过名字跳转--\u0026gt; \u0026lt;router-link :to=\u0026#34;{name:\u0026#39;hello\u0026#39;}\u0026#34;\u0026gt;跳转\u0026lt;/router-link\u0026gt; \u0026lt;!--简化写法配合传递参数--\u0026gt; \u0026lt;router-link :to=\u0026#34;{ name:\u0026#39;hello\u0026#39;, query:{ id:\u0026#39;123\u0026#39;, title:\u0026#39;hello\u0026#39; } }\u0026#34;\u0026gt;跳转\u0026lt;/router-link\u0026gt;   6.路由的params参数   在router/index.js中配置，声明接收params参数：\nroutes: [ { path: \u0026#39;/home\u0026#39;, component: Home, children: [ { path: \u0026#39;news\u0026#39;, component: Message, children: [ { name:\u0026#39;detail\u0026#39; //使用占位符声明接收params参数  path: \u0026#39;detail/:id/:title\u0026#39;, component: Detail} ] }, ] } ]   传递参数\n\u0026lt;!--跳转路由并携带params参数，to的字符串写法--\u0026gt; \u0026lt;router-link :to=\u0026#34;`/home/homeMessage/detail/123/hello`\u0026#34;\u0026gt;跳转\u0026lt;/router-link\u0026gt; \u0026lt;!--对象写法 --\u0026gt; \u0026lt;router-link :to=\u0026#34;{ name:\u0026#39;detail\u0026#39;, params:{ id:123, title:\u0026#39;hello\u0026#39; } }\u0026#34;\u0026gt;跳转\u0026lt;/router-link\u0026gt;  特别注意💣：路由携带params参数时，若使用to的对象写法，则不能使用path配置项，必须使用name配置！\n   接收参数\n$route.params.id $route.params.title     7.路由的props配置 ​\t作用：让路由组件更方便的收到参数\n{ name: \u0026#39;detail\u0026#39;, path: \u0026#39;detail/:id/:title\u0026#39;, component: MessageDetail, //props的第一种写法，值为对象，该对象中的所有key-value都会以props的形式传给Detail组件。  // props:{a:\u0026#34;1\u0026#34;,b:\u0026#34;hello\u0026#34;}  //props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件.  props: true, //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件  /* props($route) { return { id: $route.query.id, title: $route.query.title } }*/ } 8.\u0026lt;route-link\u0026gt;的replace属性  作用：控制路由跳转时操作浏览器历史记录的模试 浏览器的历史记录有两种写入方式：分别为push和replace，push是追加历史记录，replace是替换当前记录。路由跳转时候默认为push（类比栈的工作原理） 如何开启replace模式：\u0026lt;router-link replace..\u0026gt;News\u0026lt;/router-link\u0026gt;  9.编程式路由导航   作用：不借助\u0026lt;route-link\u0026gt;实现路由跳转，让路由跳转更灵活\n  具体使用：\n//$router的两个API this.$router.push({ name: \u0026#39;detail\u0026#39;, params: { id: xxx, title: xxx } }) this.$router.replace({ name: \u0026#39;detail\u0026#39;, params: { id: xxx, title: xxx } })  其中配置项与路由的query | params参数中的对象写法一致。    另外API\n $router.back()：历史记录后退 $router.forward()：历史记录前进 $router.go()：参数为Number，正负表示前进或后退    10.缓存路由组件   作用：让不展示的路由组件保持挂载，不被销毁(比如用户输入框内的内容只要页面不刷新路由后还保存)。\n  具体编码：\n\u0026lt;keep-alive include=\u0026#34;HomeNews\u0026#34;\u0026gt; \u0026lt;router-view/\u0026gt; \u0026lt;/keep-alive\u0026gt; \u0026lt;keep-alive :include=\u0026#34;[\u0026#39;HomeNews\u0026#39;,\u0026#39;Message\u0026#39;,\u0026#39;Test1\u0026#39;]\u0026#34;\u0026gt; \u0026lt;router-view/\u0026gt; \u0026lt;/keep-alive\u0026gt;  💣注意这里的include的值为组件名，如果不写，则在此处展示的所有组件都缓存\n   11.两个新的生命周期钩子   作用：路由组件所独有的两个钩子用于捕获路由组件的激活状态。\n  具体名字：\n activated路由组件被激活时触发。 deactivated路由组件失活时触发。   失活就是当前组件未被展示，组件没销毁也能触发失活。\n   加上之前的nextTick，就是所有的生命周期钩子了。如图\n\n  12.路由守卫   作用：对路由进行权限控制\n  分类：全局守卫、独享安卫、组件内守卫\n  具体使用：\n  全局守卫\n beforeEach afterEach  在router/index.js中修改：\n...... path: \u0026#39;homeMessage\u0026#39;, component: HomeMessage, meta: {title: \u0026#34;首页 | 消息\u0026#34;, isAuth: true}, ...... //全局前置守卫：初始化时执行、每次路由切换前执行 router.beforeEach((to, from, next) =\u0026gt; { if (to.meta.isAuth) { if (localStorage.getItem(\u0026#39;pwd\u0026#39;) === \u0026#34;admin666\u0026#34;) {//权限控制的具体规则  next()//放行  } else { alert(\u0026#34;无权查看！！\u0026#34;) } } else { next()//放行  } }) //全局前置守卫：初始化时执行、每次路由切换后执行 router.afterEach(((to, from) =\u0026gt; { document.title = to.meta.title || \u0026#39;首页\u0026#39; }))   独享守卫\n beforeEnter  在router/index.js中修改：\n//配置在每一个路由项里面 path: \u0026#39;homeMessage\u0026#39;, component: HomeMessage, meta: {title: \u0026#34;首页 | 消息\u0026#34;, isAuth: true}, beforeEnter:((to, from, next) =\u0026gt; { //do something }),   组件内守卫\n在相应组件内调用\n//进入守卫：通过路由规则，进入该组件时被调用 beforeRouteEnter(to, from, next) { //... } //进入守卫：通过路由规则，离开该组件时被调用 beforeRouteLeave(to, from, next) { //... }     13.路由器的两种工作模式   对于一个url来说，什么是hash值？#及其后面的内容就是hash值。\n  hash值不会包含在HTTP请求中，即：hash值不会带给服务器\n  hash模式：\n  地址中永远带着#号，不美观。\n  若以后将地址通过第三方手机app分享，若app校验严格，则地址会被标记为不合法。\n  兼容性较好。\n    history模式：\n  地址干净，美观。\n  兼容性和hash模式相比略差。\n  应用部署上线时需要后端人员支持，解决刷新页面服务端404的问题。\n    ","permalink":"https://blog.archai.space/p/vue2.0%E5%AD%A6%E4%B9%A0%E5%B0%8F%E7%BB%93/","summary":"关于Vue和VueComponent   一个重要的内置关系:VueComponent.prototype.proto===vue.prototype\n\n  为什么要有这个关系？\n让组件实例对象(vc)可以访间到Vue原型上的属性、方法.\n  ref属性 1.被用来给元素或子组件注册引用信息(id的替代者)\n2.应用在htm1标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)\n3.使用方式: 打标识: \u0026lt;h1 ref=\u0026quot;xxx\u0026quot;\u0026gt;.....\u0026lt;/h1\u0026gt;或\u0026lt;School ref=\u0026quot;xxx\u0026quot;\u0026gt;\u0026lt;/School\u0026gt; 获取:this.$refs.xxx\n配置项props 功能: 让组件接收外部传过来的数据\n(1).传递数据:\n\u0026lt;Demo name=\u0026quot;xxx\u0026quot;/\u0026gt;\n(2).接收数据:\n第一种方式(只接收):\nprops:['name']\n第二种方式(限制类型):\nprops:{ name:String }\n第三种方式(限制类型、限制必要性、指定默认值):\nprops:{ name:{ type:String,//类型限制  required:true,//必要性  default:\u0026#34;JQF\u0026#34;//默认值  } } 备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告, 若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据.\nmixin(混入) 功能: 可以把多个组件共用的配置提取成一个混入对象\n使用方式:\n第一步：定义混合\nconst mixData={ data(){...} }, const mixMethods= { methods:{...} } 第二步：使用混入\n(1).全局混入:Vue.mixin(xxx)\n(2).局部混入:mixins:[xxx']\n插件 功能:用于增强vue\n本质:包含install方法的一个对象,install的第一个参数是vue,第二个以后的参数是插件使用者传递的数据。\n定义插件:\n对象.install = function (Vue, options) { //1.","title":"Vue2.0学习小结"},{"content":"Vue中支持的事件 资源事件    事件名称 何时触发     error 资源加载失败时。   abort 正在加载资源已经被中止时。   load 资源及其相关资源已完成加载。   beforeunload window，document 及其资源即将被卸载。   unload 文档或一个依赖资源正在被卸载。    网络事件    事件名称 何时触发     online 浏览器已获得网络访问。   offline 浏览器已失去网络访问。    焦点事件    事件名称 何时触发     focus 元素获得焦点（不会冒泡）。   blur 元素失去焦点（不会冒泡）。    WebSocket 事件    事件名称 何时触发     open WebSocket 连接已建立。   message 通过 WebSocket 接收到一条消息。   error WebSocket 连接异常被关闭（比如有些数据无法发送）。   close WebSocket 连接已关闭。    会话历史事件    事件名称 何时触发     pagehide A session history entry is being traversed from.   pageshow A session history entry is being traversed to.   popstate A session history entry is being navigated to (in certain cases).    CSS 动画事件    事件名称 何时触发     animationstart 某个 CSS 动画开始时触发。   animationend 某个 CSS 动画完成时触发。   animationiteration 某个 CSS 动画完成后重新开始时触发。    CSS 过渡事件    事件名称 何时触发     transitionstart A CSS transition has actually started (fired after any delay).   transitioncancel A CSS transition has been cancelled.   transitionend A CSS transition has completed.   transitionrun A CSS transition has begun running (fired before any delay starts).    表单事件    事件名称 何时触发     reset 点击重置按钮时   submit 点击提交按钮    打印事件    时间名称 何时触发     beforeprint 打印机已经就绪时触发   afterprint 打印机关闭时触发    文本写作事件    Event Name Fired When     compositionstart The composition of a passage of text is prepared (similar to keydown for a keyboard input, but works with other inputs such as speech recognition).   compositionupdate A character is added to a passage of text being composed.   compositionend The composition of a passage of text has been completed or canceled.    视图事件    Event Name Fired When     fullscreenchange An element was turned to fullscreen mode or back to normal mode.   fullscreenerror It was impossible to switch to fullscreen mode for technical reasons or because the permission was denied.   resize The document view has been resized.   scroll The document view or an element has been scrolled.    剪贴板事件    Event Name Fired When     cut 已经剪贴选中的文本内容并且复制到了剪贴板。   copy 已经把选中的文本内容复制到了剪贴板。   paste 从剪贴板复制的文本内容被粘贴。    键盘事件    Event Name Fired When     keydown 按下任意按键。   keypress 除 Shift、Fn、CapsLock 外的任意键被按住。（连续触发。）   keyup 释放任意按键。    鼠标事件    Event Name Fired When     auxclick A pointing device button (ANY non-primary button) has been pressed and released on an element.   click 在元素上按下并释放任意鼠标按键。   contextmenu 右键点击（在右键菜单显示前触发）。   dblclick 在元素上双击鼠标按钮。   mousedown 在元素上按下任意鼠标按钮。   mouseenter 指针移到有事件监听的元素内。   mouseleave 指针移出元素范围外（不冒泡）。   mousemove 指针在元素内移动时持续触发。   mouseover 指针移到有事件监听的元素或者它的子元素内。   mouseout 指针移出元素，或者移到它的子元素上。   mouseup 在元素上释放任意鼠标按键。   pointerlockchange 鼠标被锁定或者解除锁定发生时。   pointerlockerror 可能因为一些技术的原因鼠标锁定被禁止时。   select 有文本被选中。   wheel 滚轮向任意方向滚动。    拖放事件    Event Name Fired When     drag 正在拖动元素或文本选区（在此过程中持续触发，每 350ms 触发一次）   dragend 拖放操作结束。（松开鼠标按钮或按下 Esc 键）   dragenter 被拖动的元素或文本选区移入有效释放目标区   dragstart 用户开始拖动HTML元素或选中的文本   dragleave 被拖动的元素或文本选区移出有效释放目标区   dragover 被拖动的元素或文本选区正在有效释放目标上被拖动 （在此过程中持续触发，每350ms触发一次）   drop 元素在有效释放目标区上释放    媒体事件    Event Name Fired When     audioprocess The input buffer of a ScriptProcessorNode is ready to be processed.   canplay The browser can play the media, but estimates that not enough data has been loaded to play the media up to its end without having to stop for further buffering of content.   canplaythrough The browser estimates it can play the media up to its end without stopping for content buffering.   complete The rendering of an OfflineAudioContext is terminated.   durationchange The duration attribute has been updated.   emptied The media has become empty; for example, this event is sent if the media has already been loaded (or partially loaded), and the load() method is called to reload it.   ended Playback has stopped because the end of the media was reached.   loadeddata The first frame of the media has finished loading.   loadedmetadata The metadata has been loaded.   pause Playback has been paused.   play Playback has begun.   playing Playback is ready to start after having been paused or delayed due to lack of data.   ratechange The playback rate has changed.   seeked A seek operation completed.   seeking A seek operation began.   stalled The user agent is trying to fetch media data, but data is unexpectedly not forthcoming.   suspend Media data loading has been suspended.   timeupdate The time indicated by the currentTime attribute has been updated.   volumechange The volume has changed.   waiting Playback has stopped because of a temporary lack of data.    进度事件    Event Name Fired When     abort Progression has been terminated (not due to an error).   error Progression has failed.   load Progression has been successful.   loadend Progress has stopped (after \u0026ldquo;error\u0026rdquo;, \u0026ldquo;abort\u0026rdquo; or \u0026ldquo;load\u0026rdquo; have been dispatched).   loadstart Progress has begun.   progress In progress.   timeout Progression is terminated due to preset time expiring.    存储事件 change storage see Non-standard events\n更新事件 checking` `downloading` `error` `noupdate` `obsolete` `updateready 值变化事件 broadcast` `CheckboxStateChange` `hashchange` `input` `RadioStateChange` `readystatechange` `ValueChange 未分类的事件 invalid` `message` `message` `open` `show 不常见和非标准事件 Abortable Fetch events    Event name Fired when     abort A DOM request is aborted, i.e. using AbortController.abort().    WebVR events    Event name Fired when     vrdisplayactivate When a VR display is able to be presented to, for example if an HMD has been moved to bring it out of standby, or woken up by being put on.   vrdisplayblur when presentation to a VRDisplay has been paused for some reason by the browser, OS, or VR hardware — for example, while the user is interacting with a system menu or browser, to prevent tracking or loss of experience.   vrdisplayconnect when a compatible VRDisplay is connected to the computer.   vrdisplaydeactivate When a VRDisplay can no longer be presented to, for example if an HMD has gone into standby or sleep mode due to a period of inactivity.   vrdisplaydisconnect When a compatible VRDisplay is disconnected from the computer.   vrdisplayfocus When presentation to a VRDisplay has resumed after being blurred.   vrdisplaypresentchange The presenting state of a VRDisplay changes — i.e. goes from presenting to not presenting, or vice versa.    SVG 事件 SVGAbort` `SVGError` `SVGLoad` `SVGResize` `SVGScroll` `SVGUnload` `SVGZoom 数据库事件 abort` `blocked` `complete` `error` `success` `upgradeneeded` `versionchange 脚本事件 afterscriptexecute` `beforescriptexecute 菜单事件 DOMMenuItemActive` `DOMMenuItemInactive 窗口事件 close 弹出事件 popuphidden` `popuphiding` `popupshowing` `popupshown Tab 事件 visibilitychange 电池事件 chargingchange` `chargingtimechange` `dischargingtimechange` `levelchange 呼叫事件 alerting` `busy` `callschanged` `cfstatechange` `connected` `connecting` `dialing` `disconnected` `disconnecting` `error` `held`, `holding` `incoming` `resuming` `statechange` `voicechange 传感器事件 compassneedscalibration` `devicemotion` `deviceorientation` `orientationchange 智能卡事件 icccardlockerror` `iccinfochange` `smartcard-insert` `smartcard-remove` `stkcommand` `stksessionend` `cardstatechange 短信和USSD事件 delivered` `received` `sent` `ussdreceived 帧事件 mozbrowserclose` `mozbrowsercontextmenu` `mozbrowsererror` `mozbrowsericonchange` `mozbrowserlocationchange` `mozbrowserloadend` `mozbrowserloadstart` `mozbrowseropenwindow` `mozbrowsersecuritychange` `mozbrowsershowmodalprompt` `mozbrowsertitlechange DOM变异事件 DOMAttributeNameChanged` `DOMAttrModified` `DOMCharacterDataModified` `DOMContentLoaded` `DOMElementNameChanged` `DOMNodeInserted` `DOMNodeInsertedIntoDocument` `DOMNodeRemoved` `DOMNodeRemovedFromDocument` `DOMSubtreeModified 触摸事件 touchcancel` `touchend` `touchmove` `touchstart 指针事件 pointerover` `pointerenter` `pointerdown` `pointermove` `pointerup` `pointercancel` `pointerout` `pointerleave` `gotpointercapture` `lostpointercapture 标准事件 这些事件在官方Web规范中定义，并且应在各个浏览器中通用。 每个事件都和代表事件接收方的对象（由此您可以查到每个事件提供的数据），定义这个事件的标准或标准链接会一起列出。\n   事件名称 事件类型 规范 触发时机\u0026hellip;     abort UIEvent DOM L3 资源载入已被中止   abort ProgressEvent ProgressandXMLHttpRequest Progress被终止(不是error造成的)   abort Event IndexedDB 事务已被中止   afterprint Event HTML5 相关文档已开始打印或打印预览已被关闭   animationcancel AnimationEvent CSS Animations A CSS animation has aborted.   animationend AnimationEvent CSS Animations 完成一个CSS 动画   animationiteration AnimationEvent CSS Animations 重复播放一个CSS 动画   animationstart AnimationEvent CSS Animations 一个 CSS 动画已开始   appinstalled Event Web App Manifest A web application is successfully installed as a progressive web app.   audioprocess AudioProcessingEvent Web Audio API audioprocess 一个ScriptProcessorNode 的输入缓冲区可处理   audioend Event Web Speech API 用户代理捕捉到用以语音识别的音频   audiostart Event Web Speech API 用户代理开始捕捉用以语音识别的音频   beforeprint Event HTML5 相关文档将要开始打印或准备打印预览   beforeunload BeforeUnloadEvent HTML5 即将卸载 window，document 及其资源   beginEvent TimeEvent SVG A SMIL animation element begins.   blocked  IndexedDB An open connection to a database is blocking a versionchange transaction on the same database.   blur FocusEvent DOM L3 An element has lost focus (does not bubble).   boundary SpeechSynthesisEvent Web Speech API The spoken utterance reaches a word or sentence boundary   canplay Event HTML5 media The user agent can play the media, but estimates that not enough data has been loaded to play the media up to its end without having to stop for further buffering of content.   canplaythrough Event HTML5 media The user agent can play the media up to its end without having to stop for further buffering of content.   change Event DOM L2, HTML5 The change event is fired for ](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input), [, and `` elements when a change to the element\u0026rsquo;s value is committed by the user.   chargingchange Event Battery status The battery begins or stops charging.   chargingtimechange Event Battery status The chargingTime attribute has been updated.   click MouseEvent DOM L3 A pointing device button has been pressed and released on an element.   close Event WebSocket A WebSocket connection has been closed.   complete  IndexedDB A transaction successfully completed.   complete OfflineAudioCompletionEvent Web Audio API OfflineAudioCompletionEvent The rendering of an OfflineAudioContext is terminated.   compositionend CompositionEvent DOM L3 The composition of a passage of text has been completed or canceled.   compositionstart CompositionEvent DOM L3 The composition of a passage of text is prepared (similar to keydown for a keyboard input, but works with other inputs such as speech recognition).   compositionupdate CompositionEvent DOM L3 A character is added to a passage of text being composed.   contextmenu MouseEvent HTML5 The right button of the mouse is clicked (before the context menu is displayed).   copy ClipboardEvent Clipboard The text selection has been added to the clipboard.   cut ClipboardEvent Clipboard The text selection has been removed from the document and added to the clipboard.   dblclick MouseEvent DOM L3 A pointing device button is clicked twice on an element.   devicechange Event Media Capture and Streams A media device such as a camera, microphone, or speaker is connected or removed from the system.   devicemotion DeviceMotionEvent Device Orientation Events Fresh data is available from a motion sensor.   deviceorientation DeviceOrientationEvent Device Orientation Events Fresh data is available from an orientation sensor.   dischargingtimechange Event Battery status The dischargingTime attribute has been updated.   DOMActivate UIEvent DOM L3 A button, link or state changing element is activated (use click instead).   DOMAttributeNameChanged MutationNameEvent DOM L3 Removed The name of an attribute changed (use mutation observers instead).   DOMAttrModified MutationEvent DOM L3 The value of an attribute has been modified (use mutation observers instead).   DOMCharacterDataModified MutationEvent DOM L3 A text or another CharacterData has changed (use mutation observers instead).   DOMContentLoaded Event HTML5 The document has finished loading (but not its dependent resources).   DOMElementNameChanged MutationNameEvent DOM L3 Removed The name of an element changed (use mutation observers instead).   DOMFocusIn FocusEvent DOM L3 An element has received focus (use focus or focusin instead).   DOMFocusOut FocusEvent DOM L3 An element has lost focus (use blur or focusout instead).   DOMNodeInserted MutationEvent DOM L3 A node has been added as a child of another node (use mutation observers instead).   DOMNodeInsertedIntoDocument MutationEvent DOM L3 A node has been inserted into the document (use mutation observers instead).   DOMNodeRemoved MutationEvent DOM L3 A node has been removed from its parent node (use mutation observers instead).   DOMNodeRemovedFromDocument MutationEvent DOM L3 A node has been removed from the document (use mutation observers instead).   DOMSubtreeModified MutationEvent DOM L3 A change happened in the document (use mutation observers instead).   drag DragEvent HTML5 An element or text selection is being dragged (every 350ms).   dragend DragEvent HTML5 A drag operation is being ended (by releasing a mouse button or hitting the escape key).   dragenter DragEvent HTML5 A dragged element or text selection enters a valid drop target.   dragleave DragEvent HTML5 A dragged element or text selection leaves a valid drop target.   dragover DragEvent HTML5 An element or text selection is being dragged over a valid drop target (every 350ms).   dragstart DragEvent HTML5 The user starts dragging an element or text selection.   drop DragEvent HTML5 An element is dropped on a valid drop target.   durationchange Event HTML5 media The duration attribute has been updated.   emptied Event HTML5 media The media has become empty; for example, this event is sent if the media has already been loaded (or partially loaded), and the load() method is called to reload it.   end Event Web Speech API The speech recognition service has disconnected.   end SpeechSynthesisEvent Web Speech API The utterance has finished being spoken.   ended Event HTML5 media Playback has stopped because the end of the media was reached.   ended Event Web Audio API Playback has stopped because the end of the media was reached.   endEvent TimeEvent SVG A SMIL animation element ends.   error UIEvent DOM L3 A resource failed to load.   error ProgressEvent Progress and XMLHttpRequest Progression has failed.   error Event WebSocket A WebSocket connection has been closed with prejudice (some data couldn\u0026rsquo;t be sent for example).   error Event Server Sent Events An event source connection has been failed.   error Event IndexedDB A request caused an error and failed.   error Event Web Speech API A speech recognition error occurs.   error SpeechSynthesisErrorEvent Web Speech API An error occurs that prevents the utterance from being successfully spoken.   focus FocusEvent DOM L3 An element has received focus (does not bubble).   focusin FocusEvent DOM L3 An element is about to receive focus (bubbles).   focusout FocusEvent DOM L3 An element is about to lose focus (bubbles).   fullscreenchange Event Full Screen An element was turned to fullscreen mode or back to normal mode.   fullscreenerror Event Full Screen It was impossible to switch to fullscreen mode for technical reasons or because the permission was denied.   gamepadconnected GamepadEvent Gamepad A gamepad has been connected.   gamepaddisconnected GamepadEvent Gamepad A gamepad has been disconnected.   gotpointercapture PointerEvent Pointer Events Element receives pointer capture.   hashchange HashChangeEvent HTML5 The fragment identifier of the URL has changed (the part of the URL after the #).   lostpointercapture PointerEvent Pointer Events Element lost pointer capture.   input Event HTML5 The value of an element changes or the content of an element with the attribute contenteditable is modified.   invalid Event HTML5 A submittable element has been checked and doesn\u0026rsquo;t satisfy its constraints.   keydown KeyboardEvent DOM L3 A key is pressed down.   keypress KeyboardEvent DOM L3 A key is pressed down and that key normally produces a character value (use input instead).   keyup KeyboardEvent DOM L3 A key is released.   languagechange Event HTML 5.1 NavigatorLanguage.languages The user\u0026rsquo;s preferred languages have changed.   levelchange Event Battery status The level attribute has been updated.   load UIEvent DOM L3 A resource and its dependent resources have finished loading.   load ProgressEvent Progress and XMLHttpRequest Progression has been successful.   loadeddata Event HTML5 media The first frame of the media has finished loading.   loadedmetadata Event HTML5 media The metadata has been loaded.   loadend ProgressEvent Progress and XMLHttpRequest Progress has stopped (after \u0026ldquo;error\u0026rdquo;, \u0026ldquo;abort\u0026rdquo; or \u0026ldquo;load\u0026rdquo; have been dispatched).   loadstart ProgressEvent Progress and XMLHttpRequest Progress has begun.   mark SpeechSynthesisEvent Web Speech API The spoken utterance reaches a named SSML \u0026ldquo;mark\u0026rdquo; tag.   message MessageEvent WebSocket A message is received through a WebSocket.   message MessageEvent Web Workers A message is received from a Web Worker.   message MessageEvent Web Messaging A message is received from a child (i)frame or a parent window.   message MessageEvent Server Sent Events A message is received through an event source.   messageerror MessageEvent MessagePort, Web Workers, Broadcast Channel, Window A message error is raised when a message is received by an object.   message ServiceWorkerMessageEvent or ExtendableMessageEvent, depending on context. Service Workers A message is received from a service worker, or a message is received in a service worker from another context.   mousedown MouseEvent DOM L3 A pointing device button (usually a mouse) is pressed on an element.   mouseenter MouseEvent DOM L3 A pointing device is moved onto the element that has the listener attached.   mouseleave MouseEvent DOM L3 A pointing device is moved off the element that has the listener attached.   mousemove MouseEvent DOM L3 A pointing device is moved over an element.   mouseout MouseEvent DOM L3 A pointing device is moved off the element that has the listener attached or off one of its children.   mouseover MouseEvent DOM L3 A pointing device is moved onto the element that has the listener attached or onto one of its children.   mouseup MouseEvent DOM L3 A pointing device button is released over an element.   nomatch SpeechRecognitionEvent Web Speech API The speech recognition service returns a final result with no significant recognition.   notificationclick NotificationEvent Notifications API onnotificationclick A system notification spawned by ServiceWorkerRegistration.showNotification() has been clicked.   offline Event HTML5 offline The browser has lost access to the network.   online Event HTML5 offline The browser has gained access to the network (but particular websites might be unreachable).   open Event WebSocket A WebSocket connection has been established.   open Event Server Sent Events An event source connection has been established.   orientationchange Event Screen Orientation The orientation of the device (portrait/landscape) has changed   pagehide PageTransitionEvent HTML5 A session history entry is being traversed from.   pageshow PageTransitionEvent HTML5 A session history entry is being traversed to.   paste ClipboardEvent Clipboard Data has been transferred from the system clipboard to the document.   pause Event HTML5 media Playback has been paused.   pause SpeechSynthesisEvent Web Speech API The utterance is paused part way through.   pointercancel PointerEvent Pointer Events The pointer is unlikely to produce any more events.   pointerdown PointerEvent Pointer Events The pointer enters the active buttons state.   pointerenter PointerEvent Pointer Events Pointing device is moved inside the hit-testing boundary.   pointerleave PointerEvent Pointer Events Pointing device is moved out of the hit-testing boundary.   pointerlockchange Event Pointer Lock The pointer was locked or released.   pointerlockerror Event Pointer Lock It was impossible to lock the pointer for technical reasons or because the permission was denied.   pointermove PointerEvent Pointer Events The pointer changed coordinates.   pointerout PointerEvent Pointer Events The pointing device moved out of hit-testing boundary or leaves detectable hover range.   pointerover PointerEvent Pointer Events The pointing device is moved into the hit-testing boundary.   pointerup PointerEvent Pointer Events The pointer leaves the active buttons state.   play Event HTML5 media Playback has begun.   playing Event HTML5 media Playback is ready to start after having been paused or delayed due to lack of data.   popstate PopStateEvent HTML5 A session history entry is being navigated to (in certain cases).   progress ProgressEvent Progress and XMLHttpRequest In progress.   push PushEvent Push API A Service Worker has received a push message.   pushsubscriptionchange PushEvent Push API A PushSubscription has expired.   ratechange Event HTML5 media The playback rate has changed.   readystatechange Event HTML5 and XMLHttpRequest The readyState attribute of a document has changed.   repeatEvent TimeEvent SVG A SMIL animation element is repeated.   reset Event DOM L2, HTML5 A form is reset.   resize UIEvent DOM L3 The document view has been resized.   resourcetimingbufferfull Performance Resource Timing The browser\u0026rsquo;s resource timing buffer is full.   result SpeechRecognitionEvent Web Speech API The speech recognition service returns a result — a word or phrase has been positively recognized and this has been communicated back to the app.   resume SpeechSynthesisEvent Web Speech API A paused utterance is resumed.   scroll UIEvent DOM L3 The document view or an element has been scrolled.   seeked Event HTML5 media A seek operation completed.   seeking Event HTML5 media A seek operation began.   select UIEvent DOM L3 Some text is being selected.   selectstart Event Selection API A selection just started.   selectionchange Event Selection API The selection in the document has been changed.   show MouseEvent HTML5 A contextmenu event was fired on/bubbled to an element that has a contextmenu attribute   slotchange Event DOM The node contents of a HTMLSlotElement (``) have changed.   soundend Event Web Speech API Any sound — recognisable speech or not — has stopped being detected.   soundstart Event Web Speech API Any sound — recognisable speech or not — has been detected.   speechend Event Web Speech API Speech recognised by the speech recognition service has stopped being detected.   speechstart Event Web Speech API Sound that is recognised by the speech recognition service as speech has been detected.   stalled Event HTML5 media The user agent is trying to fetch media data, but data is unexpectedly not forthcoming.   start Event Web Speech API The speech recognition service has begun listening to incoming audio with intent to recognize grammars associated with the current SpeechRecognition.   start SpeechSynthesisEvent Web Speech API The utterance has begun to be spoken.   storage StorageEvent Web Storage A storage area (localStorage or sessionStorage) has changed.   submit Event DOM L2, HTML5 A form is submitted.   success Event IndexedDB A request successfully completed.   suspend Event HTML5 media Media data loading has been suspended.   SVGAbort SVGEvent SVG Page loading has been stopped before the SVG was loaded.   SVGError SVGEvent SVG An error has occurred before the SVG was loaded.   SVGLoad SVGEvent SVG An SVG document has been loaded and parsed.   SVGResize SVGEvent SVG An SVG document is being resized.   SVGScroll SVGEvent SVG An SVG document is being scrolled.   SVGUnload SVGEvent SVG An SVG document has been removed from a window or frame.   SVGZoom SVGZoomEvent SVG An SVG document is being zoomed.   timeout ProgressEvent XMLHttpRequest    timeupdate Event HTML5 media The time indicated by the currentTime attribute has been updated.   touchcancel TouchEvent Touch Events A touch point has been disrupted in an implementation-specific manners (too many touch points for example).   touchend TouchEvent Touch Events A touch point is removed from the touch surface.   touchmove TouchEvent Touch Events A touch point is moved along the touch surface.   touchstart TouchEvent Touch Events A touch point is placed on the touch surface.   transitionend TransitionEvent CSS Transitions A CSS transition has completed.   unload UIEvent DOM L3 The document or a dependent resource is being unloaded.   upgradeneeded  IndexedDB An attempt was made to open a database with a version number higher than its current version. A versionchange transaction has been created.   userproximity UserProximityEvent Proximity Sensor Fresh data is available from a proximity sensor (indicates whether the nearby object is near the device or not).   voiceschanged Event Web Speech API The list of SpeechSynthesisVoice objects that would be returned by the SpeechSynthesis.getVoices() method has changed (when the voiceschanged event fires.)   versionchange  IndexedDB A versionchange transaction completed.   visibilitychange Event Page visibility The content of a tab has become visible or has been hidden.   volumechange Event HTML5 media The volume has changed.   waiting Event HTML5 media Playback has stopped because of a temporary lack of data.   wheel WheelEvent DOM L3 A wheel button of a pointing device is rotated in any direction.    非标准事件    Event Name Event Type Specification Fired when\u0026hellip;     afterscriptexecute Event Mozilla Specific A script has been executed.   beforescriptexecute Event Mozilla Specific A script is about to be executed.   beforeinstallprompt Event Chrome specific A user is prompted to save a web site to a home screen on mobile.   cardstatechange  Firefox OS specific The MozMobileConnection.cardState property changes value.   change DeviceStorageChangeEvent Firefox OS specific This event is triggered each time a file is created, modified or deleted on a given storage area.   connectionInfoUpdate  Firefox OS specific The informations about the signal strength and the link speed have been updated.   cfstatechange  Firefox OS specific The call forwarding state changes.   datachange  Firefox OS specific The MozMobileConnection.data object changes values.   dataerror  Firefox OS specific The MozMobileConnection.data object receive an error from the RIL.   DOMMouseScroll  Mozilla specific The wheel button of a pointing device is rotated (detail attribute is a number of lines). (use wheel instead)   dragdrop DragEvent Mozilla specific An element is dropped (use drop instead).   dragexit DragEvent Mozilla specific A drag operation is being ended(use dragend instead).   draggesture DragEvent Mozilla specific The user starts dragging an element or text selection (use dragstart instead).   icccardlockerror  Firefox OS specific the MozMobileConnection.unlockCardLock() or MozMobileConnection.setCardLock() methods fails.   iccinfochange  Firefox OS specific The MozMobileConnection.iccInfo object changes.   localized  Mozilla Specific The page has been localized using data-l10n-* attributes.   mousewheel  IE invented The wheel button of a pointing device is rotated.   MozAudioAvailable Event Mozilla specific The audio buffer is full and the corresponding raw samples are available.   MozBeforeResize  Mozilla specific A window is about to be resized.   mozbrowseractivitydone  Firefox OS Browser API-specific Sent when some activity has been completed (complete description TBD.)   mozbrowserasyncscroll  Firefox OS Browser API-specific Sent when the scroll position within a browser `` changes.   mozbrowseraudioplaybackchange  Firefox OS Browser API-specific Sent when audio starts or stops playing within the browser `` content.   mozbrowsercaretstatechanged  Firefox OS Browser API-specific Sent when the text selected inside the browser `` content changes.   mozbrowserclose  Firefox OS Browser API-specific Sent when window.close() is called within a browser ``.   mozbrowsercontextmenu  Firefox OS Browser API-specific Sent when a browser `` try to open a context menu.   mozbrowserdocumentfirstpaint  Firefox OS Browser API-specific Sent when a new paint occurs on any document in the browser ``.   mozbrowsererror  Firefox OS Browser API-specific Sent when an error occured while trying to load a content within a browser iframe   mozbrowserfindchange  Firefox OS Browser API-specific Sent when a search operation is performed on the browser `` content (see HTMLIFrameElement search methods.)   mozbrowserfirstpaint  Firefox OS Browser API-specific Sent when the `` paints content for the first time (this doesn\u0026rsquo;t include the initial paint from about:blank.)   mozbrowsericonchange  Firefox OS Browser API-specific Sent when the favicon of a browser iframe changes.   mozbrowserlocationchange  Firefox OS Browser API-specific Sent when an browser iframe\u0026rsquo;s location changes.   mozbrowserloadend  Firefox OS Browser API-specific Sent when the browser iframe has finished loading all its assets.   mozbrowserloadstart  Firefox OS Browser API-specific Sent when the browser iframe starts to load a new page.   mozbrowsermanifestchange  Firefox OS Browser API-specific Sent when a the path to the app manifest changes, in the case of a browser `` with an open web app embedded in it.   mozbrowsermetachange  Firefox OS Browser API-specific Sent when a ](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/meta) elelment is added to, removed from or changed in the browser [\u0026rsquo;s content.   mozbrowseropensearch  Firefox OS Browser API-specific Sent when a link to a search engine is found.   mozbrowseropentab  Firefox OS Browser API-specific Sent when a new tab is opened within a browser `` as a result of the user issuing a command to open a link target in a new tab (for example ctrl/cmd + click.)   mozbrowseropenwindow  Firefox OS Browser API-specific Sent when window.open() is called within a browser iframe.   mozbrowserresize  Firefox OS Browser API-specific Sent when the browser ``\u0026rsquo;s window size has changed.   mozbrowserscroll  Firefox OS Browser API-specific Sent when the browser `` content scrolls.   mozbrowserscrollareachanged  Firefox OS Browser API-specific Sent when the available scrolling area in the browser `` changes. This can occur on resize and when the page size changes (while loading for example.)   mozbrowserscrollviewchange  Firefox OS Browser API-specific Sent when asynchronous scrolling (i.e. APCZ) starts or stops.   mozbrowsersecuritychange  Firefox OS Browser API-specific Sent when the SSL state changes within a browser iframe.   mozbrowserselectionstatechanged  Firefox OS Browser API-specific Sent when the text selected inside the browser `` content changes. Note that this is deprecated, and newer implementations use mozbrowsercaretstatechanged instead.   mozbrowsershowmodalprompt  Firefox OS Browser API-specific Sent when alert(), confirm() or prompt() are called within a browser iframe   mozbrowsertitlechange  Firefox OS Browser API-specific Sent when the document.title changes within a browser iframe.   mozbrowserusernameandpasswordrequired  Firefox OS Browser API-specific Sent when an HTTP authentification is requested.   mozbrowservisibilitychange  Firefox OS Browser API-specific Sent when the visibility state of the current browser iframe `` changes, for example due to a call to setVisible().   MozGamepadButtonDown  To be specified A gamepad button is pressed down.   MozGamepadButtonUp  To be specified A gamepad button is released.   MozMousePixelScroll  Mozilla specific The wheel button of a pointing device is rotated (detail attribute is a number of pixels). (use wheel instead)   MozOrientation  Mozilla specific Fresh data is available from an orientation sensor (see deviceorientation).   MozScrolledAreaChanged UIEvent Mozilla specific The document view has been scrolled or resized.   moztimechange  Mozilla specific The time of the device has been changed.   MozTouchDown  Mozilla specific A touch point is placed on the touch surface (use touchstart instead).   MozTouchMove  Mozilla specific A touch point is moved along the touch surface (use touchmove instead).   MozTouchUp  Mozilla specific A touch point is removed from the touch surface (use touchend instead).   alerting CallEvent To be specified The correspondent is being alerted (his/her phone is ringing).   busy CallEvent To be specified The line of the correspondent is busy.   callschanged CallEvent To be specified A call has been added or removed from the list of current calls.   onconnected connected CallEvent To be specified A call has been connected.   connecting CallEvent To be specified A call is about to connect.   delivered SMSEvent To be specified An SMS has been successfully delivered.   dialing CallEvent To be specified The number of a correspondent has been dialed.   disabled  Firefox OS specific Wifi has been disabled on the device.   disconnected CallEvent To be specified A call has been disconnected.   disconnecting CallEvent To be specified A call is about to disconnect.   enabled  Firefox OS specific Wifi has been enabled on the device.   error CallEvent To be specified An error occurred.   held CallEvent To be specified A call has been held.   holding CallEvent To be specified A call is about to be held.   incoming CallEvent To be specified A call is being received.   received SMSEvent To be specified An SMS has been received.   resuming CallEvent To be specified A call is about to resume.   sent SMSEvent To be specified An SMS has been sent.   statechange CallEvent To be specified The state of a call has changed.   statuschange  Firefox OS specific The status of the Wifi connection changed.   overflow UIEvent Mozilla specific An element has been overflowed by its content or has been rendered for the first time in this state (only works for elements styled with overflow != visible).   smartcard-insert  Mozilla specific A smartcard has been inserted.   smartcard-remove  Mozilla specific A smartcard has been removed.   stkcommand  Firefox OS specific The STK Proactive Command is issued from ICC.   stksessionend  Firefox OS specific The STK Session is terminated by ICC.   touchenter TouchEvent Touch Events Removed    touchleave TouchEvent Touch Events Removed    underflow UIEvent Mozilla specific An element is no longer overflowed by its content (only works for elements styled with overflow != visible).   uploadprogress ProgressEvent Mozilla Specific Upload is in progress (see progress).   ussdreceived  Firefox OS specific A new USSD message is received   voicechange  Firefox OS specific The MozMobileConnection.voice object changes values.   msContentZoom  Microsoft specific    MSManipulationStateChanged  Microsoft specific    MSPointerHover  Microsoft specific     Mozilla 特定事件 注意：这些事件不会暴露给 Web 内容使用，只能在 chrome 内容的上下文中使用。\nXUL 事件    Event Name Event Type Specification Fired when\u0026hellip;     broadcast  XUL An observer noticed a change to the attributes of a watched broadcaster.   CheckboxStateChange  XUL The state of a checkbox has been changed either by a user action or by a script (useful for accessibility).   close  XUL The close button of the window has been clicked.   command  XUL An element has been activated.   commandupdate  XUL A command update occurred on a commandset element.   DOMMenuItemActive  XUL A menu or menuitem has been hovered or highlighted.   DOMMenuItemInactive  XUL A menu or menuitem is no longer hovered or highlighted.   popuphidden PopupEvent XUL A menupopup, panel or tooltip has been hidden.   popuphiding PopupEvent XUL A menupopup, panel or tooltip is about to be hidden.   popupshowing PopupEvent XUL A menupopup, panel or tooltip is about to become visible.   popupshown PopupEvent XUL A menupopup, panel or tooltip has become visible.   RadioStateChange  XUL The state of a radio has been changed either by a user action or by a script (useful for accessibility).   ValueChange  XUL The value of an element has changed (a progress bar for example, useful for accessibility).    附加组件特定事件    Event Name Event Type Specification Fired when\u0026hellip;     MozSwipeGesture  Addons specific A touch point is swiped across the touch surface   MozMagnifyGestureStart  Addons specific Two touch points start to move away from each other.   MozMagnifyGestureUpdate  Addons specific Two touch points move away from each other (after a MozMagnifyGestureStart).   MozMagnifyGesture  Addons specific Two touch points moved away from each other (after a sequence of MozMagnifyGestureUpdate).   MozRotateGestureStart  Addons specific Two touch points start to rotate around a point.   MozRotateGestureUpdate  Addons specific Two touch points rotate around a point (after a MozRotateGestureStart).   MozRotateGesture  Addons specific Two touch points rotate around a point (after a sequence of MozRotateGestureUpdate).   MozTapGesture  Addons specific Two touch points are tapped on the touch surface.   MozPressTapGesture  Addons specific A \u0026ldquo;press-tap\u0026rdquo; gesture happened on the touch surface (first finger down, second finger down, second finger up, first finger up).   MozEdgeUIGesture  Addons specific A touch point is swiped across the touch surface to invoke the edge UI (Win8 only).   MozAfterPaint  Addons specific Content has been repainted.   DOMPopupBlocked  Addons specific A popup has been blocked   DOMWindowCreated  Addons specific A window has been created.   DOMWindowClose  Addons specific A window is about to be closed.   DOMTitleChanged  Addons specifc The title of a window has changed.   DOMLinkAdded  Addons specifc A link has been added a document.   DOMLinkRemoved  Addons specifc A link has been removed inside from a document.   DOMMetaAdded  Addons specific A meta element has been added to a document.   DOMMetaRemoved  Addons specific A meta element has been removed from a document.   DOMWillOpenModalDialog  Addons specific A modal dialog is about to open.   DOMModalDialogClosed  Addons specific A modal dialog has been closed.   DOMAutoComplete  Addons specific The content of an element has been auto-completed.   DOMFrameContentLoaded  Addons specific The frame has finished loading (but not its dependent resources).   AlertActive  Addons specific A notification element is shown.   AlertClose  Addons specific A notification element is closed.   fullscreen  Addons specific Browser fullscreen mode has been entered or left.   sizemodechange  Addons specific Window has entered/left fullscreen mode, or has been minimized/unminimized.   MozEnteredDomFullscreen  Addons specific DOM fullscreen mode has been entered.   SSWindowClosing  Addons specific The session store will stop tracking this window.   SSTabClosing  Addons specific The session store will stop tracking this tab.   SSTabRestoring  Addons specific A tab is about to be restored.   SSTabRestored  Addons specific A tab has been restored.   SSWindowStateReady  Addons specific A window state has switched to \u0026ldquo;ready\u0026rdquo;.   SSWindowStateBusy  Addons specific A window state has switched to \u0026ldquo;busy\u0026rdquo;.   TabOpen  Addons specific A tab has been opened.   TabClose  Addons specific A tab has been closed.   TabSelect  Addons specific A tab has been selected.   TabShow  Addons specific A tab has been shown.   TabHide  Addons specific A tab has been hidden.   TabPinned  Addons specific A tab has been pinned.   TabUnpinned  Addons specific A tab has been unpinned.    开发者工具特定事件    Event Name Event Type Specification Fired when\u0026hellip;     CssRuleViewRefreshed  devtools specific The \u0026ldquo;Rules\u0026rdquo; view of the style inspector has been updated.   CssRuleViewChanged  devtools specific The \u0026ldquo;Rules\u0026rdquo; view of the style inspector has been changed.   CssRuleViewCSSLinkClicked  devtools specific A link to a CSS file has been clicked in the \u0026ldquo;Rules\u0026rdquo; view of the style inspector.sd     原文来自博客园深夜独行侠\n ","permalink":"https://blog.archai.space/p/vue%E4%B8%ADv-on%E6%94%AF%E6%8C%81%E7%9A%84%E4%BA%8B%E4%BB%B6%E6%80%BB%E7%BB%93/","summary":"Vue中支持的事件 资源事件    事件名称 何时触发     error 资源加载失败时。   abort 正在加载资源已经被中止时。   load 资源及其相关资源已完成加载。   beforeunload window，document 及其资源即将被卸载。   unload 文档或一个依赖资源正在被卸载。    网络事件    事件名称 何时触发     online 浏览器已获得网络访问。   offline 浏览器已失去网络访问。    焦点事件    事件名称 何时触发     focus 元素获得焦点（不会冒泡）。   blur 元素失去焦点（不会冒泡）。    WebSocket 事件    事件名称 何时触发     open WebSocket 连接已建立。   message 通过 WebSocket 接收到一条消息。   error WebSocket 连接异常被关闭（比如有些数据无法发送）。   close WebSocket 连接已关闭。    会话历史事件    事件名称 何时触发     pagehide A session history entry is being traversed from.","title":"Vue中v-on支持的事件总结"},{"content":" 重定向后页面回退反复跳转bug\n 问题 在网易云项目时，由于Api所返回数据的问题，在Video搜索界面出现Mv的结果，这样就导致当点击的视频为Mv时，无法在Video页面利用传入的id参数渲染Video\n\n解决办法 想到利用判断，在得到MV之后跳转到MV播放界面，也即重定向redirect：\nthis.$router.push({ name: \u0026#39;MvPlay\u0026#39;, query: { id: this.id, } }) 但是，这就出现了页面回退之后又前进这样反复的BUG，于是想到replace，修改为：\nthis.$router.replace({ name: \u0026#39;MvPlay\u0026#39;, query: { id: this.id, } }) 这样，在Video页面重定向到Mv页面后，不会留下在Video页面的历史，所以就不会出现上面的BUG了！\n","permalink":"https://blog.archai.space/p/vue.js%E4%B9%8Brouter.replace%E7%9A%84%E5%AE%9E%E4%BE%8B/","summary":"重定向后页面回退反复跳转bug\n 问题 在网易云项目时，由于Api所返回数据的问题，在Video搜索界面出现Mv的结果，这样就导致当点击的视频为Mv时，无法在Video页面利用传入的id参数渲染Video\n\n解决办法 想到利用判断，在得到MV之后跳转到MV播放界面，也即重定向redirect：\nthis.$router.push({ name: \u0026#39;MvPlay\u0026#39;, query: { id: this.id, } }) 但是，这就出现了页面回退之后又前进这样反复的BUG，于是想到replace，修改为：\nthis.$router.replace({ name: \u0026#39;MvPlay\u0026#39;, query: { id: this.id, } }) 这样，在Video页面重定向到Mv页面后，不会留下在Video页面的历史，所以就不会出现上面的BUG了！","title":"Vue.js之router.replace的实例"},{"content":" 在网易云项目时，在歌手详情页点击相似的歌手跳转到query参数不同的另一个歌手详情页时，出现地址栏query参数变化，而页面却没有实时更新的现象，经过搜索找到以下解决方案：\n 方案一：watch监听路由 watch: { // 方法1 //监听路由是否变化  \u0026#39;$route\u0026#39; (to, from) { if(to.query.id !== from.query.id){ this.id = to.query.id; this.init();//重新加载数据  } } } //方法 2 设置路径变化时的处理函数 watch: { \u0026#39;$route\u0026#39;: { handler: \u0026#39;init\u0026#39;, immediate: true } } 方案二：给router-view添加一个不同的key 给router-view添加一个不同的key，这样即使是公用组件，只要url变化了，就一定会重新创建这个组件。\n\u0026lt;router-view :key=\u0026#34;$route.fullpath\u0026#34;\u0026gt;\u0026lt;/router-view\u0026gt;\t小洁🙎🏼‍♀️ 抽象问题描述：同一path的页面跳转时路由参数变化，但是组件没有对应的更新。\n实际原因：主要是因为获取参数写在了created或者mounted路由钩子函数中，路由参数变化的时候，这个生命周期不会重新执行。\n","permalink":"https://blog.archai.space/p/vue%E8%B7%AF%E7%94%B1%E5%9C%B0%E5%9D%80%E6%94%B9%E5%8F%98%E9%A1%B5%E9%9D%A2%E5%8D%B4%E4%B8%8D%E5%88%B7%E6%96%B0/","summary":"在网易云项目时，在歌手详情页点击相似的歌手跳转到query参数不同的另一个歌手详情页时，出现地址栏query参数变化，而页面却没有实时更新的现象，经过搜索找到以下解决方案：\n 方案一：watch监听路由 watch: { // 方法1 //监听路由是否变化  \u0026#39;$route\u0026#39; (to, from) { if(to.query.id !== from.query.id){ this.id = to.query.id; this.init();//重新加载数据  } } } //方法 2 设置路径变化时的处理函数 watch: { \u0026#39;$route\u0026#39;: { handler: \u0026#39;init\u0026#39;, immediate: true } } 方案二：给router-view添加一个不同的key 给router-view添加一个不同的key，这样即使是公用组件，只要url变化了，就一定会重新创建这个组件。\n\u0026lt;router-view :key=\u0026#34;$route.fullpath\u0026#34;\u0026gt;\u0026lt;/router-view\u0026gt;\t小洁🙎🏼‍♀️ 抽象问题描述：同一path的页面跳转时路由参数变化，但是组件没有对应的更新。\n实际原因：主要是因为获取参数写在了created或者mounted路由钩子函数中，路由参数变化的时候，这个生命周期不会重新执行。","title":"Vue路由地址改变，页面却不刷新？"},{"content":"《聪明人和傻子和奴才》  这篇散文诗作于1925年12月26日，鲁迅最初将其发表于1926年1月4日《语丝》周刊第60期。\n 奴才总不过是寻人诉苦。只要这样，也只能这样。有一日，他遇到一个聪明人。\n“先生！”他悲哀地说，眼泪联成一线，就从眼角上直流下来。“你知道的。我所过的简直不是人的生活。吃的是一天未必有一餐，这一餐又不过是高粱皮，连猪狗都不要吃的，尚且只有一小碗……”\n“这实在令人同情。”聪明人也惨然说。\n“可不是么！”他高兴了。“可是做工是昼夜无休息的：清早担水晚烧饭，上午跑街夜磨面，晴洗衣裳雨张伞，冬烧汽炉夏打扇。半夜要煨银耳，侍候主人耍钱；头钱从来没分，有时还挨皮鞭……。”\n“唉唉……”聪明人叹息着，眼圈有些发红，似乎要下泪。\n“先生！我这样是敷衍不下去的。我总得另外想法子。可是什么法子呢？……”\n“我想，你总会好起来……”\n“是么？但愿如此。可是我对先生诉了冤苦，又得你的同情和慰安，已经舒坦得不少了。可见天理没有灭绝……”\n但是，不几日，他又不平起来了，仍然寻人去诉苦。\n“先生！”他流着眼泪说，“你知道的。我住的简直比猪窠还不如。主人并不将我当人；他对他的叭儿狗还要好到几万倍……”\n“混帐！”那人大叫起来，使他吃惊了。那人是一个傻子。\n“先生，我住的只是一间破小屋，又湿，又阴，满是臭虫，睡下去就咬得真可以。秽气冲着鼻子，四面又没有一个窗……。”\n“你不会要你的主人开一个窗的么？”\n“这怎么行？……”\n“那么，你带我去看去！”\n傻子跟奴才到他屋外，动手就砸那泥墙。\n“先生！你干什么？”他大惊地说。\n“我给你打开一个窗洞来。”\n“这不行！主人要骂的！”\n“管他呢！”他仍然砸。\n“人来呀！强盗在毁咱们的屋子了！快来呀！迟一点可要打出窟窿来了！……”他哭嚷着，在地上团团地打滚。\n一群奴才都出来了，将傻子赶走。\n听到了喊声，慢慢地最后出来的是主人。\n“有强盗要来毁咱们的屋子，我首先叫喊起来，大家一同把他赶走了。”他恭敬而得胜地说。\n“你不错。”主人这样夸奖他。\n这一天就来了许多慰问的人，聪明人也在内。\n“先生。这回因为我有功，主人夸奖了我了。你先前说我总会好起来，实在是有先见之明……。”他大有希望似的高兴地说。\n“可不是么……”聪明人也代为高兴似的回答他。\n","permalink":"https://blog.archai.space/p/%E8%81%AA%E6%98%8E%E4%BA%BA%E5%92%8C%E5%82%BB%E5%AD%90%E5%92%8C%E5%A5%B4%E6%89%8D/","summary":"《聪明人和傻子和奴才》  这篇散文诗作于1925年12月26日，鲁迅最初将其发表于1926年1月4日《语丝》周刊第60期。\n 奴才总不过是寻人诉苦。只要这样，也只能这样。有一日，他遇到一个聪明人。\n“先生！”他悲哀地说，眼泪联成一线，就从眼角上直流下来。“你知道的。我所过的简直不是人的生活。吃的是一天未必有一餐，这一餐又不过是高粱皮，连猪狗都不要吃的，尚且只有一小碗……”\n“这实在令人同情。”聪明人也惨然说。\n“可不是么！”他高兴了。“可是做工是昼夜无休息的：清早担水晚烧饭，上午跑街夜磨面，晴洗衣裳雨张伞，冬烧汽炉夏打扇。半夜要煨银耳，侍候主人耍钱；头钱从来没分，有时还挨皮鞭……。”\n“唉唉……”聪明人叹息着，眼圈有些发红，似乎要下泪。\n“先生！我这样是敷衍不下去的。我总得另外想法子。可是什么法子呢？……”\n“我想，你总会好起来……”\n“是么？但愿如此。可是我对先生诉了冤苦，又得你的同情和慰安，已经舒坦得不少了。可见天理没有灭绝……”\n但是，不几日，他又不平起来了，仍然寻人去诉苦。\n“先生！”他流着眼泪说，“你知道的。我住的简直比猪窠还不如。主人并不将我当人；他对他的叭儿狗还要好到几万倍……”\n“混帐！”那人大叫起来，使他吃惊了。那人是一个傻子。\n“先生，我住的只是一间破小屋，又湿，又阴，满是臭虫，睡下去就咬得真可以。秽气冲着鼻子，四面又没有一个窗……。”\n“你不会要你的主人开一个窗的么？”\n“这怎么行？……”\n“那么，你带我去看去！”\n傻子跟奴才到他屋外，动手就砸那泥墙。\n“先生！你干什么？”他大惊地说。\n“我给你打开一个窗洞来。”\n“这不行！主人要骂的！”\n“管他呢！”他仍然砸。\n“人来呀！强盗在毁咱们的屋子了！快来呀！迟一点可要打出窟窿来了！……”他哭嚷着，在地上团团地打滚。\n一群奴才都出来了，将傻子赶走。\n听到了喊声，慢慢地最后出来的是主人。\n“有强盗要来毁咱们的屋子，我首先叫喊起来，大家一同把他赶走了。”他恭敬而得胜地说。\n“你不错。”主人这样夸奖他。\n这一天就来了许多慰问的人，聪明人也在内。\n“先生。这回因为我有功，主人夸奖了我了。你先前说我总会好起来，实在是有先见之明……。”他大有希望似的高兴地说。\n“可不是么……”聪明人也代为高兴似的回答他。","title":"《聪明人和傻子和奴才》"},{"content":"用vue.js写成的页面中，可能出现由于外部请求导致的页面展示阻塞现象，进而导致模板语法{{xxx}}直接展示在页面上，这其实跟页面加载的流程有关，整个页面加载的流程是：\n解析html结构 -\u0026gt; 加载外部脚本和样式表文件 -\u0026gt; 解析并执行脚本代码 -\u0026gt; 构造HTML DOM模型 -\u0026gt; 加载图片等外部文件 -\u0026gt; 页面加载完毕\n所以，当HTML加载的时候，就会把{{}} 当成文本加载出来，只有当vue初始化完成后，才会把{{}}解析成vue的语法。\n遇到这种情况有以下几种解决方案：\n  方案一(不推荐)：在head里面加入引入vue的script标签\n如果把引入vue的script放到head里面，那页面就不会出现{{}}，因为在body之前，vue就已经引入进并且加载完成了。\n   方案二：v-text|v-html\n使用v-text插入文字，使用v-html插入HTML结构，替换原本使用的模板语法{{xxx}}\n   方案三：template\n使用\u0026lt;template\u0026gt;标签包裹要渲染的HTML结构，那么只有vue解析时才会展示出该结构\n   方案四：v-cloak指令 + CSS样式\n标签上的v-cloak属性在被vue解析到时会自动去掉，那么我们可以利用CSS的属性选择器，给有v-cloak属性的元素设置display:none样式，那么，在加载HTML时自然不会显示{{xxx}}，而在vue解析时又会显示出来，问题得以解决！\n  ","permalink":"https://blog.archai.space/p/vue%E5%9C%A8%E9%A1%B5%E9%9D%A2%E7%9B%B4%E6%8E%A5%E5%B1%95%E7%A4%BA%E6%A8%A1%E6%9D%BF%E8%AF%AD%E6%B3%95xx/","summary":"用vue.js写成的页面中，可能出现由于外部请求导致的页面展示阻塞现象，进而导致模板语法{{xxx}}直接展示在页面上，这其实跟页面加载的流程有关，整个页面加载的流程是：\n解析html结构 -\u0026gt; 加载外部脚本和样式表文件 -\u0026gt; 解析并执行脚本代码 -\u0026gt; 构造HTML DOM模型 -\u0026gt; 加载图片等外部文件 -\u0026gt; 页面加载完毕\n所以，当HTML加载的时候，就会把{{}} 当成文本加载出来，只有当vue初始化完成后，才会把{{}}解析成vue的语法。\n遇到这种情况有以下几种解决方案：\n  方案一(不推荐)：在head里面加入引入vue的script标签\n如果把引入vue的script放到head里面，那页面就不会出现{{}}，因为在body之前，vue就已经引入进并且加载完成了。\n   方案二：v-text|v-html\n使用v-text插入文字，使用v-html插入HTML结构，替换原本使用的模板语法{{xxx}}\n   方案三：template\n使用\u0026lt;template\u0026gt;标签包裹要渲染的HTML结构，那么只有vue解析时才会展示出该结构\n   方案四：v-cloak指令 + CSS样式\n标签上的v-cloak属性在被vue解析到时会自动去掉，那么我们可以利用CSS的属性选择器，给有v-cloak属性的元素设置display:none样式，那么，在加载HTML时自然不会显示{{xxx}}，而在vue解析时又会显示出来，问题得以解决！\n  ","title":"Vue在页面直接展示模板语法{{xx}}"},{"content":"Bilibli   tecent   video  Your browser doesn't support HTML5 video. Here is a link to the video instead.   ","permalink":"https://blog.archai.space/p/%E6%B5%8B%E8%AF%95shortcodes/","summary":"Bilibli   tecent   video  Your browser doesn't support HTML5 video. Here is a link to the video instead.   ","title":"测试shortcodes"},{"content":"webStorage   存储内容大小一般支持5MB左右(不同浏览器可能还不一样)。\n  浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制。\n  相关API:\n  xxxStorage.setItem('key','value');\n该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。\n  xxxStorage.getItem('key');\n该方法接受一个键名作为参数，返回键名对应的值。\n  xxxStorage.removeItem('key');\n该方法接受一个键名作为参数，并把该键名从存储中删除。\n  xxxStorage.clear();\n该方法会清空存储中的所有数据。\n    备注:\n SessionStorage存储的内容会随着浏览器窗口关闭而消失。 LocalStorage存储的内容，需要手动清除才会消失。 xxxStorage.getItem('key');如果key对应的value获取不到，那么getItem的返回值是null。 可以通过JSON.stringify()存储对象，通过JSON.parse()解析对象，JSON.parse(null)的结果仍然是null。    ","permalink":"https://blog.archai.space/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8/","summary":"webStorage   存储内容大小一般支持5MB左右(不同浏览器可能还不一样)。\n  浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制。\n  相关API:\n  xxxStorage.setItem('key','value');\n该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。\n  xxxStorage.getItem('key');\n该方法接受一个键名作为参数，返回键名对应的值。\n  xxxStorage.removeItem('key');\n该方法接受一个键名作为参数，并把该键名从存储中删除。\n  xxxStorage.clear();\n该方法会清空存储中的所有数据。\n    备注:\n SessionStorage存储的内容会随着浏览器窗口关闭而消失。 LocalStorage存储的内容，需要手动清除才会消失。 xxxStorage.getItem('key');如果key对应的value获取不到，那么getItem的返回值是null。 可以通过JSON.stringify()存储对象，通过JSON.parse()解析对象，JSON.parse(null)的结果仍然是null。    ","title":"浏览器本地存储"},{"content":"1.vue会监视data中所有层次的数据. vue对于配置在data中的所有对象都会以key值作为目标进行监视，不论多少层，都会递归地为每一个key配置getter和setter(通过Object.defineProperty())。\n 相关阅读 MDN关于defineProperty的说明\n 2.如何监测对象中的数据? 通过setter实现监视,且要在new Vue时就传入要监测的数据，即在data中提前配置。\n(1).对象中后追加的属性,Vue默认不做响应式处理\n直接通过修改vm._data或者vm来添加key，并不会配置getter和setter，因此不会做响应式\n(2).如需给后添加的属性做响应式,请使用如下API: Vue.set(target,key/index,value)或vm.$set(target,key/index,value)（可以同时修改数组和对象）\n​\t特别注意: Vue.set()和vm.$set()不能直接给vm或vm的根数据对象(vm._data)添加属性!!!\n3.如何监测数组中的数据? 在数组中直接通过索引的方式修改数组并不会引起Vue的响应，那么要如何做呢？Vue通过包裹数组更新元素的方法(对原数组方法进行修改)实现,本质就是做了两件事:\n(1). 调用原生对应的方法对数组进行更新.\n(2). 重新解析模板,进而更新页面.\n就包裹了下面的七种数组方法：\n push() pop() shift() unshift() splice() sort() reverse()  也就是说，通过上面七种方法修改数组，会让Vue做出响应。\n例如想要修改数组中索引为0的位置的数据，可以通过 vm.data.arr.splice(0,0,val)或Vue.set(vm.data.arr,0,val)又或者vm.$set(vm.data.arr,0,val)，在具体的由Vue管理的方法中this指向的应该就是vm！！\n数据劫持 Vue通过setter,getter对数据进行动态监测，其实就是“劫持”了数据😯\n","permalink":"https://blog.archai.space/p/vue%E4%B8%AD%E7%9A%84%E6%95%B0%E6%8D%AE%E7%9B%91%E8%A7%86%E5%8E%9F%E7%90%86/","summary":"1.vue会监视data中所有层次的数据. vue对于配置在data中的所有对象都会以key值作为目标进行监视，不论多少层，都会递归地为每一个key配置getter和setter(通过Object.defineProperty())。\n 相关阅读 MDN关于defineProperty的说明\n 2.如何监测对象中的数据? 通过setter实现监视,且要在new Vue时就传入要监测的数据，即在data中提前配置。\n(1).对象中后追加的属性,Vue默认不做响应式处理\n直接通过修改vm._data或者vm来添加key，并不会配置getter和setter，因此不会做响应式\n(2).如需给后添加的属性做响应式,请使用如下API: Vue.set(target,key/index,value)或vm.$set(target,key/index,value)（可以同时修改数组和对象）\n​\t特别注意: Vue.set()和vm.$set()不能直接给vm或vm的根数据对象(vm._data)添加属性!!!\n3.如何监测数组中的数据? 在数组中直接通过索引的方式修改数组并不会引起Vue的响应，那么要如何做呢？Vue通过包裹数组更新元素的方法(对原数组方法进行修改)实现,本质就是做了两件事:\n(1). 调用原生对应的方法对数组进行更新.\n(2). 重新解析模板,进而更新页面.\n就包裹了下面的七种数组方法：\n push() pop() shift() unshift() splice() sort() reverse()  也就是说，通过上面七种方法修改数组，会让Vue做出响应。\n例如想要修改数组中索引为0的位置的数据，可以通过 vm.data.arr.splice(0,0,val)或Vue.set(vm.data.arr,0,val)又或者vm.$set(vm.data.arr,0,val)，在具体的由Vue管理的方法中this指向的应该就是vm！！\n数据劫持 Vue通过setter,getter对数据进行动态监测，其实就是“劫持”了数据😯","title":"Vue中的数据监视原理"},{"content":"在浏览网页时无意中发现了下面这种背景互动的效果：\n\n通过一番周折，最终找到以下实现代码\nfunction randomLine() { function n(n, e, t) { return n.getAttribute(e) || t } function e(n) { return document.getElementsByTagName(n) } function t() { var t = e(\u0026#34;script\u0026#34;), o = t.length, i = t[o - 1]; return { l: o, z: n(i, \u0026#34;zIndex\u0026#34;, -88), o: n(i, \u0026#34;opacity\u0026#34;, .5), c: n(i, \u0026#34;color\u0026#34;,\u0026#34;0,0,0\u0026#34;), n: n(i, \u0026#34;count\u0026#34;, 150) } } function o() { a = m.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, c = m.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight } function i() { r.clearRect(0, 0, a, c); var n, e, t, o, m, l; s.forEach(function (i, x) { for (i.x += i.xa, i.y += i.ya, i.xa *= i.x \u0026gt; a || i.x \u0026lt; 0 ? -1 : 1, i.ya *= i.y \u0026gt; c || i.y \u0026lt; 0 ? -1 : 1, r.fillRect(i.x - .5, i.y - .5, 1, 1), e = x + 1; e \u0026lt; u.length; e++)n = u[e], null !== n.x \u0026amp;\u0026amp; null !== n.y \u0026amp;\u0026amp; (o = i.x - n.x, m = i.y - n.y, l = o * o + m * m, l \u0026lt; n.max \u0026amp;\u0026amp; (n === y \u0026amp;\u0026amp; l \u0026gt;= n.max / 2 \u0026amp;\u0026amp; (i.x -= .03 * o, i.y -= .03 * m), t = (n.max - l) / n.max, r.beginPath(), r.lineWidth = t / 2, r.strokeStyle = \u0026#34;rgba(\u0026#34; + d.c + \u0026#34;,\u0026#34; + (t + .2) + \u0026#34;)\u0026#34;, r.moveTo(i.x, i.y), r.lineTo(n.x, n.y), r.stroke())) }), x(i) } var a, c, u, m = document.createElement(\u0026#34;canvas\u0026#34;), d = t(), l = \u0026#34;c_n\u0026#34; + d.l, r = m.getContext(\u0026#34;2d\u0026#34;), x = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (n) { window.setTimeout(n, 1e3 / 45) }, w = Math.random, y = { x: null, y: null, max: 2e4 }; m.id = l, m.style.cssText = \u0026#34;position:fixed;top:0;left:0;z-index:\u0026#34; + d.z + \u0026#34;;opacity:\u0026#34; + d.o, e(\u0026#34;body\u0026#34;)[0].appendChild(m), o(), window.onresize = o, window.onmousemove = function (n) { n = n || window.event, y.x = n.clientX, y.y = n.clientY }, window.onmouseout = function () { y.x = null, y.y = null }; for (var s = [], f = 0; d.n \u0026gt; f; f++) { var h = w() * a, g = w() * c, v = 2 * w() - 1, p = 2 * w() - 1; s.push({ x: h, y: g, xa: v, ya: p, max: 6e3 }) } u = s.concat([y]), setTimeout(function () { i() }, 100) }; 实现的效果如下：\n 观察一下代码可以看到以下几个可以设置的参数：\nzIndex canvas在页面的层级关系\nopacity 生成线条的透明度(实则为canvas的整体透明度)\ncolor 即所有线条的颜色\ncount 是线条的数量\n如果让颜色透明度随机产生，加上下面的代码\n//generate random color code function r() { return Math.floor(Math.random() * 255); } function rc() { return `${r()},${r()},${r()}` } function ro() { return (Math.floor((Math.random() * 4 + 5))) / 10 } 并且修改function t()的内容\nfunction t() { var t = e(\u0026#34;script\u0026#34;), o = t.length, i = t[o - 1]; return { l: o, z: n(i, \u0026#34;zIndex\u0026#34;, -88), o: n(i, \u0026#34;opacity\u0026#34;, ro()), c: n(i, \u0026#34;color\u0026#34;, rc()), n: n (i, \u0026#34;count\u0026#34;, 300) } } 则会有下面的效果(双击随机改变颜色和透明度)\n 此外，\n掌握以上方法，甚至还可以让每条线的颜色都不同，但是会很占用资源😥\n","permalink":"https://blog.archai.space/p/canvas%E7%BB%98%E5%88%B6%E8%B7%9F%E9%9A%8F%E9%BC%A0%E6%A0%87%E7%A7%BB%E5%8A%A8%E7%9A%84%E7%BA%BF%E6%9D%A1/","summary":"在浏览网页时无意中发现了下面这种背景互动的效果：\n\n通过一番周折，最终找到以下实现代码\nfunction randomLine() { function n(n, e, t) { return n.getAttribute(e) || t } function e(n) { return document.getElementsByTagName(n) } function t() { var t = e(\u0026#34;script\u0026#34;), o = t.length, i = t[o - 1]; return { l: o, z: n(i, \u0026#34;zIndex\u0026#34;, -88), o: n(i, \u0026#34;opacity\u0026#34;, .5), c: n(i, \u0026#34;color\u0026#34;,\u0026#34;0,0,0\u0026#34;), n: n(i, \u0026#34;count\u0026#34;, 150) } } function o() { a = m.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, c = m.","title":"canvas绘制跟随鼠标移动的线条"},{"content":"创建U盘介质  有至少 8GB 空间的空白 U 盘，若不是空白创建介质时会自动清空。\n 🐾官方工具: https://go.microsoft.com/fwlink/?LinkId=691209\n 直接下载按照提示操作就行了，不过仅限win10\n 🐾ventoy(绿色开源)： 官网 蓝奏云 直链下载\n\n第一步：选择设备\n第二步：安装\n第三步：将下载的系统镜像 .iso 文件移动到U盘即可，这里推荐一个下载windows正版系统的网站 ITELLYOU\n注意保存必要文件 如果电脑无法正常打开 ","permalink":"https://blog.archai.space/p/%E9%80%9A%E8%BF%87u%E7%9B%98%E5%AE%89%E8%A3%85windows%E7%B3%BB%E7%BB%9F/","summary":"创建U盘介质  有至少 8GB 空间的空白 U 盘，若不是空白创建介质时会自动清空。\n 🐾官方工具: https://go.microsoft.com/fwlink/?LinkId=691209\n 直接下载按照提示操作就行了，不过仅限win10\n 🐾ventoy(绿色开源)： 官网 蓝奏云 直链下载\n\n第一步：选择设备\n第二步：安装\n第三步：将下载的系统镜像 .iso 文件移动到U盘即可，这里推荐一个下载windows正版系统的网站 ITELLYOU\n注意保存必要文件 如果电脑无法正常打开 ","title":"通过U盘安装Windows系统"},{"content":"公式 \n定理 \n附录 \nYO起来！   ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E5%B8%B8%E7%94%A8%E5%85%AC%E5%BC%8F%E5%AE%9A%E7%90%86%E6%80%BB%E7%BB%93/","summary":"公式 \n定理 \n附录 \nYO起来！   ","title":"线性代数——常用公式定理总结"},{"content":"性质定理  二次型$x^{T}Ax$经坐标变换$x=Cy$得二次型$y^{T}By$，其中 $B=C^{T}C$，通俗的讲，就是 二次型矩阵经过任意的一次坐标变换得到新的二次型，前后二次型矩阵合同。\n  任一二次型都可经坐标变换变成标准型。\n  配方法\n从$x_1$开始先合并再配方，将 f(x)⇒f(y)\n  正交变换法\n   例题 \n 说明在正交变换下，A不仅与∧合同，而且A与∧相似\n 用正交变换化二次型为标准形  参考用正交矩阵相似对角化\n \n 若是求正负惯性指数，根据特征值即可得到（特征值是二次型二次项系数✅）\n 标准型到规范型 \n\n 标准型到规范性与系数的大小无关，只与系数的正负相关，即正负惯性系数，注意先排序，依次“大于0，等于0，小于0\u0026hellip;”\n 例题 \n 第一问参数的求解用到标准型化成规范型的规律。\n第二问的求解实际上就是正交矩阵相似对角化\n 正定  \n  注意正定矩阵是二次型，所以正定矩阵必对称，检验正定之前，先检验对称性，即证明$A^{T}=A$\n 正定必要条件  $a_{ii}\u0026gt;0$ |A| \u0026gt; 0  正定充分必要条件  顺序主子式全大于0 特征值大于0 正惯性指数 p=n $A=C^TEC$，其中C可逆 ▲  经典例题 \n $(A^{T}A)^{T}=A^TA$，故$A^{T}A$实对称矩阵\n现证$\\forall X$，有$X^T(A^TA)X\u0026gt;0$\n$X^T(A^TA)X=(AX)^{T}AX \\geq 0$\n现证$(AX)^{T}AX \\neq 0$\n相当于证 $AX \\neq0$\n由于A-m×n，且r(A)=n\n故 AX=0只有零解\n等价于 $\\forall X\\neq0,AX \\neq0$\n得证\n即$\\forall X, X^T(A^TA)X=(AX)^{T}AX \u0026gt; 0$\n故$A^TA$为正定矩阵\n  补充：“反对称矩阵”\n$A^T=-A$\n规律：\n$a_{ii} = 0\\text{；}a_{ij}=-a_{ji}$\n$|A|=|A^T|=|A^{-1}|=(-1)^n|A|$，即奇数项行列式必为0\n 合同 \n 相似\n若不是实对称矩阵，为普通矩阵，则通过∧作为媒介判断，其中∧为特征值组成的🤓\n\n即 “相似⇒合同，合同⇏相似”\n ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E4%BA%8C%E6%AC%A1%E5%9E%8B/","summary":"性质定理  二次型$x^{T}Ax$经坐标变换$x=Cy$得二次型$y^{T}By$，其中 $B=C^{T}C$，通俗的讲，就是 二次型矩阵经过任意的一次坐标变换得到新的二次型，前后二次型矩阵合同。\n  任一二次型都可经坐标变换变成标准型。\n  配方法\n从$x_1$开始先合并再配方，将 f(x)⇒f(y)\n  正交变换法\n   例题 \n 说明在正交变换下，A不仅与∧合同，而且A与∧相似\n 用正交变换化二次型为标准形  参考用正交矩阵相似对角化\n \n 若是求正负惯性指数，根据特征值即可得到（特征值是二次型二次项系数✅）\n 标准型到规范型 \n\n 标准型到规范性与系数的大小无关，只与系数的正负相关，即正负惯性系数，注意先排序，依次“大于0，等于0，小于0\u0026hellip;”\n 例题 \n 第一问参数的求解用到标准型化成规范型的规律。\n第二问的求解实际上就是正交矩阵相似对角化\n 正定  \n  注意正定矩阵是二次型，所以正定矩阵必对称，检验正定之前，先检验对称性，即证明$A^{T}=A$\n 正定必要条件  $a_{ii}\u0026gt;0$ |A| \u0026gt; 0  正定充分必要条件  顺序主子式全大于0 特征值大于0 正惯性指数 p=n $A=C^TEC$，其中C可逆 ▲  经典例题 \n $(A^{T}A)^{T}=A^TA$，故$A^{T}A$实对称矩阵","title":"线性代数——二次型"},{"content":"特征值与特征向量 性质  1.不同特征值的特征向量线性无关。\n2.ｋ重特征值至多有ｋ个线性无关的特征向量。(※)\n 基本运算逻辑  \n  三角矩阵特征值为主对角线元素⭐\n注意，当$\\lambda=1$时😢\n  秩为1的矩阵A\n 特征值 $\\sum_{aii},0,0\u0026hellip;$ $A^n=\\sum_{aii}^{n-1} A$​  特征值证明：\n  求特征值不能先对A做任何变换，而应该带着λ变换\n 定义法  \n  如果两矩阵相似\n\n即 “两矩阵相似则具有相同特征值，特征向量也存在关系”\n 真题\u0026amp;例题 常识题？😢\n\n关于AB=0引申出的一题多解\n\n相似  AB相似，则 A+kE ~ B+kE\n 行列式相等 俩秩相等 特征值相等 绩相等  利用已知构造方程组求参，进行预处理\n  由 $A^n=PB^nP^{-1}$\n 若取B为∧，容易求得$A^n$​的值。(其他方法) 特征值也存在关系。(特征值规律)    用相似的传递性证明两矩阵相似\n A~∧ ⭐  A~∧，$A=P∧P^{-1}$则\n ∧是矩阵A的特征值 P列向量是A的特征向量  （要对应，别错位）\n A~∧ ⇔ 矩阵A有n个线性无关的特征向量。 （注意是充要条件）  \n  要求解A~∧(相似对角化) ，注意下面图解\n 检查是否和对称矩阵相似 检查是否有n个特征值 检查k重特征值是否有k个无关的特征向量(n-r(λE-A))  \n\n 例题 \n 一正一负必能相似对角化\n \n 第(1)问这一思路一定要熟练把握$A\\alpha$出现求特征值，想到用定义！！\n第（2）问两种思路，\n 已知特征值、特征向量反求A矩阵\n\n实对称 性质    实对称矩阵必与对角矩阵相似；\n  实对称矩阵特征值不同特征向量相互正交；\n 内积为0 ⇒ 齐次方程组 ⇒ 求得特征值\n   实对称矩阵可用正交矩阵相似对角化\n $Q^{-1}AQ=Q^{T}AQ$\n   实对称矩阵特征值必为实数\n   例题 \n用正交矩阵相似对角化 \n 对比相似矩阵对角化\n这里需要用到正交化↧\n Schmidt正交化 \n ||β|| 是向量的长度\n分子为内积\n分母为平方和\n ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E7%89%B9%E5%BE%81%E5%80%BC%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F/","summary":"特征值与特征向量 性质  1.不同特征值的特征向量线性无关。\n2.ｋ重特征值至多有ｋ个线性无关的特征向量。(※)\n 基本运算逻辑  \n  三角矩阵特征值为主对角线元素⭐\n注意，当$\\lambda=1$时😢\n  秩为1的矩阵A\n 特征值 $\\sum_{aii},0,0\u0026hellip;$ $A^n=\\sum_{aii}^{n-1} A$​  特征值证明：\n  求特征值不能先对A做任何变换，而应该带着λ变换\n 定义法  \n  如果两矩阵相似\n\n即 “两矩阵相似则具有相同特征值，特征向量也存在关系”\n 真题\u0026amp;例题 常识题？😢\n\n关于AB=0引申出的一题多解\n\n相似  AB相似，则 A+kE ~ B+kE\n 行列式相等 俩秩相等 特征值相等 绩相等  利用已知构造方程组求参，进行预处理\n  由 $A^n=PB^nP^{-1}$\n 若取B为∧，容易求得$A^n$​的值。(其他方法) 特征值也存在关系。(特征值规律)    用相似的传递性证明两矩阵相似\n A~∧ ⭐  A~∧，$A=P∧P^{-1}$则","title":"线性代数——特征值特征向量"},{"content":"Ax=0  重点： n-r(A)，基础解系\n  Ax=0有非零解 $\\Leftrightarrow $​​ r(A) \u0026lt; n (未知数个数)$\\Leftrightarrow $​​​ A的列向量线性相关\n特别地(针对水平型阵和方阵)，※\n A-m×n,m\u0026lt;n, Ax=0 必有非零解 A-n×n，Ax=0有非零解 $\\Leftrightarrow $​ |A|=0（克拉默法则）    Ax=0有非零解，则其解线性组合必为Ax=0的解，所以Ax=0若有非零解，则必有无穷多解，在这无穷多解中，线性无关的解向量个数为 $t=n-r(A)$​​​​​，这就是基础解系​，基础解系是Ax=0的解向量中的极大线性无关组。此外，根据定理可知，Ax=0的任一解都可以由基础解系线性表出。\n  基础解系⭐\n 是解 是线性无关组 个数t=n-r(A)   真题 \n 【分析】“基础解系不存在”即为Ax=0没有非零解，根据 t=n-r(A) 知，本题等价转化为求解A的秩，求解秩时，找个大，找个小，通过夹逼，确定秩的值。\n【解】$A^{*}\\neq0$，则$r(A^{ *})\\geq 1$​，则$r(A)\\geq n-1$\nAx=b有解且不唯一，则$r(A) \u0026lt; n$\n联立得，r(A)=n-1\n故t = n-r(A) = 1\n ⭐⭐⭐⭐⭐\n\n 【分析】根据通解的定义可知，若为通解，解必然线性无关，本题转化为证明 $a_1,a_2,a_3,a_4$哪三个线性无关\n\n这里用到了“低维无关，添加向量后的高维必无关”\n  此外，如果本题为证明计算题（证明基础解系），即 求$A^{*}x=0$​ 的基础解系，则\n 根据$t=n-r(A^{*})$得基础解系解向量个数。  求解$r(A^{*})$​ (找大找小) ，   根据A不可逆得，$|A|=0, r(A)\u0026lt; n=4$​​ 由 $A_{12} \\neq 0$​，则$r(A) \\geq n-1=3$​​ 则r(A)=3，则$r(A^{*})$ = 1  t = 4-1=3，即基础解系有三个解向量   证明是解  $A^{*}A=|A| E=0,\\text{且}|A|=0$​​ ，有 $A^{ *} (a_1a_2a_3a_4)=0$​​，则向量$a_1,a_2,a_3,a_4$​​ 均为$A^{ *}x=0$​​​ 的解   证明线性无关（上面选择题过程）  🤓\n Ax=b  重点：有解判定，解的结构\n  解的性质：\n 非齐的解相减为齐的解 齐的解+非齐解为非齐解    解的结构：非齐特解+齐的基础解系=非齐通解\n 公共解、同解 （一）公共解   两个方程解联立得公共解 已知一个方程的基础解系，求得另一个方程的基础解系，设$\\beta$​为公共解，则$\\beta$​能由两个基础解系分别表出，进而相减得齐次方程组求解。  注意零解一定是公共解。\n\n （二）同解  对Ax=0，Bx=0，若同解，则\nn-r(A)=n-r(B)\n故必要条件 r(A)-r(B)\n \n秩关系的证明 \n应用 \n 根据已知，假设出C，解方程组拼出C，考察基本功\n 命题：若Ax=0只有零解，那么Ax=b有唯一解 ❌\n \n ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E6%96%B9%E7%A8%8B%E7%BB%84/","summary":"Ax=0  重点： n-r(A)，基础解系\n  Ax=0有非零解 $\\Leftrightarrow $​​ r(A) \u0026lt; n (未知数个数)$\\Leftrightarrow $​​​ A的列向量线性相关\n特别地(针对水平型阵和方阵)，※\n A-m×n,m\u0026lt;n, Ax=0 必有非零解 A-n×n，Ax=0有非零解 $\\Leftrightarrow $​ |A|=0（克拉默法则）    Ax=0有非零解，则其解线性组合必为Ax=0的解，所以Ax=0若有非零解，则必有无穷多解，在这无穷多解中，线性无关的解向量个数为 $t=n-r(A)$​​​​​，这就是基础解系​，基础解系是Ax=0的解向量中的极大线性无关组。此外，根据定理可知，Ax=0的任一解都可以由基础解系线性表出。\n  基础解系⭐\n 是解 是线性无关组 个数t=n-r(A)   真题 \n 【分析】“基础解系不存在”即为Ax=0没有非零解，根据 t=n-r(A) 知，本题等价转化为求解A的秩，求解秩时，找个大，找个小，通过夹逼，确定秩的值。\n【解】$A^{*}\\neq0$，则$r(A^{ *})\\geq 1$​，则$r(A)\\geq n-1$\nAx=b有解且不唯一，则$r(A) \u0026lt; n$\n联立得，r(A)=n-1\n故t = n-r(A) = 1\n ⭐⭐⭐⭐⭐\n\n 【分析】根据通解的定义可知，若为通解，解必然线性无关，本题转化为证明 $a_1,a_2,a_3,a_4$哪三个线性无关\n\n这里用到了“低维无关，添加向量后的高维必无关”\n  此外，如果本题为证明计算题（证明基础解系），即 求$A^{*}x=0$​ 的基础解系，则","title":"线性代数——方程组"},{"content":"相关、无关    向量组中含零向量必然线性相关\n  组中至少存在两个成比例的向量必然线性相关\n   相关计算  $A(\\alpha_1,\\alpha_2,\\alpha_3\u0026hellip;\\alpha_s)$​ 是否线性相关？\n$\\Leftrightarrow$​​​ AX=0是否有非零解？(联系克拉默法则)\n$\\Leftrightarrow$​​ r(A) \u0026lt; s\n 特别的，对于n维向量：⭐⭐\n  n个n维向量相关 $\\Leftrightarrow$​ 行列式得0，即|A|=0 n+1个n维向量必然线性相关   此外，还有以下几何性质\n “$\\alpha$相关”$\\Leftrightarrow$ $\\alpha=0$\n“$\\alpha_1,\\alpha_2$相关”$\\Leftrightarrow$ $\\alpha_1,\\alpha_2$​共线\n 存在$\\alpha_1=k\\alpha_2$\n “$\\alpha_1,\\alpha_2,\\alpha_3$​相关”$\\Leftrightarrow$​ $\\alpha_1,\\alpha_2,\\alpha_3$​​共面\n 证明、选择  这是难点，重点\n 证明线性无关 ​\t\n ⭐特征值不同的特征向量必线性无关。\n此外，不同特征值中如果某一特征值存在“一对多”的关系，这些特征向量也线性无关。即若$A \\alpha_1=\\lambda_1\\alpha_1,A \\alpha_2=\\lambda_1\\alpha_2,A \\alpha=\\lambda\\alpha$，那么$\\alpha_1,\\alpha_2,\\alpha_3$​线性无关​\n  恒等变形中乘的思路：\n 利用已知构造出0使得式子变短 直接两边乘A，得出式子，然后通过两个式子的加加减减化简   真题\u0026amp;经典例题\n\n 本题第二问“写出和A相似的矩阵”(3分)\n分析：$\\alpha_1，\\alpha_2，\\alpha_3$无关，后面出现$A\\alpha_1，A\\alpha_2，A\\alpha_3$​想到相似\n \n 本题可以当作结论使用\n \n 注意等式 $r(\\beta_1,\\beta_2) ≤ n-r(A)$​​ 的含义，对于一个齐次方程组AX=0，其未知数为n个，则其线性无关的解有 n-r(A) 个，即所有解的秩 ≤ n-r(A)\n 线性表出 计算 定理  转化为AX=B后有如下情况(如果含参)\n  现题型多为两个向量小组互相线性表出的问题\n$\\alpha_1\\alpha_1\\alpha_1\u0026hellip;\\alpha_s$​​ (1）\n$\\beta_1 \\beta_2 \\beta_3 \u0026hellip;\\beta_t$ (2)\n“向量组线性表出”$\\rightarrow$​ 组中任意向量可以由另一向量组线性表出\n“向量组等价” $\\rightarrow$​ 可互相线性表出\n“矩阵等价”$\\rightarrow$​ 秩相等\n 真题 \n 解：（定义法）\n\n解法二（秩的方法）\n\n 选择、证明 定理\u0026amp;方法⭐⭐⭐  (设A为向量组$\\alpha_1\\alpha_2\u0026hellip;\\alpha_s$ ，B为$\\beta_1\\beta_2\u0026hellip;\\beta_t$)\n  子集合相关，整体必相关\n  低维无关，高维必无关\n基础解系求解过程中，先构造1 0/0 1两个无关的低维向量，然后带到方程中求导的另外两个值后，组成的高维向量必然无关。\n  A线性相关 $\\Leftrightarrow $ 存在$\\alpha_i$​​ 可由A中剩余向量线性表出\n  A线性无关，$A,\\beta$​线性相关，则$\\beta$​​​可以由A线性表出，且 表示方法唯一\n  多数向量可由少数向量线性表出，多必线性相关\n (判断向量个数) A无关，A可由B线性表出，则s ≤ t\n   A可由B线性表出，则r(A) ≤ r(B)\n   方法总结🙅‍\n(1) 找出两个条件：向量组A无关，(A,β)相关(定理4)\n(2) 构造方程组，证明方程组有界 » r(A)=r(Ā)\n(3) 存在等式，找出k≠0，移项，做分母，得到线性表出（证能表出时）\n(4) 反证法！！（证不能表出时） 真题\u0026amp;例题 \n (1)两种方法\n(2)还是两种方法🤓\n \n秩 向量组的秩——极大无关组  向量组的极大线性无关组往往是不唯一的，其成员可以不一样，但极大线性无关组中向量的个数是一样的，由此引出向量组秩的概念，向量组的秩为r就是指该向量组的极大线性无关组有r个向量\n  注意以下几个说法等价：😮\n\n 例题 \n 解法(一)——定理6\n Ⅰ可以由Ⅱ表出，\n则r(Ⅰ) ≤ r(Ⅱ) ≤ s\n根据A，若Ⅰ无关，则r(Ⅰ) = r\n故 r ≤ s，A说法正确\n 解法二：定理5推论\n Ⅰ可以由Ⅱ表出，Ⅰ无关，则 r ≤ s\n   本题可以举反例加以说明\n\n 矩阵的秩  前面有涉及，回顾 GO\n 此外\n r(A) = A的列秩 = A的行秩 (概念不同，数值相等)\n就是 A的秩等于A的列向量组的秩，那么可以用列向量组的秩求解矩阵的秩，反过来用矩阵的秩可以求得向量组的秩。\n  ⭐经过初等变换矩阵的秩不变，也就意味着，在进行秩的计算时，可以对矩阵既做行变换，又做列变换，这点要区别于方程组计算过程中尽可以做行变换。\n 公式    $r(A^TA) = r(A)$ （证明过程）\n  $r(kA) = r(A)$​，当k≠0\n  $r(A+B)≤ r(A)+r(B)$​\n  r(AB)≤ min{r(A),r(B)},A-m×n,B-n×s\n  A可逆，r(AB)=r(B),r(BA)=r(B)\n   真题 \n 先利用特殊值排除掉简单的CD选项\nA选项的判断利用矩阵中AB=C的规律，GO\n ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E5%90%91%E9%87%8F/","summary":"相关、无关    向量组中含零向量必然线性相关\n  组中至少存在两个成比例的向量必然线性相关\n   相关计算  $A(\\alpha_1,\\alpha_2,\\alpha_3\u0026hellip;\\alpha_s)$​ 是否线性相关？\n$\\Leftrightarrow$​​​ AX=0是否有非零解？(联系克拉默法则)\n$\\Leftrightarrow$​​ r(A) \u0026lt; s\n 特别的，对于n维向量：⭐⭐\n  n个n维向量相关 $\\Leftrightarrow$​ 行列式得0，即|A|=0 n+1个n维向量必然线性相关   此外，还有以下几何性质\n “$\\alpha$相关”$\\Leftrightarrow$ $\\alpha=0$\n“$\\alpha_1,\\alpha_2$相关”$\\Leftrightarrow$ $\\alpha_1,\\alpha_2$​共线\n 存在$\\alpha_1=k\\alpha_2$\n “$\\alpha_1,\\alpha_2,\\alpha_3$​相关”$\\Leftrightarrow$​ $\\alpha_1,\\alpha_2,\\alpha_3$​​共面\n 证明、选择  这是难点，重点\n 证明线性无关 ​\t\n ⭐特征值不同的特征向量必线性无关。\n此外，不同特征值中如果某一特征值存在“一对多”的关系，这些特征向量也线性无关。即若$A \\alpha_1=\\lambda_1\\alpha_1,A \\alpha_2=\\lambda_1\\alpha_2,A \\alpha=\\lambda\\alpha$，那么$\\alpha_1,\\alpha_2,\\alpha_3$​线性无关​\n  恒等变形中乘的思路：\n 利用已知构造出0使得式子变短 直接两边乘A，得出式子，然后通过两个式子的加加减减化简   真题\u0026amp;经典例题\n\n 本题第二问“写出和A相似的矩阵”(3分)\n分析：$\\alpha_1，\\alpha_2，\\alpha_3$无关，后面出现$A\\alpha_1，A\\alpha_2，A\\alpha_3$​想到相似","title":"线性代数——向量"},{"content":"基本计算  对角矩阵存在交换律，即 $\\bigwedge_1 \\bigwedge_2=\\bigwedge_2 \\bigwedge_1$​\n  注意交换位置\n 对于 AB=C  B \u0026amp; C均按列分块，则$A(\\beta_1 \\beta_2 \\beta_3)=(\\gamma_1 \\gamma_2 \\gamma_3)$，故B的列向量都是$AX=\\gamma$ 的解，特别的，当AB=O即C=O时，B的列向量均是 $AX=0$ 的解。 B \u0026amp; C按行分块，则 AB的行向量均可由B的行向量线性表出。 A \u0026amp; C按列分块，则 AB的列向量均可由A的列向量线性表出。[2013]   规律1与解联系起来，尤其是AB=O推出B的列向量是Ax=0的解这一规律，除此之外AB=O也经常用r(A)+r(B)≤ n这个不等式。\n\u0026ldquo;AB=O\u0026rdquo; 👉 方程的解(B的列向量是A的解)\n​\t👉 秩 r(A)+r(B) ≤ n (n为A的列,B的行)\n  规律2,3与线性表出关联，进而可以跟秩，向量组等价(能互相线性表出则等价)联系起来。\n 几个特殊符号  $a(a_1,a_2,a_3)^T$\n 矩阵: $ab^T$ = $(ba^T)^T$\n $r(ab^T)$​​ ≤ $r(a)$​ ≤ 1\n任何两行成比例\n $aa^T$: 对称矩阵\n数: $a^Tb = b^Ta$: $ab^T$ 或 $ba^T$​​​的绩(主对角线元素之和)\n$a^Ta$: 平方和\n$A^n$​​  主要有三种形式\n 题型一：r(A)=1 \n 若秩r(A)=1,则A可分解为一个列向量与一个行向量的乘积,有A2=A之规律,从而$A^{n}=l^{n-1}A$\n\n即$A^{n}=(\\sum a_{ii})^{n-1}A$​​\n 题型二：主对角线和一侧全为0 \n 这类对于n阶矩阵A，$A^{n}=0$\n \n题型三：相似  $A^{n}=PB^{n}P^{-1}$​​，关键在于选择B矩阵，\n  通常$A ∽ \\wedge$​ ，即选B为与A相似对角矩阵，根据与相似对角化的知识，有$A^n=P \\wedge^{n} P^{-1}$​，P为特征向量组，$\\wedge$​​对角元素为A特征值。\n \n 解：\n\n 伴随矩阵，可逆矩阵 （一）伴随矩阵  由基本公式$AA^{}=A^{}A=|A|E$​,可以推导出以下几个公式：\n $A^{-1}= \\frac{1}{|A|}A^{*}$ $(A^{-1})^{ * } =(A^{*})^{-1}=\\frac{1}{|A|}A$​​    设A是n阶矩阵\n证明过程：\n\n 真题 \n 由 $A^{*}=A^{T}$，可得：\n$A_{ij}=a_{ij},\\forall i,j=1,2,3$\n则 对A按照第一行展开有：\n$A=a_{11}A_{11}+a_{12}A_{12}+a_{13}A_{13}=(a_{11})^2+(a_{12})^2+(a_{13})^2=3(a_{11})^2$​​​​\n又根据 $A^{*}=A^{T}$ 有：\n$|A^{*}|=|A^{T}|$\n即，$|A|^{3-1}=|A|$​​，$|A|(|A|-1)=0$​，易知|A|≠0，\n故 A=1​\n进而 $3(a_{11})^2=1$ ，$a_{11}=\\frac{√3}{2}$\n （二）可逆矩阵  A可逆 $\\Leftrightarrow$ |A| ≠ 0\n​\t$\\Leftrightarrow$​ r(A) = n\n​\t$\\Leftrightarrow$​ A的列(行)向量线性无关\n​\t$\\Leftrightarrow$ 0不是A的特征值\n 真题 \n 单位矩阵与矩阵的转置\n\n 初等矩阵  用初等矩阵P左（右）乘矩阵A，结果PA（AP）就是对矩阵A作一次相应的初等行（列）变换。\n“左乘行变换，右乘列变换”\n  初等矩阵均可逆，且其逆是同类型的初等矩阵:\n$[\\text{倍加}]^{-1}$​​=[倍数取反]​\n$[\\text{倍乘}]^{-1}$​​​=[取倒数] (对角矩阵也能理解)\n$[\\text{互换}]^{-1}$​=[不变]\n 例题 \n 观察下标得变换规律\n 正交矩阵  内积：\n$(\\alpha,\\beta)=a_1b_1+a_2b_2+\u0026hellip;a_nb_n$​\n$\\alpha^T \\beta,\\alpha^T\\alpha \\text{与}\\beta^T\\alpha$​表示内积！！！​\n  若$(\\alpha,\\beta)=0$，称两者正交\n  $AA^T=A^TA=E$​，A为正交矩阵。\n$\\Leftrightarrow$ $A^T=A^{-1}$​ (充要条件)\n$\\Rightarrow |A|=1 \\text{或}-1$​ (必要不充分条件)\n  由$AA^T=A^TA=E$，得其几何意义(判断正交矩阵)：\n 列(行)向量两两正交(正交名称由来) 列(行)向量长度为1   ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E7%9F%A9%E9%98%B5/","summary":"基本计算  对角矩阵存在交换律，即 $\\bigwedge_1 \\bigwedge_2=\\bigwedge_2 \\bigwedge_1$​\n  注意交换位置\n 对于 AB=C  B \u0026amp; C均按列分块，则$A(\\beta_1 \\beta_2 \\beta_3)=(\\gamma_1 \\gamma_2 \\gamma_3)$，故B的列向量都是$AX=\\gamma$ 的解，特别的，当AB=O即C=O时，B的列向量均是 $AX=0$ 的解。 B \u0026amp; C按行分块，则 AB的行向量均可由B的行向量线性表出。 A \u0026amp; C按列分块，则 AB的列向量均可由A的列向量线性表出。[2013]   规律1与解联系起来，尤其是AB=O推出B的列向量是Ax=0的解这一规律，除此之外AB=O也经常用r(A)+r(B)≤ n这个不等式。\n\u0026ldquo;AB=O\u0026rdquo; 👉 方程的解(B的列向量是A的解)\n​\t👉 秩 r(A)+r(B) ≤ n (n为A的列,B的行)\n  规律2,3与线性表出关联，进而可以跟秩，向量组等价(能互相线性表出则等价)联系起来。\n 几个特殊符号  $a(a_1,a_2,a_3)^T$\n 矩阵: $ab^T$ = $(ba^T)^T$\n $r(ab^T)$​​ ≤ $r(a)$​ ≤ 1\n任何两行成比例\n $aa^T$: 对称矩阵\n数: $a^Tb = b^Ta$: $ab^T$ 或 $ba^T$​​​的绩(主对角线元素之和)","title":"线性代数——矩阵"},{"content":"计算※ 数字型 题型 注意“存在三条对角线的情况”，通过 逐行相加 的到 “三角型”计算\n经典例题 \n\n抽象型 题型  行列式性质恒等变形 矩阵公式、法则恒等变形，E恒等变形 形特征值、相似  \n经典例题 \n 思路：利用单位矩阵恒等变形\n \n 思路一：利用矩阵相似（$\\alpha_1，\\alpha_2，\\alpha_3$无关，后面出现$A\\alpha_1，A\\alpha_2，A\\alpha_3$想到相似）\n利用乘法公式凑$PAP^{-1}=B$\n思路二：用行列式性质\n \n 思路：“不可逆”=\u0026gt;“行列式为0”=\u0026gt;观察看到为特征值形式$|\\lambda E-A|=0$，利用特征值与行列式的关系求解\n 应用 特征值 思路 “消0且得公因式”\n例题 \n\n 对于特征多项式应两行（或列）加加减减，至多是三行（或列）的加加减减找出 $\\lambda-a$ 的公因式，然后再解一个二次方程，就可求出矩阵A的三个特征值\n 克拉默法则 思路  不用来解大的方程组，常用小的证明题，\n 齐次方程AX=0有非零解→ |A|=0 齐次方程AX=0没有非零解→ |A|≠0   经典例题 \n \u0026ldquo;AB=O\u0026rdquo; 👉 方程的解(B的列向量是A的解)\n​\t👉 秩 r(A)+r(B) ≤ n (n为A的列,B的行)\n 矩阵秩 注意点  r(A) = r 👉A中有r阶子式不为0,任何r+1阶子式(若还有)必全为0.\nr(A) \u0026lt; r 👉A中每一个r阶子式全为0\nr(A)≥r 👉A中有r阶子式不为0.(能确定r的范围)\nA ≠ O 👉 r|A| ≥ 1\nA是n阶,\n r(A) = n 👉 |A|≠0👉A可逆 r(A) \u0026lt; n 👉 |A| = 0👉A不可逆  A是m×n矩阵,则r(A)≤min(m,n)\n  \u0026ldquo;三阶矩阵r(A)=2\u0026rdquo; → |A|=0\n  A — m×n , B — n×s ※\nr(AB) ≤ min { r(A), r(B) }\n 若A可逆，👉 r(AB)=r(BA)=r(B) 若A列满秩(r(A)=n)，👉 r(AB)=r(B)   证 |A| = 0 ？  构思一：证Ax=0有非零解(克拉默法则)\n  构思二：假设|A|≠0，用$A^{-1}$ 找出矛盾\n  构思三：证明 r(A) \u0026lt; n\n  构思四：$|A|= \\Pi \\lambda_i$​ (特征值)\n  构思五：证明 $|A|$ = $|A|^{-1}$\n 经典例题 \n 这里又用到了 AB=O 型！！！！\n \n 解法一：用秩\n​\tr(AB) ≤ r(A) ≤ n \u0026lt; m\n​\t∵ AB为m阶\n​\t故，|AB| = 0\t( r(AB) \u0026lt; m )\n解法二：用克拉默法则\n​\t构造两个齐次方程组，如下：\n​\tABX = 0 (1)\n​\tBX = 0 (2)\n​\t显然，(2)的解一定为(1)的解 ※\n​\t又∵ B为n×m，且 n \u0026lt; m\n​\t∴ BX=0 一定有非零解\n​\t故，ABX = 0 一定有非零解\n​\t|AB|=0\n \n 用到$|A|=|A|^{-1} \\rightarrow |A|=0$\n 代数余子式 定理  i≠j时，有：\n$a_{i1}A_{j1}+a_{i2}A_{j2}+\u0026hellip;+a_{in}A_{jn}=0$\n$a_{1j}A_{1k}+a_{2j}A_{2k}+\u0026hellip;+a_{nj}A_{nk}=0$​\n某一行元素与另一行对应余子式乘积和为0（第j行与第i行的元素完全相同，故行列式为0）\n  $A_{ij}$元素大小与$a_{ij}$无关，可构造以$A_{ij}$元素系数为一行(列)的向量，则该构造的向量按$A_{ij}$​元素系数一行(列)的展开的行列式的值就为$\\sum A_{ij}$的值\n 例题 \n解答如下：\n\n真题 \n\n","permalink":"https://blog.archai.space/p/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E8%A1%8C%E5%88%97%E5%BC%8F/","summary":"计算※ 数字型 题型 注意“存在三条对角线的情况”，通过 逐行相加 的到 “三角型”计算\n经典例题 \n\n抽象型 题型  行列式性质恒等变形 矩阵公式、法则恒等变形，E恒等变形 形特征值、相似  \n经典例题 \n 思路：利用单位矩阵恒等变形\n \n 思路一：利用矩阵相似（$\\alpha_1，\\alpha_2，\\alpha_3$无关，后面出现$A\\alpha_1，A\\alpha_2，A\\alpha_3$想到相似）\n利用乘法公式凑$PAP^{-1}=B$\n思路二：用行列式性质\n \n 思路：“不可逆”=\u0026gt;“行列式为0”=\u0026gt;观察看到为特征值形式$|\\lambda E-A|=0$，利用特征值与行列式的关系求解\n 应用 特征值 思路 “消0且得公因式”\n例题 \n\n 对于特征多项式应两行（或列）加加减减，至多是三行（或列）的加加减减找出 $\\lambda-a$ 的公因式，然后再解一个二次方程，就可求出矩阵A的三个特征值\n 克拉默法则 思路  不用来解大的方程组，常用小的证明题，\n 齐次方程AX=0有非零解→ |A|=0 齐次方程AX=0没有非零解→ |A|≠0   经典例题 \n \u0026ldquo;AB=O\u0026rdquo; 👉 方程的解(B的列向量是A的解)\n​\t👉 秩 r(A)+r(B) ≤ n (n为A的列,B的行)","title":"线性代数——行列式"},{"content":"寻址方式    基本寻址方式 特 征 优 点 缺 点 备 注     隐含寻址 操作数的存放地由操作码决定      立即寻址 操作数直接在指令中 加快执行速度 增加指令长度，不方便修改操作数 适用提供常数，设定初始值   寄存器寻址 操作数在指令指定的寄存器中 方便修改，访问寄存器加快指令执行，缩短指令长度，编程更灵活     直接寻址 操作数地址在指令中，操作数在主存单元中  指令字较长，不方便地址修改    间接寻址 操作数地址的地址在指令中，操作数在主存中 方便修改指针，编程更灵活 访问两次主存获取操作数，降低执行速度 形式地址，有效地址EA(=操作数地址)   寄存器间接寻址 操作数地址在指令指定的寄存器中，操作数在主存单元中 压缩指令长度，修改寄存器内容就可以修改主存地址指针  方便编写循环程序   相对寻址 操作数地址由PC和指令提供的地址偏移量决定,操作数在主存单元中   EA=PC+D，适用与地址无关的程序设计   基址寻址 操作数地址由基址寄存器(RB)和指令提供的地址偏移量决定，操作数在主存单元中 缩短指令长度，扩大寻址空间  大型计算机，用户的逻辑地址→主存的物理地址，EA=(RB)+D   变址寻址 操作数地址由变址寄存器(RI)和指令提供的地址偏移量决定，操作数在主存单元中   寻址到操作数RI内容(地址)自动修改，EA=(RI)+D   堆栈寻址 寻址方式由指令操作码决定   适用涉及堆栈操作的指令，EA=(SP)    \n存储模式 “按字节编址” 主存将一个存储单元定为8位，即一个字节，这称为按字节编址的存储器\n 区分字，字节，位\n  1Byte(字节) = 8bit(位或比特)\n  32位系统 ⇒ 32位为1字 ⇒由4个字节组成 ⇒ 在主存中占用4个连续的字节单元\n  64位系统 ⇒ 64位为1字 ⇒由8个字节组成 ⇒ 在主存中占用8个连续的字节单元\n  一般英文字母数字为1个字节，汉字为2字节(ASCII)\n 1.ASCII码: 一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。 2.UTF-8编码: 一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。中文标点占三个字节,英文标点占一个字节 3.Unicode编码: 一个英文等于两个字节,一个中文(含繁体)等于两个字节。中文标点占两个字节,英文标点占两个字节     数据存储顺序 大端存储： 数据的最低有效字节存储在高地址单元\n小端存储： 数据的最低有效字节存储在低地址单元\n对于下面这个数据结构\nstruct{ double i; //0x1112131415161718 } 在主存中的大端存储和小端存储如图所示\n边界对齐 数据存储在地位地址以全0开始的连续存储单元中，若不满足，可填充一至多个空白字节。否则，读写一个完整的数据需要访问两次存储器。\n\n冯·诺依曼结构和哈佛结构 冯：指令数据混合存放在主存中\n 在cache中采用冯或哈不等价于主存也采用相应的结构\n 哈：主存分成指令地址空间和数据地址空间两部分(多见于RISC)\n\n","permalink":"https://blog.archai.space/p/%E5%AF%BB%E5%9D%80%E6%96%B9%E5%BC%8F%E4%B8%8E%E5%AD%98%E5%82%A8%E6%A8%A1%E5%BC%8F/","summary":"寻址方式    基本寻址方式 特 征 优 点 缺 点 备 注     隐含寻址 操作数的存放地由操作码决定      立即寻址 操作数直接在指令中 加快执行速度 增加指令长度，不方便修改操作数 适用提供常数，设定初始值   寄存器寻址 操作数在指令指定的寄存器中 方便修改，访问寄存器加快指令执行，缩短指令长度，编程更灵活     直接寻址 操作数地址在指令中，操作数在主存单元中  指令字较长，不方便地址修改    间接寻址 操作数地址的地址在指令中，操作数在主存中 方便修改指针，编程更灵活 访问两次主存获取操作数，降低执行速度 形式地址，有效地址EA(=操作数地址)   寄存器间接寻址 操作数地址在指令指定的寄存器中，操作数在主存单元中 压缩指令长度，修改寄存器内容就可以修改主存地址指针  方便编写循环程序   相对寻址 操作数地址由PC和指令提供的地址偏移量决定,操作数在主存单元中   EA=PC+D，适用与地址无关的程序设计   基址寻址 操作数地址由基址寄存器(RB)和指令提供的地址偏移量决定，操作数在主存单元中 缩短指令长度，扩大寻址空间  大型计算机，用户的逻辑地址→主存的物理地址，EA=(RB)+D   变址寻址 操作数地址由变址寄存器(RI)和指令提供的地址偏移量决定，操作数在主存单元中   寻址到操作数RI内容(地址)自动修改，EA=(RI)+D   堆栈寻址 寻址方式由指令操作码决定   适用涉及堆栈操作的指令，EA=(SP)","title":"寻址方式与存储模式"},{"content":"外部排序  由于数据元素太多，无法一次全部读入内存进行内部排序，这是就要通过外部排序来解决\n 1.外排原理 目的：通过内存的读写操作(每次读写操作都是成块的进行，比如每次1KB)，将存放于磁盘中的大量数据变得有序。\n(拿二路归并举例)\n如图，对于在磁盘中分块存放的数据，每块存入三个元素，共16块\n在内存中建立三个缓冲区输出缓冲区、输入缓冲区1以及输入缓冲区2\n\n1.1构造初始归并段 首先，依次地读入前两块数据，分别存入内存中的 缓冲区1、缓冲区2\n\n将输入缓冲区1以及输入缓冲区2中存放的数据经过 内存中的二路归并排序(内排)后，将生成的有序的块经 输出缓冲区 写入磁盘 得到一个有序的归并段\n\n同样的，对剩余块进行同样的操作可以得到\n\n1.2以归并段为单位进行归并 分别选取归并段1和2中较小的一块，依次读入至缓冲区1,2\n\n内排之后，写入内存，注意，输入缓冲区1(或2)空缺后要立即在归并段1(或2)中读入新的块到其中进行归并排序\n（以保证输出缓冲区始终输出归并段中较小的元素）\n\n\n最终，完成所有归并段的第一趟归并之后，会有\n\n之后，4块成一个归并段，两两归并\n\n……\n最终。经过3趟归并，整体会变得有序！\n\n2.优化思路 2.1时间开销分析 在整个排序过程中，时间开销分析如下\n\n可以看到，\n外部排序时间开销=读写外存的时间+内部排序所需时间+内部归并所需时间\n而读写外存时间是关键的时间开销，因此优化应该针对怎么减少读写外存的次数展开\n而文件总块数无法优化，只能针对归并的趟数优化\n为此，我们需要采用多路归并来解决\n2.3结论 \n2.4 优化思路一：采用多路归并 对上面的例子，如果采用四路归并\n\n只需96次读写即可！！\n2.5 优化思路二：减少初始归并段数量r \n败者树 归并段数增加之后，内存中缓冲区数目增加，从中对比得出最小关键字的对比次数也会随之增多……\n1.算法思想 \n构造 如图所示的树结构，叶节点对应（脑补）各归并段（假设共有8个归并段），分支结点记录败者来自哪个归并段，最后根节点记录冠军来自哪个归并段，并且将冠军输出，为这8个归并段中的最小值。\n下轮选择冠军记录的那个归并段（归并段3）中的元素6，代替1的位置，如图，并依次向上的与各败者结点对比，胜则往上，败则留下，最终输出冠军\n接下来，循环这个过程\n2.效率分析 对于k路归并，第一次构造败者树需要对比关键字k-1次 有了败者树，选出最小元素，只需对比关键字次$\\left \\lceil \\log_2k \\right \\rceil$\n置换选择排序 ⽤于内部排序的内存⼯作区WA可容纳l个记录，则每个初始归并段也只能包含l个记录，若⽂件共有n个记录，则初始归并段的数量r=n/l,这是之前的做法 1.算法思想 \n设初始待排文件为F，初始归并段输出文件为FO，内存工作区为WA,FO和WA的初始状态为空，WA可容纳w个记录。置换选择算法的步骤如下： 1）从F输入个记录到工作区WA。 2）从WA中选出其中关键字取最小值的记录，记为 MINIMAX记录3）将MINIMAX记录输出到FO中去。 4）若F不空，则从F输入下一个记录到WA中。 5）从w中所有关MINIMAX键字比记录的关键字大的记录中选出最小关键字记录，作为新的MINIMAX记录。 6）重复3）~5），直至在A中选不 MININ出新的记录为止，由此得到一个初始归并段，输出一个归并段的结束标志到FO中去。 7）重复2）~6），直至WA为空。由此得到全部初始归并段。\n\n最佳归并树  利用置换选择排序构造初始归并段，归并段长短不一\n 对于一个归并树，如图数字表示该归并段占多少个磁盘块。\n\n每个初始归并段看作⼀个叶⼦结点，归并段的⻓度作为结点权值，则 上⾯👆这棵归并树的带权路径⻓度 WPL = 2*1 + (5+1+6+2) * 3 = 44\n重要结论：归并过程中的磁盘I/O次数 = 归并树的WPL * 2 16 = 读磁盘的次数 = 写磁盘的次数\n因此\n\n我们可以构造出一棵带权路径最小的归并树——哈夫曼树！\n注意：\n对于k叉归并，若初始归并段的数量⽆法构成严格的 k 叉归并树， 则需要补充⼏个⻓度为 0 的“虚段”，再进⾏ k 叉哈夫曼树的构造\n ①若（初始归并段数量 -1）% （k-1）= 0，说明刚好可以构成严格k叉树，此时不需要添加虚段\n②若（初始归并段数量 -1）% （k-1）= u ≠ 0，则需要补充 (k-1) - u 个虚段\n ","permalink":"https://blog.archai.space/p/%E5%A4%96%E9%83%A8%E6%8E%92%E5%BA%8F%E7%9B%B8%E5%85%B3/","summary":"外部排序  由于数据元素太多，无法一次全部读入内存进行内部排序，这是就要通过外部排序来解决\n 1.外排原理 目的：通过内存的读写操作(每次读写操作都是成块的进行，比如每次1KB)，将存放于磁盘中的大量数据变得有序。\n(拿二路归并举例)\n如图，对于在磁盘中分块存放的数据，每块存入三个元素，共16块\n在内存中建立三个缓冲区输出缓冲区、输入缓冲区1以及输入缓冲区2\n\n1.1构造初始归并段 首先，依次地读入前两块数据，分别存入内存中的 缓冲区1、缓冲区2\n\n将输入缓冲区1以及输入缓冲区2中存放的数据经过 内存中的二路归并排序(内排)后，将生成的有序的块经 输出缓冲区 写入磁盘 得到一个有序的归并段\n\n同样的，对剩余块进行同样的操作可以得到\n\n1.2以归并段为单位进行归并 分别选取归并段1和2中较小的一块，依次读入至缓冲区1,2\n\n内排之后，写入内存，注意，输入缓冲区1(或2)空缺后要立即在归并段1(或2)中读入新的块到其中进行归并排序\n（以保证输出缓冲区始终输出归并段中较小的元素）\n\n\n最终，完成所有归并段的第一趟归并之后，会有\n\n之后，4块成一个归并段，两两归并\n\n……\n最终。经过3趟归并，整体会变得有序！\n\n2.优化思路 2.1时间开销分析 在整个排序过程中，时间开销分析如下\n\n可以看到，\n外部排序时间开销=读写外存的时间+内部排序所需时间+内部归并所需时间\n而读写外存时间是关键的时间开销，因此优化应该针对怎么减少读写外存的次数展开\n而文件总块数无法优化，只能针对归并的趟数优化\n为此，我们需要采用多路归并来解决\n2.3结论 \n2.4 优化思路一：采用多路归并 对上面的例子，如果采用四路归并\n\n只需96次读写即可！！\n2.5 优化思路二：减少初始归并段数量r \n败者树 归并段数增加之后，内存中缓冲区数目增加，从中对比得出最小关键字的对比次数也会随之增多……\n1.算法思想 \n构造 如图所示的树结构，叶节点对应（脑补）各归并段（假设共有8个归并段），分支结点记录败者来自哪个归并段，最后根节点记录冠军来自哪个归并段，并且将冠军输出，为这8个归并段中的最小值。\n下轮选择冠军记录的那个归并段（归并段3）中的元素6，代替1的位置，如图，并依次向上的与各败者结点对比，胜则往上，败则留下，最终输出冠军\n接下来，循环这个过程\n2.效率分析 对于k路归并，第一次构造败者树需要对比关键字k-1次 有了败者树，选出最小元素，只需对比关键字次$\\left \\lceil \\log_2k \\right \\rceil$","title":"外部排序相关"},{"content":"   排序算法 平均时间复杂度 空间复杂度 稳定性 适用情况     插入排序 $O(n^2)$ O(1) 稳定 n较小，初始序列基本有序   希尔排序 $O(n^{1.3})$ O(1) 不稳定    冒泡排序 $O(n^2)$ O(1) 稳定 n较小，初始序列基本有序   快速排序 $O(n\\log_2n)$ $O(nlog_2n)$ 不稳定 初始序列无序   简单选择排序 $O(n^2)$ O(1) 不稳定 n较小   堆排序 $O(n\\log_2n)$ O(1) 不稳定 n较大或只排前几位   2-路归并排序 $O(n\\log_2n)$ O(n) 稳定 n很大   链式基数排序 $O(d(n+rd))$ $O(rd)$ 稳定 n大，关键字值小    相关概念 1.评价指标 时间复杂度，空间复杂度，算法的稳定性\n\n2.分类 \n\n插入排序 1.算法思想 每次将⼀个待排序的记录按其关键字⼤⼩插⼊到前⾯已排好序的⼦序列中， 直到全部记录插⼊完成。\n\n2.代码实现 //递增排序 void InsertSort(int A[],int n){//长度为n的int型数组  int i,j,temp; for (i = 1; i \u0026lt; n; i++) if (A[i] \u0026lt; A[i-1]) {//如果A[i]的小于其前驱则进行A[i]的移动  temp=A[i];//临时保存A[i]  for (j = i-1; j\u0026gt;=0 \u0026amp;\u0026amp; A[j]\u0026gt;temp; j--)//依次检查A[i]之前已经排好序的元素  A[j+1]=A[j];//大于temp的后移  A[j+1]=temp;//最终复制到插入位置  } } 3.优化思路 折半插入排序——先用折半查找找到应该插入的位置，再进行移动元素。\nvoid InsertSort(int A[],int n){ int i,j,low,high,mid; for (i = 2; i \u0026lt; n; i++) { A[0]=A[i]; low=1,high=i-1;//折半查找的范围  while (low\u0026lt;=high) { mid=(low+high)/2; if (A[mid\u0026gt;A[0]]) high=mid-1;//查找左部分  else low=mid+1;//查找右部分  } for (j = i-1; j\u0026gt;=high+1; j--)//i之前元素全部后移  A[j+1]=A[j]; A[high+1]=A[0];//插入  } } 注意： 一直到low\u0026gt;high时才停止折半查找当mid所指元素等于当前元素时，应继续令low=mid+1，以保证“稳定性“。最终应将当前元素插入到low所指位置（即high+1）\n希尔排序 1.算法思想 先将待排序表分割成若干形如 {i,i+d,i+2d, \u0026hellip; , i+kd} 的“特殊”子表，对各个子表分别进行直接插入排序。缩小增量d(建议缩小一半)，重复上述过程，直到d=1为止。\n\n2.代码实现 void ShellSort(int A[],int n){ int i,j,d; for (d = n/2; d \u0026gt;= 1; d=d/2) //步长不断衰减  for ( i = d+1; i \u0026lt;= n; i++) //遍历各步长为d的子表,从子表第二个元素开始处理即可  if(A[i]\u0026lt;A[i-d]){//如果发现逆序  A[0]=A[i];//暂存需要交换位置的A[i]  for ( j = i-d; j \u0026gt; 0 \u0026amp;\u0026amp; A[0]\u0026lt;A[j]; j-=d) //当子表中存在元素，判断与A[i]大小，寻找A[i]插入位置  A[j+d]=A[j];//子表记录后移  A[j+d]=A[0];//插入  }//if } 冒泡排序 1.算法思想 从后往前（或从前往后）两两比较相邻元素的值，若为逆序（即A[i-1]\u0026gt;A[i]），则交换它们，直到整个序列比较完毕。\n\n2.代码实现 void swap(int \u0026amp;a,int \u0026amp;b){ int temp=a; a=b; b=temp; } void BubbleSort(int A[],int n){ for (int i = 0; i \u0026lt; n; i++){ //i之前的所有元素必然已经有序  bool flag=false; for (int j = n-1; j \u0026gt; i; j--) //从后往前的一趟冒泡  if (A[j-1] \u0026gt; A[j]) {//逆序,(相等不会交换，是稳定的)  swap(A[j-1],A[j]); flag=true; }//if  if (!flag) { return;//本趟遍历flag未改变，说明已经有序,可能会提前结束  } }//for } 快速排序 1.算法思想 在待排序表L[1\u0026hellip;n]中任取一个元素 pivot作为枢轴（或基准，通常取首元素），通过一趟排序将待排序表划分为独立的两部分L[1\u0026hellip;k-1]和L[k+1\u0026hellip;n]使得L[1\u0026hellip;k-1]中的所有元素小于pivot，L[k+1\u0026hellip;n]中的所有元素大于等于pivot，则 pivot放在了其最终位置L[k]上这个过程称为一次“划分”。然后分别递归地对两个子表重复上述过程，直至每部分内只有一个元素或空为止，即所有元素放在了其最终位置上。\n\n2.代码实现 void QuickSort(int A[],int low,int high) {//快速排序  if (low\u0026lt;high) {//递归跳出的条件  int pivotpos=Partition(A,low,high);//进行“划分”  QuickSort(A,low,pivotpos-1);//处理左子表  QuickSort(A,pivotpos+1,high);//处理右子表  } } int Partition(int A[],int low,int high){//\u0026#34;划分\u0026#34;函数，返回 枢轴 位置  int pivot=A[low];//取low作为枢轴  while (low\u0026lt;high) {//用low,high搜寻枢轴位置  while (low\u0026lt;high \u0026amp;\u0026amp; A[high]\u0026gt;pivot) high--; A[low]=A[high];//比枢轴小的元素移动到左端  while (low\u0026lt;high \u0026amp;\u0026amp; A[low]\u0026lt;pivot) low++; A[high]=A[low];//比枢轴大的元素移动到右端  } A[low]=pivot;//改变枢轴low位置  return low; } 3.优化思路 枢轴的选择会直接影响快速排序算法的效率，因此优化应该从枢轴的选择角度考虑。\n① 选择首、中、尾三个位置的元素，取其中中间值作为枢轴元素。\n② 随机选一个作为枢轴……\n4.注意  “一次划分” 与 “一趟排序”\n 一次划分可以确定一个元素的最终位置 一趟排序也许可以确定多个元素的最终位置  \n 简单选择排序 1.算法思想 每一趟在待排序元素中选取关键字最小的元素加入有序子序列\n\n2.代码实现 void swap(int \u0026amp;a,int \u0026amp;b){ int temp=a; a=b; b=temp; } void SelectSort(int A[],int n) { for (int i = 0; i \u0026lt; n-1; i++) {//从头开始遍历，最后一个元素无需处理(最终一定是最大)  int min=i;//初始最小元素位置  for (int j = i+1; j \u0026lt; n; j++) //在i之后所有元素中寻找最小元素位置  if (A[j]\u0026lt;A[min]) min=j;//更新最小元素位置  if (min!=i) swap(A[min],A[i]);//将最小元素置于表头  } } 堆排序☆ 堆这种数据结构可以类比完全二叉树二叉树结构的顺序存储\n\n1.建立大根堆 核心特性： 根≥左，右\n思路 把所有非终端结点($i \\leq \\left \\lfloor n/2 \\right \\rfloor$)都检查一遍，是否满足大根堆的要求。\n检查当前结点是否满足 根≥左、右，若不满足，将当前结点与更大的一个孩子互换。\n若元素互换破坏了下一级的堆，则采用相同的方法继续往下调整（小元素不断“下坠”）\n代码实现 void BuildMaxHeap(int A[],int len){ for (int i = len/2; i \u0026gt; 0; i--)//从编号最大非终端节点开始  HeadAdjust(A,i,len); } //调整以k为根节点的树为大根堆 void HeadAdjust(int A[],int k,int len){ A[0]=A[k]; //寻找A[k]应该插入的位置--“下坠”  for (int i = 2*k; i \u0026lt;= len; i*=2) { if (i\u0026lt;len\u0026amp;\u0026amp;A[i]\u0026lt;A[i+1]) //找到key更大的子结点的下标  i++; if(A[0]\u0026gt;=A[i]) break;//满足“根”\u0026gt;左、右，end for  else{ A[k]=A[i];//将A[i]调整到双亲结点上  k=i; } }//for  A[k]=A[0];//已找到应该插入的位置 } 2.大根堆排序 算法思想 利用大根堆 根≥左，右 的特性，\nStep1 交换堆顶与堆底元素\nStep2 去掉堆底元素，len-1，重新调整为大根堆结构（HeadAjust）\nLoop step1 , step2.\n代码实现 void BuildMaxHeap(int A[],int len); void HeadAdjust(int A[],int k,int len); void swap(int \u0026amp;a,int \u0026amp;b); void HeapSort(int A[],int n){ BuildMaxHeap(A,n);//初始建立大根堆  for (int i = n; i \u0026gt; 1; --i) { //n-1趟交换与建堆  swap(A[i],A[1]);//堆底与堆顶元素互换  HeadAdjust(A,i,i-1);//剩余待排序元素整理成堆  } }  基于大根堆数据结构，经过排序后得到 升序序列 基于小根堆数据结构，经过排序后则会得到 降序序列\n 3.效率分析 对于BuildMaxHeap(A,n)\n不难得出，对于一个结点，每\u0026quot;下坠\u0026quot;一层，最多只需对比关键字2次(子树之间对比，根与子树中较大的对比)\n若树高为h，某结点在第i层，则将这个结点向下调整最多只需要\u0026quot;下坠\u0026quot;h-i层，关键字对比次数不超过2(h-i)\n而n个结点的完全二叉树树高$h=\\left \\lfloor \\log_2n \\right \\rfloor+1$\n第 i 层最多有$2^{i-1}$个结点，而只有第1~（h-1）层的结点才有可能需要“下坠”调整\n故将整棵树调整为大根堆，关键字对比次数不超过\n$\\sum_{i=h-1}^{1} 2^{i-1} 2(h-i)=\\sum_{i=h-1}^{1} 2^{i}(h-i)=\\sum_{j=1}^{h-1} 2^{h-j} j \\leq 2 n \\sum_{j=1}^{h-1} \\frac{j}{2^{j}} \\leq 4 n$\n建堆的过程,关键字对比次数不超过4n，建堆时间复杂度=O(n)\n对于n-1趟交换与建堆\n根节点最多“下坠” h-1 层，\n⽽每“下坠”⼀层，最多只需对⽐关键字2次，\n因此每⼀趟排序复杂度不超过 $O(h) = O(log_2n)$ 共n-1 趟，\n总的时间复杂度 = $O(nlog_2n)$\n因此，\n堆排序时间复杂度=$O(n) + O(nlog_2n)=O(nlog_2n)$ \n4.堆的插入与删除 4.1 插入 对于小根堆，新元素放到表尾，与父节点对比，若新元素比父节点更小，则将二者互换。新元素就这样一路\u0026quot;上升\u0026quot;，直到无法继续上升为止。\n4.2 删除 被删除的元素用堆底元素替代，然后让该元素不断“下坠”，直到无法下坠为止\n归并排序 1.算法思想 对于一个给定的序列\n第一趟，将每1个元素看做一个组，相邻的两组进行二路归并\n第二趟，将每相邻的2个有序元素序列看做一组，相邻的两组进行二路归并\n第三趟，将每相邻的4个有序元素序列看做一组，相邻的两组进行二路归并\n\u0026hellip;.\n直至所有元素都有序\n\n2.代码实现 int *B=(int *)malloc(n*sizeof(int));//辅助数组，长度等于A数组  //A[low...mid]和A[mid+1...high]各自有序，将两个部分归并 void Merge(int A[],int low,int mid,int high) { int i,j,k; for (k = low; k \u0026lt;= high; k++) //将A[low...high]复制到B  B[k]=A[k]; for (i = low,j=mid+1,k=i; i \u0026lt;= mid\u0026amp;\u0026amp;j\u0026lt;=k; k++) {//归并  if (B[i]\u0026lt;=B[j]) //较小者复制到A中  A[k]=B[i++]; else A[k]=B[j++]; }//for  while (i\u0026lt;=mid) A[k++]=B[i++]; while (j\u0026lt;=high) A[k++]=B[j++]; } void MergeSort(int A[],int low,int high) { if (low\u0026lt;high) { int mid=(low+high)/2;//从中间划分  MergeSort(A,low,mid);//左部分归并  MergeSort(A,mid+1,high);//右部分归并  Merge(A,low,mid,high);//两部分归并  } }  关于merge方法可以对照下图理解\n\n 基数排序 基数算法不是基于“比较”的排序算法\n1.算法思想 （具体的例子）\n对于一个元素最高位为3位数的序列，将不足3位的元素前面补0.\n第一趟 按“个位“分配、收集：得到按“个位”递减排序的序列\n第二趟按“十位分配、收集：得到按“十位”递减排序的序列，“十位相同的按“个位递减排序\n第三趙按“百位”分配、收集：得到一个按“百位递减排列的序列，若“百位”相同则按“十位递减排列，若“十位还相同则按“个位递减排列。\n定义如下\n\n2.效率分析 2.1 空间 需要 r 个辅助队列，空间复杂度 = O(r)\n2.2 时间 ⼀趟分配O(n)，⼀趟收集O(r)，总共 d 趟分配、收集，总的时间复杂度=$O(d(n+r))$\n2.3稳定性 \n3.拓展应用 \n应用方向\n①数据元素的关键字可以⽅便地拆分为 d 组，且 d 较⼩\n②每组关键字的取值范围不⼤，即 r 较⼩\n③数据元素个数 n 较⼤\n","permalink":"https://blog.archai.space/p/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9B%B8%E5%85%B3/","summary":"排序算法 平均时间复杂度 空间复杂度 稳定性 适用情况     插入排序 $O(n^2)$ O(1) 稳定 n较小，初始序列基本有序   希尔排序 $O(n^{1.3})$ O(1) 不稳定    冒泡排序 $O(n^2)$ O(1) 稳定 n较小，初始序列基本有序   快速排序 $O(n\\log_2n)$ $O(nlog_2n)$ 不稳定 初始序列无序   简单选择排序 $O(n^2)$ O(1) 不稳定 n较小   堆排序 $O(n\\log_2n)$ O(1) 不稳定 n较大或只排前几位   2-路归并排序 $O(n\\log_2n)$ O(n) 稳定 n很大   链式基数排序 $O(d(n+rd))$ $O(rd)$ 稳定 n大，关键字值小    相关概念 1.","title":"排序算法相关"},{"content":"顺序查找 typedef struct{//查找表的数据结构（顺序表）  int *elem;//动态数组基址  int TableLen;//查找表长度 }SSTable; int Seq_Search(SSTable ST,int key){ ST.elem[0]=key; int i; for (i = ST.TableLen;ST.elem[i]!=key ; i--) {}//从后往前查找，最终返回下标i  return i;//返回0说明没找到 } 效率分析 对于长度为n的顺序表，如果查找成功 $$ ASL={\\frac{1+2+\u0026hellip;+n}{n}}=\\frac{n+1}{2} $$ 若果查找失败，则 $ASL=n+1$\n总体上，该算法时间复杂度为 $O(n)$\n优化思路 1.如果使得表中的元素有序存放……，可以构造出一棵查找判定树\n此时，查找失败时$ASL=\\frac{1+2+\u0026hellip;+n+n}{n+1}=\\frac{n}{2}+\\frac{n}{n+1}$\n优点： 容易查找失败时ASL更小\n2.如果各元素被查找的概率不同……，可以把概率大的靠前\n优点： 容易查找成功时ASL更小\n折半查找  折半查找，又称“二分查找”，仅适用于有序的顺序表。\n 针对升序排列的顺序表，代码实现如下\ntypedef struct{//查找表的数据结构（顺序表）  int *elem;//动态数组基址  int TableLen;//查找表长度 }SSTable; int BinarySearch(SSTable L,int key){ int low=0,high=L.TableLen-1,mid; while (low\u0026lt;=high) { mid=(low+high)/2; if (L.elem[mid]==key) return key; else if(L.elem[mid]\u0026gt;key)//从前半部分继续查找  high=mid-1; else//从后半部分继续查找  low=mid+1; } return -1; } 折半查找判定树 对于一个给定的有序顺序表，\n若其中元素个数为奇数个，则折半后构造出的判定树满足数量关系“左子树=右子树”；\n若元素个数为偶数个，则根据$mid=\\left \\lfloor \\frac{low+high}{2} \\right \\rfloor$,即mid向下取整，此时有”左子树=右子树-1“\n故，不考虑失败结点，折半查找二叉树中满足 右子树结点数-左子树结点数=0或1，因此对于编号为1~16的元素，不考虑失败结点，构造其折半查找判定树应该为：\n特征：\n① 折半查找判定树一定是平衡二叉树(AVL)；\n②判定树结点的关键字满足 左\u0026lt;中\u0026lt;右，因此满足二叉排序树(BST)；\n③ 失败结点有n+1个（等于成功结点的空链域数量）\n效率分析 由于折半查找判定树的树高不超过$\\left \\lceil \\log_2(n+1) \\right \\rceil$，因此\n查找失败|成功的情况下，$ASL \\leq n$\n其时间复杂度为$O(\\log_2n)$\n分块查找  分块查找最大的特征就是“块内无序，块间有序”。\n //定义分块的索引表 typedef struct{ int maxValue;//块中最大值  int low,high;//块在顺序表中的起始和结束地址 }Index; //顺序表存储实际元素 int List[100]; 算法思想 对于给定的一串元素，整体上可能是无序的，但是如果按照最大有序序列划分之后，各块内就变得有序了。这样我们就可以根据分好的块建立的索引来快速定位key所在的区间。\n分块查找，又称索引顺序查找，算法过程如下：\n①在索引表中确定待查记录所属的分块（可顺序、可折半）\n💥注意在索引表中折半查找时，如果key没有直接等于maxValue，那么根据二分查找算法，当low\u0026gt;high时，需要在low指向的块内继续进行查找\n②在块内顺序查找\n查找效率分析 只考虑特殊情况，假设长度为n的表被均匀地分成b块，每块s个元素\u0026gt;，那么：\n设索引表和块内的平均查找长度分别为$L_I,L_S$，则整个查找过程的平均查找长度 $$ ASL=L_I+L_S $$ 具体地，如果对索引表采用顺序查找的方式：\n$L_I={\\frac{1+2+\u0026hellip;+b}{b}}=\\frac{b+1}{2}$\n$L_S={\\frac{1+2+\u0026hellip;+s}{s}}=\\frac{s+1}{2}$\n则$ASL=\\frac{s^2+2s+n}{2s}$，当$s=\\sqrt{n}$ 时，$ASL_{min}=\\sqrt{n}+1$\n如果采用折半查找的方式：\n$ASL=\\left \\lceil \\log_2(b+1) \\right \\rceil+\\frac{s+1}{2}$\n优化思路 如果插入某个元素，顺序表方式复杂度太高，故可采用链式存储的方式存储数据元素。\nB树  B树，又称多路平衡查找树，B树中所有结点的孩子个数的最大值称为B树的阶，通常用m表示。\n 对于一个m阶B树其核心特征如下：\n\nB树高度  对于B树的高度，一般不包括叶结点（失败的结点）\n 最小高度 为使得有n个关键字的m阶B树有最小高度，那么每个结点应该尽可能地满，有m-1个关键字，m个分叉，则有\n$n\\leq(m-1)(1+m+m^2+\u0026hellip;+m^{h-1})=m^h-1$ 可以得到，$h\\geq \\log_m(n+1)$\n最大高度 方式一：\n\n方式二：\n\n插入删除操作 核心要求： ①对m阶B树除根节点外，结点关键字个数 m/2 -1 ≤ n ≤ m-1 ②子树0插入操作 新元素一定是插入到最底层“终端节点”，用“查找”来确定插入位置\n在插入key后，若导致原结点关键字数超过上限，则从中间位置（m/2上取整）将其中的关键字分为两部分，左部分包含的关键字放在原结点中，右部分包含的关键字放到新结点中，中间位置（m/2）的结点插入原结点的父结点。\n若此时导致其父结点的关键字个数也超过了上限，则继续进行这种分裂操作，直至这个过程传到根结点为止，进而导致B树高度增1。\n下图分别演示两种关键字超出结点上限的情况\n\n\n删除操作 \nB+树  有类似分块查找，上层保存下层结点中的最大值，但又保存了B树的一些特性\n \n一棵m阶B+树满足以下条件：\n\n查找 B+树中，无论查找成功与否，最终一定要走到最下面一层结点。\n可以从根节点出发逐层查找，也可以借助指针p进行顺序查找\n对比B树 \n散列查找  散列表(Hash Table)，又称哈希表。是一种数据结构，特点是：数据元素的关键字与其存储地址直接相关。\n 相关概念 同义词： 若不同的关键字通过散列函数映射到同一个值，则称它们为“同义词”\n冲突： 通过散列函数确定的位置已经存放了其他元素，则称这种情况为“冲突”\n哈希函数： 关键字与存放地址之间的转换函数。\n**查找长度： **在查找运算中，需要对比关键字的次数称为查找长度。可能为0。\n**装填因子：**对一个有n个关键字形成的长度为l的散列表，其装填因子 $\\alpha=\\frac{n}{l}$，表示的是查找失败时的平均查找长度ASL（当查找长度可为0的时）。装填因子越大说明散列表越满。\n处理冲突的方法 1.拉链法(链接法或链地址法) \n2.开放定址法 所谓开放定址法，是指可存放新表项的空闲地址既向它的同义词表项开放，又向它的非同义词表开放。其数学递推公式为：\n\n其中，i=0,1,2,\u0026hellip;,k(k ≤ m-1)，m表示散列表长；$d_i$为增量序列；i可理解为“第i次发生冲突”\n对于增量序列$d_i$，有三种方法确定：①线性探测法；②平方探测法；③伪随机序列法\n①线性探测法 $d_i$=0,1,2,3，\u0026hellip; ，m-1；即发生冲突时，每次往后探测相邻的下一个单元是否为空\n例如，有一堆数据元素，关键字分别为{19,14,23,1,68,20,84,27,55,11,10,79}，散列函数H（key）=key%13，\n在存放1时，由于 1%13=1，但位置1处已经存在14，因此会后移一位存放到$H_1$=(1+$d_1$)%16=2的位置:\n\n查找\n对于一个给定的key，通过开放定址法给出的递推公式确定其位置，若冲突，则寻找下一个位置，直至找到或遇到空地址失败为止。值得注意的是，在计算平均查找长度时，空位置的比较也记作一次，此外如果能够越早遇到空位置，就能越早确定查找失败。\n删除\n删除操作不能简单地将某位置置为空（若置为空，将截断在它之后填⼊ 散列表的同义词结点的查找路径，可能导致查找操作误判），而应该做⼀个“删除标记”，进⾏逻辑删除。\n平均查找长度\n对于下图中的散列表{19,14,23,1,68,20,84,27,55,11,10,79}，散列函数H（key）=key%13\n\n成功：\n\n失败：\n\n如果通过递推式映射到0的位置，只需1次对比\n如果是1的位置，需要1,2,3\u0026hellip;13，总共13次对比\n如果是2的位置，需要2,3，\u0026hellip; 13，总共12次对比\n\u0026hellip;\u0026hellip;\n线性探测法很容易造成同义词、⾮同义词的“聚集（堆积）”现象，严重影响查找效率\n②平方探测法 $d_i=0^2,1^2，-1^2,2^2，-2^2\u0026hellip;k^2,-k^2$；称为平方探测法，又称二次探测法，其中k≤m/2.\n注意： 散列表⻓度m必须是⼀个可以表示成4j + 3的素数，才能探测到所有位置\u0026mdash;\u0026mdash;《数论》\n\n如图，可见，当表长为8时，没有探测到整个表。\n③伪随机序列法 di 是⼀个伪随机序列，如 di= 0, 5, 24, 11, …\n3.再散列法(再哈希) 除了原始的散列函数 H(key) 之外，多准备⼏个散列函数， 当散列函数冲突时，⽤下⼀个散列函数计算⼀个新地址，直到不冲突为⽌。\n常见的散列函数  目标：让不同关键字的冲突尽可能地少\n 1.除留余数法 H(key)= key % p\n散列表表长为m，取一个不大于m但最接近或等于m的质数p，分布更均匀，冲突更少。参见《数论》\n2. 直接定址法 H(key) = key 或 H(key) = a × key + b\n其中，a和b是常数。这种方法计算最简单且不会产生冲突。它适合关键字的分布基本连续的情况，若关键字分布不连续，空位较多，则会造成存储空间的浪费。\n例如：学生学号的保存\n3.数字分析法 选取数码分布较为均匀的若干位作为散列地址\n设关键字是r进制数（如十进制数），而r个数码在各位上出现的频率不一定相同，可能在某些位上分布均匀一些，每种数码出现的机会均等；而在某些位上分布不均匀，只有某几种数码经常出现，此时可选取数码分布较为均匀的若干位作为散列地址。这种方法适合于已知的关键字集合，若更换了关键字，则需要重新构造新的散列函数。\n例如：对手机号码，可以设计后四位作为散列地址\n4.平方取中法 取关键字的平方值的中间几位作为散列地址。\n具体取多少位要视实际情况而定。这种方法得到的散列地址与关键字的每位都有关系，因此使得散列地址分布比较均匀，适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数。\n例：要存储整个学校的学生信息，以“身份证号”作为关键字设计散列函数\n 散列查找是“空间换时间”的算法，在散列表设计合理的情况下，散列表越长，冲突概率越低。\n ","permalink":"https://blog.archai.space/p/%E6%9F%A5%E6%89%BE%E7%AE%97%E6%B3%95%E7%9B%B8%E5%85%B3/","summary":"顺序查找 typedef struct{//查找表的数据结构（顺序表）  int *elem;//动态数组基址  int TableLen;//查找表长度 }SSTable; int Seq_Search(SSTable ST,int key){ ST.elem[0]=key; int i; for (i = ST.TableLen;ST.elem[i]!=key ; i--) {}//从后往前查找，最终返回下标i  return i;//返回0说明没找到 } 效率分析 对于长度为n的顺序表，如果查找成功 $$ ASL={\\frac{1+2+\u0026hellip;+n}{n}}=\\frac{n+1}{2} $$ 若果查找失败，则 $ASL=n+1$\n总体上，该算法时间复杂度为 $O(n)$\n优化思路 1.如果使得表中的元素有序存放……，可以构造出一棵查找判定树\n此时，查找失败时$ASL=\\frac{1+2+\u0026hellip;+n+n}{n+1}=\\frac{n}{2}+\\frac{n}{n+1}$\n优点： 容易查找失败时ASL更小\n2.如果各元素被查找的概率不同……，可以把概率大的靠前\n优点： 容易查找成功时ASL更小\n折半查找  折半查找，又称“二分查找”，仅适用于有序的顺序表。\n 针对升序排列的顺序表，代码实现如下\ntypedef struct{//查找表的数据结构（顺序表）  int *elem;//动态数组基址  int TableLen;//查找表长度 }SSTable; int BinarySearch(SSTable L,int key){ int low=0,high=L.TableLen-1,mid; while (low\u0026lt;=high) { mid=(low+high)/2; if (L.","title":"查找算法相关"},{"content":"一、最小生成树 📌什么是生成树？ 连通图的生成树是包含图中所有顶点的一个极小连通子图，通俗地讲，就是“边尽可能少，但需保持连通”。\n规律： 对于一个顶点数|V|=n的树，其生成树的边数|E|=n-1。如果将|E|+1，必然会形成回路；如果将|E|-1，则会成为非连通图。\n\n📌什么是最小生成树？  最小生成树，也称最小代价树(Minimum Spanning Tree，MST)\n 是带权连通无向图的生成树中边的权值之和最小的一棵树，联系实际问题不难理解其中“最小代价”的意味。\nPrim（普利姆算法），Kruskal（克鲁斯卡尔算法）就是寻找最小生成树的常用算法。\n1.Prim（普利姆算法）  从某一顶点开始，每次将代价最小的新顶点纳入生成树，直至所有顶点都纳入为止。\n 图示 \n易知，此方法得到的最小生成子树是不唯一的。\n代码实现 void MiniSpanTree_PRIMI(Graph G,int u){ //从顶点u出发找G的最小生成树  for (int i = 0; i \u0026lt;G.vexnum; ++i) {//辅助数组初始化  if(i!=u){ closedge[i]={u,G.arcs[u][i]}; } } closedge[u].lowcost=0; for (int j = 0; j \u0026lt; G.vexnum; j++) { k=minimum(closedge);//求生成树的下一个节点  cout\u0026lt;\u0026lt;cloedge[k].adjvex\u0026lt;\u0026lt;G.vex[k]; closedge[k].lowcost=0; for (int i = 0; i \u0026lt; G.vexnum; i++) { if (G.arc[k][j].adj\u0026lt;closedge[j].lowcost) { closedge[j]={G.vexs[k],G.arcs[k][j].adj}; } } } } 2.Kruskal（克鲁斯卡尔算法）  每次选择一条权值最小的边，使这条边的两头连通（原本已经连通的就不选），直到所有结点都连通。\n 图示 \n代码实现 void Kruskal(Graph G){ //定义保存一条边的权值的数组，任意两个顶点之间的边权值都可能被保存进去  int SortVal[G.arcnum][G.arcnum]; for (int i = 0; i \u0026lt; G.vexnum; i++) for (int j = 0; j \u0026lt; G.vexnum; j++) if (Adjacent(G,G.Vex[i],G.Vex[j])) {//如果存在边|弧  SortVal[i][j]=Get_edge_value(G,G.Vex[i],G.Vex[j]);//获取权值并保存  } // Paixu(SortVal); } 时间复杂度    算法 时间复杂度 适用方向     P O(V^2) 边稠密图   K $O(E\\log_2 E)$ 边稀疏图    二、最短路径    最短路径问题 算法     单源路径问题 BFS (无权图)\nDijkstra(带权图、无权图)   各点间路径 Floyd(带权图、无权图)    1.BFS  之所以叫BFS，是因为该方法求最短路径是在图的广度优先遍历算法(BFS)的基础上，求得了最短路径。\n 代码实现 //求顶点u到其他顶点的最短路径 void BFS_MIN_Distance(Graph G,int u){ bool visited[MAX_VERTEX_NUM]; SqQueue Q;//辅助队列  int d[G.vexnum];//d[i]表示从u到i的最短路径  int path[G.vexnum];//path[i]表示最短路径从哪个结点出来（前驱）  for (int i = 0; i \u0026lt; G.vexnum; ++i) {//初始化  d[i]=INIFINITY; path[i]=-1; } d[u]=0; visited[u]=true; EnQueue(Q,u); while(!isEmpty(Q)){ DeQueue(Q,u); for (w=FirstNeighbor(G,u);w\u0026gt;=0;w=NextNeighbor(G,u,w)) if (!visited[w]) { // visit(w);  d[w]=d[u]+1; path[w]=u; visited[w]=true; EnQueue(Q,w); }//if  }//while } 🧐实际上就是对DFS算法中visit这个没有具体实现的方法的修改操作,在 visit一个顶点时，修改其最短路径长度d[]并在path[]记录前驱结点。要注意出现的三个数组的意义:d[] path[] visited[]\n\n如图，是从2出发得到的数组，则2→8的最短路径长度d[8]=3，路径为8←7←6←2\n2.Dijkstra（迪杰斯特拉算法） 图示 \n使用 对于最后得到的结果：\n 例如求 V0到V2的最短路径，查表知，最短路径长dist[2]=9,其路径为 V2←V1←V4←V0\n复杂度 时间复杂度为$O(|V|^2)$\n代码实现  代码实现上有点类似Prim算法\n //从V0出发，总共需要n-1轮处理 //每一轮处理：循环遍历所有个结点，找到 lowCost最低的，且还没加入树的顶点。 //再次循环遍历，更新还没加入的各个顶点的 lowCost值 bool fina[G.vexnum];//是否已经找到最短路径 int dist[G.vexnum];//最短路径长度 int path[G.vexnum];//路径上的前驱 一点不足 Dijkstra(迪杰斯特拉算法)无法处理存在负权值的图，这点Floyd算法可以。\n3.Floyd（弗洛伊德算法） 算法思想（动态规划） Floyd算法:求出每一对顶点之间的最短路径 使用动态规划思想，将问题的求解分为多个阶段 对于n个顶点的图G，求任意一对顶点Vi → Vj之间的最短路径可分为如下几个阶段: #️⃣:不允许在其他顶点中转，最短路径是? 0️⃣:若允许在V0中转，最短路径是? 1️⃣:若允许在V0、V1 中转，最短路径是? 2️⃣:若允许在V0、V1、 V2中转，最短路径是?\n⏩. . .\n⏹:若允许在V0、V1、V2 \u0026hellip;. Vn-1中转，最短路径是?\n实例演示 对于一个有向带权图：\n\n#️⃣初始条件下，即不允许在其他顶点中转，有：\n\n0️⃣:若允许在V0中转，计算得知，A和path更新为：\n\n1️⃣:若允许在V0、V1 中转，经计算，A和path更新为：\n\n2️⃣:若允许在V0、V1、 V2中转，经计算，A和path更新为：\n\n即，从$A^{(-1)},path^{(-1)}$出发，经过n轮递推，得到$A^{(n-1)},path^{(n-1)}$ \n根据$A^{(2)}$可知，$V_0$到$V_2$最短路径长度为10，根据$path^{(2)}$可知，完整路径信息为$V_0→V_2→V_0$\n代码实现 核心更新代码如下：\n//...准备工作，初始化矩阵A和path  for (int k = 0; k \u0026lt; n; k++) {//考虑以Vk作为中转点  for (int i = 0; i \u0026lt; n; i++) {//遍历矩阵，i为行，j为列  for (int j = 0; j \u0026lt; n; j++) { if (A[i][j]\u0026gt;A[i][k]+A[k][j]) { //以Vk为中转点的路径是否更短  A[i][j]=A[i][k]+A[k][j];//更新最短路径长度  path[i][j]=k;//修改中转点  }//if  } } } 复杂度 时间复杂度：$O(|V|^3)$\n空间复杂度：$O(|V|^2)$\n一点不足 对于有回路的带负权图（负权回图），会陷入死循环……\n\n因为这种图本身可能就无解，即无最短路径，循环次数越多，路径越短。\n4.总结     BFS算法 Dijkstra算法 Floyd算法     无权图 ✔ ✔ ✔   带权图 ❌ ✔ ✔   带负权值的图 ❌ ❌ ✔   带负权回路的图 ❌ ❌ ❌   时间复杂度 $O(V^2)$或$O(V+E)$ $O(V^2)$ $O(V^3)$   通常用于 求无权图的单源最短路径 求带权图的单源最短路径 求带权图中各顶点间的最短路径    三、有向无环图相关 📌什么是有向无环图？\n若一个有向图中不存在环，则称为有向无环图，简称DAG图（Directed Acyclic Graph）\n\n1.描述表达式  对于给定的一个表达式，可通过有向无环图将其表示出来。\n 示例 👉$((a+b)(b(c+d))+(c+d)e)((c+d)*e)$\n1️⃣将各个操作数不重复地排成一排，之后把所有的运算符按照执行的先后顺序排序\n\n2️⃣分层（上层运算必须依赖下层结果）按顺序加入运算符\n\n\n\n最终：\n\n4️⃣自下而上逐层检查各层运算符之间是否可以合并\n\n最终结果就是：\n\n真题 29.【2019统考真题】用有向无环图描述表达式$(x+y)(x+y)/x)$，需要的顶点个数至少是( )。 A.5 B.6 C.8 D.9 \n答案：A\n\n2.拓扑排序  什么是拓扑排序？拓扑排序 - 中文维基百科 拓扑排序_百度百科\n 可见拓扑排序针对的是有向无环图，对于特殊的有向无环图——顶点活动网(Activity On Vertex network)，简称AOV网。整个AOV网通常表示一项具体的工程，其中顶点表示活动，用弧表示活动之间的优先关系。例如：\n\n算法描述 对于一个给定的AOV网，通过以下几个步骤即可找到拓扑排序的序列：\n1️⃣从AOV中选择没有前驱（入度为0）的顶点输出；\n2️⃣ 删除与输出顶点相关的边；\n3️⃣ 重复1️⃣2️⃣直至AOV网为空；\n代码实现 bool TopologicalSort(Graph G){ InitStack(S);//初始化栈，存储顶点的入度  for (int i = 0; i \u0026lt; G.vexnum; i++) { if (indegree[i]==0) { Push(S,i);//入度为0的顶点入栈  } } int count=0;//记录已经输出的顶点数  while(!isEmpty(S)){ Pop(S,i); print[count++]=i;//输出顶点i，并计数  for (p=G.vextices[i].firstarc;p;p=p-\u0026gt;nextarc) { //将所有i指向的顶点入度减1，并且将入度减为0的顶点入栈scanf  v=p-\u0026gt;adjvex; if (!(--indegree[v])) { Push(S,v);//入度为0，入栈  } }//for  }//while  if (count\u0026lt;G.vexnum) { return false;//排序失败，有向图中有回路  }else{ return true; //成功  } } 注意💬\n🔸其中用到了两个数组：indegree[] 表示当前顶点的入度；print[] 用于记录拓扑序列；\n🔸代码中的图用邻接表的形式保存；\n🔸还引入辅助栈S，用于保存入度为0的结点；\n🔸最后还加入一步判断，用之前定义的int count ，记录输出的顶点数。如果count \u0026lt; G.vexnum ，提前退出while循环，原因是图中存在回路，导致栈在某时刻为空，因为拓扑排序针对无环图，所以拓扑排序失败！\n时间复杂度 每个顶点要处理一次，每条边也要处理一次，因此时间复杂度为$O(E+V)$，如果采用邻接矩阵则需要 $O(V^2)$。\n逆拓扑排序 对一个AOV网，如果采用下列步骤进行排序，则称之为逆拓扑排序：\n①从AOV网中选择一个没有后继（出度为0）的顶点并输出。\n②从网中删除该顶点和所有以它为终点的有向边。\n③重复①和②直到当前的AOV网为空。\n参照拓扑排序实现 //abababa DFS算法实现 //如果图中存在回路，应该怎么判断？？ void DFSTraverse(Graph G){ for (int i = 0; i \u0026lt; G.vexnum; ++i) visited[i]=false; for (int v = 0; v \u0026lt; G.vexnum; ++v) if (!visited[v]) DFS(G,v); } bool TopologicalSort_DFS(Graph G,int v){ visit(v); visited[v]=true; for (w=FirstNeighbor(G,v);w\u0026gt;=0;w=FirstNeighbor(G,v))//处理v的所有邻接点  if (!visited[w]) { TopologicalSort_DFS(G,w); } print(v);//输出顶点 } 特性 💨拓扑，逆拓扑排序序列可能不唯一。\n💨如果存在回路，则没有拓扑，逆拓扑排序序列。\n3.关键路径  什么是关键路径？关键路径_百度百科\n 用顶点表示事件，弧表示活动，弧上的权值表示活动持续的时间的有向图叫AOE（Activity On Edge Network）网。\n几个术语 开始顶点（源点）\n结束顶点（汇点）\n关键路径 从源点到汇点的延时最长的路径\n关键活动 关键路径上的所有活动。\n事件\u0026amp;活动\n\n图中$V_k$表示事件，$a_i$表示活动。\n   事件$v_k$的最早发生时间$ve(k)$ 决定了所有从开始的活动能够开工的最早时间     活动$a_i$的最早开始时间$e(i)$ 该活动弧的起点所表示的事件的最早发生时间   事件$v_k$的最迟发生时间$vl(k)$ 在不推迟整个工程完成的前提下，该事件最迟必须发生的时间   活动$a_i$的最迟开始时间$l(i)$ 该活动弧的终点所表示事件的最迟发时间与该活动所需时间之差    \n求关键路径的步骤 对图中有向无环图求关键路径：\n\n1️⃣求所有事件的最早发生时间ve(k) 按拓扑排序序列，依次求各个顶点的$ve(k)$: $ve(源点) = 0$ $ve(k) = Max\\left {ve(j) + Weight(vj, vk)\\right }$, vj为vk 的任意前驱 \n2️⃣求所有事件的最迟发生时间vl(k) 按逆拓扑排序序列，依次求各个顶点的 $vl(k)$: $vl(汇点) = ve(汇点)$ $vl(k) = Min\\left {vl(j) - Weight(vk, vj)\\right }$ , vj为vk的任意后继\n\n3️⃣求所有活动的最早发生时间e(k) 若边$\u0026lt;vk,vj\u0026gt;$表⽰活动$ai$，则有$e(k) = ve(k)$\n\n(ai对应于图中已标注的弧，但图中的数值表示该弧的权值)\n4️⃣求所有活动的最迟发生时间l(k) 若边$\u0026lt;vk,vj\u0026gt;$表⽰活动$ai$，则有$l(i) = vl(j) - Weight(vk, vj)$\n\n5️⃣求所有活动的时间余量d(k) $d(k)=l(k)-e(k)$\n\n此时，时间余量为0的活动就是关键活动，由关键活动组成关键路径 \n即，关键活动：a2, a5, a7 =\u0026gt; 关键路径：V1→V3→V4→V6\n关键路径特点 🌚 若关键活动耗时增加，则整个⼯程的⼯期将增⻓ 🌚 缩短关键活动的时间，可以缩短整个⼯程的⼯期 🌚 当缩短到⼀定程度时，关键活动可能会变成⾮关键活动 🌚 可能有多条关键路径，只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期，只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的 代码实现 //adada 说明：文中图片来自王道考研辅导机构。\n","permalink":"https://blog.archai.space/p/%E5%9B%BE%E7%9A%84%E5%BA%94%E7%94%A8/","summary":"一、最小生成树 📌什么是生成树？ 连通图的生成树是包含图中所有顶点的一个极小连通子图，通俗地讲，就是“边尽可能少，但需保持连通”。\n规律： 对于一个顶点数|V|=n的树，其生成树的边数|E|=n-1。如果将|E|+1，必然会形成回路；如果将|E|-1，则会成为非连通图。\n\n📌什么是最小生成树？  最小生成树，也称最小代价树(Minimum Spanning Tree，MST)\n 是带权连通无向图的生成树中边的权值之和最小的一棵树，联系实际问题不难理解其中“最小代价”的意味。\nPrim（普利姆算法），Kruskal（克鲁斯卡尔算法）就是寻找最小生成树的常用算法。\n1.Prim（普利姆算法）  从某一顶点开始，每次将代价最小的新顶点纳入生成树，直至所有顶点都纳入为止。\n 图示 \n易知，此方法得到的最小生成子树是不唯一的。\n代码实现 void MiniSpanTree_PRIMI(Graph G,int u){ //从顶点u出发找G的最小生成树  for (int i = 0; i \u0026lt;G.vexnum; ++i) {//辅助数组初始化  if(i!=u){ closedge[i]={u,G.arcs[u][i]}; } } closedge[u].lowcost=0; for (int j = 0; j \u0026lt; G.vexnum; j++) { k=minimum(closedge);//求生成树的下一个节点  cout\u0026lt;\u0026lt;cloedge[k].adjvex\u0026lt;\u0026lt;G.vex[k]; closedge[k].lowcost=0; for (int i = 0; i \u0026lt; G.vexnum; i++) { if (G.arc[k][j].adj\u0026lt;closedge[j].lowcost) { closedge[j]={G.","title":"图的应用"},{"content":"广度优先遍历（BFS）  BFS(Breadth-First-Search)，参考对树的层序遍历\n \n对上面的图从①出发进行BFS得到序列：\n①②⑤ ⑥ ③⑦ ④⑧\n若采用不同的储存结构，可能会得到不同的遍历结果（这个差异主要来自寻找邻接点的过程），对于邻接矩阵存储的图，由于邻接矩阵是唯一的，所以BFS序列也是唯一的；同理，邻接表存储的图BFS序列不唯一。\nBFS算法  与树的层序遍历不同的是，由于图中存在回路，遍历过程中会出现重复访问的问题，故可构造visited数组，用来标记已访问过的数组。\n此外，还应针对非连通图做额外的判断，遍历完一个连通分量（极大连通子图）后，遍历查找visited数组中是否还存在未遍历的，如果有即为另一连通分量，继续调用BFS即可。\n void BFS(Graph G,int v); bool visited[MAX_VERTEX_NUM]; SqQueue Q;//辅助队列 void BFSTraverse(Graph G){ //初始化visited数组  for (int i = 0; i \u0026lt; G.vexnum; ++i) {//使下标从1开始  visited[i]=false; } //对非连通图的处理  for (int v = 0; v \u0026lt; G.vexnum; ++v) { if (!visited[v]) BFS(G,v); } } //从顶点v出发广度优先遍历图G void BFS(Graph G,int v){ visit(v); visited[v]=true; EnQueue(Q,v);//顶点v入队列Q  while(!isEmpty(Q)){ DeQueue(Q,v);//顶点v出队列Q  for (w=FirstNeighbor(G,v);w\u0026gt;=0;w=NextNeighbor(G,v,w))//处理v的所有邻接点  if (!visited[w]) { visit(w); visited[w]=true; EnQueue(Q,w); }//if  }//while } 需要借助辅助队列存储v的所有邻接结点\n复杂度 （一）空间 空间复杂度主要来自辅助队列，在访问v的邻接结点时会入队，因此最坏的情况下\n\n复杂度为：$O(|V|)$\n（二）时间 对时间复杂度的分析，不必纠结于算法中的具体循环，只需宏观地分析：\n①访问|V|个结点需要的时间复杂度？\n②访问各个结点的邻接结点需要的时间？\n    邻接矩阵型 邻接表型     ① $O(V)$ $O(V)$   ② $O(V^2)$ $O(2E)=O(E)$   时间复杂度 $O(V^2)$ $O(V+E)$    时间复杂度=访问各结点所需时间+探索各条边所需时间\n广度优先生成树  简而言之，就是 $\\text{连通图}\\overset{BFS}{\\rightarrow}\\text{树}$\n \n即 仅保留每个节点第一次被访问的那条通路\n 此外还有广度优先生产森林，即 $\\text{非连通图}\\overset{BFS}{\\rightarrow}\\text{森林}$\n 深度优先遍历（DFS）  BFS(Depth-First-Search)，参考对树的先序遍历\n \n对上面的图从②出发进行DFS得到序列：\n② ① ⑤ ⑥ ③ ④⑦ ⑧ \n同样的，邻接矩阵存储得到序列唯一，邻接表则不唯一。\nDFS算法 void DFS(Graph G,int v); bool visited[MAX_VERTEX_NUM]; void DFSTraverse(Graph G){ //初始化visited数组  for (int i = 0; i \u0026lt; G.vexnum; ++i) visited[i]=false; //对非连通图的处理  for (int v = 0; v \u0026lt; G.vexnum; ++v) if (!visited[v]) DFS(G,v); } //从顶点v出发广度优先遍历图G void DFS(Graph G,int v){ visit(v); visited[v]=true; for (w=FirstNeighbor(G,v);w\u0026gt;=0;w=FirstNeighbor(G,v))//始终遍历v的第一个邻接点，往“深”处走  if (!visited[w]) { DFS(G,w); }//if } 复杂度 （一）空间 主要来自函数调用栈，因此最坏的情况与最好的情况分别是\n\n因此，空间复杂度为$O(|V|)$\n（二）时间     邻接矩阵型 邻接表型     ① $O(V)$ $O(V)$   ② $O(V^2)$ $O(E)$   时间复杂度 $O(V^2)$ $O(V+E)$    深度优先生成树 同BFS\n思考：图的遍历与图的连通性 根据上述算法可知，对于无向图，调用BFS/DFS的次数与连通分量的数量有关，即 $\\text{BFS|DFS调用次数}\\propto \\text{连通分量数}$ \n对于有向图：\n\n1.若从①出发，需要调用几次DFS?\n答：4次。\n2.若从⑦出发，需要调用几次DFS?\n答：1次。\n因此，有\n   图的分类 调用BFS|DFS次数     无向图 连通：1次\n非连通：连通分量数   有向图 普通有向图：具体分析（与出发点的选择有关）\n强连通图：1次    🖋\n","permalink":"https://blog.archai.space/p/%E5%9B%BE%E7%9A%84%E9%81%8D%E5%8E%86/","summary":"广度优先遍历（BFS）  BFS(Breadth-First-Search)，参考对树的层序遍历\n \n对上面的图从①出发进行BFS得到序列：\n①②⑤ ⑥ ③⑦ ④⑧\n若采用不同的储存结构，可能会得到不同的遍历结果（这个差异主要来自寻找邻接点的过程），对于邻接矩阵存储的图，由于邻接矩阵是唯一的，所以BFS序列也是唯一的；同理，邻接表存储的图BFS序列不唯一。\nBFS算法  与树的层序遍历不同的是，由于图中存在回路，遍历过程中会出现重复访问的问题，故可构造visited数组，用来标记已访问过的数组。\n此外，还应针对非连通图做额外的判断，遍历完一个连通分量（极大连通子图）后，遍历查找visited数组中是否还存在未遍历的，如果有即为另一连通分量，继续调用BFS即可。\n void BFS(Graph G,int v); bool visited[MAX_VERTEX_NUM]; SqQueue Q;//辅助队列 void BFSTraverse(Graph G){ //初始化visited数组  for (int i = 0; i \u0026lt; G.vexnum; ++i) {//使下标从1开始  visited[i]=false; } //对非连通图的处理  for (int v = 0; v \u0026lt; G.vexnum; ++v) { if (!visited[v]) BFS(G,v); } } //从顶点v出发广度优先遍历图G void BFS(Graph G,int v){ visit(v); visited[v]=true; EnQueue(Q,v);//顶点v入队列Q  while(!isEmpty(Q)){ DeQueue(Q,v);//顶点v出队列Q  for (w=FirstNeighbor(G,v);w\u0026gt;=0;w=NextNeighbor(G,v,w))//处理v的所有邻接点  if (!","title":"图的遍历"},{"content":"邻接矩阵  Vextex/Vertices 顶点; Martix 矩阵; Arc 弧.\n \n代码实现 #define MaxVextexNum 100//容许存储的最大顶点数  typedef struct{ char Vex[MaxVextexNum]; //可以将定点之间的关系用int 类型01表示，也可定义为boolean/枚举类型，占空间更小  bool Edge[MaxVextexNum][MaxVextexNum]; int vexnum,arcnum;//顶点数和弧|边数 }MGraph; 即\n找度 根据邻接矩阵计算结点的度TD\n    无向图 有向图     $TD(V_i)$ 第i行（或i列）中非零元素的个数 $ID(V_i)$ : i行非零元素个数\n$OD(V_i)$: i列非零元素个数\nTD=ID+OD    对于带权图（网） \n#define MaxVextexNum 100//容许存储的最大顶点数 #define INIFINITY //宏定义，表示无穷  typedef char VextexType;//顶点 typedef int EdgeType;//权值  typedef struct{ VextexType Vex[MaxVextexNum]; EdgeType Edge[MaxVextexNum][MaxVextexNum]; int vexnum,arcnum; }MGraph; 复杂度 空间复杂度来自数组Vex[]跟Edge[]，故空间复杂度为$|V|+|V|^2=O(|V|^2)$，即为顶点数量的二次方，故此方法更适合存储稠密图，不然有较多浪费。\n性质  设图G的邻接矩阵为A（矩阵元素为0/1），则$A^n$的元素$A^n[i][j]$等于由顶点$i$到顶点$j$的长度为$n$的路径的数目。  \n对于$A^2[1][4]=a_{1,1}a_{1,4}+a_{1,2}a_{2,4}+a_{1,3}a_{3,4}+a_{4,1}a_{4,4}=1$， $a_{1,2}a_{2,4}=1*1$表示，有一条从$Vex[1]→Vex[2]→Vex[4]$的线，总结果$A^2[1][4]=1$表示从A到D路径长为2的路径数目为1，也就是$A→B→$D.\n对给定的图G，其邻接矩阵是唯一的。  邻接表  adjacency list 邻接表;\n \n代码实现 #define MaxVextexNum 100 typedef char VextexType;//顶点  //\u0026#34;边|弧\u0026#34; typedef struct ArcNode{ int adjvex;//边|弧指向哪一节点,\u0026#34;相邻的结点\u0026#34;  typedef struct ArcNode *next;//指向下一条弧的指针  //Infotype info; //边权值 }ArcNode;//定义边结点  //\u0026#34;顶点\u0026#34; typedef struct VNode{ VextexType data;//数据域  ArcNode *first;//第一条边|弧 }VNode,AdjList[MaxVextexNum];//定义顶点结点，AdjList为VNode类型的数组  //\u0026#34;邻接表\u0026#34; typedef struct{ AdjList vertices;//定义一个邻接链表  int vexnum,arcnum;//节点数，边|弧数 }ALGraph; 复杂度    无向图 有向图     $O(V+2E)$ $O(V+E)$ [只记出度]    适合稀疏图。\n找度    入度 出度     结点$i$的*first+*next数量 遍历所有节点，找到指向当前节点的所有指针数，之和即为出度（较复杂）    性质  邻接表的表示方式不唯一。 找有向图的入边不太方便。  十字链表（有向图）  十字链表只用于存储有向图\n \n复杂度 空间复杂度： $O(|V|+|E|)$，存储了所有顶点和边\n找度    入度 出度     顺着橙色🍍找，直至^ 顺着绿色🥒找，直至^    拿找A结点的出度为例\n从A绿色🥒出发，箭头指向01，即找到A→B的路径\n继续从绿色🥒出发，箭头指向02，即找到A→C的路径\n02绿色🥒处为^即再无出度\n邻接多重表（无向图） \n复杂度 空间复杂度： $O(|V|+|E|)$\n找度 从A出发，第一条边为01即AB这条边\n顺着iLink（即橙色）找到下一条边为01，即AD这条边\n至此，橙色为^即，无与0（即A）直接相连的边！\n其他类似\n横向对比 \n🤩后续研究一般只针对邻接矩阵和邻接表进行\n","permalink":"https://blog.archai.space/p/%E5%9B%BE%E7%9A%84%E5%AD%98%E5%82%A8/","summary":"邻接矩阵  Vextex/Vertices 顶点; Martix 矩阵; Arc 弧.\n \n代码实现 #define MaxVextexNum 100//容许存储的最大顶点数  typedef struct{ char Vex[MaxVextexNum]; //可以将定点之间的关系用int 类型01表示，也可定义为boolean/枚举类型，占空间更小  bool Edge[MaxVextexNum][MaxVextexNum]; int vexnum,arcnum;//顶点数和弧|边数 }MGraph; 即\n找度 根据邻接矩阵计算结点的度TD\n    无向图 有向图     $TD(V_i)$ 第i行（或i列）中非零元素的个数 $ID(V_i)$ : i行非零元素个数\n$OD(V_i)$: i列非零元素个数\nTD=ID+OD    对于带权图（网） \n#define MaxVextexNum 100//容许存储的最大顶点数 #define INIFINITY //宏定义，表示无穷  typedef char VextexType;//顶点 typedef int EdgeType;//权值  typedef struct{ VextexType Vex[MaxVextexNum]; EdgeType Edge[MaxVextexNum][MaxVextexNum]; int vexnum,arcnum; }MGraph; 复杂度 空间复杂度来自数组Vex[]跟Edge[]，故空间复杂度为$|V|+|V|^2=O(|V|^2)$，即为顶点数量的二次方，故此方法更适合存储稠密图，不然有较多浪费。","title":"图的存储"},{"content":" 平衡二叉树是Adelson-Velsky和 Landis发明，故命名为AVL树。也称平衡二叉查找树。\n ✨特点： ①左子树\u0026lt;根\u0026lt;右子树； ②任一节点，左右子树高度之差不超过1.\n平衡因子 $平衡因子=左子树高-右子树高$\nAVL树的插入操作 AVL树插入新结点导致不平衡之后，只需将最小不平衡子树平衡，其他祖先结点会随之恢复平衡。\n调整最小不平衡子树 注意：调整过后必须保证其BST的特性，即“左子树1.LL  即在以A为根节点的树的左孩子B的左子树上插入新结点，导致A成为最小不平衡子树。\n \n调整过程如下：\n\n2.RR  即在以A为根节点的树的右孩子B的右子树上插入新结点，导致A成为最小不平衡子树。\n \n调整过程如下：\n\n3.LR  即在以A为根节点的树的左孩子B的右子树上插入新结点，导致A成为最小不平衡子树。\n \n观察得知，所进行的调整就是保证$|平衡因子|\u0026lt;=1$，因此若插入操作使得\n左 - 右 \u0026gt; 1 =\u0026gt; 右旋\n右 - 左 \u0026gt; 1 =\u0026gt;左旋\n而当进行了LR插入操作之后，导致以A为根节点的树 左-右\u0026gt;1，理应右旋但是，由上述结果可知，经过右旋之后：\n\n可以看到，为了保证其左子树\u0026lt;根\u0026lt;右子树的特性，经过调整后，依然存在右-左\u0026gt;1的问题；\n因此，对于LR型不能简单进行右旋调整，应该先将其转化为LL型 (左旋)，再进行右旋；\n为此，我们需要将BR结点展开，之后旋转成为LL型插入\n\n可以看到，展开后又出现两种插入情况CL\u0026amp;CR，但其实两者处理大同小异：\nCR插入调整过程如下：\n\n4.RL  即在以A为根节点的树的右孩子B的左子树上插入新结点，导致A成为最小不平衡子树。\n \n参考LR型，其调整过程如下：\n\n查找操作效率分析 Assuming that, $n_h$表示深度为h的平衡树中含有的最少结点，则\n$n_0=0$,$n_1=1$,$n_2=2$\u0026hellip;存在递归关系 $n_h=n_{h-1}+n_{h-2}+1$,即左右子树结点之和+根节点。\n可以证明(AVL证明)，n个结点的平衡二叉树最大深度数量级为$\\log_2n$，则其查找操作的复杂度为$O(\\log_2n)$\n","permalink":"https://blog.archai.space/p/avl%E6%A0%91/","summary":"平衡二叉树是Adelson-Velsky和 Landis发明，故命名为AVL树。也称平衡二叉查找树。\n ✨特点： ①左子树\u0026lt;根\u0026lt;右子树； ②任一节点，左右子树高度之差不超过1.\n平衡因子 $平衡因子=左子树高-右子树高$\nAVL树的插入操作 AVL树插入新结点导致不平衡之后，只需将最小不平衡子树平衡，其他祖先结点会随之恢复平衡。\n调整最小不平衡子树 注意：调整过后必须保证其BST的特性，即“左子树1.LL  即在以A为根节点的树的左孩子B的左子树上插入新结点，导致A成为最小不平衡子树。\n \n调整过程如下：\n\n2.RR  即在以A为根节点的树的右孩子B的右子树上插入新结点，导致A成为最小不平衡子树。\n \n调整过程如下：\n\n3.LR  即在以A为根节点的树的左孩子B的右子树上插入新结点，导致A成为最小不平衡子树。\n \n观察得知，所进行的调整就是保证$|平衡因子|\u0026lt;=1$，因此若插入操作使得\n左 - 右 \u0026gt; 1 =\u0026gt; 右旋\n右 - 左 \u0026gt; 1 =\u0026gt;左旋\n而当进行了LR插入操作之后，导致以A为根节点的树 左-右\u0026gt;1，理应右旋但是，由上述结果可知，经过右旋之后：\n\n可以看到，为了保证其左子树\u0026lt;根\u0026lt;右子树的特性，经过调整后，依然存在右-左\u0026gt;1的问题；\n因此，对于LR型不能简单进行右旋调整，应该先将其转化为LL型 (左旋)，再进行右旋；\n为此，我们需要将BR结点展开，之后旋转成为LL型插入\n\n可以看到，展开后又出现两种插入情况CL\u0026amp;CR，但其实两者处理大同小异：\nCR插入调整过程如下：\n\n4.RL  即在以A为根节点的树的右孩子B的左子树上插入新结点，导致A成为最小不平衡子树。\n \n参考LR型，其调整过程如下：\n\n查找操作效率分析 Assuming that, $n_h$表示深度为h的平衡树中含有的最少结点，则\n$n_0=0$,$n_1=1$,$n_2=2$\u0026hellip;存在递归关系 $n_h=n_{h-1}+n_{h-2}+1$,即左右子树结点之和+根节点。\n可以证明(AVL证明)，n个结点的平衡二叉树最大深度数量级为$\\log_2n$，则其查找操作的复杂度为$O(\\log_2n)$","title":"AVL树"},{"content":"基本概念 1.结点的权\n每个结点带有的具有某种现实意义的数值(比如代表重要性等)\n2.结点的带权路径长度\n从树的根到该结点的路径长度（经过的边数）与该结点上权值的乘积\n3.树的带权路径长度\n树中所有叶结点的带权路径长度之和（WPL, Weighted Path Length）\n$WPL=\\sum_{i=1}^{n}w_il_i$\n✨4.哈夫曼树\n在含有n个带权叶结点的二叉树中，其中带权路径长度（WPL）最小的二叉树称为哈夫曼树，也称最优二叉树\n构造哈夫曼树 给定n个权值分别为 $w_1,w_2,\u0026hellip;,w_n$ 的结点，其构造过程描述如下：\n① 将这n个结点分别作为n棵仅含一个结点的二叉树，构成森林F\n② 构造一个新结点，从F中选取两棵根结点权值最小的树作为新结点的左右子树，并且将新结点的权值置为左、右子树上根结点的权值之和。\n③ 从F中删除刚才选出的两棵树，同时将新得到的树加入F中。\n④ 重复步骤②和③，直至F中只剩下一棵树为止。\n假设给定结点在经过步骤①之后如下\n\n则其②③④步骤为：\n\n$WPL_{min}=1*7+2*3+3*2+4*1+4*2=31$\n或者\n\n$WPL_{min}=1*7+3*(1+2+2+3)=31$\n👇👇👇👇👇👇\n哈夫曼树特点： ① 每个初始结点最终都成为叶结点，且权值越小的结点到根结点的路径长度越大。 ② 哈夫曼树的结点总数为2n-1。 ③ 哈夫曼树中不存在度为1的结点。 ④ 哈夫曼树并不唯一，但WPL必然相同且为最优。\n哈夫曼编码 固定长度编码 如ASCII码\n可变长度编码 前缀编码 没有一个编码是另一个编码的前缀\n\n哈夫曼编码 由哈夫曼树得到哈夫曼编码——字符集中的每个字符作为一个叶子结点，各个字符出现的频度作为结点的权值，根据之前介绍的方法构造哈夫曼树。\n✨ 应用\n可用于数据压缩\n如图，为英文字母使用频率表，若不进行压缩，第 i 个字母频率用$l_i表示$，路径长至少为5($2^4\u0026lt;26\u0026lt;2^5$)，则\n$WPL=5×\\sum_{i=1}^{n}l_i$=500\n\n若通过构造哈夫曼树进行哈夫曼编码\n则$WPL_{min}=$\n$v=\\frac{WPL_{min}}{WPL}$\n","permalink":"https://blog.archai.space/p/%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91/","summary":"基本概念 1.结点的权\n每个结点带有的具有某种现实意义的数值(比如代表重要性等)\n2.结点的带权路径长度\n从树的根到该结点的路径长度（经过的边数）与该结点上权值的乘积\n3.树的带权路径长度\n树中所有叶结点的带权路径长度之和（WPL, Weighted Path Length）\n$WPL=\\sum_{i=1}^{n}w_il_i$\n✨4.哈夫曼树\n在含有n个带权叶结点的二叉树中，其中带权路径长度（WPL）最小的二叉树称为哈夫曼树，也称最优二叉树\n构造哈夫曼树 给定n个权值分别为 $w_1,w_2,\u0026hellip;,w_n$ 的结点，其构造过程描述如下：\n① 将这n个结点分别作为n棵仅含一个结点的二叉树，构成森林F\n② 构造一个新结点，从F中选取两棵根结点权值最小的树作为新结点的左右子树，并且将新结点的权值置为左、右子树上根结点的权值之和。\n③ 从F中删除刚才选出的两棵树，同时将新得到的树加入F中。\n④ 重复步骤②和③，直至F中只剩下一棵树为止。\n假设给定结点在经过步骤①之后如下\n\n则其②③④步骤为：\n\n$WPL_{min}=1*7+2*3+3*2+4*1+4*2=31$\n或者\n\n$WPL_{min}=1*7+3*(1+2+2+3)=31$\n👇👇👇👇👇👇\n哈夫曼树特点： ① 每个初始结点最终都成为叶结点，且权值越小的结点到根结点的路径长度越大。 ② 哈夫曼树的结点总数为2n-1。 ③ 哈夫曼树中不存在度为1的结点。 ④ 哈夫曼树并不唯一，但WPL必然相同且为最优。\n哈夫曼编码 固定长度编码 如ASCII码\n可变长度编码 前缀编码 没有一个编码是另一个编码的前缀\n\n哈夫曼编码 由哈夫曼树得到哈夫曼编码——字符集中的每个字符作为一个叶子结点，各个字符出现的频度作为结点的权值，根据之前介绍的方法构造哈夫曼树。\n✨ 应用\n可用于数据压缩\n如图，为英文字母使用频率表，若不进行压缩，第 i 个字母频率用$l_i表示$，路径长至少为5($2^4\u0026lt;26\u0026lt;2^5$)，则\n$WPL=5×\\sum_{i=1}^{n}l_i$=500\n\n若通过构造哈夫曼树进行哈夫曼编码\n则$WPL_{min}=$\n$v=\\frac{WPL_{min}}{WPL}$","title":"哈夫曼树"},{"content":"普通树 对于一棵普通类型的树形结构，可将其转化为二叉树之后再参考二叉树的方法进行相关操作。\n孩子兄弟表示法（链式结构） 通过此方法可将普通树转化为二叉树\n//孩子兄弟即Child, Sibling typedef struct CSNode{ Elemtype data; struct CSNode *firstchild,*nextsibling;//第一个孩子和右兄弟指针,等价于*lchild,*rchild }CSNode,*CSTree 图示如下\n\n树的遍历 1.深度优先遍历（先根遍历\u0026amp;后根遍历）\n 先根遍历   若树非空，先访问根结点，再依次对每棵子树进行先根遍历(递归)。\n 对如上图所示的树进行先根遍历：\nA B C D\nA\t(BE )\t(CF)\t(DG)\nA\t(BEH) (CF)\t(DG)\n即先根遍历序列为A B E H C F D G\n发现与通过“孩子兄弟法”将树转为二叉树后的先序遍历序列相同\n 后根遍历   若树非空，先依次对每棵子树进行后根遍历，最后再访问根结点。\n 对如上图所示的树进行后根遍历：\nB\tC\tD\tA\n(E\tB) (F\tC) (G\tD) A\n(H\tE\tB) (F\tC) (GD) A\n即后根遍历序列为H E B F C G D A\n发现与通过“孩子兄弟法”将树转为二叉树后的中序遍历序列相同\n2.广度优先遍历（层序遍历）\n利用队列实现（参考二叉树层序遍历）\n对于上图所示的树进行层序遍历：\nA B C D E F G H\n 😯不难看出深度优先和广度优先分别表示遍历时的路径走向。前者是先往深处走，故为深度优先遍历；后者是先往宽了走，即为广度优先遍历。\n 森林 对于森林，也可以将其转化为二叉树之后进行操作\n同样的，我们采用孩子兄弟法将树转化为二叉树，具体操作为将森林中各个树的根节点视为兄弟关系。如下图所示：\n\n###　1.先序遍历\n若森林为非空，则按如下规则进行遍历：\n访问森林中第一棵树的根结点。\n先序遍历第一棵树中根结点的子树森林。\n先序遍历除去第一棵树之后剩余的树构成的森林。\nB\n(B E)\n(B E K L)\n(B E K L F)\n(B E K L F) C\n(B E K L F) (C G)\n(B E K L F) (C G) D\n(B E K L F) (C G) (D H)\n(B E K L F) (C G) (D H M)\n(B E K L F) (C G) (D H M I J)\n最终，对森林的先序遍历序列为 B E K L F C G D H M I J(也可以对各个树先根遍历得到)\n发现与通过“孩子兄弟法”将树转为二叉树后的先序遍历序列相同\n2.中序遍历 若森林为非空，则按如下规则进行遍历：\n中序遍历森林中第一棵树的根结点的子树森林。\n访问第一棵树的根结点。\n中序遍历除去第一棵树之后剩余的树构成的森林。\nK L\nK L E\nK L E F\n(K L E F B)\n(K L E F B) (G C)\n(K L E F B) (G C) (M)\n(K L E F B) (G C) (M H I J)\n(K L E F B) (G C) (M H I J D)\n最终，对森林的先序遍历序列为 K L E F B G C M H I J D(也可以对各个树后根遍历得到)\n发现与通过“孩子兄弟法”将树转为二叉树后的中序遍历序列相同\n总结 对于树或者森林进行遍历操作，与转化为二叉树之后的操作对应关系如下：\n   树 森林 二叉树     先根遍历 先序遍历 先序遍历   后根遍历 中序遍历 中序遍历    ","permalink":"https://blog.archai.space/p/%E6%A0%91/","summary":"普通树 对于一棵普通类型的树形结构，可将其转化为二叉树之后再参考二叉树的方法进行相关操作。\n孩子兄弟表示法（链式结构） 通过此方法可将普通树转化为二叉树\n//孩子兄弟即Child, Sibling typedef struct CSNode{ Elemtype data; struct CSNode *firstchild,*nextsibling;//第一个孩子和右兄弟指针,等价于*lchild,*rchild }CSNode,*CSTree 图示如下\n\n树的遍历 1.深度优先遍历（先根遍历\u0026amp;后根遍历）\n 先根遍历   若树非空，先访问根结点，再依次对每棵子树进行先根遍历(递归)。\n 对如上图所示的树进行先根遍历：\nA B C D\nA\t(BE )\t(CF)\t(DG)\nA\t(BEH) (CF)\t(DG)\n即先根遍历序列为A B E H C F D G\n发现与通过“孩子兄弟法”将树转为二叉树后的先序遍历序列相同\n 后根遍历   若树非空，先依次对每棵子树进行后根遍历，最后再访问根结点。\n 对如上图所示的树进行后根遍历：\nB\tC\tD\tA\n(E\tB) (F\tC) (G\tD) A\n(H\tE\tB) (F\tC) (GD) A","title":"树"},{"content":"线索二叉树 WHY  方便从任一个结点出发，找到其前驱、后继；方便遍历\n 普通二叉树中，对任意一个结点，若想找到其前驱/后继结点，只能再进行一次相应的前/中/后序遍历才行，复杂度太高。\n为此，我们引入前驱线索，后继线索的概念。其中，前驱线索由左孩子指针充当，后继线索由右孩子指针充当。\ntypedef struct BiTNode{ ElemType data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; 构建出如下图所示的结构：\n\n但是，\n*lchild(*rchild)有可能指向存在的结点，为此我们引入线索标志。当线索标志为1时，表示孩子指针指向前驱后继，线索标志为0时，表示孩子指针指向左右孩子。此时\ntypedef struct ThreadNode{ ElemType data; struct ThreadNode *lchild,*rchild; int ltag,rtag;//左右线索标志 }ThreadNode,*ThreadTree; 这样，每一个线索链表中的结点就可以图示为:\n\nHOW  如何分别用代码实现前中后序遍历下的线索链表\n 1.中序线索化  其实中序线索化的过程就是再进行一遍中序遍历，为每个节点添加额外的信息(lchild, rcild, ltag, rtag).\n 🍔初始定义结构体\ntypedef struct ThreadNode{ ElemType data; struct ThreadNode *lchild,*rchild; int ltag,rtag; }ThreadNode,*ThreadTree; 🍔定义前驱指针\nThreadNode *pre=NULL;  前驱指针还可以定义在局部，这里为了方便起见，将pre定义为全局。\n 🍔开始定义处理函数\nvoid CreatInThread(ThreadTree T){ pre=NULL; if(T!=NULL){ InThread(T); if(pre-\u0026gt;rchild==NULL){ pre-\u0026gt;rtag=1; } } }  注意：最后一个节点单独处理\n 🍔中序遍历二叉树，顺便线索化\nvoid InThead(ThreadTree T){ if(T!=NULL){ //左根右  InThead(T-\u0026gt;lchild); visit(T); InThead(T-\u0026gt;rchild); } } 🍔访问节点时加上线索信息\nvoid visit(ThreadNode q){ if(q-\u0026gt;lchild==NULL){ q-\u0026gt;lchild=pre; q-\u0026gt;ltag=1; } if(pre!=NULL \u0026amp;\u0026amp; pre-\u0026gt;rchild==NULL){ pre-\u0026gt;rchild=p; pre-\u0026gt;rtag=1; } pre=q; } 此外，合并之后：\nvoid InThread(ThreadTree p,Thread \u0026amp;pre){ if(p!=NULL){ //左  InThread(p-\u0026gt;lchild,pre); //根  if(p-\u0026gt;lchild==NULL){ p-\u0026gt;lchild=pre; p-\u0026gt;ltag=1; } if(pre!=NULL \u0026amp;\u0026amp; pre-\u0026gt;rchild==NULL){ pre-\u0026gt;rchild=p; pre-\u0026gt;rtag=1; } pre=p; //右  InThread(p-\u0026gt;rchild,pre); } } void CreatInThread(ThreadTree T){ ThreadTree pre=NULL; if(T!=NULL){ InThread(T,pre); //处理最后一个节点  pre-\u0026gt;rchild==NULL pre-\u0026gt;rtag=1; } }  注意在CreatInThread并不需要判断pre-\u0026gt;rchild是否为NULL\n因为，按照中序遍历的顺序“左根右”，最后退出循环之后pre不可能有rchild，否则还会按照“根右”的顺序继续执行InThread,所以退出循环的pre必然为中序遍历下的最后一个结点。\n 2.先序线索化  代码参考中序线索化\n void PreThead(ThreadTree T){ if(T!=NULL){ //根左右  visit(T); PreThead(T-\u0026gt;lchild); PreThead(T-\u0026gt;rchild); } } 由于先进行根节点的vist操作，如图，在vist(4)完成之后，根据规则，④的lchild指针指向②，因此就会陷入无限循环的困境……\n\n为此，只需在进行PreThead(T-\u0026gt;lchild)前加一句判断即可，具体如下：\nvoid InThead(ThreadTree T){ if(T!=NULL){ //根左右  visit(T); if(T-\u0026gt;ltag==0){//lchild不是前驱线索时才访问lchild  PreThead(T-\u0026gt;lchild); } PreThead(T-\u0026gt;rchild); } } 其余参考中序线索化即可。\n3.后续线索化 void PreThead(ThreadTree T){ if(T!=NULL){ //左右根  PreThead(T-\u0026gt;lchild); PreThead(T-\u0026gt;rchild); visit(T); } } 其余参考中序线索化即可。\nWHERE  如何使用线索二叉树实现前中后序遍历下的查找前驱后继的问题\n \n1.中序线索二叉树 对于一颗中序线索化后的二叉树\n\n🍖当某结点ltag==1时，前驱后继分别为lchild,rchild\n🍖当某结点ltag==0时，根据中序遍历“左根右”的顺序：\n 前驱为lchild在中序遍历下最后被访问的那个结点 后继为rchild在中序遍历下最先被访问的那个结点  2.先序线索二叉树 🍖对于后继，参考中序线索二叉树即可。\n🍖对于前驱，由于先序遍历“根左右”的遍历顺序，该节点无法找到前驱😰\n  解决办法一：从头至尾进行一次先序遍历，找到其前驱。\n  解决办法二：引入三叉树，为每个节点存储parent指针，即指向父节点，此时前驱为：\n\n  3.后序线索二叉树  参考先序线索二叉树\n 4. 另外 🧐中序遍历下最后被访问的那个结点如何通过代码找到？\n根据中序遍历 左根右 的顺序，即为最“右下”的结点，但不一定是叶节点哦\nThreadNode *Lastnode(ThreadNode *p){ while(p-\u0026gt;rtag==0) p=p-\u0026gt;rchild return p; } ","permalink":"https://blog.archai.space/p/%E7%BA%BF%E7%B4%A2%E4%BA%8C%E5%8F%89%E6%A0%91/","summary":"线索二叉树 WHY  方便从任一个结点出发，找到其前驱、后继；方便遍历\n 普通二叉树中，对任意一个结点，若想找到其前驱/后继结点，只能再进行一次相应的前/中/后序遍历才行，复杂度太高。\n为此，我们引入前驱线索，后继线索的概念。其中，前驱线索由左孩子指针充当，后继线索由右孩子指针充当。\ntypedef struct BiTNode{ ElemType data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; 构建出如下图所示的结构：\n\n但是，\n*lchild(*rchild)有可能指向存在的结点，为此我们引入线索标志。当线索标志为1时，表示孩子指针指向前驱后继，线索标志为0时，表示孩子指针指向左右孩子。此时\ntypedef struct ThreadNode{ ElemType data; struct ThreadNode *lchild,*rchild; int ltag,rtag;//左右线索标志 }ThreadNode,*ThreadTree; 这样，每一个线索链表中的结点就可以图示为:\n\nHOW  如何分别用代码实现前中后序遍历下的线索链表\n 1.中序线索化  其实中序线索化的过程就是再进行一遍中序遍历，为每个节点添加额外的信息(lchild, rcild, ltag, rtag).\n 🍔初始定义结构体\ntypedef struct ThreadNode{ ElemType data; struct ThreadNode *lchild,*rchild; int ltag,rtag; }ThreadNode,*ThreadTree; 🍔定义前驱指针\nThreadNode *pre=NULL;  前驱指针还可以定义在局部，这里为了方便起见，将pre定义为全局。\n 🍔开始定义处理函数\nvoid CreatInThread(ThreadTree T){ pre=NULL; if(T!=NULL){ InThread(T); if(pre-\u0026gt;rchild==NULL){ pre-\u0026gt;rtag=1; } } }  注意：最后一个节点单独处理","title":"线索二叉树"},{"content":"由遍历序列构造出二叉树 仅知道一种遍历序列是无法确定唯一的二叉树的，以中序遍历为例，对于一个中序遍历序列“BDCAE”，其对应的树形结构可能有下面三种：\n\n因此，至少需要两种遍历序列才可以推知树形结构。\n1.前序+中序遍历序列 🎈基本步骤 由前序遍历的特性得知，前序遍历中第一个节点必然为根节点，因此根据中序遍历特性，根节点左边为左子树下的所有节点，右边为右子树下的所有节点，然后分别在左子树序列右子树序列中重复进行即可。\n🎈示例\n 前序遍历序列：A D B C E 中序遍历序列：B D C A E  首先能确定根节点为A，根据中序遍历序列可以得到：\n\n对于左子树BDC，根据前序遍历，此子树根节点为D，根据中序遍历序列：\n\n至此，二叉树的还原工作就完成了！至于更复杂的序列，逐步推断即可😋\n2.后序+中序遍历序列 🔑与1不用的是，后序遍历中根节点为后序遍历序列尾部的那个节点，其余参照1即可！\n3.层序遍历+中序遍历 🔑 根据层序遍历特性，层序遍历中根节点始终在子树前面，“根左右”\n🎈示例\n 层序遍历序列：A D E B C 中序遍历序列：B D C A E  根节点为A，根据中序遍历序列可以得到：\n\n对于左子树BDC，根据前序遍历，此子树根节点为D，根据中序遍历序列：\n\n思考  如果前序，后续，层序两两组合能否确定唯一的树结构？\n \n假设给定序列如下：\n 前序：A B 后序：B A 层序：A B  其两两组合都满足两种结构：\n\n因此前序，后续，层序两两组合不能确定唯一的树结构。\n","permalink":"https://blog.archai.space/p/%E7%94%B1%E9%81%8D%E5%8E%86%E5%BA%8F%E5%88%97%E6%9E%84%E9%80%A0%E5%87%BA%E4%BA%8C%E5%8F%89%E6%A0%91/","summary":"由遍历序列构造出二叉树 仅知道一种遍历序列是无法确定唯一的二叉树的，以中序遍历为例，对于一个中序遍历序列“BDCAE”，其对应的树形结构可能有下面三种：\n\n因此，至少需要两种遍历序列才可以推知树形结构。\n1.前序+中序遍历序列 🎈基本步骤 由前序遍历的特性得知，前序遍历中第一个节点必然为根节点，因此根据中序遍历特性，根节点左边为左子树下的所有节点，右边为右子树下的所有节点，然后分别在左子树序列右子树序列中重复进行即可。\n🎈示例\n 前序遍历序列：A D B C E 中序遍历序列：B D C A E  首先能确定根节点为A，根据中序遍历序列可以得到：\n\n对于左子树BDC，根据前序遍历，此子树根节点为D，根据中序遍历序列：\n\n至此，二叉树的还原工作就完成了！至于更复杂的序列，逐步推断即可😋\n2.后序+中序遍历序列 🔑与1不用的是，后序遍历中根节点为后序遍历序列尾部的那个节点，其余参照1即可！\n3.层序遍历+中序遍历 🔑 根据层序遍历特性，层序遍历中根节点始终在子树前面，“根左右”\n🎈示例\n 层序遍历序列：A D E B C 中序遍历序列：B D C A E  根节点为A，根据中序遍历序列可以得到：\n\n对于左子树BDC，根据前序遍历，此子树根节点为D，根据中序遍历序列：\n\n思考  如果前序，后续，层序两两组合能否确定唯一的树结构？\n \n假设给定序列如下：\n 前序：A B 后序：B A 层序：A B  其两两组合都满足两种结构：\n\n因此前序，后续，层序两两组合不能确定唯一的树结构。","title":"由遍历序列构造出二叉树"},{"content":" 向量的空间管理，有静态和动态两种策略\n 静态空间管理策略 开辟内部数组_elem[]并使用一段地址连续的物理空间，_capacity表示总容量 ，_size表示当前的实际规模n，示意图如下：\n\n若采用静态空间管理策略，容量_capacity固定，则有明显的不足\u0026hellip;\n上溢/overflow： _elem[]不足以存放所有元素，尽管此时系统往往仍有足够的空间\n下溢/underflow：  _elem[]中的元素寥寥无几，装填因子 λ = _size / _capacity \u0026laquo; 50%\n动态空间管理策略 在即将上溢时，适当扩大内部数组的容量\n递增策略 当需要扩容时，为_capacity追加固定大小的空间，即\nT* oldElem = _elem; _elem = new T[ _capacity += INCREMENT ]; 考虑最坏情况，在初始容量为0的空向量中连续插入n = m*I个元素\n那么，在第1，I+1，2I+1，3I+1\u0026hellip;次插入元素时都需要扩容，表示为\n\n倍增策略 当需要扩容时，增加_capacity 为原来的两倍，即\nT* oldElem = _elem; _elem = new T[ _capacity \u0026lt;\u0026lt;= 1 ]; 考虑最坏情况，在初始容量为1的空向量中连续插入n = 2^m个元素\n那么，在第1，2，4，8\u0026hellip;次插入元素时都需要扩容，表示为\n\n两种策略的复杂度分析 考虑最坏的情况，两种策略的复杂度分别为\n 递增策略： 为算术级数，0+I+2I+\u0026hellip;=O(n ²) 倍增策略： 为几何级数，1+2¹+2²+2³+\u0026hellip;=O(2^m)=O(n)      递增策略 倍增策略     累计时间 O(n ²) O(n)   分摊时间 O(n) O(1)   装填因子 λ ≈100% \u0026gt;50%    可以看出，倍增策略在牺牲空间的基础上，换取时间上的巨大提升，可采取√\n 注意这里用到了分摊分析的概念，区别于平均/期望分析\n ","permalink":"https://blog.archai.space/p/%E5%90%91%E9%87%8F%E7%A9%BA%E9%97%B4%E7%AE%A1%E7%90%86%E7%AD%96%E7%95%A5/","summary":"向量的空间管理，有静态和动态两种策略\n 静态空间管理策略 开辟内部数组_elem[]并使用一段地址连续的物理空间，_capacity表示总容量 ，_size表示当前的实际规模n，示意图如下：\n\n若采用静态空间管理策略，容量_capacity固定，则有明显的不足\u0026hellip;\n上溢/overflow： _elem[]不足以存放所有元素，尽管此时系统往往仍有足够的空间\n下溢/underflow：  _elem[]中的元素寥寥无几，装填因子 λ = _size / _capacity \u0026laquo; 50%\n动态空间管理策略 在即将上溢时，适当扩大内部数组的容量\n递增策略 当需要扩容时，为_capacity追加固定大小的空间，即\nT* oldElem = _elem; _elem = new T[ _capacity += INCREMENT ]; 考虑最坏情况，在初始容量为0的空向量中连续插入n = m*I个元素\n那么，在第1，I+1，2I+1，3I+1\u0026hellip;次插入元素时都需要扩容，表示为\n\n倍增策略 当需要扩容时，增加_capacity 为原来的两倍，即\nT* oldElem = _elem; _elem = new T[ _capacity \u0026lt;\u0026lt;= 1 ]; 考虑最坏情况，在初始容量为1的空向量中连续插入n = 2^m个元素\n那么，在第1，2，4，8\u0026hellip;次插入元素时都需要扩容，表示为\n\n两种策略的复杂度分析 考虑最坏的情况，两种策略的复杂度分别为\n 递增策略： 为算术级数，0+I+2I+\u0026hellip;=O(n ²) 倍增策略： 为几何级数，1+2¹+2²+2³+\u0026hellip;=O(2^m)=O(n)      递增策略 倍增策略     累计时间 O(n ²) O(n)   分摊时间 O(n) O(1)   装填因子 λ ≈100% \u0026gt;50%    可以看出，倍增策略在牺牲空间的基础上，换取时间上的巨大提升，可采取√","title":"向量空间管理策略"},{"content":"减而治之(Decrease and conquer)  什么是“减而治之”？\n为求解一个大规模的问题，可以将其划分为两个问题，其一平凡，另一规模缩减 ，分别求解子问题，由子问题的解得原问题的解\n \n比如说，对于一个数组A的求和问题，可以设计如下的算法\nsum( int A[], int n ) { return n \u0026lt; 1? 0 : sum(A, n - 1) + A[n - 1]; } 当规模缩减到一定程度后，抵达递归基，返回0\n复杂度分析 1. 递归跟踪\n 绘出计算过程中出现过的所有递归实例（及其调用关系） ，那么它们各自所需时间之总和，即为整体运行时间\n 上例中，共计n+1个递归实例(分析)，各自只需O(1)时间 故总体运行时间为：\nT(n) =O(1) × (n+1) =O(n)\n2.递推方程\n 对于大规模的问题、复杂的递归算法，递归跟踪不再适用 此时可采用另一抽象的方法\u0026hellip;\n 在本例中，有T(n)=T(n-1) + O(1),T(0)=O(1)\n则，T(n) = T(n-2)+O(2) = T(n-3)+O(3) = T(n-n)+O(n)=O(n)\n 可以看到，两种分析方法的出来复杂度都为O(n)\n 分而治之(Divide and conquer)  什么是“分而治之”？\n为求解一个大问题，可以将其划分为若干子问题，规模大体相当（可理解为将大问题等分），分别求解子问题，由子问题的解得原问题的解\n \n对于数组A的求和问题，利用分而治之的思想可以设计出下面的算法\nsum( int A[], int lo, int hi ) {//区间范围A[lo, hi)  if ( hi - lo \u0026lt; 2 ) return A[lo]; int mi = (lo + hi) \u0026gt;\u0026gt; 1; return sum( A, lo, mi ) + sum( A, mi, hi ); } //入口形式为sum( A, 0, n ) 复杂度分析 1.递归跟踪\n对于上述算法，分析可知，其复杂度\nT(n)=O(1) × (1+2¹+2²+...+2^㏒n) = O(1) × (2^logn - 1) = O(n)\n更快捷地，作为几何级数，其复杂度与末项同阶，因此由分析图可知，其末项共有n项[(0,0)，(1,1,) \u0026hellip; (n,n)]，因此其复杂度为O(n)\n2.递推方程\n为求解sum(A,lo,hi),\n需求解 sum(A, lo, mi) 和 sum(A, mi+1, hi) =\u0026gt; T(n/2)\n进而将子问题累加 =\u0026gt;O(1)\n递归基sum(A, lo, lo) =\u0026gt; O(1)\n递推关系：T(n)=2·T(n/2) + O(1) ===\u0026gt; T(n) = O(n)\n","permalink":"https://blog.archai.space/p/%E5%87%8F%E8%80%8C%E6%B2%BB%E4%B9%8B%E4%B8%8E%E5%88%86%E8%80%8C%E6%B2%BB%E4%B9%8B/","summary":"减而治之(Decrease and conquer)  什么是“减而治之”？\n为求解一个大规模的问题，可以将其划分为两个问题，其一平凡，另一规模缩减 ，分别求解子问题，由子问题的解得原问题的解\n \n比如说，对于一个数组A的求和问题，可以设计如下的算法\nsum( int A[], int n ) { return n \u0026lt; 1? 0 : sum(A, n - 1) + A[n - 1]; } 当规模缩减到一定程度后，抵达递归基，返回0\n复杂度分析 1. 递归跟踪\n 绘出计算过程中出现过的所有递归实例（及其调用关系） ，那么它们各自所需时间之总和，即为整体运行时间\n 上例中，共计n+1个递归实例(分析)，各自只需O(1)时间 故总体运行时间为：\nT(n) =O(1) × (n+1) =O(n)\n2.递推方程\n 对于大规模的问题、复杂的递归算法，递归跟踪不再适用 此时可采用另一抽象的方法\u0026hellip;\n 在本例中，有T(n)=T(n-1) + O(1),T(0)=O(1)\n则，T(n) = T(n-2)+O(2) = T(n-3)+O(3) = T(n-n)+O(n)=O(n)\n 可以看到，两种分析方法的出来复杂度都为O(n)\n 分而治之(Divide and conquer)  什么是“分而治之”？","title":"减而治之与分而治之"},{"content":"导论  所谓算法，即特定计算模型下，旨在解决特定问题的指令序列。输入：待处理的信息（问题）输出：经处理的信息（答案）\n 正确性 的确可以解决指定的问题\n确定性 任一算法都可以描述为一个由基本操作组成的序列\n可行性 每一基本操作都可实现，且在常数时间内完成\n有穷性 对于任何输入，经有穷次基本操作，都可以得到输出\n::: tip Algorithms + Data Structures = Programs\n(Algorithms + Data Structures) x Efficiency = Computation\n:::\n如何评判算法的其优劣(计算模型)  实验统计是最直接的方法，但足以准确反映算法的真正效率？不足够！ - 不同的算法，可能更适应于不同规模的输入 - 不同的算法，可能更适应于不同类型的输入 - 同一算法，可能由不同程序员、用不同程序语言、经不同编译器生成 - 同一算法，可能实现并运行于不同的体系结构、操作系统\u0026hellip; 为给出客观的评判，需要抽象出一个理想的平台或模型 - 不再依赖于上述种种具体的因素 - 从而直接而准确地描述、测量并评价算法\n 1.图灵机模型(TM) \n   组成 说明     Tape 依次均匀地划分为单元格 各存有某一字符，初始均为'#'   Head 总是对准某一单元格，并可 读取或改写其中的字符。每经过一个节拍，可 转向左侧或右侧的邻格   Alphabet 字符的种类有限   State TM总是处于有限种状态中的某一种 。每经过一个节拍 可按照规则转向另一种状态 。统一约定，\u0026lsquo;h\u0026rsquo; = halt(停止)    2.RAM模型(Random Access Machine) \n语法：\n 赋值操作 R[i] \u0026lt;- C , R[i] \u0026lt;- R[j], R[i] \u0026lt;-R[R[j]] 仅加减运算 R[j] + R[k] R[i] \u0026lt;- R[j] + R[k] 判断语句 IF R[i] = 0 GOTO # keyword STOP, GOTO  总之 在这些理想化模型中，\n✨独立于具体的平台，假定空间是无限的(不考虑空间)，对算法的效率做出可信的比较与评判\n✨算法的运行时间 =\u0026gt; 算法需要执行的基本操作次数\n✨T(n) = 算法为求解规模为n的问题，所需执行的基本操作次数\nBig-O notation(大O记号)  Big-O就是T (n)的上限，对一个具体的f(n) 不断放大产生的结果，下图就是Big-O的原理\n \n关系式\nT(n) =O(f(n)) iff ∃c\u0026gt;0 s.t. T(n) \u0026lt; c.f(n) ∀ n \u0026gt;\u0026gt; 2\n规则\n✨常系数忽略，即O(f(n)) = O(c*f(n))\n✨低次数项忽略，即O(n^a+n^b) = O(n^a),a\u0026gt;b\u0026gt;0\n其他记号 Ω记号 :表示T(n)的下界，Ω(f(n))\nT(n) =O(f(n)) iff ∃c\u0026gt;0 s.t. T(n) \u0026gt; c.f(n) ∀ n \u0026gt;\u0026gt; 2\nΘ记号：是O和Ω的结合\nT(n) =O(f(n)) iff ∃c₁\u0026gt;c₂\u0026gt;0 s.t. c₁·f(n) \u0026gt; T(n) \u0026gt; c₂·f(n) ∀ n \u0026gt;\u0026gt; 2\n 三者的关系可以用下图表示\n \n::: tip\n不含转向(循环，调用，递归等)，必然是顺序执行，即是O(1)的复杂度\n:::\n几个注意点 对数O(logn)\n\n多项式复杂度\n\n级数 \n1.算术级数 T(n)=1+2+3+...+n=n(n+1)/2 = O(n²)\n::: tip\n与末项平方同阶\n:::\n2. 幂方级数 T₂(n)=1²+2²+3²+...+n²=n(n+1)(2n+1)/6 = O(n³)\n\u0026hellip;\n\n::: tip\n比幂次高出一阶\n:::\n3.几何级数(a\u0026gt;1) T(n)=1+2+2²+...+2ⁿ=O(2ⁿ)\n::: tip\n与末项同阶\n:::\n4.收敛级数 1/1×2 + 1/2×3 + 1/3×4 +...+ 1/(n-1)·n= 1-1/n = O(1)\n几何分布：(1- λ) · [1+ 2λ + 3λ² + 4λ³ +...]= 1/(1-λ)=O(1), 0\u0026lt;λ\u0026lt;1\n::: tip\n收敛级数复杂度最终趋于常数，为O(1)\n:::\n5.未必收敛，但长度有限 \n","permalink":"https://blog.archai.space/p/ds%E5%AF%BC%E8%AE%BA/","summary":"导论  所谓算法，即特定计算模型下，旨在解决特定问题的指令序列。输入：待处理的信息（问题）输出：经处理的信息（答案）\n 正确性 的确可以解决指定的问题\n确定性 任一算法都可以描述为一个由基本操作组成的序列\n可行性 每一基本操作都可实现，且在常数时间内完成\n有穷性 对于任何输入，经有穷次基本操作，都可以得到输出\n::: tip Algorithms + Data Structures = Programs\n(Algorithms + Data Structures) x Efficiency = Computation\n:::\n如何评判算法的其优劣(计算模型)  实验统计是最直接的方法，但足以准确反映算法的真正效率？不足够！ - 不同的算法，可能更适应于不同规模的输入 - 不同的算法，可能更适应于不同类型的输入 - 同一算法，可能由不同程序员、用不同程序语言、经不同编译器生成 - 同一算法，可能实现并运行于不同的体系结构、操作系统\u0026hellip; 为给出客观的评判，需要抽象出一个理想的平台或模型 - 不再依赖于上述种种具体的因素 - 从而直接而准确地描述、测量并评价算法\n 1.图灵机模型(TM) \n   组成 说明     Tape 依次均匀地划分为单元格 各存有某一字符，初始均为'#'   Head 总是对准某一单元格，并可 读取或改写其中的字符。每经过一个节拍，可 转向左侧或右侧的邻格   Alphabet 字符的种类有限   State TM总是处于有限种状态中的某一种 。每经过一个节拍 可按照规则转向另一种状态 。统一约定，\u0026lsquo;h\u0026rsquo; = halt(停止)    2.","title":"DS导论"},{"content":"基本语法 输出语句    语句 功能     echo 输出字符串类型   print_r 输出引用类型(对象,数组等)   var_dunp 检测变量类型    ::: tip echo语句可用于给前端返回响应体。比如前端通过ajax请求，可以在xhr.response中直接得到echo的内容 :::\n变量\u0026amp;常量 👉🏼 变量\n   语句 功能 返回值     isset() 检测变量是否存在 boolean   unset() 删除某个变量 none    👉🏼 常量\n常量用const 或 define 定义，常量名一般全部大写，不受作用域的限制\n::: tip\n一般是define在类外定义常量，const在类内定义常量，并且const必须通过类名::变量名来进行访问。但是php5.3以上支持类外通过const定义常量。\n:::\n:::danger\nconst不能在条件语句中使用，必出错\n:::\n 参考文章 《PHP中define() 与 const定义常量的区别详解》\n 三元表达式及@使用 省略写法\necho $name?:\u0026#39;NO\u0026#39;;//输出本身的话，可以省略！ ??的作用\n①检测变量是否定义(isset) ②检测变量是否为空(is_null)\necho $name??\u0026#39;NO\u0026#39;;//NO @用来屏蔽错误\n@(9/0)//不会报错 字符串相关函数 定界符 以\u0026lt;\u0026lt;\u0026lt;开头，用相同字符串定义开头\u0026amp;结尾。\n::: details\n$temp=\u0026lt;\u0026lt;\u0026lt;tem \u0026lt;h1 style=\u0026#34;color: #e01\u0026#34;\u0026gt;这是一段临时HTML模板\u0026lt;/h1\u0026gt; \u0026lt;h5 style=\u0026#34;color: #00b3ff\u0026#34;\u0026gt;hhc\u0026lt;/h5\u0026gt; \u0026lt;script\u0026gt; document.querySelector(\u0026#34;h5\u0026#34;).addEventListener(\u0026#34;mouseenter\u0026#34;,function (){ this.style.color=\u0026#34;#000\u0026#34;; }) \u0026lt;/script\u0026gt; tem; :::\n字符串连接 .符号用来连接字符串\n::: details\n$str=\u0026#34;fer.com\u0026#34;; echo $str.\u0026#34;=\u0026gt;\u0026#34;.\u0026#34;hhhhc\u0026#34;; :::\n字符串长度    方法 参数 作用     strlen()  [$str] 获得字符串长度，中文一个字两个字符！   mb_strlen() [$str,encoding] 指定编码格式获取长度，指定为utf8，中文一个字一个字符    字符串格式    方法 作用     trim() 去空格   strtoupper()\u0026amp;strtolower 大小写转换   ucfirst() 指定一段字符串首字母大写   ucwords($sentence,delimiters) 以delimiters为分割，首字母大写    ::: details\n$sentence=\u0026#34;my name is jqf.let\u0026#39;s go shopping!\u0026#34;; echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; echo ucwords($sentence,\u0026#39;.\u0026#39;);//My name is jqf.Let\u0026#39;s go shopping! :::\n与数组相互转化 explode(delimiter,$str) \u0026amp; implode(glue,$arr)\n截取字符串 substr($str,start,len) \u0026amp; mb_substr($str,start,len,encoding)，返回截取的字符串，原字符串不会发生改变\nmd5加密 ::: theorem MD5 MD5信息摘要算法（英语：MD5 Message-Digest Algorithm），一种被广泛使用的密码散列函数，可以产生出一个128位（16字节）的散列值（hash value），用于确保信息传输完整一致。\n::: right 来自百度百科 :::\n引入模块    方法 说明     include \u0026amp; include_once 弱引用，如果引入的文件不存在则会warning(可以用@屏蔽掉)，但后续代码仍然执行   require \u0026amp; require_once 强引用，所引文件不存在则会报fatal mistake,后续不再执行    ::: tip\ninclude_once \u0026amp; require_once用来避免引入多次某个模块而造成的错误，具体如下\n:::\n::: details\n//tools.php \u0026lt;?php function show(){ return \u0026#39;https://www.fintinger.xyz\u0026#39;; } //1.php \u0026lt;?php include \u0026#39;tools.php\u0026#39;; echo show(); //2.php include_once \u0026#34;1.php\u0026#34;; include_once \u0026#34;tools.php\u0026#34;; 2.php中引入了两次tools.php，通过include_once可以避免产生错误\n:::\n函数相关 点语法 类似于javascript，可以在函数传参数的时候实现\u0026quot;聚合\u0026quot;的效果\n::: details\n\u0026lt;?php function sum(...$nums){ print_r($nums); echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; echo array_sum($nums); } sum(1,2,3,4,4,5,6,6,7,7,7,7); :::\n传址\u0026amp;传值 \u0026amp; 用来传址\n严格模式 declare(strict_types=1);声明严格模式，严格模式下，如果约束了函数参数的类型，传入不是约束的值，会报错。\ndeclare(strict_types=1); function sum(int ...$nums){ return array_sum($nums); } echo sum(1,2,3,4,\u0026#39;5\u0026#39;);//Fatal error php标准的函数 function show(int $var): string { return \u0026#39;hhc\u0026#39;; }//参数(php\u0026gt;5)和返回值(php\u0026gt;7)都有明确的类型约束 好用的变量函数！ $action=\u0026#34;fn\u0026#34;; function fn(){ //do something } $action()//即可调用fn函数 具体应用 ：根据图片后缀设定对应处理函数\n::: details\nfunction png() { return \u0026#39;png fn running...\u0026#39;; } function jpg() { return \u0026#39;ipg fn running...\u0026#39;; } $file = \u0026#39;logo.png\u0026#39;; $action = explode(\u0026#39;.\u0026#39;, $file)[1]; echo function_exists($action) ? $action() : \u0026#39;无法处理这种格式的图片！\u0026#39;; :::\n数组相关 指针读取数组 current($arr)\u0026amp;next($arr) 操作数组指针，如果读取不到，current则为0\nlist() list — 把数组中的值赋给一组变量。\n$info = array(\u0026#39;coffee\u0026#39;, \u0026#39;brown\u0026#39;, \u0026#39;caffeine\u0026#39;); // 列出所有变量 list($drink, $color, $power) = $info; echo \u0026#34;$drinkis $colorand $powermakes it special.\\n\u0026#34;; ::: tip\n在 PHP 7.1.0 之前的版本，list() 仅能用于数字索引的数组，并假定数字索引从 0 开始。\n:::\n也就是说 PHP\u0026gt;7.1.0，list可以用来解构key=\u0026gt;value型数组了\n::: details\n$arr=[ \u0026#34;age\u0026#34;=\u0026gt;\u0026#34;18\u0026#34;, \u0026#34;name\u0026#34;=\u0026gt;\u0026#34;fin\u0026#34;, ]; list(\u0026#34;name\u0026#34;=\u0026gt;$name,\u0026#34;age\u0026#34;=\u0026gt;$age)=$arr; echo $name;//fin 关于PHP中类似于ES6的对象解构赋值操作，有种更加稳妥方便的方式，那就是extract()\n$arr=[ \u0026#34;age\u0026#34;=\u0026gt;\u0026#34;18\u0026#34;, \u0026#34;name\u0026#34;=\u0026gt;\u0026#34;fin\u0026#34;, ]; extract($arr, EXTR_PREFIX_SAME, \u0026#34;wddx\u0026#34;);//重复则覆盖，前缀为\u0026#34;wddx\u0026#34; echo $name;//fin :::\n::: warning\nPHP 5 里，list() 从最右边的参数开始赋值； PHP 7 里，list() 从最左边的参数开始赋值。\n如果你用单纯的变量，不用担心这一点。 但是如果你用了具有索引的数组，通常你期望得到的结果和在 list() 中写的一样是从左到右的，但在 PHP 5 里实际上不是， 它是以相反顺序赋值的。\n通常而言，不建议依赖于操作的顺序，在未来可能会再次发生修改。\n:::\n操作数组 foreach()遍历数组\nforeach($arr $index=\u0026gt;$item){ //可以获取索引和数组中的item } 传址修改原数组\nforeach ($arr as \u0026amp;$item){ //do something(修改传址进来的数组元素) } 数组增删操作     增 删     从末尾 array_push array_pop   从开始位置 array_unshift array_shift    其他方法（类似JS中） /*遍历*/ array_map() array_values() array_keys() array_filter() /*判断*/ array_key_exists() in_array() /*合并*/ array_merge()//[originArr,newArr] array_change_key_case() //修改key的大小写小:0/CASE_LOWER 大:1/CASE_UPPER ::: details\n//递归实现多维数组key的大小写转换 \u0026lt;?php $config = [ \u0026#34;hOst\u0026#34; =\u0026gt; \u0026#34;localhost\u0026#34;, \u0026#34;POST\u0026#34; =\u0026gt; 8000, \u0026#34;uSeR\u0026#34; =\u0026gt; \u0026#34;admin\u0026#34;, \u0026#34;cache\u0026#34; =\u0026gt; [ \u0026#34;hOst\u0026#34; =\u0026gt; \u0026#34;127.0.0.1\u0026#34;, \u0026#34;user\u0026#34; =\u0026gt; [ \u0026#34;namE\u0026#34; =\u0026gt; \u0026#34;jqf\u0026#34;, \u0026#34;token\u0026#34; =\u0026gt; \u0026#34;qq9ad2437c622bdd38\u0026#34; ] ] ]; /** * @param array $arr * @param string $type * @return array */ function fin_array_change_key_case(array $arr, string $type = \u0026#39;CASE_UPPER\u0026#39;): array { $action = $type == \u0026#34;CASE_UPPER\u0026#34; ? \u0026#39;strtoupper\u0026#39; : \u0026#39;strtolower\u0026#39;; foreach ($arr as $k =\u0026gt; $v) { //删掉原先的  unset($arr[$k]); //新的数组  $arr[$action($k)] = is_array($v) ? fin_array_change_key_case($v) : $v; } return $arr; } $res = fin_array_change_key_case($config); print_r($res); :::\n::: tip\narray_walk_recursive($arr,function (\u0026amp;$v,\u0026amp;$k,$case){}，专门用于递归遍历多维数组，注意其中自动传入址\n:::\n时间相关 基本操作 设置时区\ndate_default_timezone_set(timezone_identifier) 注： timezone_identifier取值有\u0026quot;PRC\u0026quot;，\u0026ldquo;Asia/chongqing \u0026ldquo;，\u0026ldquo;Asia/shanghai\u0026rdquo;，\u0026ldquo;Asia/urumqi\u0026quot;等，分别对应时区\n格式化时间\ndate(\u0026#39;Y-m-d hⓜ️s\u0026#39;); 时间戳\ntime() microtime(true)//true 返回浮点数 false 返回字符串 ::: details\n\u0026lt;?php //得到函数执行时间的函数 /**测试函数执行时间 * @param null $begin * @param null $end * @return array|float|mixed|string */ function runtime($begin = null, $end = null) { static $cache = []; if (is_null($begin)) { return $cache; } elseif (is_null($end)) { return $cache[$begin] = microtime(true); } else { //👇🏼！！！！  $end = $cache[$end] ?? microtime(true); return round($end - $cache[$begin],2); } } //测试for和while循环 $LEN=10000000; //1.for runtime(\u0026#39;for\u0026#39;); for ($i = 0; $i \u0026lt; $LEN; $i++) {} runtime(\u0026#39;forEnd\u0026#39;); echo \u0026#39;for循环:\u0026#39;.runtime(\u0026#39;for\u0026#39;, \u0026#39;forEnd\u0026#39;); echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; //2.while runtime(\u0026#39;while\u0026#39;); $i=0; while ($i\u0026lt;$LEN){$i++;}; runtime(\u0026#39;whileEnd\u0026#39;); echo \u0026#39;while循环:\u0026#39;.runtime(\u0026#39;while\u0026#39;, \u0026#39;whileEnd\u0026#39;); echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; echo \u0026#39;总执行时间:\u0026#39;.runtime(\u0026#39;for\u0026#39;,\u0026#39;whileEnd\u0026#39;); :::\n一些相关函数 \u0026lt;?php //date() =\u0026gt;填参数得到不同的结果 //getdate() =\u0026gt;返回数组  /*转为时间戳*/ //具体查手册 //strtotime() =\u0026gt;参数为str,比如\u0026#34;NOW\u0026#34;,\u0026#34;2020-04-02 10:00:00\u0026#34;... //strtotime(\u0026#39;+1 year +10 day\u0026#39;)=\u0026gt;1年10天后 //strtotime(\u0026#39;next friday\u0026#39;)=\u0026gt;下个星期五 内置时间类 Date()对象\n\u0026lt;?php //new Date()-\u0026gt;format()//%m月%d天%h小时,一共%a天 //new Date()-\u0026gt;setdate() //new Date()-\u0026gt;diff() 获取俩时间差值 //...  DateInterval()对象\n//参数\u0026#39;P2DT2H5M\u0026#39;=\u0026gt;增加2d2h5m //P-\u0026gt;开始, D-\u0026gt;\u0026#39;day\u0026#39;, T-\u0026gt;分割日期和时间 //配合new Date-\u0026gt;add($interval)函数使用！！！ 正则表达式 相关函数\n\u0026lt;?php //preg_match(reg,str,res) =\u0026gt;匹配一个 //preg_match_all()=\u0026gt;所有  //preg_split(reg,str)=\u0026gt;拆分字符串，返回处理后结果  //preg_replace(reg,val_replace,str)=\u0026gt;替换 //preg_replace_callback(reg,fn,str)=\u0026gt;fn中接收$matches实现复杂逻辑 ::: warning\n注意字符串操作函数的区别,str_split 等\n:::\nCOOKIE cookie是客户端存储数据的手段，并在请求服务器时自动携带cookie数据。\n::: tip\nCOOKIE 保存在浏览器\n:::\n设置 ⚡ setcookie()\n参数:\n   参数 说明     name Cookie的名称   value cookie的价值。该值存储在客户端计算机上; 不要存储敏感信息   expires Cookie过期的时间。这是一个Unix时间戳，可以使用time（）函数加上希望它到期之前的秒数来设置它。   path 服务器上可以使用cookie的路径。如果设置为*“/”*，则cookie将在整个范围内可用   domain cookie可用的域。（例如*“www.houdunren.com”*）将使cookie可用于该子域及其所有其他子域（即w2.www.houdunren.com）。要使cookie可用于整个域（包括其所有子域），只需将值设置为域名（在本例中为*“houdunren.com”*）   secure 表示cookie应仅通过客户端的安全HTTPS连接传输   httponly 当TRUEcookie只能通过HTTP协议访问时。这意味着脚本语言（例如JavaScript）无法访问cookie。    来源: 后盾人教程\nSESSION 概述 session是服务器会话状态，可用于记录访问用户后台会话数据。不同用户或一个用户用不同浏览器在同一网站发起php请求会产生不同的session，然后保存在本地文件夹(session.save_path)或者其它端。\n::: tip\n与COOKIE不同，SESSION保存在本地\n:::\n\n保存的session在同一域下都可以通过$_SESSION访问到，为数组形式。\n基本使用 session_start();//开始 $_SESSION[\u0026#34;web\u0026#34;] = \u0026#34;https://www.fintinger.xyz\u0026#34;;//设置数据 print_r($_SESSION);//可以通过超全局数组获取 配置 SESSION储存目录 session_save_path(\u0026#34;./temp\u0026#34;);//查看或设置 自定义Name\u0026amp;Value //id一般在自定义session引擎的时候设置 session_name(\u0026#34;jqf\u0026#34;); session_id(\u0026#34;123\u0026#34;); session_start();//配置在开始之前 \nGC垃圾回收机制  如果使用默认的SESSION处理引擎，修改php.ini的配置即可\n 过期时间 session.gc_maxlifetime 设置文件过期时间，默认为1440s = 24min。\n::: tip\n如果(下面讲到的概率)调用这个函数，就会遍历保存SESSION的文件夹，判断如果本次产生SESSION与上次产生相差超过24min就会删除上次产生的SESSION，更新为本次保存的SESSION($session_id不会改变，即文件名)。\n:::\n调用概率  如果每次调用php，都回去保存SESSOIN的文件夹遍历，进行垃圾回收(GC)，当用户量很多时，就会很大程度上影响性能，因此加入了概率这一概念，即每次调用php时，启动垃圾回收程序的概率session.gc_probability/session.gc_divisor\n 👉 session.gc_probability 基率\nsession清除无效session的基率。\n👉 session.gc_divisor \n启动垃圾回收程序的概率。概率计算公式为：session.gc_probability / session.gc_divisor，如果网站访问量大建议将概率降低如 1 / 1000 ~ 5000。\n自定义SESSION处理引擎 使用 ::: warning\n在start之前设置，session_set_save_handle(new FileHandle)括号中为自定义的处理类\n:::\n然后按照正常session处理使用即可！\n定义 🎈 使用 SessionHandlerInterface 接口(implements)\nclass FileHandle inplements SessionlandlerInterface{} 🎈标准模板\n\u0026lt;?php class FileHandle implements SessionHandlerInterface { /** * @inheritDoc */ public function close() { // TODO: Implement close() method.  } /** * @inheritDoc */ public function destroy($session_id) { // TODO: Implement destroy() method.  } /** * @inheritDoc */ public function gc($maxlifetime) { // TODO: Implement gc() method.  } /** * @inheritDoc */ public function open($save_path, $name) { // TODO: Implement open() method.  } /** * @inheritDoc */ public function read($session_id) { // TODO: Implement read() method.  } /** * @inheritDoc */ public function write($session_id, $session_data) { // TODO: Implement write() method.  } } 逐一实现其中方法即可！\n实例 \u0026lt;?php /** * Class FileHandle * @param $path //session储存路径 * @param $maxlifetime //session文件过期时间 */ class FileHandle implements SessionHandlerInterface { protected $path; protected $maxlifetime; public function __construct($path = \u0026#34;session\u0026#34;, $maxlifetime = \u0026#34;1440\u0026#34;) { $this-\u0026gt;path = $this-\u0026gt;mkdir($path); $this-\u0026gt;maxlifetime = $maxlifetime; } private function mkdir($path) { is_dir($path) || mkdir($path); return realpath($path); } public function close(): bool { return true; } public function destroy($session_id): bool { if (is_file($this-\u0026gt;path.\u0026#34;/\u0026#34;.$session_id)) { @unlink($this-\u0026gt;path.\u0026#34;/\u0026#34;.$session_id); } return true; } public function gc($maxlifetime): bool { foreach (glob($this-\u0026gt;path.\u0026#34;/*\u0026#34;) as $file) { if (filemtime($file) + $this-\u0026gt;maxlifetime \u0026lt; time()) { @unlink($file); } } return true; } public function open($path, $name): bool { return true; } public function read($session_id): string { return (string)@file_get_contents($this-\u0026gt;path.\u0026#34;/\u0026#34;.$session_id); } public function write($session_id, $session_data): bool { return (bool)@file_put_contents($this-\u0026gt;path.\u0026#34;/\u0026#34;.$session_id, $session_data); } } 注意：\n⚡ 处理的路径要为绝对路径才行，可以利用realpath($path)得到\n⚡ 实现的方法就是，“开，关，读，写，卸，垃”，本方法中\u0026quot;开，关\u0026quot;没有做过多的处理\nWhat\u0026rsquo;t more💭 在write方法中添加延迟sleep()，并且修改php.ini 中的调用概率为1(session.gc_divisor =1)，即每次都调用垃圾回收，就可以看到session是在删除之后再被重新创建的！\n文件操作 指针操作函数 fseek($handle,int offset) =\u0026gt;移动文件中指针\nfread($handle,length) =\u0026gt;读取文件操作，指针会自动移动到length位置\n::: tip\n$handle 为source 类型，通过fopen()函数获得\n:::\n文件打开与写入操作fopen函数详解： fopen($path,mode) ,mode的取值如下\n   模式 功能 +模式 二进制文件 文件不存在 文件存在     r 读取文件不可写 r+可写入文件 r+b 操作二进制文件 创建 操作   w 从头写入，不可读 w+可读取文件 w+b.. 创建 操作   a 追加内容不可读取 a+可读取 a+b 创建 操作   x 从头写入，不可读 x+可读 x+b 创建 不会打开，fopen返回false，产生一个警告    ::: warning\n操作二进制文件(图片等，需要设置头信息)，比如:header('Content-type:image/jpg');\n:::\n其他操作函数 读取操作\n\u0026lt;?php $handle=fopen(\u0026#34;file/test.txt\u0026#34;,\u0026#39;r\u0026#39;); //feof($handle)=\u0026gt;文件读取是否完毕 //while (!feof($handle)){ // echo fread($handle,1); //}  //fgetc($handle)=\u0026gt;每次读取一个字符！ while (!feof($handle)){ echo fgetc($handle); } //fgets($handle)=\u0026gt; 每次一行！  //fgetss($handle)=\u0026gt;v7.3弃用， //fgetcsv($handle,split)=\u0026gt;读取，并指定用什么分割开来 权限判断，检查文件\n//文件\u0026amp;目录权限 //is_writable() //is_readable()  //文件\u0026amp;目录检查 //file_exists() //is_file()\u0026amp;is_dir() 快速读取\u0026amp;写入文件 file_get_contents(filename|url) 获取文件内容\nfile_put_contents(filename,data) =\u0026gt;w模式写入\nfile_put_contents(filename,data,FILE_APPEND) =\u0026gt;a模式写入\n文件复制移动 文件复制\ncopy($source,$dest)\n文件移动\nrename($oldname,$newname)\n注： rename() 同时具有重命名和移动文件的功能\n目录操作函数 👉 is_dir()\n用途： 判断是否存在该目录\n👉 mkdir($pathname,mode,recursive)\n参数：\n[1] $pathname 路径\n[2] mode 默认0777,意味着最大可能的访问权，一般设定为0755\n::: warning\nmode在windows 下被忽略\n:::\nrecursive 是否递归创建，\nmkdir(\u0026#34;a/b/c\u0026#34;,0755,true) 👉 readdir()\n说明： 调用一次就读取一个当前目录下的文件或文件夹，读取不到则返回false\n注意： readdir 方法会读取到\u0026rdquo;.\u0026rdquo; 和 \u0026ldquo;..\u0026ldquo;这两个默认存在的目录\n👉 scandir()\n说明： 相当于循环readdir()\n👉 glob()\n说明： 目录的遍历操作\n参数：\n[1] pattern 模式\nglob(\u0026#34;./*\u0026#34;)//当前路径下所有 glob(\u0026#34;./*.php\u0026#34;)//当前路径下的php文件 glob(\u0026#34;{./*.php,*.txt}\u0026#34;)//当前路径下php和txt [2] flag\n//GLOB_MARK =\u0026gt;后面补 \u0026#34;/\u0026#34; //GLOB_NOSORT =\u0026gt;使用系统默认排序 //GLOB_ERR =\u0026gt;没有权限操作目录时，停止所有动作（默认跳过） //GLOB_NOCHECK =\u0026gt;目录不存在，返回模式 共享锁和独占锁  flock($handle,$operation) 函数用来给文件上锁\n    $operation 名称 作用     LOCK_SH 共享锁 如果是读取，不需要等待，但如果是写入，需要等待读取完成。   LOCK_EX 独占锁 无论写入/读取都需要等待。   LOCK_UN 释放锁 无论使用共享/读占锁，使用完后需要解锁。   LOCK_NB  当被锁定时，不阻塞，而是提示锁定    ::: warning\nwindows不支持$operation = LOCN_NB ，也不支持flock的第四个参数,具体GO\n:::\n 情景1 用户u1正在读取文件，u2的写入操作等到u1读取完毕之后(产生阻塞)才能再进行\n //u1.php $handle=fopen(\u0026#34;jqf.txt\u0026#34;,\u0026#34;r\u0026#34;); flock($handle,LOCK_SH); sleep(3);//模拟写入操作的延时 echo fread($handle,99); flock($handle,LOCK_UN); fclose($handle); //u2.php $handle = fopen(\u0026#34;jqf.txt\u0026#34;, \u0026#34;a+\u0026#34;); flock($handle, LOCK_EX); fwrite($handle, \u0026#39;*hm\u0026#39;); fseek($handle, 0); echo fread($handle, 99); flock($handle,LOCK_UN); fclose($handle);  情景2 u1写完之后（产生阻塞）u2才能读取\n //u1.php $handle = fopen(\u0026#39;jqf.txt\u0026#39;, \u0026#39;a+\u0026#39;); flock($handle, LOCK_EX); sleep(3); fwrite($handle, \u0026#39;*hm\u0026#39;); fseek($handle, 0); echo fread($handle, 99); flock($handle, LOCK_UN); fclose($handle); //u2.php $handle=fopen(\u0026#39;jqf.txt\u0026#39;,\u0026#39;r+\u0026#39;); flock($handle,LOCK_SH); echo fread($handle,99); flock($handle,LOCK_UN); fclose($handle); 关于文件和目录操作常用的一些函数 [1] 格式化获取磁盘大小以及磁盘可用大小\n::: details\n/**格式化获取磁盘空间大小 * 注意数组的顺序，判断由大到小!!!! * @param int $total * @return string */ function space_total(int $total):string { $config=[3=\u0026gt;\u0026#39;GB\u0026#39;,2=\u0026gt;\u0026#39;MB\u0026#39;,1=\u0026gt;\u0026#39;KB\u0026#39;]; foreach ($config as $num=\u0026gt;$unit) { if ($total\u0026gt;pow(1024,$num)){ return round($total/pow(1024,$num)).$unit; } } return $total.\u0026#39;B\u0026#39;; } echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; echo \u0026#39;磁盘总大小：\u0026#39;.space_total(disk_total_space(\u0026#39;.\u0026#39;)); echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; echo \u0026#39;剩余大小：\u0026#39;.space_total(disk_free_space(\u0026#39;.\u0026#39;)); :::\n[2]利用filetime实现缓存机制\n::: details\n/** * 如果刷新页面前后不超过10s，将会一直走缓存,否则会更新缓存 * ob_start()\u0026amp;ob_get_contents()可以在引入时获取引入内容 */ $CACHE = \u0026#39;file/文件缓存/file.cache.php\u0026#39;; $BLADE = \u0026#39;file/文件缓存/file.blade.php\u0026#39;; if (is_file($CACHE) \u0026amp;\u0026amp; filemtime($CACHE) \u0026gt; time() - 10) { echo \u0026#39;is cache...\u0026#39;; include $CACHE; } else { ob_start(); include $BLADE; $content = ob_get_contents(); file_put_contents($CACHE, $content); } :::\n[3] 利用var_export()快速生成配置文件\n::: tip\nvar_export 类似于 var_dump，当其第二个参数为TRUE时，会返回一个变量，而不是输出它\n:::\n::: details\n\u0026lt;?php $db = [\u0026#34;host\u0026#34; =\u0026gt; \u0026#34;localhost\u0026#34;, \u0026#34;port\u0026#34; =\u0026gt; 2000, \u0026#34;pwd\u0026#34; =\u0026gt; \u0026#34;admin888\u0026#34;]; //不带true会输出符合php语法的数组 $config = var_export($db, true); file_put_contents(\u0026#39;file/var_export_config.php\u0026#39;, \u0026#34;\u0026lt;?php return \u0026#34; . $config . \u0026#34;;\u0026#34;); :::\n[4] 统计目录大小\n::: details\n/**统计目录大小 * @param string $dir * @return int */ function dir_size(string $dir = \u0026#34;.\u0026#34;): int { $size = 0; foreach (glob($dir . \u0026#34;/*\u0026#34;) as $file) { $size += is_file($file)?filesize($file):dir_size($file); } return $size; } :::\n[5] 递归复制整体目录\n:::details\n/**递归复制整体目录 * @param string $from * @param string $to * @return bool */ function copy_dir(string $from, string $to): bool { !is_dir($to) \u0026amp;\u0026amp; mkdir($to, 0755, true); foreach (glob($from . \u0026#34;/*\u0026#34;) as $file) { $target = $to . \u0026#34;/\u0026#34; . basename($file); is_file($file) ? copy($file, $target) : copy_dir($file, $target); }; return true; } :::\n[6] 删除多级目录\n::: danger\n目录的删除操作一定要小心！避免目录层级错误导致的误删\n:::\n::: details\n/**删除多级目录 * 千万小心！！！ * @param string $dir * @return bool */ function del_dir(string $dir) { if (!is_dir($dir)) { return true; } foreach (glob($dir . \u0026#34;/*\u0026#34;) as $file) { is_file($file) ? unlink($file) : del_dir($file); } return rmdir($dir); } :::\n::: tip\n目录的移动 等价于复制+删除\n:::\n命名空间  在电脑的文件系统中，fin.txt这个文件在一个文件夹下不能同时存在两份，只能另建文件夹将二者分开，这就类似命名空间的概念，利用命名空间可以将类，函数或者常量的同名者分隔开来，避免产生冲突\n 几个关键字 1. namespace 用namespace关键字声明一个类的命名空间，如果当做文件系统来看就是讲文件放到某个确定的位置，可以通过层级的文件夹路径找到。\n::: tip\n标准规范就是，类的命名空间与其所处文件夹名对应\n:::\n::: danger\nnamespace之前的代码都不会执行！\n:::\n2.use use关键字导入声明了命名空间的类等，所有支持命名空间的PHP版本支持三种别名或导入方式：为类名称使用别名、为接口使用别名或为命名空间名称使用别名。PHP 5.6开始允许导入函数或常量或者为它们设置别名。\n具体方法：\n::: details\nnamespace foo; use My\\Full\\Classname as Another; // 下面的例子与 use My\\Full\\NSname as NSname 相同 use My\\Full\\NSname; // 导入一个全局类 use ArrayObject; // importing a function (PHP 5.6+) use function My\\Full\\functionName; // aliasing a function (PHP 5.6+) use function My\\Full\\functionName as func; // importing a constant (PHP 5.6+) use const My\\Full\\CONSTANT; :::\n3.自动加载 ::: tip\n如果两个类定义在不同的php文件中，使用use声明命名空间或者类之后，还需要用include/require引入进来，略微有点麻烦，我们希望有种自动处理的方法，使得我们只需要使用use声明，而include自动完成！\n:::\n方法1 函数实现\nspl_autoload_register() 函数当使用use声明的命名空间类不存在时，会，自动调用该函数\n\u0026lt;?php namespace App; spl_autoload_register( function ($name) { $file = str_replace(\u0026#34;\\\\\u0026#34;, \u0026#34;/\u0026#34;, $name).\u0026#34;.php\u0026#34;; require $file; } ); use App\\Module\\Shop\\Server\\Member; Member::show(); 方法2 面向对象形式\n::: warning\n自动加载类需要与使用者在同一命名空间下，或者使用use\u0026amp;include正确引入\n:::\n//Autoload.php类 namespace App; class Autoload { public static function boot() { spl_autoload_register([new self, \u0026#34;autoload\u0026#34;]);//注意传参方式  } public function autoload($name) { $file = str_replace(\u0026#34;\\\\\u0026#34;, \u0026#34;/\u0026#34;, $name).\u0026#34;.php\u0026#34;; require $file; } } Autoload::boot();//自动调用静态方法，引入之后不需调用 //use.php namespace App; include \u0026#34;Autoload.php\u0026#34;; use App\\Module\\Shop\\Server\\Member; Member::show(); 方法3 第三方工具—composer\n初始化\ncomposer init 修改配置\n\u0026#34;autoload\u0026#34;:{ \u0026#34;psr-4\u0026#34;:{ \u0026#34;App\\\\\u0026#34;:\u0026#34;App\u0026#34; } } 构建\nconposer build 然后引入生成的vendor文件夹下的autoload.php即可！\n面向对象  使用类的方式实现某一功能，通过继承让代码变得更易维护\n 类中的量\u0026amp;方法 静态变量\u0026amp;方法 class User{ protected static $name=\u0026#34;fin\u0026#34;; public static function show(){ return self::$name;//self 关键字表示当前对象，  } } $u=new User(); echo $u-\u0026gt;show(); echo User::show(); 静态方法/属性通过类名::方法/属性的方式调用\n常量 class Model { const EXISTS_VALUE=1; } echo Model::EXISTS_VALUE; $m=new Model(); echo $m::EXISTS_VALUE;   常量名规范上全部大写\n  常量永远不会被改变\n  private\u0026amp;protect的区别 主要区别在继承上：\n protect定义的方法，继承后能在子类中直接访问 private只供当前对象使用，继承后的子类无法访问  构造函数与解析函数(魔术方法) class Test { public function __construct() { echo \u0026#34;__construct\u0026#34;; } public function __destruct() { echo \u0026#34;__destruct\u0026#34;; } /* public function __run() { echo \u0026#34;__run\u0026#34;; }*/ } new Test(); 说明：\n 两者都会在new对象的时候自动执行 名称不可以自定义 前者一般用来赋值操作，后者做一些资源的释放等  继承\u0026amp;特殊类 单链式继承  用到extends关键字\n 🎈防止继承后子类覆盖父类方法\nclass Father{ private static $name=\u0026#34;jqf\u0026#34;; public final function show(){ return Father::$name; } } class Child extends Father{ // public function show(){}//Cannot override final method } 利用final关键字定义父类中的方法，就可以避免父类方法被重写\n多继承  用trait 关键字定义类，用use引入\n \u0026lt;?php trait Commit { public function publish() { return __METHOD__; } } trait Log { public function show() { return __METHOD__; } } class Topic { use Commit, Log; } $topic = new Topic; echo $topic-\u0026gt;publish(); echo \u0026#34;\u0026lt;hr\u0026gt;\u0026#34;; echo $topic-\u0026gt;show(); 🎈 多继承后的优先级\n当继承后方法名重复时，优先级为:子类方法 \u0026gt; trait \u0026gt; extend\n🎈 多继承方法名冲突问题\n//类中方法名冲突，通过替换+改名解决 use Commit, Log{ Commit::show insteadof Log;//设置优先级  Log::show as export;//优先级低的改名 } 设置优先级→优先级低的改名\n🎈 重新定义父类方法的访问权限\nuse Commit{ Commit::publish as protected; } protect与public之间相互转换\n特殊类    特殊类 使用 说明     抽象类 abstract 子类必须实现其中声明的抽象方法   接口 interface 类似一个标准,凡是通过 implements 关键字使用该接口的类，必须实现其中的所有方法   特征类 trait 定义类用于多继承    魔术方法  魔术方法定义在父类当中，由子类触发\n 属性相关    方法 调用条件     __get($name) 获取对象中的不可访问或不存在的变量时会自动调用   __set($name,$value) 给对象设置不可访问值会自动调用   __unset($name) 删除对象中的不可访问或不存在变量会调用   __isset($name) 判断对象中是否存在某变量调用    方法相关    方法 调用条件     __call($name, $arguments) 子类实例化调用的方法在子类中不存在时   __callStatic($name,$arguments) 子类静态方法不存在时    特殊    方法 调用条件     __construct() new 即调用   __destruct() new 即调用   __toString 直接输出对象引用，不会产生错误，自动调用该方法,输出该方法中返回的字符串    错误\u0026amp;异常处理 错误  通过error_reporting(0)屏蔽系统默认的所有错误处理，然后通过set_error_handler设置自定义错误处理函数，第一个参数为函数，第二个为要处理的错误，默认为处理所有错误(E_ALL | E_STRICT),[官网]\n 错误处理函数的参数    参数 是否可选(Y/N) 作用     $errcode N 是一个 integer，包含了错误的级别(错误码)   $errmsg N 是一个string，包含了错误的信息   $errfile Y 是一个string，包含了发生错误的文件名   $errline Y 是一个 integer，包含了错误发生的行号     然后通过error_log函数选择将错误相关的信息发送到某个地方\n error_log ( string $message [, int $message_type = 0 [, string $destination [, string $extra_headers ]]] ) : bool    message_type 操作     0 message 发送到 PHP 的系统日志，使用 操作系统的日志机制或者一个文件，取决于 error_log 指令设置了什么。 这是个默认的选项。   1 message 发送到参数 destination 设置的邮件地址。 第四个参数 extra_headers 只有在这个类型里才会被用到。   2 不再是一个选项。   3 message 被发送到位置为 destination 的文件里。 字符 message 不会默认被当做新的一行。   4 message 直接发送到 SAPI 的日志处理程序中。    异常  异常处理不同于错误，异常必须是使用throw 抛出异常，然后才能被try catch 捕获\n 自定义异常的处理 依赖方法 👉 set_exception_handler [官网]\n 设置用户自定义的异常处理函数\n //范例 \u0026lt;?php function exception_handler($exception) { echo \u0026#34;Uncaught exception: \u0026#34; , $exception-\u0026gt;getMessage(), \u0026#34;\\n\u0026#34;; } set_exception_handler(\u0026#39;exception_handler\u0026#39;); throw new Exception(\u0026#39;Uncaught Exception\u0026#39;); echo \u0026#34;Not Executed\\n\u0026#34;; ?\u0026gt;在exception_handler可以接收到产生异常后的异常处理类，如果没有自定义异常处理类，默认为Exception类\n基本思路  自定义继承自Exception的异常处理的类，让其中默认存在render() 处理函数(用来区分默认的异常处理类Exception)\n  定义异常监测类，其作用为执行set_exception_handler，然后在其设置的exception_handler函数中做判断：🎈如果该函数接受的参数(异常处理类)中存在render()函数，则该异常由自定义的异常处理类抛出，调用该异常处理类的render() 函数即可；🎈如果不存在render()函数，说明该异常没有自定义的异常处理类(是通过throw new Exceprtion() 抛出的异常)，再做处理即可；\n  然后定义具体逻辑，在具体位置判断需要抛出哪种异常即可。\n 本例实现思路  本例主要实现两个模块，视图渲染+表单验证\n ⚔ 视图渲染  主要作用是加载位于Views文件夹下的每个页面\n 主要逻辑\n 在入口文件index.php中引入定义在bootstrap.php 中的composer自动加载功能(使用use引入类，会自动调用include)，以及异常监测类。其中，异常检测类中的set_exception_handler自动执行\n  然后使用Server\\View视图渲染类的make方法加载Views 下的index.blade.php文件。存在，则通过include引入，如果不存在会抛出ViewException异常\n  一旦抛出ViewException异常，就会通过View类的make方法加载error.blade.php，这个文件一定是要存在的\n ⚔ 表单验证  提交表单到后台，通过验证类判断是否能通过验证，不能通过则携带着提示信息返回表单页面，能通过则加载成功的页面\n 主要逻辑\n 视图渲染模块无异常后会转到index.blade.php页面，其中含有展示在前端的表单，提交至controller.php\n  controller.php 也需要引入bootstrap.php ，然后通过表单验证类Server\\ValidateException进行验证。无异常执行下一句，即通过View 类加载成功页面success.blade.php.否则：\n  表单验证类Server\\ValidateException.php中判断$_POST的内容，本例只判断是否为空，如果为空抛出表单验证异常ValidateException，controller.php中的后续不再执行\n  一旦抛出ValidateException表单验证异常，会将ValidateException.php中抛出异常时的消息存到SESSION中，然后通过header(\u0026quot;location:index.php\u0026quot;)返回入口文件路径，即重新开始输入表单。在表单中可以把存放在SESSION中的异常消息展示给用户作为提示。\n ","permalink":"https://blog.archai.space/p/php%E8%AF%AD%E6%B3%95%E5%B0%8F%E7%BB%93/","summary":"基本语法 输出语句    语句 功能     echo 输出字符串类型   print_r 输出引用类型(对象,数组等)   var_dunp 检测变量类型    ::: tip echo语句可用于给前端返回响应体。比如前端通过ajax请求，可以在xhr.response中直接得到echo的内容 :::\n变量\u0026amp;常量 👉🏼 变量\n   语句 功能 返回值     isset() 检测变量是否存在 boolean   unset() 删除某个变量 none    👉🏼 常量\n常量用const 或 define 定义，常量名一般全部大写，不受作用域的限制\n::: tip\n一般是define在类外定义常量，const在类内定义常量，并且const必须通过类名::变量名来进行访问。但是php5.3以上支持类外通过const定义常量。\n:::\n:::danger\nconst不能在条件语句中使用，必出错\n:::\n 参考文章 《PHP中define() 与 const定义常量的区别详解》","title":"PHP语法小结"},{"content":" 利用php中的GD库可以完成验证码类的开发。后盾人教程\n PHP创建图像步骤 发送HTTP头信息，声明内容为图像 header(\u0026#39;Content-type:image/gif\u0026#39;); header(\u0026#39;Content-type:image/jpeg\u0026#39;); header(\u0026#39;Content-type:image/png\u0026#39;); 通过设置头信息让浏览器渲染出图像，而不是HTML等其他类型\n创建画布 imageCreateTrueColor(width,height); width \u0026amp; height 画布宽高，即为输出图片的尺寸,返回为source 类型，后续操作都是针对这个资源展开。\n创建绘图所需要的颜色 imageColorAllocate(img_resource,R,G,B); 颜色从属于创建画布产生的图像资源而存在，后面三个值分别为红绿蓝三个通道的值，为int类型,在0—255之间。\n绘图（填充画布、画圆、画方块、画线条、画布上写字） 👉 填充画布(画布背景)\nimageFill(img_resource,x,y,color); 👉 画圆\n//绘制空心圆形 imageEllipse(img_res,x,y,w,h,color); //绘制填充好的实心圆 imageFilledEllipse(img_res,x,y,w,h,color); 绘制 圆心(x,y) 宽 x，高 h，的圆\n👉 画方\n//空心矩形 imageRectangle(img_res,x1,y1,x2,y2,color); //实心矩形 imageFilledRectangle (img_res,x1,y1,x2,y2,color); (x1,y1)为左上角坐标， (x2,y2)为右下角坐标\n\n👉画线条\nimageLine(img_res,x1,y1,x2,y2,color) (x1,y1)与(x2,y2)两点确定的直线。\n👉 绘制像素(点)\nimagesetpixel ( img_res , x , y , color ) 👉 输入文本\nimagettftext (img_res , size , angle , x , y , color , fontfile ,text ) 图像资源，字体尺寸，角度，第一个字符的基本点（大概是字符的左下角），Y 坐标（字体基线的位置），颜色 ，字体文件绝对路径(realpath($path)获取)，文本字符串（UTF-8 编码）\n ✨文本盒子？\n imagettfbbox ( size , angle , fontfile , text ); 返回一个含有 8 个单元的数组表示了文本外框的四个角，得到文本范围的盒子大小，可以方便控制文本输出位置：\n   变量 位置     0 左下角 X 位置   1 左下角 Y 位置   2 右下角 X 位置   3 右下角 Y 位置   4 右上角 X 位置   5 右上角 Y 位置   6 左上角 X 位置   7 左上角 Y 位置    输出图像 imagegif(img_resource[,filename]); imagejpeg(img_resource[,filename]); imagepng(img_resource[,filename]); imagebmp(img_resource[,filename]); 当设置第二个参数时表示储存文件，如果存在同名文件会覆盖\n释放画布资源 imageDestroy(img_resource); 图像输出完毕及时释放资源，把内存空间留给更需要的程序。\n验证码类 Verificationcode类  绘制画布，设置背景色，先写入随机文本，然后绘制随机干扰线，干扰点就可\n \u0026lt;?php class Verificationcode { protected $width; protected $height; protected $len; public $imgsource; protected $size; protected $fontfile; protected $rescode; public function __construct($w = 200, $h = 50, $len = 4, $size = null) { $this-\u0026gt;width = $w; $this-\u0026gt;height = $h; $this-\u0026gt;len = $len; $this-\u0026gt;size = is_null($size) ? $h * 0.4 : $size; $this-\u0026gt;fontfile = realpath(\u0026#34;source.ttf\u0026#34;); } public function render() { $this-\u0026gt;bg(); $this-\u0026gt;line();//文字下  $this-\u0026gt;text(); $this-\u0026gt;line();//文字上  $this-\u0026gt;pix(); $this-\u0026gt;show(); return $this-\u0026gt;rescode; } //绘制画布  private function bg() { $img = imageCreateTrueColor($this-\u0026gt;width, $this-\u0026gt;height); $this-\u0026gt;imgsource = $img; imagefill( $img, 0, 0, imageColorAllocate($img, 120, 120, 120) ); } //绘制随机线条  private function line() { for ($i = 0; $i \u0026lt; 3; $i++) { imagesetthickness($this-\u0026gt;imgsource, mt_rand(1, 3)); imageLine( $this-\u0026gt;imgsource, mt_rand(0, $this-\u0026gt;width), mt_rand(0, $this-\u0026gt;height), mt_rand(0, $this-\u0026gt;width), mt_rand(0, $this-\u0026gt;height), $this-\u0026gt;ran_color() ); } } //绘制干扰点  private function pix() { for ($i = 0; $i \u0026lt; 800; $i++) { imagesetpixel( $this-\u0026gt;imgsource, mt_rand(0, $this-\u0026gt;width), mt_rand(0, $this-\u0026gt;height), $this-\u0026gt;ran_color() ); } } //随机文本  private function text() { $range = \u0026#34;abcdefghijklmnopqrstuvwxyz0123456789\u0026#34;; $code = \u0026#39;\u0026#39;; for ($i = 0; $i \u0026lt; $this-\u0026gt;len; $i++) { $text = $range[mt_rand(0, strlen($range) - 1)]; $box = imagettfbbox($this-\u0026gt;size, 0, $this-\u0026gt;fontfile, $range[$i]); imagettftext( $this-\u0026gt;imgsource, $this-\u0026gt;size, mt_rand(-30, 30), ($this-\u0026gt;width / $this-\u0026gt;len) * $i + 10, $this-\u0026gt;height / 2 + ($box[0] - $box[7]) / 2, $this-\u0026gt;ran_text_color(), $this-\u0026gt;fontfile, strtoupper($text) ); $code .= strtoupper($text); } $this-\u0026gt;rescode = $code; } //设置头信息并且输出png  private function show() { header(\u0026#39;Content-type:image/png\u0026#39;); imagepng($this-\u0026gt;imgsource); } private function ran_color() { return imageColorAllocate( $this-\u0026gt;imgsource, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255), ); } private function ran_text_color() { return imageColorAllocate( $this-\u0026gt;imgsource, mt_rand(0, 80), mt_rand(0, 80), mt_rand(0, 80), ); } } 说明：\n 如果不设置参数，默认图片尺寸为200×50，验证码长度为4，字体为 (0.4*图片高度) 通过计算使得文本始终处在垂直居中，水平分散对齐的状态  后端使用 ⚡server.php\n\u0026lt;?php //引入渲染验证码图片，并将这次返回的验证码存到session中 include \u0026#34;Verificationcode.php\u0026#34;; session_start();//session开始之前不能有输出 $code = new Verificationcode(); $res = $code-\u0026gt;render(); $_SESSION[\u0026#34;captcha\u0026#34;]=$res; imageDestroy($code-\u0026gt;imgsource); 说明：\n 调用render()函数，即可绘制出验证码图片，并返回这次的验证码字符串。 通过将返回的验证码字符串存入session中，可以进行后续验证操作  前端使用 \u0026lt;img src=\u0026#34;server.php\u0026#34; alt=\u0026#34;\u0026#34; onclick=\u0026#34;this.src=\u0026#39;server.php?\u0026#39;+Math.random()\u0026#34;\u0026gt; ✨ 可以将整个form表单提交至php后端，然后通过session中存储的验证码字符串进行验证，例如\n\u0026lt;form action=\u0026#34;check.php\u0026#34; method=\u0026#34;post\u0026#34; enctype=\u0026#34;multipart/form-data\u0026#34;\u0026gt; \u0026lt;table\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;\u0026lt;input type=\u0026#34;text\u0026#34; placeholder=\u0026#34;请输入验证码\u0026#34; name=\u0026#34;captcha\u0026#34;\u0026gt; \u0026lt;td\u0026gt;\u0026lt;img src=\u0026#34;server.php\u0026#34; alt=\u0026#34;\u0026#34; onclick=\u0026#34;this.src=\u0026#39;server.php?\u0026#39;+Math.random()\u0026#34;\u0026gt; \u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;button\u0026gt;确定\u0026lt;/button\u0026gt; \u0026lt;/form\u0026gt; ⚡check.php\n\u0026lt;?php session_start(); include \u0026#34;main.html\u0026#34;; if (@strtoupper($_POST[\u0026#34;captcha\u0026#34;])==@$_SESSION[\u0026#34;captcha\u0026#34;]){ echo \u0026#34;\u0026lt;p style=\u0026#39;color: #4FEF10\u0026#39;\u0026gt;验证通过\u0026lt;/p\u0026gt;\u0026#34;; }else{ echo \u0026#34;\u0026lt;p style=\u0026#39;color: #e01\u0026#39;\u0026gt;验证失败！\u0026lt;/p\u0026gt;\u0026#34;; } 🎉🎉🎉\n","permalink":"https://blog.archai.space/p/php%E5%BC%80%E5%8F%91%E9%AA%8C%E8%AF%81%E7%A0%81%E7%B1%BB/","summary":"利用php中的GD库可以完成验证码类的开发。后盾人教程\n PHP创建图像步骤 发送HTTP头信息，声明内容为图像 header(\u0026#39;Content-type:image/gif\u0026#39;); header(\u0026#39;Content-type:image/jpeg\u0026#39;); header(\u0026#39;Content-type:image/png\u0026#39;); 通过设置头信息让浏览器渲染出图像，而不是HTML等其他类型\n创建画布 imageCreateTrueColor(width,height); width \u0026amp; height 画布宽高，即为输出图片的尺寸,返回为source 类型，后续操作都是针对这个资源展开。\n创建绘图所需要的颜色 imageColorAllocate(img_resource,R,G,B); 颜色从属于创建画布产生的图像资源而存在，后面三个值分别为红绿蓝三个通道的值，为int类型,在0—255之间。\n绘图（填充画布、画圆、画方块、画线条、画布上写字） 👉 填充画布(画布背景)\nimageFill(img_resource,x,y,color); 👉 画圆\n//绘制空心圆形 imageEllipse(img_res,x,y,w,h,color); //绘制填充好的实心圆 imageFilledEllipse(img_res,x,y,w,h,color); 绘制 圆心(x,y) 宽 x，高 h，的圆\n👉 画方\n//空心矩形 imageRectangle(img_res,x1,y1,x2,y2,color); //实心矩形 imageFilledRectangle (img_res,x1,y1,x2,y2,color); (x1,y1)为左上角坐标， (x2,y2)为右下角坐标\n\n👉画线条\nimageLine(img_res,x1,y1,x2,y2,color) (x1,y1)与(x2,y2)两点确定的直线。\n👉 绘制像素(点)\nimagesetpixel ( img_res , x , y , color ) 👉 输入文本\nimagettftext (img_res , size , angle , x , y , color , fontfile ,text ) 图像资源，字体尺寸，角度，第一个字符的基本点（大概是字符的左下角），Y 坐标（字体基线的位置），颜色 ，字体文件绝对路径(realpath($path)获取)，文本字符串（UTF-8 编码）","title":"PHP开发验证码类"},{"content":"基本命令 git config --global #全局配置 git config #当前仓库配置 mkdir #创建文件夹 ls #列出当前目录下(文件，文件夹) ls -a #所有all rm -rf * #删除当前目录下所有 touch #新建文件 cat #查看文件中的内容  不想`git add . 所有？\n 配置.gitignore 定义忽略提交的文件，其中\n 所有空行或者以注释符号 ＃ 开头的行都会被 Git 忽略。 匹配模式最后跟反斜杠（/）说明要忽略的是目录。 可以使用标准的 glob 模式(php)匹配。  从版本库删除 git rm ... #从版本库中删除，本地也会删除 git rm --cached ... #只从版本库删除  从版本库改名 git mv ... ... #改名 log git log -p #显示文件变动信息 git log -1 #最近一次提交 git log --oneline 修改最新一次提交commit描述 git commit --amend -m \u0026#34;xxx\u0026#34; #最近一次提交的描述  也可以把最新的git add . 方法放到这次commit 中\n 撤销add . git reset HEAD a.php #文件并未被改变，只是撤销add . alias命令配置快捷操作 git config --global alias.a add #在.gitconfig中设置a表示add atom .gitconfig #家目录执行即可看到修改后的配置  关于分支  只有commit后才会有分支！！！\n 创建分支 git branch ... #创建分支 git checkout ... #切换分支 git checkout -b ...#创建并切换分支 合并分支 git merge ... #合并分支到master 查看 git branch #所有分支 git branch --merged #已经合并的分支 git branch --no--merged #未合并的  合并产生冲突？conflict？？\n 手动修改冲突的文件，保留想要的内容在冲突文件中即可！然后提交\n删除分支 git branch -d ... #删除已经合并的分支 git branch -D ... #删除未合并的分支（慎用！写了几天的代码不要了？？） git 标准分支工作流 \nrebase优化分支合并 \ngit rebase master 说明： 在分支执行rebase，会将当前分支的提交紧接到master之后，然后merge，就相当于合并分支的第一种情况\n区别：\n\n\n暂存区  当前分支还未commit不允许跳转到其他分支！可以生成暂存区解决\n git stash #生成暂存区 git stash list # 暂存区列表 git stash apply... # 恢复暂存区(不填后面标识默认为恢复第0个(stash${0})) git stash drop ... #删除暂存区 git stash pop #恢复并删除 标签  只有稳定代码才有资格打标签！\n git tag #标签列表 git tag ...#打标签 生成代码发布压缩包 git archive master --prefix=\u0026#34;fin/\u0026#34; --format=zip \u0026gt; fin.zip 解释： 为master分支生成名为fin.zip的压缩包\nSSH密钥 使用ssh连接Github发送指令更加安全可靠，也可以免掉每次输入密码的困扰。\n在命令行中输入以下代码（windows用户使用 Git Bash）\nssh-keygen -t rsa 一直按回车键直到结束。系统会在~/.ssh 目录中生成 id_rsa和id_rsa.pub，即密钥id_rsa和公钥id_rsa.pub。然后向GitHub添加秘钥即可。\n","permalink":"https://blog.archai.space/p/git%E7%9B%B8%E5%85%B3/","summary":"基本命令 git config --global #全局配置 git config #当前仓库配置 mkdir #创建文件夹 ls #列出当前目录下(文件，文件夹) ls -a #所有all rm -rf * #删除当前目录下所有 touch #新建文件 cat #查看文件中的内容  不想`git add . 所有？\n 配置.gitignore 定义忽略提交的文件，其中\n 所有空行或者以注释符号 ＃ 开头的行都会被 Git 忽略。 匹配模式最后跟反斜杠（/）说明要忽略的是目录。 可以使用标准的 glob 模式(php)匹配。  从版本库删除 git rm ... #从版本库中删除，本地也会删除 git rm --cached ... #只从版本库删除  从版本库改名 git mv ... ... #改名 log git log -p #显示文件变动信息 git log -1 #最近一次提交 git log --oneline 修改最新一次提交commit描述 git commit --amend -m \u0026#34;xxx\u0026#34; #最近一次提交的描述  也可以把最新的git add .","title":"Git相关"},{"content":" 关于正则表达式的创建，就不做过多的赘述\n ::: tip\n由于不知名原因，span标签报错，因此全部替换为[sp]\n:::\n关于转义 1.字面量形式创建的RegExp 转义形式： \\d, \\., \\s 等\nlet str=\u0026#39;fintinger2592030861.com$323e\u0026#39; let reg=/\\d+\\.com/ str.match(reg);//2592030861.com 2.对象形式创建的RegExp 转义形式： \\\\d, \\\\., \\\\s等\nlet str = \u0026#39;fintinger2592030861.com$323e\u0026#39; let reg = new RegExp(\u0026#39;\\\\d+\\\\.com\u0026#39;, \u0026#39;g\u0026#39;) str.match(reg)//2592030861.com 边界约束  利用^和$分别限制开头和结尾\n let str = \u0026#39;bdhw42556jbjkhb2592030861jrh4jbhkfehrbj43jk32kbbjhb\u0026#39; let reg = /\\d{3,6}/ let reg2 = /^\\d{3,6}$/ str.match(reg)//42556 str.match(reg2)//null 元字符    元字符 用法     \\d \u0026amp; \\D 表示数字和非数字   \\s \u0026amp; \\S 表示空白（空格，换行，Tab制表符等）和非空白   \\w \u0026amp; \\W 表示字母，数字，下划线和非\u0026hellip;   . 查找单个字符，除了换行和行结束符    模式修正符    修饰符 描述     i 执行对大小写不敏感的匹配。   g 执行全局匹配（查找所有匹配而非在找到第一个匹配后停止）。   m 执行多行匹配。    字符属性匹配  Unicode Property：字符属于标点、空格、字母等等。每个Unicode字符只能属于唯一Unicode Property。.NET、Java、PHP和Ruby等语言支持。具体，GO\n \n原子组和原子表 1.原子表 定义方法 ：[]\n常用的一些方法：\n👉 编组就会用 \u0026lsquo;或\u0026rsquo; 关系匹配\n/[123]/ //表示1|2|3 👉 排除匹配\n/[^/d:-,]/ //表示匹配不是数字,\u0026#39;:\u0026#39;,\u0026#39;-\u0026#39;,\u0026#39;,\u0026#39;的 注意： 原子表中的字符全为其本义（除非转义\\），不会进行解析，比如 ' . ' 等\n👉匹配所有\n/[\\d\\D]/ //匹配数字与非数字，即所有 👉范围匹配\n/[a-z]/ //表示a-z的所有字母 2.原子组 定义方法： ()\n常用方法：\n👉重复匹配\n/(abc)/ //表示匹配\u0026#39;abc\u0026#39;整体字符串  原子组中的分组会依次编号，用\\1,\\2...获取到\n let str = `\u0026lt;span\u0026gt;ohh[/sp]` let reg = /\u0026lt;(sp)\u0026gt;(.+)\u0026lt;\\/\\1\u0026gt;/ let res = str.replace(reg, `\u0026lt;p\u0026gt;$2\u0026lt;/p\u0026gt;`) console.log(res);//\u0026lt;p\u0026gt;ohh\u0026lt;/\u0026gt; 注意： 正则表达式中用\\1获取，而replace中用$2获取！\n编号的更多用法\n👉 **标记不记录组 **=\u0026gt; ?:\nlet str = `\u0026lt;span\u0026gt;ohh[/sp]` let reg = /(?:\u0026lt;span\u0026gt;)(.+)(\u0026lt;\\/sp\u0026gt;)/ let res = str.replace(reg, `$1`) console.log(res);//ohh 这里的$1表示\u0026rsquo;ohh\u0026rsquo;这一组，因为'[sp]\u0026lsquo;被标记为不记录组\n👉 自定义组名 =\u0026gt;  ?\u0026lt;name\u0026gt;\nlet str = `\u0026lt;span\u0026gt;ohh[/sp]` let reg = /[sp](?\u0026lt;content\u0026gt;.+)\u0026lt;\\/sp\u0026gt;/ let res = str.replace(reg, `\u0026lt;p\u0026gt;$\u0026lt;content\u0026gt;\u0026lt;/p\u0026gt;`) console.log(res);//\u0026lt;p\u0026gt;ohh\u0026lt;/p\u0026gt; 当原子组比较多时用这种方法即可(原来的$1也可以得到，不会改变顺序)\n重复匹配与禁止贪婪 重复匹配    量词 描述     + 匹配1或多个，相当于{1,}   * 匹配0或多个，相当于{0,}   ? 匹配0或1个，相当于“有或没有”   {2} 只允许匹配2个，限定数量   {2,4} 允许匹配 [2, 10] 个    let str=\u0026#39;124ffr9fre12345ver\u0026#39; reg=/\\d{2,5}/g//只允许匹配2~5个数字 let res=str.match(reg) console.log(res);/[ \u0026#39;124\u0026#39;, \u0026#39;12345\u0026#39; ] 禁止贪婪  在重复匹配后面加上·?\n    量词 描述     +? 只允许匹配1个   *? 只允许0个   {2,100}? / {2,}? / {2}? 只允许2个    let str=` [sp]123[/sp] [sp]叼[/sp] \u0026lt;span\u0026gt;ohh[/sp] ` reg=/\u0026lt;(sp)\u0026gt;[\\s\\S]*?\u0026lt;\\/\\1\u0026gt;/g let res=str.match(reg) console.log(res); //[ \u0026#39;[sp]123[/sp]\u0026#39;, \u0026#39;[sp]叼[/sp]\u0026#39;, \u0026#39;\u0026lt;span\u0026gt;ohh[/sp]\u0026#39; ] 利用禁止贪婪，每次只匹配一个符合正则的字串(一个sp及中间内容)，如果不禁止贪婪（去掉正则中?），则会匹配到从头开始到最后一个满足结尾的字串（所有的sp及中间内容）。\n相关方法 字符串的正则方法  即 String.prototype中的关于正则的方法\n 👉 search()\n返回正则所匹配到的值的索引，找不到则返回-1\n👉 match()\n返回正则所匹配的到的字符串，找不到返回null,返回值与是否有\\g有关\n 如果没有\\g，即不进行全局查找，它将返回一个数组，其中存放了与它找到的匹配文本有关的信息  \n 如果有\\g， match() 方法将执行全局检索，找到 stringObject 中的所有匹配子字符串  👉 matchAll()，新方法，具体了解GO\n返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器\nconst regexp = /t(e)(st(\\d?))/g; const str = \u0026#39;test1test2\u0026#39;; const array = [...str.matchAll(regexp)]; console.log(array[0]); // expected output: Array [\u0026#34;test1\u0026#34;, \u0026#34;e\u0026#34;, \u0026#34;st1\u0026#34;, \u0026#34;1\u0026#34;]  console.log(array[1]); // expected output: Array [\u0026#34;test2\u0026#34;, \u0026#34;e\u0026#34;, \u0026#34;st2\u0026#34;, \u0026#34;2\u0026#34;] 👉 split() / replace()也支持正则\n\u0026hellip;\n正则对象方法 👉 test()\n返回检测的结果，true/false\n👉 exec()\n 在全局模式\\g下，调用一次返回一次存放了与它找到的匹配文本有关的信息的数组，并且会修改该正则的lastIndex为匹配字符下一个字符在整个字符串中的索引，即\u0026quot;last index\u0026quot;，直至找不到，则会返回null，lastIndex变为0\n let str=` [sp]123[/sp] [sp]叼[/sp] [sp]ohh[/sp] ` reg=/\u0026lt;(sp)\u0026gt;[\\s\\S]*?\u0026lt;\\/\\1\u0026gt;/g console.log(reg.exec(str));//找到的匹配文本及其有关的信息 console.log(reg.lastIndex);//17 console.log(reg.exec(str)); console.log(reg.lastIndex);//32 console.log(reg.exec(str)); console.log(reg.lastIndex);//49 console.log(reg.exec(str)); console.log(reg.lastIndex);//0  利用这一特性，可以在较低端浏览器下实现字符串中的matchAll方法\nwhile((res=reg.exec(str))){ result.push(res) } //res=reg.exec(str)仅为赋值操作，判断为\u0026#34;===\u0026#34;!!! //将每次的exec结果临时储存在res，然后push到result数组中 👉 compile()方法\nemmm\u0026hellip;\n断言匹配  断言匹配就是正则是否匹配的条件，写在原子组中\n    量词 描述     (?=) 后面为\u0026hellip;   (?\u0026lt;=) 前面为\u0026hellip;   (?!) 后面不为\u0026hellip;   (?\u0026lt;!) 前面不为\u0026hellip;    /(?!.*.辣鸡*)/ //开始到结尾任意位置不能有\u0026#34;辣鸡\u0026#34; ","permalink":"https://blog.archai.space/p/javascript%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/","summary":"关于正则表达式的创建，就不做过多的赘述\n ::: tip\n由于不知名原因，span标签报错，因此全部替换为[sp]\n:::\n关于转义 1.字面量形式创建的RegExp 转义形式： \\d, \\., \\s 等\nlet str=\u0026#39;fintinger2592030861.com$323e\u0026#39; let reg=/\\d+\\.com/ str.match(reg);//2592030861.com 2.对象形式创建的RegExp 转义形式： \\\\d, \\\\., \\\\s等\nlet str = \u0026#39;fintinger2592030861.com$323e\u0026#39; let reg = new RegExp(\u0026#39;\\\\d+\\\\.com\u0026#39;, \u0026#39;g\u0026#39;) str.match(reg)//2592030861.com 边界约束  利用^和$分别限制开头和结尾\n let str = \u0026#39;bdhw42556jbjkhb2592030861jrh4jbhkfehrbj43jk32kbbjhb\u0026#39; let reg = /\\d{3,6}/ let reg2 = /^\\d{3,6}$/ str.match(reg)//42556 str.match(reg2)//null 元字符    元字符 用法     \\d \u0026amp; \\D 表示数字和非数字   \\s \u0026amp; \\S 表示空白（空格，换行，Tab制表符等）和非空白   \\w \u0026amp; \\W 表示字母，数字，下划线和非\u0026hellip;   .","title":"Javascript正则表达式"},{"content":" class实质上是对js中继承的封装组合，用一种“语法糖”的形式实现原型链的继承\n 静态属性/方法  静态方法/属性是指，函数作为对象时，定义在其原型(__proto__)上的方法，而需要实例化继承的动态方法是定义在其原型对象(prototype) 中的，静态方法中的参数都是固定的，直接通过整个构造函数调用，实例化对象并不会继承；而动态方法中的参数会因为实例化对象的不同而产生改变，实例化对象默认是继承的。\n function User(){} User.__proto__.show=function(){} console.dir(User) //这里的show()方法就属于静态方法 在class中，利用static定义静态属性/方法\nclass Admin{ static show(){}//__proto__中  show(){}//prototype中 } console.dir(Admin) 关于class一些注意点  class中声明的方法在prototype对象中，由于默认的属性特征设置，这些方法是不可遍历的！  class Admin{ show(){} } console.log( Object.getOwnPropertyDescriptor( Admin.prototype,\u0026#34;show\u0026#34; )) //enumerable: false class中声明方法时，方法之间不用,隔开！ class中默认开启严格模式  class中的属性保护  主要可以通过三种途径实现属性保护：Symbol,weakmap,私有属性\n 1.Symbol /*利用Symbol创建一个受保护的属性的集合对象,无法直接获取，只能通过自定义的get/set方法*/ const protects = Symbol(\u0026#39;受保护的属性\u0026#39;) class User { constructor(name) { this.name = name this[protects] = { host: \u0026#39;https://www.fintinger.xyz\u0026#39;, token: \u0026#39;5574f1b81bbe4e3847e7a83c0a84a442\u0026#39;, password: \u0026#39;123456\u0026#39; } } set host(url) { if (!/^https?:/i.test(url)) { throw new Error(\u0026#39;不合法的地址!\u0026#39;) } this[protects].host = url } get password(){ return this[protects].password } } let jqf = new User(\u0026#39;jqf\u0026#39;) jqf.name = \u0026#39;fin\u0026#39; console.log(jqf); jqf.host=\u0026#39;https://123.com\u0026#39; console.log(jqf); console.log(jqf.password);//123456，由于设置了get，所以可以获取到 注意： 1.通过[]给value设置symbol类型的值，具有唯一性！\n​\t2.利用Symbol定义一个保护组，把所有需要保护的属性、方法封闭起来，这样还是可以通过查看结构看到受保护属性的key,value。 👇\n\n2.weakMap /*利用Weakmap创建一个受保护的属性的集合对象,打印看不到*/ const protects = new WeakMap() class User { constructor(name) { this.name = name protects.set(this, { host: \u0026#39;https://www.fintinger.xyz\u0026#39;, token: \u0026#39;5574f1b81bbe4e3847e7a83c0a84a442\u0026#39;, password: \u0026#39;5_q38.ep%6\u0026#39; }) } set host(host) { if (!/^https?:/i.test(host)) { throw new Error(\u0026#39;不合法的地址!\u0026#39;) } protects.set(this, {...protects.get(this), host}) } get host() { return protects.get(this).host } } let jqf = new User(\u0026#39;jqf\u0026#39;) // jqf.name = \u0026#39;fin\u0026#39; console.log(jqf); console.log(jqf.host); jqf.host = \u0026#39;https://123.com\u0026#39; console.log(jqf); console.log(jqf.host); /*console.log(jqf.password);*/ 注意： 1.利用weakset定义受保护属性的组，之后用.set()\u0026amp;.get() 去获取/设置其中的值\n​\t2weakSet设置的值，外界无法直接看到。👇\n\n3.私有属性 /*利用#定义私有方法，私有属性！*/ class User{ name=\u0026#39;jqf\u0026#39; #age=18 #show=() =\u0026gt; {console.log(\u0026#39;ohhh\u0026#39;)} show(){console.log(this.#age)} } console.dir(User) let a=new User() console.dir(a) console.log(a.name) a.show() // console.log(a.#age)  // console.log(a.#age) //Uncaught SyntaxError: Private field \u0026#39;#age\u0026#39; must be declared in an enclosing class 注意： 1.私有方法的定义需要利用变量赋值的方式，直接在函数前加#会报错\n​\t2.私有方法/属性只能在class内部访问\n4.总结     公共属性/方法 受保护属性/方法 私有属性/方法     定义方法 class User{name='jqf'} Symbol,Weakmap #   class本身可否获取修改 √ √ √   实例化对象可否获取修改 √ 不可以直接获取，需要在class中设置set/get 不可以直接获取，需要在class中设置set/get    class中的继承  通过extends继承，继承后必须执行super()，而且是在使用this之前执行\n class User{ constructor(name) { console.log(\u0026#39;user.name\u0026#39;); } show(){ return this.name } } class Admin extends User{ constructor(name) { console.log(\u0026#39;admin.name\u0026#39;); super(); } show(){ return this.name } } let ad=new Admin() console.dir(ad); super关键字  super关键字用于访问和调用一个对象的父对象上的函数。GO\n 在多重继承中 👉 this.__proto__\n 三层以上的继承链中，反复使用this.__proto__会报错\n let father={ name:\u0026#39;father\u0026#39;, show(){ console.log(this.name); } } let user={ __proto__: father, name:\u0026#39;user\u0026#39;, show (){ this.__proto__.show.call(this) // console.log(this.name);  //报错：这里的this.__proto__是user本身！！！  } } let admin={ __proto__:user, name:\u0026#39;admin\u0026#39;, show(){ this.__proto__.show.call(this) } } admin.show() 👉 super\n//使用super代替this.__proto__，不会出现同样的问题 let father={ name:\u0026#39;father\u0026#39;, show(){ console.log(this.name); } } let user={ __proto__: father, name:\u0026#39;user\u0026#39;, show (){ super.show.call(this) } } let admin={ __proto__:user, name:\u0026#39;admin\u0026#39;, show(){ super.show.call(this) } } admin.show() 说明： super始终是当前对象的父级！显得很稳~\nsuper使用父类方法以及传参 class Tools { max(arr) { return arr.sort((a, b) =\u0026gt; b - a)[0] } getByKey(key) { return this.data.filter(i =\u0026gt; i.name.toUpperCase().includes(key.toUpperCase())) } } class Lesson extends Tools { constructor(data) { super(); this.data = data } /*利用super使用父类方法*/ get mostExpensive() { return super.max.call(null, this.data.map(i =\u0026gt; i.price)) } /*重写父类的方法*/ getByKey(key) { return super.getByKey(key).map(i =\u0026gt; i.name) } } let data = [ {name: \u0026#34;JS\u0026#34;, price: 198}, {name: \u0026#34;CSS\u0026#34;, price: 98}, {name: \u0026#34;Vue.js\u0026#34;, price: 99} ]; let lesson = new Lesson(data) console.log(lesson); console.log(lesson.mostExpensive);//198 console.log(lesson.getByKey(\u0026#39;js\u0026#39;));//[ \u0026#39;JS\u0026#39;, \u0026#39;Vue.js\u0026#39; ] 说明：\n super 关键字，既可以当作函数使用，也可以当作对象使用。在这两种情况下，它的用法完全不同。\n 1、super当做函数使用 super 作为函数调用时，代表父类的构造函数。ES6 要求，子类的构造函数必须执行一次 super() 函数。注意：作为函数时，super() 只能用在子类的构造函数之中，用在其他地方就会报错。\n👉 super 作为函数调用时，内部的 this 指的是子类实例\nclass A { constructor() { this.show(); } } class B extends A { constructor() { super(); } show(){ console.log(\u0026#39;实例\u0026#39;); } static show(){ console.log(\u0026#39;子类\u0026#39;); } } new B() //输出 \u0026#39;实例\u0026#39; ，new B 时触发了 B 的构造函数，所以触发了 super 方法，即触发了父类 A 的构造函数，此时的 this.show 的 this 指的是子类 2、super 作为对象使用 super 作为对象时，在普通方法中，指向父类的原型对象(this.prototype)；在静态方法中，指向父类(this.__proto__)。\n👉 super在普通方法中（即非静态方法）的 this 关键字指向\n在子类普通方法中通过 super 调用父类的方法时，方法内部的 this 指向的是当前的子类实例。\nclass A { constructor() { this.x = 1; } print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; super.y = 123;//如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。  } m() { super.print(); } } let b = new B(); b.m() // 2 console.log(b.y); //123 👉 super在静态方法中及此时的 this 关键字指向\nsuper作为对象，用在静态方法之中，这时 super 将直接指向父类，而不是父类的原型对象。\n在子类的静态方法中通过 super 调用父类的方法时，方法内部的 this 指向当前的子类，而不是子类的实例。\nclass A { constructor() { this.x = 1; } static print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } static m() { super.print(); } } B.x = 3; B.m() // 3 静态属性\u0026amp;方法的继承  如果把构造函数当做对象来看，就可以在其本身加上属性，方法，这些其实都为静态的，在class中，用extends继承时，会自动继承静态的属性和方法\n // function User() {} // // User.name = \u0026#39;jqf\u0026#39; // User.show = function () { // console.log(\u0026#39;User.static method\u0026#39;); // } // // function Admin() {} // // Admin.__proto__ = User // Admin.show()  /*在class中，用extends继承时，会自动继承静态的属性和方法*/ class User { static name=\u0026#39;jqf\u0026#39; static show=function (){ console.log(\u0026#39;User.static method\u0026#39;); } } class Admin extends User{} Admin.show() // let ad=new Admin() // ad.show() 注意： 类的实例化中并不会继承静态的属性/方法，只能用父类调用\n内置类的继承增强  可以利用extends 继承js中的内置类，为其增加一些更加方便快捷的方法，是增强\n //为Array增加了remove()方法 class Arr extends Array { constructor(...args) { super(...args); } remove(item) { let pos = super.findIndex(val =\u0026gt; val === item); super.splice(pos,1) return this } } let arr = new Arr(1, 2, 3) console.log(arr); console.log(arr.remove(2)); 对象合并实现多继承 参考之前js继承和原型链章节中的对象合并实现多继承\n只需要合并User.prototype和需要的对象即可！\n","permalink":"https://blog.archai.space/p/es6%E4%B8%AD%E7%9A%84class%E7%B1%BB/","summary":"class实质上是对js中继承的封装组合，用一种“语法糖”的形式实现原型链的继承\n 静态属性/方法  静态方法/属性是指，函数作为对象时，定义在其原型(__proto__)上的方法，而需要实例化继承的动态方法是定义在其原型对象(prototype) 中的，静态方法中的参数都是固定的，直接通过整个构造函数调用，实例化对象并不会继承；而动态方法中的参数会因为实例化对象的不同而产生改变，实例化对象默认是继承的。\n function User(){} User.__proto__.show=function(){} console.dir(User) //这里的show()方法就属于静态方法 在class中，利用static定义静态属性/方法\nclass Admin{ static show(){}//__proto__中  show(){}//prototype中 } console.dir(Admin) 关于class一些注意点  class中声明的方法在prototype对象中，由于默认的属性特征设置，这些方法是不可遍历的！  class Admin{ show(){} } console.log( Object.getOwnPropertyDescriptor( Admin.prototype,\u0026#34;show\u0026#34; )) //enumerable: false class中声明方法时，方法之间不用,隔开！ class中默认开启严格模式  class中的属性保护  主要可以通过三种途径实现属性保护：Symbol,weakmap,私有属性\n 1.Symbol /*利用Symbol创建一个受保护的属性的集合对象,无法直接获取，只能通过自定义的get/set方法*/ const protects = Symbol(\u0026#39;受保护的属性\u0026#39;) class User { constructor(name) { this.name = name this[protects] = { host: \u0026#39;https://www.fintinger.xyz\u0026#39;, token: \u0026#39;5574f1b81bbe4e3847e7a83c0a84a442\u0026#39;, password: \u0026#39;123456\u0026#39; } } set host(url) { if (!","title":"Es6中的class类"},{"content":"原型链自定义设置  原型链的设置可以通过Object.creative , __proto__, Object.setProperty三种方法\n Object.creative () 说明： 该方法只能设置原型链，而不能获取\nlet a1 = {name: \u0026#39;a1\u0026#39;} let b1 = Object.create(a1) b1.name = \u0026#39;b1\u0026#39; console.log(b1);  Object.creative()为创建对象的方法，第一个参数为其原型对象，第二个参数为其默认属性，这些属性默认是不可写，不可遍历，不可修改的\n let b=Object.create(null,{ a:{value:\u0026#39;ohh\u0026#39;, enumerable:true}, b:{value:\u0026#39;jqf\u0026#39;}, }) console.log(Object.getOwnPropertyDescriptors(b)); //a: {value: \u0026#34;ohh\u0026#34;, writable: false, enumerable: true, configurable: false} //b: {value: \u0026#34;jqf\u0026#34;, writable: false, enumerable: false, configurable: false} __proto__ 说明： 能设置，能获取，实质上是setter，getter\nlet a2 = {name: \u0026#39;a2\u0026#39;} let b2 = {name: \u0026#39;b2\u0026#39;} a2.__proto__ = b2 console.log(a2); console.log(a2.__proto__);//{name: \u0026#34;b2\u0026#34;}  关于__proto__是setter,getter？\n let a = Object.prototype console.log(Object.getOwnPropertyDescriptor(a, \u0026#39;__proto__\u0026#39;)); //可以看到\u0026#39;__proto__\u0026#39;的属性描述中有set,get Object.setProperty let a3 = {name: \u0026#39;a3\u0026#39;} let b3 = {name: \u0026#39;b3\u0026#39;} Object.setPrototypeOf(a3, b3) console.log(a3); console.log(Object.getPrototypeOf(a3));//b3 原型链示意图 \n关于检测 原型链检测 👉  instanceof\n说明： 检测a的原型链上有无A的原型(A.prototype)=\u0026gt;构造函数\nfunction A() {} let a = new A() console.log(a instanceof A);//true 👉 isPrototypeOf\n说明： 检测b的原型链上有无c对象=\u0026gt;对象\nlet b = {name: \u0026#39;b\u0026#39;} let c = {name: \u0026#39;c\u0026#39;} Object.setPrototypeOf(b, c) console.log(c.isPrototypeOf(b));//true 属性检测 👉 in\n说明： 检测本对象及其原型链有无指定属性\n👉 hasOwnProperty\n说明： 只是检测本对象有无指定属性\nlet a = {name: \u0026#39;jqf\u0026#39;} let b = {age: 19} Object.setPrototypeOf(a, b) console.log(\u0026#34;age\u0026#34; in a);//true console.log(a.hasOwnProperty(\u0026#34;age\u0026#34;));//false  使用for循环时，加上判断是否为本身的key，以免遍历继承与父级的key\n for (const key in a) { console.log(\u0026#39;non-if:\u0026#39;+key); if (a.hasOwnProperty(key)){ console.log(\u0026#39;if-\u0026#39;+key); } } /*Output non-if:name if:name non-if:age */ 关于继承 构造函数 function User() { this.name = \u0026#39;User\u0026#39; } User.prototype.show = function () { console.warn(\u0026#39;show method\u0026#39;); } function Admin() { this.name = \u0026#39;Admin\u0026#39; } 说明： User 和 Admin分别为两个不相干的构造函数。如果想在Admin的实例化对象能用到User.prototype的方法(prototype为一个对象，其中含有对应构造函数的方法)，让Admin.prototype的上级为User.prototype，这样Admin.prototype中，就能用User.prototype中的方法了，简而言之，就是修改子类Admin.prototype 的原型链，使其父级为User.prototype\n 注意：构造函数中的方法一般都定义在其原型prototype对象中，这样，当这个构造函数实例化的时候，不会重新拷贝出来定义在构造函数中的方法，造成不必要的资源浪费,切记！用对象的方法定义构造函数的prototype之后，必须加上constructor属性\n function Animation() {} Animation.prototype = { constructor: Animation,//修改其构造函数!!!  show: function () { this.style.display = \u0026#39;block\u0026#39; }, hide: function () { this.style.display = \u0026#39;none\u0026#39; } } 开始继承 //__proto__方法/Object.create()方法/Object.setPrototypeOf()方法  Object.setPrototypeOf(Admin.prototype, User.prototype) Object.defineProperty(Admin.prototype, \u0026#39;constructor\u0026#39;, { value: Admin, enumerable: false }) Admin.prototype.info = function () { console.log(\u0026#39;Admin\u0026#39;); } 完成继承 let _admin = new Admin() _admin.show()//show method _admin.info()//Admin console.log(_admin.__proto__.constructor === Admin)//true //实例化的对象可以调用Admin中的方法，也可以用User中的方法 图示\n\n继承函数封装 /** * 下级继承上级构造函数 * @param{Function}sub 下级函数 * @param{Function}sup 上级函数 */ function inherit(sub, sup) { Object.setPrototypeOf(sub.prototype, sup.prototype) Object.defineProperty(sub.prototype, \u0026#39;constructor\u0026#39;, { value: sub, enumerable: false }) } 对象合并实现多继承  Javascript 中一个构造函数无法继承多个基函数的方法，即多继承，如果都采用构造函数的方式，就只能一层层往上继承，才能做到多继承的效果，但是这样写显然会存在很多问题。于是想到，可以把一些方法写成对象的形式，然后在继承的prototype中合并，这样，就能分门别类地实现按需多继承\n 方法对象及基函数：\nconst Request = { ajax: function () { return \u0026#39;请求后台中....\u0026#39; } } const Address = {getAddress: _ =\u0026gt; \u0026#39;获取地址中....\u0026#39;} const Access = {getAccess: _ =\u0026gt; \u0026#39;获取权限中....\u0026#39;} const Info = { __proto__: Request, getMore() { //super=this.__proto__  console.log(super.ajax(), \u0026#39;获取更多信息中...\u0026#39;); } } function User(name, age) { this.name = name this.age = age } User.prototype.show = function () { console.log(this.name, this.age) } 继承：\n/*管理员构造函数*/ function Admin(name, age) { User.call(this, name, age) } inherit(Admin, User) Object.assign(Admin.prototype, Access, Request, Info)//使用哪些合并哪些 let a = new Admin(\u0026#39;jqf\u0026#39;, 19) a.show() console.log(a.getAccess()); a.getMore() /*会员构造函数*/ function Member(name, age) { User.call(this, name, age) } inherit(Member, User) Object.assign(Member.prototype, Access, Address) let b = new Member(\u0026#39;fin\u0026#39;, 18) b.show() console.log(b.getAccess()); console.log(b.getAddress()); 说明：\n  super关键字用于访问和调用一个对象的父对象上的函数。GO\n  利用call/apply 调用父级构造函数！\n  ","permalink":"https://blog.archai.space/p/javascript%E4%B8%AD%E7%9A%84%E7%BB%A7%E6%89%BF%E4%B8%8E%E5%8E%9F%E5%9E%8B%E9%93%BE%E5%88%86%E6%9E%90/","summary":"原型链自定义设置  原型链的设置可以通过Object.creative , __proto__, Object.setProperty三种方法\n Object.creative () 说明： 该方法只能设置原型链，而不能获取\nlet a1 = {name: \u0026#39;a1\u0026#39;} let b1 = Object.create(a1) b1.name = \u0026#39;b1\u0026#39; console.log(b1);  Object.creative()为创建对象的方法，第一个参数为其原型对象，第二个参数为其默认属性，这些属性默认是不可写，不可遍历，不可修改的\n let b=Object.create(null,{ a:{value:\u0026#39;ohh\u0026#39;, enumerable:true}, b:{value:\u0026#39;jqf\u0026#39;}, }) console.log(Object.getOwnPropertyDescriptors(b)); //a: {value: \u0026#34;ohh\u0026#34;, writable: false, enumerable: true, configurable: false} //b: {value: \u0026#34;jqf\u0026#34;, writable: false, enumerable: false, configurable: false} __proto__ 说明： 能设置，能获取，实质上是setter，getter\nlet a2 = {name: \u0026#39;a2\u0026#39;} let b2 = {name: \u0026#39;b2\u0026#39;} a2.__proto__ = b2 console.log(a2); console.log(a2.__proto__);//{name: \u0026#34;b2\u0026#34;}  关于__proto__是setter,getter？","title":"JavaScript中的继承与原型链分析"},{"content":"自定义一个模块引擎 要求： 能够让模块之间产生依赖关系\n代码\nlet _module = (function () { let moduleList = {} function define(name, modules, action) { modules.map((n, i) =\u0026gt; { //将传入的只含名称的模块数组 =\u0026gt; 指定名称模块的返回结果  modules[i] = moduleList[n] }) moduleList[name] = action.apply(null, modules) // console.log(moduleList);  } return {define} })(); //定义一个工具库，暴露给外界使用，输出 _module.define(\u0026#39;tools\u0026#39;, [], function () { return { max(arr) { return arr.sort((a, b) =\u0026gt; b - a)[0]//获取最大值挺别致昂  }, min(arr) { return arr.sort((a, b) =\u0026gt; b - a)[arr.length - 1]; } } }); //使用之前定义的工具库，引入 _module.define(\u0026#39;use\u0026#39;, [\u0026#39;tools\u0026#39;], function (m) { console.log(m.max([1, 2, 5, 3, 8]));//8  console.log(m.min([1, 2, 5, 3, 8]));//1 }) JS中模块化的使用 1.基本使用  export 暴露给外界使用，import 引入\n /*module.js*/ let a = \u0026#34;jqf\u0026#34; function getName() { console.log(a); } //利用export暴露给外界 export {a, getName} \u0026lt;!--use.html--\u0026gt; \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; // type=\u0026#34;module\u0026#34;，./ 都必须书写！  import {a,getName} from \u0026#34;./module.js\u0026#34;; console.log(a);//jqf getName();//jqf \u0026lt;/script\u0026gt; 2.批量导入 import * as api from \u0026#39;./modules.js\u0026#39; 使用： api.a |api.getName()\u0026hellip;\n 不太建议直接使用这种方法，因为有时候并不需要全部导入，只导入需要即可\n 3.别名导出导入 import {a as user,getName} from \u0026#39;./modules.js\u0026#39; =\u0026gt; 将导入的a 命名为 user\n同理，导出时也可以重命名\nexport {a as user,getName} from \u0026#39;./modules.js\u0026#39; 4.默认导出 export default function getName(){ //... } //或者 export {getName as default} 导入时，\nimport gName from \u0026#39;./modules.js\u0026#39; 说明： 默认导出只有一个，用任意名接收即可，但是，规范化的要求是导入默认导出的时候，名称尽量与文件名一样或者相同\n另外，当混合时\nexport {a, getName as default} import modules,{a} from \u0026#39;./modules.js\u0026#39; //默认导出和普通导出用\u0026#39;,\u0026#39;隔开 5.模块的合并导出 方法： 建立一个js文件，导入所有的模块，并且编成组再导出\nimport * as m1 from \u0026#39;./module1.js\u0026#39; import * as m2 from \u0026#39;./module2.js\u0026#39; export {m1,m2}  编成组之后可以避免不同模块中导出重名的情况\n //使用 import * as api from \u0026#39;./index.js\u0026#39; api.m1.getName() 6.按需加载  由于 import 只能在外部顶层，无法在{ }等中使用，因此无法做到按需加载，事件触发加载\u0026hellip;\u0026hellip;\n **方法： ** import(src) 函数\n参数： src js文件的加载路径\n返回值： 返回一个Promise，进行后续操作\ndocument.querySelector(\u0026#34;button\u0026#34;).addEventListener(\u0026#34;click\u0026#34;, function () { import(\u0026#39;./modules.js\u0026#39;).then((module) =\u0026gt; { console.log(module.max([1, 2, 5, 7, 3, 8, 5])); }) }) ","permalink":"https://blog.archai.space/p/%E5%85%B3%E4%BA%8Ejavascript%E6%A8%A1%E5%9D%97%E5%8C%96/","summary":"自定义一个模块引擎 要求： 能够让模块之间产生依赖关系\n代码\nlet _module = (function () { let moduleList = {} function define(name, modules, action) { modules.map((n, i) =\u0026gt; { //将传入的只含名称的模块数组 =\u0026gt; 指定名称模块的返回结果  modules[i] = moduleList[n] }) moduleList[name] = action.apply(null, modules) // console.log(moduleList);  } return {define} })(); //定义一个工具库，暴露给外界使用，输出 _module.define(\u0026#39;tools\u0026#39;, [], function () { return { max(arr) { return arr.sort((a, b) =\u0026gt; b - a)[0]//获取最大值挺别致昂  }, min(arr) { return arr.sort((a, b) =\u0026gt; b - a)[arr.length - 1]; } } }); //使用之前定义的工具库，引入 _module.","title":"关于JavaScript模块化"},{"content":"对象中参数获取和值的接收 1.对象中利用key获取value. |  [] let obj = {name: \u0026#39;jqf\u0026#39;, age: 19} for (let key in obj) { // console.log(obj.key);//会报undefined  console.log(obj[key]);//jqf,19 } 👉 delete Object.key()\ndelete obj.name console.log(obj);//{ age: 19 } 2.参数/配置合并 👉 展开语法\n 利用展开语法合并对象，让后面传入的参数覆盖默认参数，完成合并\n function upload(params) { let config = { url: \u0026#39;/images\u0026#39;, type: \u0026#39;*.jpg,*.png\u0026#39; } config = {...config, ...params} //do something...  console.log(config); } upload({type:\u0026#39;*.webp\u0026#39;})//{ url: \u0026#39;/images\u0026#39;, type: \u0026#39;*.webp\u0026#39; } 👉 解构赋值\n 解构赋值会把相同key的进行合并，后面覆盖掉前面的\n function upload(params = {}) { let {url = \u0026#39;/images\u0026#39;, type = \u0026#39;*.jpg,*.png\u0026#39;} = params //do something...  console.log(url, type); } upload({type: \u0026#39;*.webp\u0026#39;})///images *.webp 关于解构赋值？\nlet user = {name: \u0026#39;jqf\u0026#39;, age: 18} let {name: n, age: a} = user console.log(n, a); 注意： 等号左边是源数据，右边是要赋值的变量，二者相同时可省略\n3.hasOwnProperty() 自身属性中是否具有指定的属性  与in 运算符不同，该方法会忽略掉那些从原型链上继承到的属性\n let obj={ name:\u0026#39;jqf\u0026#39;, age:19 } console.log(obj.hasOwnProperty(\u0026#39;name\u0026#39;));//true console.log(\u0026#39;name\u0026#39; in obj);///true console.log(obj.hasOwnProperty(\u0026#39;hasOwnProperty\u0026#39;));//false console.log(\u0026#39;hasOwnProperty\u0026#39; in obj);//true 4.Object.assign() 对象合并 👉 参数：Object.assign(target, ...sources)\ntarget 表示目标对象\nsources 表示源对象\n👉 返回值： 目标对象\nlet params={ url:\u0026#39;/image\u0026#39;, type:\u0026#39;*.jpg\u0026#39; } let config={ type: \u0026#39;*.png\u0026#39; } let returnObj=Object.assign(config,params) console.log(config);//{ type: \u0026#39;*.jpg\u0026#39;, url: \u0026#39;/image\u0026#39; } console.log(returnObj);//{ type: \u0026#39;*.jpg\u0026#39;, url: \u0026#39;/image\u0026#39; } 5.用for-of遍历对象？  对象可以直接用for-in遍历，for-of专门用于遍历可迭代对象\n let user={ name:\u0026#39;jqf\u0026#39;, age:18 } for (let [key,value] of Object.entries(user)) { console.log(key,value); } 说明： Object.entries(obj) 返回一个数组，是Object.keys() 和 Object.values() 的合并\n对象的拷贝 1.浅拷贝 let a = { name: \u0026#39;jqf\u0026#39;, age: 18 } //#1 解构赋值 let b = {name: a.name, age: a.age} console.log(b); //#2 for-in循环赋值 let c = {} for (let aKey in a) { let key = aKey c[key] = a[key] } console.log(c); //#3 对象合并 let d={} // Object.assign(d,a) d={...a} console.log(d); 问题： 浅拷贝之后，对于多层次的对象，赋值之后会修改掉原来的对象！\nlet user={ name:\u0026#39;jqf\u0026#39;, lessons:{ title:\u0026#39;EN\u0026#39;, score:29 } } let b={...user} b.name=\u0026#39;fin\u0026#39; b.lessons.score=18 console.log(b);//{ name: \u0026#39;fin\u0026#39;, lessons: { title: \u0026#39;EN\u0026#39;, score: 18 } } console.log(user);//{ name: \u0026#39;jqf\u0026#39;, lessons: { title: \u0026#39;EN\u0026#39;, score: 18 } } /** *目的是只修改b中的score，原来user中的却也被修改 */ 2.深拷贝  深拷贝利用递归的方式，深层次拷贝整个对象\n function deepCopy(obj) { let res = {}; for (let objKey in obj) { res[objKey] = typeof obj[objKey] === \u0026#39;object\u0026#39; ? deepCopy(obj[objKey]) : obj[objKey] } return res } let user = {name: \u0026#39;jqf\u0026#39;, lessons: {title: \u0026#39;EN\u0026#39;, score: 29}} let b = deepCopy(user) console.log(b);//{ name: \u0026#39;jqf\u0026#39;, lessons: { title: \u0026#39;EN\u0026#39;, score: 29 } }  b.lessons.score = 0 console.log(user);//{ name: \u0026#39;jqf\u0026#39;, lessons: { title: \u0026#39;EN\u0026#39;, score: 29 } } console.log(b);//{ name: \u0026#39;jqf\u0026#39;, lessons: { title: \u0026#39;EN\u0026#39;, score: 0 } } 这样原对象中的value就不会被修改了！\n弊端： 如果user对象中含有数组类型的数据，该方法会出现问题！\n递归深拷贝方法的改进：\n instanceof() 可以检测是不是属于Array等\nObject.entries() 可以将数组分解为索引+值的形式储存起来\n 优化：\nfunction deepCopy(obj) { let res = obj instanceof Array ? [] : {} for (const [k, v] of Object.entries(obj)) { res[k] = typeof v == \u0026#34;object\u0026#34; ? deepCopy(v) : v } return res } let user = { name: \u0026#39;jqf\u0026#39;, lessons: {title: \u0026#39;EN\u0026#39;, score: 29}, skills: [\u0026#39;CSS\u0026#39;, \u0026#39;JS\u0026#39;, \u0026#39;H5\u0026#39;] } let b = deepCopy(user) console.log(b);//{name: \u0026#39;jqf\u0026#39;,lessons: { title: \u0026#39;EN\u0026#39;, score: 29 },skills: [ \u0026#39;CSS\u0026#39;, \u0026#39;JS\u0026#39;, \u0026#39;H5\u0026#39; ]} 构造函数创建对象 function User(name) { this.name = name; this.show = function () { console.log(`My name is ${this.name}`) } } let jqf = new User(\u0026#39;jqf\u0026#39;) jqf.show()//My name is jqf  javascript中的很多类型都是由构造函数定义的，定义其方法属性等等\u0026hellip;可以通过new 的方式实例化，比如new Array、new Boolean()、new String()、new Function()...\n 对象的属性描述 获取属性描述符 方法： Object.getOwnPropertyDescriptor(obj, PropertyKey)\n作用： 返回指定对象上一个自有属性对应的属性描述符。（自有属性指的是直接赋予该对象的属性，不需要从原型链上进行查找的属性\nlet obj={ name:\u0026#39;jqf\u0026#39;, age:18 } console.log(Object.getOwnPropertyDescriptor(obj, \u0026#39;name\u0026#39;)); //{value: \u0026#34;jqf\u0026#34;, writable: true, enumerable: true, configurable: true} 属性描述符 👉value\n该属性的值(仅针对数据属性描述符有效)\n👉 writable 可写 (可修改的)\n当且仅当属性的值可以被改变时为true。(仅针对数据属性描述有效）\n👉enumerable 可枚举\n当且仅当指定对象的属性可以被枚举出时，为 true。\n 设置为false，entries(),keys(),values(),for-in等都无法使用\n 👉configurable 可配置\n当且仅当指定对象的属性描述可以被改变或者属性可被删除时，为true。\n 设置为false，delete删除key,不可用。Object.defineProperty() 都无法使用\n 👉set\n获取该属性的设置器函数（setter）。 如果没有设置器， 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)\n👉get\n获取该属性的访问器函数（getter）。如果没有访问器， 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)\n控制属性描述符 方法： Object.defineProperty()\nlet obj={ name:\u0026#39;jqf\u0026#39;, age:18 } //单个修改 Object.defineProperty(obj,\u0026#39;name\u0026#39;,{ writable:false }); Object.defineProperty(obj,\u0026#39;age\u0026#39;,{ writable:false }) //批量修改 Object.defineProperties(obj,{ name:{enumerable:false}, age:{enumerable:false} }) console.log(Object.getOwnPropertyDescriptors(obj)); 其他一些方法 👉 Object.preventExtensions(obj)\n作用： 阻止往对象中添加属性/方法\n判断： Object.isExtensible() 判断是否可以添加属性\nlet obj={ name:\u0026#39;jqf\u0026#39;, age:18 } Object.preventExtensions(obj) obj.info=\u0026#39;ohhhhh\u0026#39; console.log(Object.isExtensible(obj));//false console.log(obj);//... 👉 Object.seal(obj) 封闭对象\n作用： 阻止往对象中添加属性/方法(但可以修改)，属性描述符configurable和enumerable不可被修改，writable可单向修改为false，但不可以由false改为true；\n判断： Object.isSealed(obj)\nlet obj={ name:\u0026#39;jqf\u0026#39;, age:18 } Object.seal(obj) obj.name=\u0026#39;fin\u0026#39; console.log(Object.isSealed(obj));//true console.log(Object.getOwnPropertyDescriptors(obj));//configurable: false,name:\u0026#39;fin\u0026#39; 👉 Object.freeze(obj)\n作用： 阻止往对象添加属性/方法， 也阻止修改属性/方法，属性描述符不可修改\n判断： Object.isFrozen(obj)\nlet obj={ name:\u0026#39;jqf\u0026#39;, age:18 } Object.freeze(obj) console.log(Object.isFrozen(obj));//true console.log(Object.getOwnPropertyDescriptors(obj));//configurable: false  Object.seal(obj)\u0026amp; Object.freeze(obj)区别:\nObject.freeze(obj)“冰冻”对象本身以及一切现有的属性值(value)以及属性的特性(property descriptor).在函数Object.seal()中也许还可以修改属性值以及修改 属性的特性writable(true\u0026ndash;\u0026gt;false)，但是在Object.freeze ()中，这些都干不了\n 访问器  ECMAScript 5 (2009) 引入了 Getter 和 Setter。Getter 和 Setter 允许您定义对象访问器（被计算的属性）。\n 基本用法 let user = { data: {name: \u0026#39;jqf\u0026#39;, age: 19}, get age() { return `${this.data.name}现在${this.data.age}岁` }, set age(v) { if (typeof v !== \u0026#39;number\u0026#39; || v \u0026lt;= 10 || v \u0026gt; 100) { throw new Error(\u0026#39;年龄不合法\u0026#39;) } this.data.age = v } } console.log(Object.getOwnPropertyDescriptors(user,\u0026#39;age\u0026#39;))//get:f set:f console.log(user.age);//jqf现在18岁 console.log(user.age = 10);//Uncaught Error: 年龄不合法 应用 👉 伪造属性\nlet cart = { lists: [ {name: \u0026#39;Macbook\u0026#39;, price: 8988}, {name: \u0026#39;shirt\u0026#39;, price: 59}, {name: \u0026#39;office table\u0026#39;, price: 11900}, ], get total(){ return this.lists.reduce((t,i)=\u0026gt; t+i.price,0) } } console.log(cart.total);//20947 👉 访问器实现批量设置对象属性\nlet user = { data: {name: \u0026#39;jqf\u0026#39;, age: 19}, set config(cfg) { [this.data.name, this.data.age] = cfg.split(\u0026#34;,\u0026#34;) } } user.config = \u0026#39;fin,18\u0026#39; console.log(user); 👉 token()的读写处理\n localStorage 和 sessionStorage 属性允许在浏览器中存储 key/value 对的数据。localStorage 用于长久保存整个网站的数据，保存的数据没有过期时间，直到手动去删除。localStorage 属性是只读的。如果你只想将数据保存在当前会话中，可以使用 sessionStorage 属性， 该数据对象临时保存同一窗口(或标签页)的数据，在关闭窗口或标签页之后将会删除这些数据。具体GO\n let Request = { set token(v) { localStorage.setItem(\u0026#39;token\u0026#39;, v) }, get token() { let token = localStorage.getItem(\u0026#39;token\u0026#39;) return token } } // Request.token=\u0026#39;EF3%F^323D\u0026#39; console.log(Request.token); 优先级  定义在set或get中的属性优先级高于普通定义的\n let user = { name: \u0026#39;jqf\u0026#39;, get name() { return \u0026#39;get-name\u0026#39; } } console.log(user.name);//get-name Proxy代理拦截  Proxy 对象用于定义基本操作的自定义行为（如属性查找、赋值、枚举、函数调用等）。\n 方法 👉handler.getPrototypeOf()\nObject.getPrototypeOf 方法的捕捉器。\n👉handler.setPrototypeOf()\nObject.setPrototypeOf 方法的捕捉器。\n👉handler.isExtensible()\nObject.isExtensible 方法的捕捉器。\n👉handler.preventExtensions()\nObject.preventExtensions 方法的捕捉器。\n👉handler.getOwnPropertyDescriptor()\nObject.getOwnPropertyDescriptor 方法的捕捉器。\n👉handler.defineProperty()\nObject.defineProperty方法的捕捉器。\n👉handler.has()\nin 操作符的捕捉器。\n👉handler.get()\n属性读取操作的捕捉器。\n👉handler.set()\n属性设置操作的捕捉器。\n👉handler.deleteProperty()\ndelete 操作符的捕捉器。\n👉handler.ownKeys()\nObject.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。\n👉handler.apply()\n函数调用操作的捕捉器。\n👉handler.construct()\nnew 操作符的捕捉器。\n使用案例 👉 使用代理查看函数执行时间：\nfunction factorial(num) { return num === 1 ? num : num * factorial(num - 1) } let p = new Proxy(factorial, { apply(target, thisArg, argArray) { console.time(\u0026#39;run\u0026#39;) factorial.apply(null, argArray)//argArray为原函数传入的参数  console.timeEnd(\u0026#39;run\u0026#39;) } }) p(6)//run: 0.159ms 👉 使用代理对数组过滤拦截：\nlet lessons = [ {title: \u0026#39;Javascript进阶教程\u0026#39;, click: 9800}, {title: \u0026#39;web全栈\u0026#39;, click: 8000}, {title: \u0026#39;CSS属性深度剖析\u0026#39;, click: 9900} ] let proxy = new Proxy(lessons, { get(target, p, value, receiver) { const len = 5 const tarTitle=target[p].title target[p].title = tarTitle.length \u0026gt; len ? tarTitle.substr(0, len) + \u0026#39;.\u0026#39;.repeat(3) : tarTitle return target[p] } }) console.log(proxy[2]); console.log(proxy); 👉 使用代理实现vue.js数据双向绑定：\n\u0026lt;!--HTML部分--\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; v-model=\u0026#34;title\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; v-model=\u0026#34;title\u0026#34;\u0026gt; \u0026lt;h3 v-bind=\u0026#34;title\u0026#34;\u0026gt;这里的数据也会改变\u0026lt;/h3\u0026gt; //JS部分 function Vue() { let proxy = new Proxy({}, { set(target, p, value, receiver) { document.querySelectorAll(`[v-model=${p}]`).forEach(i =\u0026gt; { i.value = value }) document.querySelectorAll(`[v-bind=${p}]`).forEach(i =\u0026gt; { i.innerHTML = value }) }, get(target, p, receiver) {} }) this.init = function () { document.querySelectorAll(\u0026#39;[v-model]\u0026#39;).forEach(i =\u0026gt; { i.addEventListener(\u0026#39;keyup\u0026#39;, function () { proxy[i.getAttribute(\u0026#39;v-model\u0026#39;)] = i.value }) }) } } new Vue().init() ","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%AF%B9%E8%B1%A1/","summary":"对象中参数获取和值的接收 1.对象中利用key获取value. |  [] let obj = {name: \u0026#39;jqf\u0026#39;, age: 19} for (let key in obj) { // console.log(obj.key);//会报undefined  console.log(obj[key]);//jqf,19 } 👉 delete Object.key()\ndelete obj.name console.log(obj);//{ age: 19 } 2.参数/配置合并 👉 展开语法\n 利用展开语法合并对象，让后面传入的参数覆盖默认参数，完成合并\n function upload(params) { let config = { url: \u0026#39;/images\u0026#39;, type: \u0026#39;*.jpg,*.png\u0026#39; } config = {...config, ...params} //do something...  console.log(config); } upload({type:\u0026#39;*.webp\u0026#39;})//{ url: \u0026#39;/images\u0026#39;, type: \u0026#39;*.webp\u0026#39; } 👉 解构赋值\n 解构赋值会把相同key的进行合并，后面覆盖掉前面的\n function upload(params = {}) { let {url = \u0026#39;/images\u0026#39;, type = \u0026#39;*.","title":"JavaScript基础之对象"},{"content":"关于函数传参  当参数数量不确定时，通常用arguments 来统一接收\n function sum() { return [...arguments].reduce((res, i,) =\u0026gt; res + i, 0) } let res = sum(1, 2, 3) console.log(res);//6 可以用展开语法代替arguments接收所有的参数：\nfunction sum(...args) { return args.reduce((res, i,) =\u0026gt; res + i, 0) } let res = sum(1, 2, 3) console.log(res);//6 注意： 接收参数时展开语法放其他形参后面，做统一接收\n递归函数  递归函数有两个关键点，一是要有退出执行的条件，二是要不断执行自身并返回\n 递归思想实现阶乘函数 function factorial(num) { if (num === 1) { return num } return num * factorial(--num) } 如果用到箭头函数，可以简写为：\nfunction factorial(num) { return num === 1 ? num : num * factorial(--num) } console.log(factorial(3));//6 call\u0026amp;apply  call 或者 apply都是用来改变函数内部this指向，并且传递参数的\n 基本用法：\nlet a = {name: \u0026#39;jqf\u0026#39;} let b = {name: \u0026#39;fin\u0026#39;} function getName(age, gender) { return `${this.name}今年${age}岁，性别${gender}` } let call = getName.call(a, 18, \u0026#39;男\u0026#39;) let apply = getName.apply(b, [19, \u0026#39;男\u0026#39;]) console.log(call);//jqf今年18岁，性别男 console.log(apply);//fin今年19岁，性别男 说明：\n call()和apply() 传递参数的方式不一样，call 接收普通传参，apply()则接收数组形式的参数 可以用来调用公用的一些方法，避免代码的重复  bind  与call(),apply() 不同的是，bind() 改变this指向后，并不会执行函数，只用于绑定this指向，也可以像call() 那样传递参数，bind()` 调用之后会返回新的函数\n function ChangeColor(target) { this.tar = target this.colorArr = [\u0026#34;#eccc68\u0026#34;, \u0026#34;#ff4757\u0026#34;, \u0026#34;#2ed573\u0026#34;, \u0026#34;#3742fa\u0026#34;] this.run=function (){ setInterval(function (){ let i=Math.floor(Math.random()*this.colorArr.length) this.tar.style.backgroundColor=this.colorArr[i] }.bind(this)//只用来改变this指向  ,1000) } } ","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%87%BD%E6%95%B0%E7%9B%B8%E5%85%B3/","summary":"关于函数传参  当参数数量不确定时，通常用arguments 来统一接收\n function sum() { return [...arguments].reduce((res, i,) =\u0026gt; res + i, 0) } let res = sum(1, 2, 3) console.log(res);//6 可以用展开语法代替arguments接收所有的参数：\nfunction sum(...args) { return args.reduce((res, i,) =\u0026gt; res + i, 0) } let res = sum(1, 2, 3) console.log(res);//6 注意： 接收参数时展开语法放其他形参后面，做统一接收\n递归函数  递归函数有两个关键点，一是要有退出执行的条件，二是要不断执行自身并返回\n 递归思想实现阶乘函数 function factorial(num) { if (num === 1) { return num } return num * factorial(--num) } 如果用到箭头函数，可以简写为：\nfunction factorial(num) { return num === 1 ?","title":"JavaScript基础之函数相关"},{"content":"Map类型 1.属性 👉 set() 设置值\nlet map = new Map() let fn = function () {console.log(\u0026#39;this is function\u0026#39;)} let user = {name: \u0026#39;jqf\u0026#39;, age: 18} map.set(\u0026#39;key\u0026#39;, \u0026#39;value\u0026#39;) map.set(fn, \u0026#39;function\u0026#39;) map.set(user, \u0026#39;object\u0026#39;) console.log(map); /*Map(3){\u0026#34;key\u0026#34; =\u0026gt; \u0026#34;value\u0026#34;, ƒ =\u0026gt; \u0026#34;function\u0026#34;, {…} =\u0026gt; \u0026#34;object\u0026#34;}*/ 注意： Map类型，字符串、函数、对象都可以作为key设置\n👉 size Map长度\nconsole.log(map.size);//3 👉get() 获取值\nconsole.log(map.get(fn));//function  //通过key获取value 👉 delete() / clear() 删除某个，清空Map\nconsole.log(map.delete(fn));//true console.log(map); /*Map(3){\u0026#34;key\u0026#34; =\u0026gt; \u0026#34;value\u0026#34;, {…} =\u0026gt; \u0026#34;object\u0026#34;}*/ 👉 has() 是否有某个key\nconsole.log(map.has(user));//true 2.遍历 👉forEach(val,key,map)\nmap.forEach((value, key, map) =\u0026gt;{ console.log(value);//value,function,object  console.log(key);//key,Function,Object  console.log(map);//map } ) 👉for-of(ele)\nfor (const [key,value] of map) {//利用解构赋值的方式分别获取！  console.log(value);//value,function,object } 注意： 如果用个变量接收for-of遍历的值，得到\nfor (const ele of map) { console.log(ele); } /*OUTPUT: * [\u0026#34;key\u0026#34;,\u0026#34;value\u0026#34;] * [f,\u0026#34;function\u0026#34;] * [{...},\u0026#34;object\u0026#34;] * */ 3.类型转换  利用展开语法可以快速转换Map为普通数组\n console.log([...map]);//[Array(2), Array(2), Array(2)] console.log([...map.values()])//[\u0026#34;value\u0026#34;, \u0026#34;function\u0026#34;, \u0026#34;object\u0026#34;] console.log([...map.keys()]);//[\u0026#34;key\u0026#34;, ƒ, {Object}] 4.用途 利用Map保存DOM及其属性值，key=DOM,value=属性值 ！！\nWeakmap 1.属性 与Map基本一致，参考Weakset！\n注意： weakMap的key不能是字符串！\n","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8Bmap%E4%B8%8Eweakmap%E7%B1%BB%E5%9E%8B/","summary":"Map类型 1.属性 👉 set() 设置值\nlet map = new Map() let fn = function () {console.log(\u0026#39;this is function\u0026#39;)} let user = {name: \u0026#39;jqf\u0026#39;, age: 18} map.set(\u0026#39;key\u0026#39;, \u0026#39;value\u0026#39;) map.set(fn, \u0026#39;function\u0026#39;) map.set(user, \u0026#39;object\u0026#39;) console.log(map); /*Map(3){\u0026#34;key\u0026#34; =\u0026gt; \u0026#34;value\u0026#34;, ƒ =\u0026gt; \u0026#34;function\u0026#34;, {…} =\u0026gt; \u0026#34;object\u0026#34;}*/ 注意： Map类型，字符串、函数、对象都可以作为key设置\n👉 size Map长度\nconsole.log(map.size);//3 👉get() 获取值\nconsole.log(map.get(fn));//function  //通过key获取value 👉 delete() / clear() 删除某个，清空Map\nconsole.log(map.delete(fn));//true console.log(map); /*Map(3){\u0026#34;key\u0026#34; =\u0026gt; \u0026#34;value\u0026#34;, {…} =\u0026gt; \u0026#34;object\u0026#34;}*/ 👉 has() 是否有某个key\nconsole.log(map.has(user));//true 2.遍历 👉forEach(val,key,map)","title":"JavaScript基础之Map与Weakmap类型"},{"content":"Set类型 1.属性 👉 size ：数组长度\nlet set = new Set([1, 2, 3]) console.log(set.size);//9 👉 add() ：添加元素\nconsole.log(set.add(99));//返回修改后的数组Set(4) { 1, 2, 3, 99 } console.log(set);//原数组已经被修改Set(4) { 1, 2, 3, 99 } 👉 delete() ：删除元素\nconsole.log(set.delete(99));//true console.log(set);//Set(3) { 1, 2, 3 } 👉 has() ：有某个元素\nconsole.log(set.has(3)); //true 👉 clear()：清空数组\nset.clear()//返回undefined console.log(set);//Set(0) {} 2.遍历Set 👉 forEach() 方法\nlet set=new Set([\u0026#39;jqf\u0026#39;,\u0026#39;fin\u0026#39;,\u0026#39;zzh\u0026#39;]) set.forEach((value, samevalue, set) =\u0026gt; { console.log(value);//jqf,fin,zzh  console.log(samevalue);//jqf,fin,zzh  console.log(set);//Set(3) { \u0026#39;jqf\u0026#39;, \u0026#39;fin\u0026#39;, \u0026#39;zzh\u0026#39; } }) //这里回调中的参数，前两个都是一样的值， //第三个参数为遍历的Set 👉 [...set] 把Set类型转为普通数组，再遍历\n[...set].map((v) =\u0026gt; { console.log(v); }) //就可以用到遍历普通数组的多种方法 3.利用Set实现数组去重 说明： Set中不会存在重复的元素，利用这一特性，可以达到快速去重的目的\nlet arr = [1, 2, 3, 2, 1, 4, 3, 2] arr = [...new Set(arr)] console.log(arr);//[1,2,3,4] 4.交并差集的实现 👉 并集\nlet a=new Set([1,2,3]) let b=new Set([2,3,4]) let res=new Set([...a,...b]) console.log(res);//Set(4) { 1, 2, 3, 4 } 👉 交集\nres = new Set( [...a].filter(v =\u0026gt; b.has(v)) ) console.log(res);//Set(2) { 2, 3 } 👉 差集\n//a与b的差值，返回a中有b种没有的元素 res = new Set( [...a].filter(v =\u0026gt; !b.has(v)) ) console.log(res);//Set(1) { 1 } Weakset类型 1. 属性 没有size属性，其他set中的属性都有\n2. 遍历 由于没有size属性，因此是无法遍历的\n3. 赋值 注意： Weakset中只能保存对象等引用类型的数据，无法保存Number等类型\nlet b=new WeakSet([{name:\u0026#39;jqf\u0026#39;},{name:\u0026#39;fin\u0026#39;}]) console.log(b);//WeakSet {{…}, {…}}  let a=new WeakSet([1,2,3,4]) console.log(a);//Uncaught TypeError: Invalid value used in weak set 4.用途 可以专门用来保存DOM元素等\u0026hellip;\n","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8Bset%E4%B8%8Eweakset/","summary":"Set类型 1.属性 👉 size ：数组长度\nlet set = new Set([1, 2, 3]) console.log(set.size);//9 👉 add() ：添加元素\nconsole.log(set.add(99));//返回修改后的数组Set(4) { 1, 2, 3, 99 } console.log(set);//原数组已经被修改Set(4) { 1, 2, 3, 99 } 👉 delete() ：删除元素\nconsole.log(set.delete(99));//true console.log(set);//Set(3) { 1, 2, 3 } 👉 has() ：有某个元素\nconsole.log(set.has(3)); //true 👉 clear()：清空数组\nset.clear()//返回undefined console.log(set);//Set(0) {} 2.遍历Set 👉 forEach() 方法\nlet set=new Set([\u0026#39;jqf\u0026#39;,\u0026#39;fin\u0026#39;,\u0026#39;zzh\u0026#39;]) set.forEach((value, samevalue, set) =\u0026gt; { console.log(value);//jqf,fin,zzh  console.log(samevalue);//jqf,fin,zzh  console.log(set);//Set(3) { \u0026#39;jqf\u0026#39;, \u0026#39;fin\u0026#39;, \u0026#39;zzh\u0026#39; } }) //这里回调中的参数，前两个都是一样的值， //第三个参数为遍历的Set 👉 [.","title":"JavaScript基础之Set与Weakset"},{"content":" Symbol() 类型不能当做普通对象(不能加属性值)，只能当做一种永远都不会重复的字符串\n 定义 方法： Symbol(desc)、Symbol.for(desc)\nlet s1 = Symbol(\u0026#39;d\u0026#39;) let s2 = Symbol.for(\u0026#39;d\u0026#39;) let s3 = Symbol.for(\u0026#39;d\u0026#39;) console.log(s1.description);//d console.log(Symbol.keyFor(s2));//d console.log(s1 === s3);//false console.log(s2 === s3);//true 注意： 利用Symbol() 定义的值永远都不会相等，而Symbol.for() 定义的，如果描述相同，则值相等\n定义对象时，key相同会覆盖的问题 普通方式定义：\nlet user1={ name:\u0026#39;李四\u0026#39; } let user2={ name:\u0026#39;李四\u0026#39; } let grade={ [user1.name]:{C:98,E:60}, [user2.name]:{C:99,E:59} } console.log(grade);//{\u0026#39;李四\u0026#39;:{C:99,E:59}} 可以看到，后面的覆盖掉了前面的\n利用Symbol() 定义\nlet user1={ name:\u0026#39;李四\u0026#39;, key:Symbol() } let user2={ name:\u0026#39;李四\u0026#39;, key:Symbol() } let grade={ [user1.key]:{C:98,E:60}, [user2.key]:{C:99,E:59} } console.log(grade)//{[Symbol(user1)]: { C: 98, E: 60 },[Symbol(user2)]: { C: 99, E: 59 }} console.log(grade[user1.key])//{C:98,E:60} 定义在对象中的Symbol无法访问到 let symbol = Symbol(\u0026#39;desc\u0026#39;) let user = { name: \u0026#39;李四\u0026#39;, age: 18, [symbol]: 123 } for (let key in user) { console.log(key);//name,age } 普通方式无法获取Symbol类型的key\nfor (let key of Object.getOwnPropertySymbols(user)) { console.log(key);//Symbol(desc) } console.log(Object.getOwnPropertySymbols(user));//[Symbol(desc)] Object.getOwnPropertySymbols(user) 是只获取Symbol 类型\nfor (let key of Reflect.ownKeys(user)) { console.log(key);//name, age, Symbol(desc) } console.log(Reflect.ownKeys(user));//[ \u0026#39;name\u0026#39;, \u0026#39;age\u0026#39;, Symbol(desc) ] Reflect.ownKeys(user) 能获取到包括Symbol类型的所有的key\n说明： 后面这两种方法都是把key转为数组，然后用for-of 操作，而对象的遍历用for-in\n举个🌰  对象属性保护\n let pwd = Symbol(\u0026#39;This is a Symbol\u0026#39;) class User { constructor(name, password) { this.name = name this[pwd] = password } getPWD() { return `${this.name}先生/女士您好,您的密码为：${this[pwd]},请务必牢记!` } } let jqf = new User(\u0026#39;jqf\u0026#39;, \u0026#39;wrBh8w7ZDDANDyn\u0026#39;) console.log(jqf.getPWD());//jqf先生/女士您好,您的密码为：wrBh8w7ZDDANDyn,请务必牢记! for (let jqfKey in jqf) { console.log(jqfKey + \u0026#39;:\u0026#39; + jqf[jqfKey]);//name:jqf,没有获取到this.pwd } 实例化之后，利用简单粗暴的for-in是无法获取this.pwd这个值的，只好通过内置方法getPWD() 获取！\n","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8Bsymbol%E7%B1%BB%E5%9E%8B/","summary":"Symbol() 类型不能当做普通对象(不能加属性值)，只能当做一种永远都不会重复的字符串\n 定义 方法： Symbol(desc)、Symbol.for(desc)\nlet s1 = Symbol(\u0026#39;d\u0026#39;) let s2 = Symbol.for(\u0026#39;d\u0026#39;) let s3 = Symbol.for(\u0026#39;d\u0026#39;) console.log(s1.description);//d console.log(Symbol.keyFor(s2));//d console.log(s1 === s3);//false console.log(s2 === s3);//true 注意： 利用Symbol() 定义的值永远都不会相等，而Symbol.for() 定义的，如果描述相同，则值相等\n定义对象时，key相同会覆盖的问题 普通方式定义：\nlet user1={ name:\u0026#39;李四\u0026#39; } let user2={ name:\u0026#39;李四\u0026#39; } let grade={ [user1.name]:{C:98,E:60}, [user2.name]:{C:99,E:59} } console.log(grade);//{\u0026#39;李四\u0026#39;:{C:99,E:59}} 可以看到，后面的覆盖掉了前面的\n利用Symbol() 定义\nlet user1={ name:\u0026#39;李四\u0026#39;, key:Symbol() } let user2={ name:\u0026#39;李四\u0026#39;, key:Symbol() } let grade={ [user1.key]:{C:98,E:60}, [user2.key]:{C:99,E:59} } console.log(grade)//{[Symbol(user1)]: { C: 98, E: 60 },[Symbol(user2)]: { C: 99, E: 59 }} console.","title":"JavaScript基础之Symbol类型"},{"content":" 数组是经常用到的一种数据结构\n 定义方法 let a = new Array(3) let b = [1, 2, 3]  如何定义一个只有一个元素的数组？\n new Array(n)表示有n个空元素的的数组，利用Array.of(n),可以定义一个只有n元素的数组\n类型转换  利用join 可以把数组转成字符串\n let a = [1, 2, 3, 4, 5] let b = a.join(\u0026#39;\u0026#39;) console.log(b); 说明： join 的作用就是将数组中的所有元素用指定的符号连接，成为一个字符串\n 如何把DOM集合NodeList转换成普通数组形式？\n Nodelist形式无法使用.map(),reduce()等方法(能用forEach())，处理不太方便，因此需要转换成普通数组类型\nlet titles = document.querySelectorAll(\u0026#39;h1\u0026#39;); [...titles].map((v, i,titles) =\u0026gt; { console.log(v); }) titles.map()//Uncaught TypeError: titles.map is not a function 注意： 利用展开语法可以把DOM集合转换成普通数组形式\n解构赋值  是一种数组间批量赋值的方法\n let [name, age] = [\u0026#39;fin\u0026#39;, 18] console.log(name)//fin console.log(age)//18 如果用到展开语法\nlet arr = [...\u0026#39;fin\u0026#39;] console.log(arr);//[ \u0026#39;f\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;n\u0026#39; ]  let [a, ...b] = [1, \u0026#39;fin\u0026#39;, 2, 3] console.log(a, b);//1 [ \u0026#39;fin\u0026#39;, 2, 3 ] 注意：\n...在变量(前)位置，表示 “合并，聚拢”\n...在值位置(后)，表示 “分散，拆解”\n关于数组的一些方法 1.截取数组 方法： slice(from,to) \u0026amp; splice(from,len)\n区别：\n slice()只是截取并返回一部分，原数组不会改变 splice() 为截取并返回部分，原数组变为截取后剩余部分  let arr = [1, 2, 3, 4, 5] console.log(arr.slice(0, 2));//[1,2] console.log(arr);//[1,2,3,4,5]  console.log(arr.splice(2, 2));//[3,4] console.log(arr);//[1,2,5] 2.元素添加与移除 方法： unshift() \u0026amp; shift() 与 push() \u0026amp; pop()\n区别：\n   方向/方式 添加 移除     前面 unshift() shift()   后面 push() pop()     如何在中间某个位置插入？\n let arr = [1, 2, 3, 4, 5] arr.splice(2, 0, \u0026#39;fin\u0026#39;) console.log(arr);//[ 1, 2, \u0026#39;fin\u0026#39;, 3, 4, 5 ] 说明：\narr.splice(2, 0, 'fin')表示在原数组中截去2开始0长度的一段，然后用'fin'替换，相当于在原数组索引为2的位置插入fin 。\n/** * 指定数组中的某个元素移动到指定位置 * @param{Array}arr * @param{Number}from * @param{Number}to * @returns {*} */ function move(arr, from, to) { if (from \u0026lt; 0 || to \u0026gt; arr.length) { console.error(\u0026#39;参数错误\u0026#39;) return } let moveItem = arr.splice(from, 1)//得到的是一个元素的数组形式、  arr.splice(to, 0, ...moveItem) return arr } let arr = [1, 2, \u0026#39;fin\u0026#39;, 3, 4] move(arr, 2, 4) console.log(arr);//[ 1, 2, 3, 4, \u0026#39;fin\u0026#39; ] 3.清空数组 方法：\nlet arr = [1, 2, 3, 4] arr=[] console.log(arr);//[] let arr = [1, 2, 3, 4] arr.length=0 console.log(arr);//[] 区别：\narr=[]只是改变了arr的指针指向；arr.length = 0是真正意义上的清空\n4.查找操作  关于indexof() lastInsexof() includes() 三个方法，可以看前一篇文章,GO\n 补充方法： find() \u0026amp; findIndex()\nlet arr = [1, 2, 3, 4] let a = arr.find((v, i, o) =\u0026gt; { return v === 3 }) console.log(a);//3  arr = [ {name: \u0026#39;jqf\u0026#39;, age: \u0026#39;18\u0026#39;}, {name: \u0026#39;fin\u0026#39;, age: \u0026#39;19\u0026#39;} ] let res = arr.find((value) =\u0026gt; { return value.name = \u0026#39;jqf\u0026#39; }) console.log(res);//{name: \u0026#39;jqf\u0026#39;, age: \u0026#39;18\u0026#39;} 说明： find() 方法更适用于查找对象等引用类型组成的数组，这样更具有优势\n注意：includes()方法查找对象等引用类型时会出错\n５.排序方法 方法： sort()\n使用：\nlet arr = [1, 8, 3, 9, 6, 5, 2, 7, 0] arr.sort((a, b) =\u0026gt; a - b) console.log(arr);//[0, 1, 2, 3, 5, 6, 7, 8, 9]  let col = [\u0026#39;a\u0026#39;, \u0026#39;c\u0026#39;, \u0026#39;b\u0026#39;] col.sort() console.log(col);//[\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;] 说明： 如果调用该方法时没有使用参数，将按字母顺序对数组中的元素进行排序，说得更精确点，是按照字符编码的顺序进行排序。\n6.循环遍历数组 方法： for-of、for-in、forEach()、map()、reduce()、filter()、every()、some().\n说明：\n👉for-of\nlet arr = [ {name: \u0026#34;jqf\u0026#34;, age: 18, gender: \u0026#39;male\u0026#39;}, {name: \u0026#39;zzh\u0026#39;, age: 20, gender: \u0026#39;female\u0026#39;}, {name: \u0026#34;fin\u0026#34;, age: 19, gender: \u0026#39;male\u0026#39;} ] for (const val of arr) { console.log(val);//直接获得arr中的值 } 👉for-in\nfor (const eleKey in arr[0]) { console.log(eleKey);//name, age, gender } 注意： for-in  获得的是对象中的 \u0026ldquo;key\u0026rdquo; ，无法直接对数组使用\n👉forEach()\narr.forEach((v, i, a) =\u0026gt; { console.log(v); }) 👉map()\narr.map((v, i, a) =\u0026gt; { console.log(v); }) 👉reduce()❗\nlet res = arr.reduce((res, v, i, a) =\u0026gt; { v.intro = \u0026#39;ohhhh\u0026#39; return res }, arr) console.log(res); 注意： reduce 方法会改变初始数组，接收五个参数\nreduce(callbackfn(total,currentValue,currentIndex,array)=\u0026gt;{ //...do something },initialValue) total 用来接收initialvalue 初始值，并且记录最终返回值\n👉filter()\nres = arr.filter((v, i, a) =\u0026gt; { return v.gender === \u0026#39;male\u0026#39; }) console.log(res);//{name: \u0026#34;jqf\u0026#34;, age: 18, gender: \u0026#39;male\u0026#39;},{name: \u0026#34;fin\u0026#34;, age: 19, gender: \u0026#39;male\u0026#39;} 注意： 顾名思义，filter 用来批量过滤数组，最终返回处理后的数组\n👉every()\nlet res = arr.every((v, i, a) =\u0026gt; { console.log(i);//0,1  return v.gender === \u0026#39;male\u0026#39; }) console.log(res ? \u0026#39;全部是male\u0026#39; : \u0026#39;有female\u0026#39;)//有female 注意： every() 只有当数组中的所有元素都满足return 后的函数时，整个结果才为true ，否则为false\n👉some()\nres = arr.some((v, i, a) =\u0026gt; { console.log(i);//0  return v.gender = \u0026#39;male\u0026#39; }) console.log(res ? \u0026#39;有male\u0026#39; : \u0026#39;全部是female\u0026#39;)//有male 注意： some() 与every 相对应，表示只要有满足return 后的函数的值，就立即返回true\n👉 另外\nmap()、reduce()、filter()、every()、some() 后面都可以跟一个thisArg的参数，表示在该作用域在this的指向！\narr.map(() =\u0026gt; { console.log(this)//Window(),Window(),Window() },window) skr~skr~~\n","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8B%E6%95%B0%E7%BB%84%E6%B7%B1%E5%80%94/","summary":"数组是经常用到的一种数据结构\n 定义方法 let a = new Array(3) let b = [1, 2, 3]  如何定义一个只有一个元素的数组？\n new Array(n)表示有n个空元素的的数组，利用Array.of(n),可以定义一个只有n元素的数组\n类型转换  利用join 可以把数组转成字符串\n let a = [1, 2, 3, 4, 5] let b = a.join(\u0026#39;\u0026#39;) console.log(b); 说明： join 的作用就是将数组中的所有元素用指定的符号连接，成为一个字符串\n 如何把DOM集合NodeList转换成普通数组形式？\n Nodelist形式无法使用.map(),reduce()等方法(能用forEach())，处理不太方便，因此需要转换成普通数组类型\nlet titles = document.querySelectorAll(\u0026#39;h1\u0026#39;); [...titles].map((v, i,titles) =\u0026gt; { console.log(v); }) titles.map()//Uncaught TypeError: titles.map is not a function 注意： 利用展开语法可以把DOM集合转换成普通数组形式\n解构赋值  是一种数组间批量赋值的方法\n let [name, age] = [\u0026#39;fin\u0026#39;, 18] console.","title":"JavaScript基础之数组深倔"},{"content":"字符串 1.截取字符串  主要有slice()、substr()、substring() 三种方法\n  一个参数[i]  都是从i开始截取，返回n及其后面的全部字符串。\n 第二个参数[i,n]  slice(i,n)和substring(i,n)表示从i开始截取到n；substr(i,n)，表示从i开始截取n个\n 负参数  slice()和substr()将最后一位记为-1，然后依次编号；substring()则会将任何负参数变为0，然后查找\nlet w = \u0026#39;fintinger.xyz\u0026#39; let a = w.slice(-4, w.length) let b = w.substring(-1, 2) let c = w.substr(-4, 4) console.log(a, b, c);//.xyz fi .xyz 2.查找字符串  主要有indexof()、includes()、lastIndexof() 三种方法\n 共同点：\n 两个参数[searchString,position]，第一个为要检索的字符串，第二个为开始检索位置； 若position \u0026lt; 0，则position = 0 position可以省略  不同之处：\n lastIndexof()为从右往左找，找到第一个立即返回索引！其他两个都是从左往右 indexof(),lastIndexof()找不到则返回-1，includes()找不到返回false position 省略之后，includes(),indexof()默认值为0，lastIndexof()则为str.length  3.重复函数 repeat() 作用： 复制粘贴指定字符串指定次数\n案例：\n/** * 电话号码模糊处理 * @param{String|Number}number * @param{Number}len * @returns {Error|string} * @constructor */ function Phone(number, len = 4) { number = String(number) return number.length \u0026lt; len ? new Error(\u0026#39;参数错误\u0026#39;) : number.slice(0, -len) + \u0026#39;*\u0026#39;.repeat(len) } Phone(6995498, 4);//699**** 4.去空格函数trim() 作用： trim()方法用于删除字符串的头尾空格。trim() 方法不会改变原始字符串。\n案例： 输入密码时限制输入空格\nlet inp = document.querySelector(\u0026#39;input\u0026#39;) let tip = document.querySelector(\u0026#39;#tip\u0026#39;) inp.addEventListener(\u0026#39;keyup\u0026#39;, function () { tip.innerHTML = \u0026#39;\u0026#39; this.value = this.value.trim()//无法输入空格，动态监听！！  if (this.value.length \u0026lt; 6 || this.value.length \u0026gt; 12) { tip.innerHTML = \u0026#39;密码长度为6-11位\u0026#39; } }) 5.获取字符串某个位置的值函数 charAt() 或 索引 let w = \u0026#39;fintinger\u0026#39; console.log(w.charAt(3));//t console.log(w[3]);//t 区别： 如果找不到，charAt()返回空字符串，索引则返回undefined\n布尔类型  对于其他类型，分以下两种：\nundefined,null,0  都相当于false\n\u0026gt;=1的数值,字符串等  都相当于true\n 注意： 凡是\u0026gt;1的数都为true，但≠true，true=1\nconsole.log(true == 1);//true 类型转换： 利用!!可以转为布尔类型\nconsole.log(!!1);//true console.log(!!0);//false 数值类型 1.判断是否为整数函数 isTnteger() 使用：\nNumber.isInteger(2.1)//fasle  也可以利用parseInt()判断\n let num=2 console.log(Number.isInteger(num));//true console.log(parseInt(num) === num);//true 2.Math()对象  Math为内置对象，包含数学上的多种方法\n 取最大/最小值 max()/min()：\n/*依次传入形式*/ let b = Math.max(1, 2, 4, 6) console.log(b);//6  /*数组形式*/ let c = [1, 2, 4, 6] b = Math.max.apply(null, c) console.log(b);//6 说明： 利用apply 传递数组参数，call 不能传递数组参数。另外，利用展开语法(Spread syntax)\nb=Math.max(...c) console.log(b)//6 3.随机数  利用Math.random()可以取到 [0,1) 之间的随机数\n 注意： 假设取 0~3 之间的随机数，利用Math.round(Math.random()*3) 取到的数：\n   范 围 取 到 的 值 概 率     [ 0, 0.5 ) 0 1 / 6   [ 0.5, 1.5 ) 1 1 / 3   [ 1.5, 2.5 ) 2 1 / 3   [ 2.5, 3 ) 3 1 / 6    用到Math.floor() 向下取整，Math.floor(Math.random * (3+1) )：\n   范 围 取 到 的 值 概 率     [ 0, 1) 0 1 / 4   [ 1, 2 ) 1 1 / 4   [ 2, 3 ) 2 1 / 4   [ 3, 4 ) 3 1 / 4    因此总结出一般规律：\n取 0~n 之间的随机整数：\nMath.floor(Math.random() * (n + 1)) 取 m~n 之间的随机整数：\nMath.floor(m + Math.random() * (n - m + 1)) 4.时间  Date() 为内置对象，处理与时间相关的问题\n 1.类型转换 标准时间 =\u0026gt; 时间戳：\nconst date = new Date(\u0026#39;2001-04-02 12:43:56\u0026#39;) console.log(date * 1); //986186636000 console.log(Number(date)); //986186636000 console.log(date.valueOf()); //986186636000 console.log(date.getTime()); //986186636000 时间戳 =\u0026gt; 标准时间：\nlet timeStamp = 986186636000 console.log(new Date(timeStamp));//2001-04-02T04:43:56.000Z 2.格式化时间 利用内置属性，可以封装一个简单的时间格式化函数\n/** * 格式化时间，时间戳 * @param{Date|Number}date * @param{String}format * @returns {string} */ function dateFormat(date, format = \u0026#34;YYYY-MM-DD HH:mm:SS\u0026#34;) { date = new Date(date) const config = { YYYY: date.getFullYear(), MM: date.getMonth() + 1, DD: date.getDate(), HH: date.getHours(), mm: date.getMinutes(), SS: date.getSeconds(), } for (let key in config) { //补 \u0026#34;0\u0026#34; 操作  config[key] = String(config[key]).length \u0026lt; 2 ? \u0026#39;0\u0026#39; + config[key] : config[key] format = format.replace(key, config[key]) } return format } dateFormat(Date.now());//2020-08-23 13:05:03 注意：\n getMonth()返回的月份是从0开始的，需要+1 获取日期是getDate(),而不是getDay()   另外，可以利用第三方库达到需要的效果，比如Moment.js\n skr skr~~~\n","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%80%BC%E7%B1%BB%E5%9E%8B/","summary":"字符串 1.截取字符串  主要有slice()、substr()、substring() 三种方法\n  一个参数[i]  都是从i开始截取，返回n及其后面的全部字符串。\n 第二个参数[i,n]  slice(i,n)和substring(i,n)表示从i开始截取到n；substr(i,n)，表示从i开始截取n个\n 负参数  slice()和substr()将最后一位记为-1，然后依次编号；substring()则会将任何负参数变为0，然后查找\nlet w = \u0026#39;fintinger.xyz\u0026#39; let a = w.slice(-4, w.length) let b = w.substring(-1, 2) let c = w.substr(-4, 4) console.log(a, b, c);//.xyz fi .xyz 2.查找字符串  主要有indexof()、includes()、lastIndexof() 三种方法\n 共同点：\n 两个参数[searchString,position]，第一个为要检索的字符串，第二个为开始检索位置； 若position \u0026lt; 0，则position = 0 position可以省略  不同之处：\n lastIndexof()为从右往左找，找到第一个立即返回索引！其他两个都是从左往右 indexof(),lastIndexof()找不到则返回-1，includes()找不到返回false position 省略之后，includes(),indexof()默认值为0，lastIndexof()则为str.length  3.重复函数 repeat() 作用： 复制粘贴指定字符串指定次数\n案例：\n/** * 电话号码模糊处理 * @param{String|Number}number * @param{Number}len * @returns {Error|string} * @constructor */ function Phone(number, len = 4) { number = String(number) return number.","title":"JavaScript基础之值类型"},{"content":" JavaScript中最容易忽视的一些点\n let和var  都用来定义变量，但二者之间有些细微的差别\n 区别1: 是否有块作用域 var a = 1 { // var a=2  let a = 2 } console.log(a); //output var:a=2 let:a=1 var i=1 // for (var i = 0; i \u0026lt;3; i++) { for (let i = 0; i \u0026lt;3; i++) { //..do something } console.log(i); //output var:i=3 let:i=1 说明： 可以看出，var并没有作用域的概念，定义在特定作用于内的变量会受到作用域外的影响，let则不会\n区别2：全局污染问题 console.log(window.screenLeft);//600 // var screenLeft = 1 let screenLeft = 1 console.log(window.screenLeft); //output var:1 let:600 说明： var定义的变量用window对象也可以访问，当定义一些特定的key时，会造成全局的污染!\n区别3：重复声明的问题 var a=1 var a=2 console.log(a);//2 let b=1 let b=2 console.log(b);//SyntaxError: Identifier \u0026#39;b\u0026#39; has already been declared 说明： var重复声明变量不会报错，而let 或者 const则会报错\n变量冻结  利用Object的方法 freeze(obj) 可以冻结对象，无法修改\n const HOST = { url: \u0026#39;https://www.fintinger.xyz\u0026#39;, port: 8000, } HOST.port = 9000 console.log(HOST);//port:9000  Object.freeze(HOST) HOST.port = 3000 console.log(HOST);//port:9000 说明： 可以看到，利用Object.freeze(HOST) 冻结HOST 对象之后，其中的参数已经无法修改\n传值\u0026amp;传址  传址和传值在内存中的表现形式是不一样的。传值会重新开辟空间储存；而传址只是让地址指向，而不会重新开辟空间\n let a = 1 let b = a a = 2 console.log(a, b);//2,1 let obj1 = { name: \u0026#39;jqf\u0026#39;, age: 19 } let obj2 = obj1 obj2.name = \u0026#39;fin\u0026#39; console.log(obj1, obj2) //{ name: \u0026#39;fin\u0026#39;, age: 19 } { name: \u0026#39;fin\u0026#39;, age: 19 } 说明：\n 第一段代码中，b和a分别指向不同的地址，只是值都是1，修改a并不会影响到b 第二段代码中，b和a指向相同地址，修改b就同样影响了a  图示：\n\n\n严格模式  使用严格模式会使代码的严谨性更好，适用更广，不易出错\n 使用： 在指定位置加上\u0026quot;use strict\u0026quot;\n作用域： 指定位置及以下作用域\na = 2 function tes() { \u0026#34;use strict\u0026#34; b = 2 console.log(b); } console.log(a);//2 tes()//ReferenceError: b is not defined ","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8Bjs%E9%BB%91%E6%B4%9E/","summary":"JavaScript中最容易忽视的一些点\n let和var  都用来定义变量，但二者之间有些细微的差别\n 区别1: 是否有块作用域 var a = 1 { // var a=2  let a = 2 } console.log(a); //output var:a=2 let:a=1 var i=1 // for (var i = 0; i \u0026lt;3; i++) { for (let i = 0; i \u0026lt;3; i++) { //..do something } console.log(i); //output var:i=3 let:i=1 说明： 可以看出，var并没有作用域的概念，定义在特定作用于内的变量会受到作用域外的影响，let则不会\n区别2：全局污染问题 console.log(window.screenLeft);//600 // var screenLeft = 1 let screenLeft = 1 console.log(window.screenLeft); //output var:1 let:600 说明： var定义的变量用window对象也可以访问，当定义一些特定的key时，会造成全局的污染!","title":"JavaScript基础之JS黑洞"},{"content":"++的前置与后置 let a1 = 1 let b1 = 2 let c1 = b1 + a1++ console.log(a1, b1, c1);//2,2,3  let a2 = 1 let b2 = 2 let c2 = b2 + ++a2 console.log(a2, b2, c2);//2,2,4 说明：\n 后置是先计算再++ 前置是先++再计算  短路运算 let a = 0; let b = 1; let c = a || b; console.log(c);//1 利用这个原理，我们可以简化一些函数\nlet sex = prompt(\u0026#39;您的性别?\u0026#39;) if (!sex){ sex=\u0026#34;保密\u0026#34; } console.log(sex); =\u0026gt;\nlet sex = prompt(\u0026#39;您的性别?\u0026#39;) || \u0026#34;保密\u0026#34; console.log(sex); 再比如:\n/*function judge(num) { if (num % 2 === 0) { return \u0026#34;偶数\u0026#34; } else { return \u0026#34;奇数\u0026#34; } }*/ function judge(num) { return num % 2 === 0 \u0026amp;\u0026amp; \u0026#34;偶数\u0026#34; || \u0026#34;奇数\u0026#34; //return num % 2 === 0 ? \u0026#34;偶数\u0026#34; : \u0026#34;奇数\u0026#34; } console.log(judge(2));//偶数 说明： 可以用来简化if条件判断语句，具体如下面这段伪代码\nif (flag) { //...do something } else { //...do something } 简化为 =\u0026gt;\nflag \u0026amp;\u0026amp; {...do someting} || {...do someting} ","permalink":"https://blog.archai.space/p/javascript%E5%9F%BA%E7%A1%80%E4%B9%8B%E8%BF%90%E7%AE%97%E7%AC%A6/","summary":"++的前置与后置 let a1 = 1 let b1 = 2 let c1 = b1 + a1++ console.log(a1, b1, c1);//2,2,3  let a2 = 1 let b2 = 2 let c2 = b2 + ++a2 console.log(a2, b2, c2);//2,2,4 说明：\n 后置是先计算再++ 前置是先++再计算  短路运算 let a = 0; let b = 1; let c = a || b; console.log(c);//1 利用这个原理，我们可以简化一些函数\nlet sex = prompt(\u0026#39;您的性别?\u0026#39;) if (!sex){ sex=\u0026#34;保密\u0026#34; } console.log(sex); =\u0026gt;\nlet sex = prompt(\u0026#39;您的性别?","title":"JavaScript基础之运算符"},{"content":"Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。其目的主要是解决以往回调中嵌套回调的\u0026quot;嵌套地狱\u0026quot;问题，使代码可读性更好，更美观!\n基本用法 对于一个标准的Prommise，其基本写法为:\nnew Promise(function (resolve, reject) { //do something...  //success  resolve(\u0026#39;success\u0026#39;) //fail \u0026amp; reject  // reject(\u0026#39;rejected\u0026#39;) *resolve和reject只能出现一个 }).then( function (value) { //if succeed,do something...  }, function (reason) { //if fail \u0026amp; reject,do something...  } ) 如果采用ES6的箭头函数写法，则为:\nnew Promise((resolve, reject) =\u0026gt; { resolve(\u0026#39;success\u0026#39;) //reject(\u0026#39;rejected\u0026#39;) }).then( value =\u0026gt; {}, reason =\u0026gt; {} )  始终牢记，前一个Promise()必须是在后面一个.then()中处理，\n如果前一个Promsie()中没有改变状态，即没有resolve()/reject()方法，后面的.then就不会针对这个Promsie()处理\n Promise错误处理  Promise()中的错误处理有两种方式,.then()和.catch()\n .then()  特点： \u0026ldquo;一一对应\u0026rdquo;，即一个then()对应处理上一个Promise 用法：  new Promise((resolve, reject) =\u0026gt; { // 12+p  // throw new Error(\u0026#39;fail\u0026#39;)  reject(\u0026#39;失败\u0026#39;) }).then(null, reason =\u0026gt; { // console.log(reason.message);  console.log(reason); }) /*output: p is not defined fail 失败 */  因为是reject()或者失败状态，因此,.then()中的value没有意义，直接把value =\u0026gt; {}用null代替\n .catch()  特点： \u0026ldquo;总览全局,全包\u0026rdquo;，及最后调用.catch()的位置以上所有的Promise中的错误都由.catch()捕获 用法：  new Promise((resolve, reject) =\u0026gt; { resolve(\u0026#39;success\u0026#39;) }).then(v =\u0026gt; { console.log(v); return new Promise((resolve, reject) =\u0026gt; { 1 + b resolve(\u0026#39;success-2\u0026#39;) }) } ).then(v =\u0026gt; console.log(v)) .catch(error =\u0026gt; console.log(error.message)) /*output: success b is not defined */\t 在.then()中返回一个Promise，后面的.then()就是对这个Promise的处理，因为返回的Promise中发生了b is not defined的错误，因此被.catch()捕获，后面的resolve()不再执行。\n finally()  无论resolve还是reject，.finally()最后都会执行\n new Promise((resolve, reject) =\u0026gt; { //resolve(\u0026#39;请求成功\u0026#39;)  reject(\u0026#39;请求失败\u0026#39;) }).then( value =\u0026gt; { console.log(value); }, reason =\u0026gt; { console.log(reason); } ).finally(() =\u0026gt; { //do something.... }) all()  所有的Promise都会执行的部分可以写到.all()中，用一个Promise集合promises(自定义)调用\n const promises = [\u0026#39;fin\u0026#39;, \u0026#39;jqf\u0026#39;].map(name =\u0026gt; { return ajax(\u0026#39;http://127.0.0.1:3000/users/\u0026#39; + name) }) console.log(promises);//(2) [Promise, Promise] Promise.all(promises).then(user=\u0026gt;console.log(user))//(2) [{…}, {…}] 使用： 可以用来批量获取数据，利用map数组方法\n/** * 提供一个username数组，就会依次返回需要的数据 * @param{Array}names * @returns {Promise\u0026lt;unknown[]\u0026gt;} */ function search(names) { const promises = names.map(name =\u0026gt; { return ajax(\u0026#39;http://127.0.0.1:3000/users?username=\u0026#39; + name) }) return Promise.all(promises) } /*******使用*******/ search(names).then(res =\u0026gt; { //...do something }) race()  与all()类似，接收一个可迭代的参数——Promise集合等，哪个先返回状态(resolve\u0026amp;reject)就执行哪个，其他的不予执行。\n const p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 300, \u0026#39;p1 done\u0026#39;) }); const p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 50, \u0026#39;p2 done\u0026#39;); }); const p3 = new Promise(function (resolve, reject) { setTimeout(reject, 100, \u0026#39;p3 rejected\u0026#39;); }); Promise.race([p1, p2, p3]).then(function(data) { // 显然p2更快，所以状态变成了fulfilled  // 如果p3更快，那么状态就会变成rejected  console.log(data); // p2 doned }).catch(function(err) { console.log(err); // 不执行 }); 使用Promise封装一些函数  使用Promise封装一些常用的方法，更加扁平，完美！\n 原生Ajax封装 function ajax(url) { return new Promise((reslove, reject) =\u0026gt; { let x = new XMLHttpRequest() x.open(\u0026#34;GET\u0026#34;, url) x.send() x.onreadystatechange = function () { if (x.readyState === 4) { if (x.status \u0026gt;= 200 \u0026amp;\u0026amp; x.status \u0026lt; 300) { reslove(JSON.parse(x.response)) }else { reject(\u0026#34;加载失败\u0026#34;) } } } x.onerror=function (){ reject(\u0026#34;其它错误\u0026#34;) } }) } 使用：\najax(url).then(res =\u0026gt; { //...do something }) Promise队列  利用Promise可以实现前一个任务完成之后再完成后一个任务这样的任务队列顺序\n 基本用法 let promise = Promise.resolve(\u0026#34;1\u0026#34;) promise.then(v =\u0026gt; { //返回一个新的Promise，  // 后面的.then就是针对这个Promise的  // 因此会等1s第一个.then()执行完毕后再执行后面的.then()  return new Promise(resolve =\u0026gt; { setTimeout(() =\u0026gt; { console.log(v); resolve(\u0026#34;2\u0026#34;) }, 1000) }) }).then(v =\u0026gt; { setTimeout(() =\u0026gt; { console.log(v); }, 1000) })  Promise.resolve()直接返回一个成功状态的Promise，同理，Promise.reject()则返回一个拒绝状态的Promise.。\n 利用map()方法  map() 方法返回一个新数组，数组中的元素为原始数组元素调用函数处理后的值。map() 方法按照原始数组元素顺序依次处理元素。GO\n function queue(arr) { //定义一个初始完成状态的Promise  let promise = Promise.resolve() arr.map(v =\u0026gt; { //不断更新promise的值  //使之永远是上一次执行的Promise  promise = promise.then(value =\u0026gt; { return new Promise(resolve =\u0026gt; { setTimeout(() =\u0026gt; { // console.log(v);  v() resolve() }, 1000) }) }) }) } 说明：\n v代表arr数组中的value，可以使函数组成arr 关键是利用promise接收上一个执行的Promise()，不断更新就能做到循环期间不断循环  使用：\nfunction f1(){ console.log(\u0026#39;my name is jqf,\u0026#39;); } function f3(){ console.log(\u0026#39;ohhhhhhhh.\u0026#39;); } function f2(){ console.log(\u0026#34;i\u0026#39;m 19.\u0026#34;); } queue([f1,f2,f3]) //my name is jqf,i\u0026#39;m 19. ohhhhhhhh. 利用reduce()方法  reduce()是数组的方法，作用是首先有一个最后的返回值，这个值给定初始值，然后利用数组遍历的当前值，索引最终返回这个最后的返回值，GO\n //reduce()为数组中的方法 //\u0026#34;_\u0026#34;是占位的，因为这里的参数没有意义，用\u0026#34;_\u0026#34;占位即可 function queue(arr) { arr.reduce((promise, num) =\u0026gt; { return promise.then(_ =\u0026gt; { return new Promise(resolve =\u0026gt; { setTimeout(_ =\u0026gt; { console.log(num); resolve() }, 1000) }) }) }, Promise.resolve()) } 说明：\n reduce的参数  array.reduce(function(total, currentValue, currentIndex, arr), initialValue) ","permalink":"https://blog.archai.space/p/es6%E4%B9%8Bpromise%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/","summary":"Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。其目的主要是解决以往回调中嵌套回调的\u0026quot;嵌套地狱\u0026quot;问题，使代码可读性更好，更美观!\n基本用法 对于一个标准的Prommise，其基本写法为:\nnew Promise(function (resolve, reject) { //do something...  //success  resolve(\u0026#39;success\u0026#39;) //fail \u0026amp; reject  // reject(\u0026#39;rejected\u0026#39;) *resolve和reject只能出现一个 }).then( function (value) { //if succeed,do something...  }, function (reason) { //if fail \u0026amp; reject,do something...  } ) 如果采用ES6的箭头函数写法，则为:\nnew Promise((resolve, reject) =\u0026gt; { resolve(\u0026#39;success\u0026#39;) //reject(\u0026#39;rejected\u0026#39;) }).then( value =\u0026gt; {}, reason =\u0026gt; {} )  始终牢记，前一个Promise()必须是在后面一个.then()中处理，\n如果前一个Promsie()中没有改变状态，即没有resolve()/reject()方法，后面的.then就不会针对这个Promsie()处理\n Promise错误处理  Promise()中的错误处理有两种方式,.then()和.catch()\n .then()  特点： \u0026ldquo;一一对应\u0026rdquo;，即一个then()对应处理上一个Promise 用法：  new Promise((resolve, reject) =\u0026gt; { // 12+p  // throw new Error(\u0026#39;fail\u0026#39;)  reject(\u0026#39;失败\u0026#39;) }).","title":"ES6之Promise用法小结"},{"content":"\n","permalink":"https://blog.archai.space/p/%E7%BA%A2%E8%AD%A6%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E6%94%B6%E5%BD%95%E4%B8%AD/","summary":"","title":"红警常见问题(收录中……)"},{"content":" 在发送某一次请求时，如果不对请求做去重处理那么，同一个请求有可能会发送很多次，一个个慢慢响应，这会很大程度影响性能以及页面的使用体验\n 全局变量\n/** * baseURL 是每次请求的地址 * x 表示XMLHttpRequest实例，初始为null * isSending 是用来判断请求是否处于发送状态的标识 */ const baseURL = \u0026#34;http://127.0.0.1:8000/delay\u0026#34; let x = null, isSending = false 原生请求 if (isSending) x.abort() x = new XMLHttpRequest() //正处于发送状态，修改标识 isSending = true x.open(\u0026#34;GET\u0026#34;, baseURL) x.send() x.onreadystatechange = function () { if (x.readyState === 4) {//当readyState=4,表示已经发送完毕，修改标识，是否成功返回则无需关心  isSending = false if (x.status \u0026gt;= 200 \u0026amp;\u0026amp; x.status \u0026lt; 300) { console.log(x.response); } } } jQuery 请求  jQuery做ajax请求时，可以仅针对一次请求，也可以利用jQuery全局事件对所有请求加以限制\n 仅本次请求 $.ajax({ url: baseURL, type: \u0026#34;GET\u0026#34;, beforeSend: function (jqxhr, settings) { if (isSending) x.abort() x = jqxhr isSending = true }, success: data =\u0026gt; { isSending = false console.log(data) } complete: function () { /*isSending = false*/ }, }) 说明：\n beforeSend中将XMLHttpRequest 对象jqxhr和设置settings作为参数传递给回调函数。 修改isSending = false在complete或者success中j均可。  complete[XHR,TS]参数为XMLHttpRequest对象和说明请求状态的字符串textStatus，有（\u0026quot;success\u0026quot;，\u0026quot;notmodified\u0026quot;，\u0026quot;nocontent\u0026quot;，\u0026quot;error\u0026quot;，\u0026quot;timeout\u0026quot;，\u0026quot;abort\u0026quot;，或\u0026quot;parsererror\u0026quot;） success[data, TS, XHR]参数为响应体data，textstatus，和XMLHttpRequest对象 complete在success之前执行    全局请求 $(document).ajaxSend(function (event,jqXHR,ajaxOptions) { if (isSending) x.abort() x = jqXHR isSending = true }).ajaxComplete(function () { isSending = false }); 说明：\n 要作此限制，要求在所有的jQuery请求中设置global = true，当然，这是个默认值，所以不单独设置即为接受全局事件，如果不希望某个请求生效全局事件，设置global = false即可。 全局事件永远不会针对跨域脚本或JSONP请求触发，无论其global值如何。 从jQuery 1.9开始，所有jQuery全局Ajax事件的处理程序都必须附加到document  jQuery Ajax各个事件的执行顺序  来自kelelipeng\n \naxios请求 const CancelToken = axios.CancelToken; let cancel, isSending = false; //请求拦截，即请求之前do something.... axios.interceptors.request.use(function (config) { if (isSending) cancel(\u0026#34;Do not submit requests multiple times\u0026#34;) isSending = true return config }) //响应拦截，即请求之后do something.... axios.interceptors.response.use(function (res) { isSending = false return res }) axios.defaults.baseURL = \u0026#34;http://127.0.0.1:8000\u0026#34; //开始请求 axios({ url:\u0026#39;/delay\u0026#39;, method:\u0026#34;POST\u0026#34;, cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数  cancel = c; }) }).then(res =\u0026gt; { //do something.... }).catch(err =\u0026gt; { //err.message为前面cancel()携带的提示信息  console.error(err.message); }) 说明：\n 请求拦截时，如果运行到cancel(),后续会被.catch(err)捕获，可在catch(err)中做一些处理，cancel()中携带的提示信息会被catch(err)捕捉，储存在err.message中 这里利用到的cancelToken比较晦涩，具体研读  ","permalink":"https://blog.archai.space/p/ajax%E9%87%8D%E5%A4%8D%E8%AF%B7%E6%B1%82/","summary":"在发送某一次请求时，如果不对请求做去重处理那么，同一个请求有可能会发送很多次，一个个慢慢响应，这会很大程度影响性能以及页面的使用体验\n 全局变量\n/** * baseURL 是每次请求的地址 * x 表示XMLHttpRequest实例，初始为null * isSending 是用来判断请求是否处于发送状态的标识 */ const baseURL = \u0026#34;http://127.0.0.1:8000/delay\u0026#34; let x = null, isSending = false 原生请求 if (isSending) x.abort() x = new XMLHttpRequest() //正处于发送状态，修改标识 isSending = true x.open(\u0026#34;GET\u0026#34;, baseURL) x.send() x.onreadystatechange = function () { if (x.readyState === 4) {//当readyState=4,表示已经发送完毕，修改标识，是否成功返回则无需关心  isSending = false if (x.status \u0026gt;= 200 \u0026amp;\u0026amp; x.status \u0026lt; 300) { console.log(x.response); } } } jQuery 请求  jQuery做ajax请求时，可以仅针对一次请求，也可以利用jQuery全局事件对所有请求加以限制","title":"Ajax重复请求"},{"content":"JSONP  jsonp跨域的实现仅限于GET请求，不可用于POST\n 说明：实现的基本思路是利用html中script标签本身可跨域的特性，在发送请求时，在页面中创建script标签，追加到页面中。这实际上就像利用script标签引入外部资源\n/*main.js*/ //申明handle函数 function handle(data) { //do something.... } ele.onclick = function () { //1.创建script标签  const script = document.createElement(\u0026#34;script\u0026#34;) //2.修改script的src属性  script.src = \u0026#34;http://127.0.0.1:8000/jsonP\u0026#34; script.id=\u0026#34;tempScript\u0026#34;//添加id方便移除  //3.追加到页面中  document.body.appendChild(script) } /*server.js*/ app.get(\u0026#39;/jsonP\u0026#39;, (request, response) =\u0026gt; { const data = { exist:1, msg:\u0026#34;用户名已经存在!\u0026#34; } let str = JSON.stringify(data) response.send(` handle(${str}); document.body.removeChild(document.querySelector(\u0026#34;#tempScript\u0026#34;)) `) }) 注意：response.send()/response.end()中利用ES6语法规范中的模板字符串直接返回一段js代码，script标签会自动解析并作用到页面上\n\nCORS 如果要实现跨域，官方的解决方案是**CORS**，即通过设置CORS响应头实现跨域，这种跨域GET或POST请求均有效\n//服务端设置响应头 app.all(\u0026#39;/data\u0026#39;, (request, response) =\u0026gt; { response.setHeader(\u0026#39;Access-Control-Allow-Origin\u0026#39;, \u0026#39;*\u0026#39;)//允许来自所有域的请求  response.setHeader(\u0026#39;Access-Control-Allow-Headers\u0026#39;, \u0026#39;*\u0026#39;)//请求中允许携带的首部字段(可以携带自定义请求头)  response.setHeader(\u0026#39;Access-Control-Allow-Methods\u0026#39;, \u0026#39;*\u0026#39;)//允许使用的所有请求方法 }) Jquery跨域 /*main.js*/ $.getJSON(\u0026#39;http://127.0.0.1:8000/jqueryJsonp?callback=?\u0026#39;, function (data) { //do something.... }) //或者,  $.ajax({ url:\u0026#34;http://127.0.0.1:8000/jqueryJsonp\u0026#34;, type:\u0026#34;GET\u0026#34;, dataType:\u0026#34;jsonp\u0026#34;, success:function(data){ //do something....  } }) 注意： url后的callback=?必须加上，jQuery 将自动替换?为正确的函数名，以执行回调函数。 此行以后的代码将在这个回调函数执行前执行。\napp.all(\u0026#39;/jqueryJsonp\u0026#39;, (request, response) =\u0026gt; { const data = { name: \u0026#34;jqf\u0026#34;, age: \u0026#34;19\u0026#34; } let str = JSON.stringify(data) let cb = request.query.callback response.send(`${cb}(${str})`) }) 注意： 这里的cb = request.query.callback是上面main.js中的回调函数，jQuery会自动注册一个新的函数，用来代替mian.js中$.getJSON()后的回调函数，对比上面jsonp原理剖析即可理解，在response.send()中返回的js代码中的函数必须是已经声明过的！而在这里，这个已经申明过的函数就是mian.js中$.getJSON()后的回调函数！\n\nskr~~\n","permalink":"https://blog.archai.space/p/%E8%B7%A8%E5%9F%9F%E5%8F%8Acors%E5%AE%98%E6%96%B9%E8%B7%A8%E5%9F%9F/","summary":"JSONP  jsonp跨域的实现仅限于GET请求，不可用于POST\n 说明：实现的基本思路是利用html中script标签本身可跨域的特性，在发送请求时，在页面中创建script标签，追加到页面中。这实际上就像利用script标签引入外部资源\n/*main.js*/ //申明handle函数 function handle(data) { //do something.... } ele.onclick = function () { //1.创建script标签  const script = document.createElement(\u0026#34;script\u0026#34;) //2.修改script的src属性  script.src = \u0026#34;http://127.0.0.1:8000/jsonP\u0026#34; script.id=\u0026#34;tempScript\u0026#34;//添加id方便移除  //3.追加到页面中  document.body.appendChild(script) } /*server.js*/ app.get(\u0026#39;/jsonP\u0026#39;, (request, response) =\u0026gt; { const data = { exist:1, msg:\u0026#34;用户名已经存在!\u0026#34; } let str = JSON.stringify(data) response.send(` handle(${str}); document.body.removeChild(document.querySelector(\u0026#34;#tempScript\u0026#34;)) `) }) 注意：response.send()/response.end()中利用ES6语法规范中的模板字符串直接返回一段js代码，script标签会自动解析并作用到页面上\n\nCORS 如果要实现跨域，官方的解决方案是**CORS**，即通过设置CORS响应头实现跨域，这种跨域GET或POST请求均有效\n//服务端设置响应头 app.all(\u0026#39;/data\u0026#39;, (request, response) =\u0026gt; { response.setHeader(\u0026#39;Access-Control-Allow-Origin\u0026#39;, \u0026#39;*\u0026#39;)//允许来自所有域的请求  response.","title":"跨域及CORS官方跨域"},{"content":" 学习地址:https://www.bilibili.com/video/BV1WC4y1b78y，主要通过express的简单功能来搭建本地的服务，进而更好地学习Ajax\n 发送Ajax请求的方式 原生Ajax //1.创建对象 const xhr = new XMLHttpRequest() //2.初始化 xhr.open([type], [url])//xhr.open(\u0026#34;GET\u0026#34;,\u0026#34;https://www.x.com\u0026#34;) //3.发送 xhr.send() //4.处理返回结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status \u0026gt;= 200 \u0026amp;\u0026amp; xhr.status \u0026lt; 300) { //...do something  console.log(xhr.status)//状态码  console.log(xhr.statusText)//状态字符串  console.log(xhr.getAllResponseHeaders)//所有请求头信息  console.log(xhr.response)//响应体  } } } 说明：\n readyState: 返回一个 XMLHttpRequest 代理当前所处的状态     值 状态 描述     0 UNSENT 代理被创建，但尚未调用 open() 方法。   1 OPENED open() 方法已经被调用。   2 HEADERS_RECEIVED send() 方法已经被调用，并且头部和状态已经可获得。   3 LOADING 下载中； responseText 属性已经包含部分数据。   4 DONE 下载操作已完成。     因此，xhr.readyState === 4表示整个请求过程已经完毕，可以进行后续\n  status: 返回了XMLHttpRequest 响应中的数字状态码  HTTP 响应状态代码指示特定 HTTP 请求是否已成功完成。响应分为五类：\n   响应状态码 响应信息     100-199 信息响应   200-299 成功响应   300-399 重定向   400-499 客户端错误   500-599 服务器错误     状态代码由 section 10 of RFC 2616定义\n  关于获取以及处理json数据   由于在服务端response.send()只接受String类型的数据\n /*服务端*/ let data={name:\u0026#34;jqf\u0026#34;,age:18,} JSON.stringify(data)//转为String类型  /*接收时*/ //第一种方式 JSON.parse(data)//重新转为json类型 //第二种方式 xhr.responseType=\u0026#34;json\u0026#34;//直接设置  关于超时和网络错误  //设置超时时间 xhr.timeout=2000 //超时回调 xhr.ontimeout=function(){ //...do something } //网络错误 xhr.onerror=function(){ //...do something }  取消请求  xhr.abort() Jquery Ajax  $.get() / $.post()  //参数(url, [data], [callback], [type]) $.get(\u0026#34;http://127.0.0.1:8000/jquery\u0026#34;, {a: 1, b: 2}, function (data) { //do something.... }, \u0026#34;json\u0026#34;)  \u0026quot;json\u0026quot;会将响应体response中原来属于json格式的数据直接转化成json格式\n  $.ajax()  //参数 url,[settings] //也可以直接写对象配置url $.ajax({ //路径  url: \u0026#34;http://127.0.0.1:8000/jquery\u0026#34;, //数据  data: { a: 1, b: 2 }, //请求类型  type: \u0026#34;GET\u0026#34;, //返回数据类型  dataType: \u0026#34;json\u0026#34;, //超时设置  timeout: 2000, //请求头设置  headers: { a: 1, w: \u0026#34;https://www.fintinger.xyz\u0026#34; }, //成功回调  success: (data) =\u0026gt; console.log(data), //失败回调  error: (err) =\u0026gt; console.log(\u0026#34;出错啦!\u0026#34;), }) Axios  通用默认设置  axios.defaults.baseURL = \u0026#34;[baseurl]\u0026#34;//默认地址前缀，http://127.0.0.1:8000  axios.get(url[, config])  axios.get(\u0026#34;/axios\u0026#34;, {//由于已经配置了baseURL，所以路径直接简写  params: {//url参数  t: Date.now(), w: \u0026#34;get\u0026#34; }, headers: {//请求头设置  a: 1, b: 2 } }).then(response =\u0026gt; {//成功之后的回调  console.log(response) })  axios.post(url[, data[, config]])  axios.post(\u0026#34;/axios\u0026#34;, { username: \u0026#34;fintinger\u0026#34;, password: \u0026#34;Asknjska723#$@df\u0026#34; }, { params: { t: Date.now(), w: \u0026#34;post\u0026#34; }, headers: { a: 1, b: 2 } }).then(response =\u0026gt; { console.log(response) })  通用型方法  axios({ //URL  url: \u0026#34;/axios\u0026#34;, //方法  method: \u0026#34;POST\u0026#34;, //数据(GET不会携带数据,写会报错)  data: { u: \u0026#34;fintinger\u0026#34;, p: \u0026#34;103dfs@#d\u0026#34; }, //URL后缀  params: { t: Date.now(), w: \u0026#34;all\u0026#34; } }) fetch[URL,config]  具体用法：GO  fetch(\u0026#39;http://127.0.0.1:8000/fetch\u0026#39;,{ method:\u0026#34;POST\u0026#34;, headers:{ a:1, b:2 }, body:\u0026#39;u=fintinger\u0026amp;pwd=1cdf@#ds\u0026#39; }).then(response=\u0026gt;response.json()) .then(res=\u0026gt;console.log(res))  不管是请求还是响应都能够包含 body 对象,Body类定义了以下方法（这些方法都被Request和Response所实现）以获取 body 内容。这些方法都会返回一个被解析后的Promise对象和数据。\n arrayBuffer() blob() json() text() formData()   IE 缓存问题  对于ajax请求，IE浏览器会进行本地缓存，再次发送请求时不会返回服务器的最新数据，而是返回本地的缓存数据\n 解决办法 在路径后面加上t=Date.now()，即加上时间戳，这样再次请求时就会是与之前不同的请求路径，可以解决IE缓存机制带来的异常问题\nconst xhr = new XMLHttpRequest() xhr.open(\u0026#39;GET\u0026#39;, \u0026#39;http://127.0.0.1:8000/server-ie?t=\u0026#39; + Date.now())//加时间戳  xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status \u0026gt;= 200 \u0026amp;\u0026amp; xhr.status \u0026lt; 300) { //...do something  } } } 重复请求的问题  用户多次做同一个请求，很大程度影响性能，因此是否可以在用户发送这次请求之前，判断是否与上次请求为同一请求，如果是，则取消掉上一次请求，重新发送这次请求\n  对于原生Ajax  //定义一个用于判断是否处于发送状态的标识 let isSending = false let xhr = null //如果同一个请求正在发送，则取消 if (isSending) xhr.abort() xhr = new XMLHttpRequest() xhr.open(\u0026#39;GET\u0026#39;, url) xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { //已经发送完毕，修改标识，是否成功返回则无需关心  isSending = false } }  Jquery 和 axios 发送ajax请求时的重复问题也存在，具体解决办法后续有讲到 ☞GO\n 同源策略  同源策略是浏览器的一种安全策略，同源指的是协议、域名、端口号必须相同，违背同源策略的请求称为跨域请求\n Demo\n/*描述*/ /* - 创建本地服务，在9000端口的/home路径下打开index.html - 然后在index.html中访问同源的/user路径，体现同源策略 */ const btn = document.querySelector(\u0026#34;button\u0026#34;) btn.onclick = function () { const xhr = new XMLHttpRequest() //因为请求是同源的，因此URL不用加协议前缀，浏览器会自动补全的  xhr.open(\u0026#34;POST\u0026#34;, \u0026#34;/user\u0026#34;) xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status \u0026gt;= 200 \u0026amp;\u0026amp; xhr.status \u0026lt; 300) { console.log(xhr.response); } } } } const express = require(\u0026#39;express\u0026#39;) const app = express() app.get(\u0026#39;/home\u0026#39;, (request, response) =\u0026gt; { response.sendFile(__dirname + \u0026#39;/index.html\u0026#39;) }) app.post(\u0026#39;/user\u0026#39;, (request, response) =\u0026gt; { response.send(\u0026#34;账号:fintinger\u0026#34; + \u0026#34;\\n\u0026#34; + \u0026#34;密码:fe#5@42\u0026#34;) }) app.listen(9000, () =\u0026gt; { console.log(\u0026#34;服务已经启动,9000端口监听中...\u0026#34;) }) skr~\n","permalink":"https://blog.archai.space/p/ajax%E5%AD%A6%E4%B9%A0%E5%B0%8F%E7%BB%93/","summary":"学习地址:https://www.bilibili.com/video/BV1WC4y1b78y，主要通过express的简单功能来搭建本地的服务，进而更好地学习Ajax\n 发送Ajax请求的方式 原生Ajax //1.创建对象 const xhr = new XMLHttpRequest() //2.初始化 xhr.open([type], [url])//xhr.open(\u0026#34;GET\u0026#34;,\u0026#34;https://www.x.com\u0026#34;) //3.发送 xhr.send() //4.处理返回结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status \u0026gt;= 200 \u0026amp;\u0026amp; xhr.status \u0026lt; 300) { //...do something  console.log(xhr.status)//状态码  console.log(xhr.statusText)//状态字符串  console.log(xhr.getAllResponseHeaders)//所有请求头信息  console.log(xhr.response)//响应体  } } } 说明：\n readyState: 返回一个 XMLHttpRequest 代理当前所处的状态     值 状态 描述     0 UNSENT 代理被创建，但尚未调用 open() 方法。   1 OPENED open() 方法已经被调用。   2 HEADERS_RECEIVED send() 方法已经被调用，并且头部和状态已经可获得。   3 LOADING 下载中； responseText 属性已经包含部分数据。   4 DONE 下载操作已完成。     因此，xhr.","title":"Ajax学习小结"},{"content":"Baike  Ajax 即“AsynchronousJavascript And XML”（异步 JavaScript 和 XML），是指一种创建交互式、快速动态网页应用的网页开发技术，无需重新加载整个网页的情况下，能够更新部分网页的技术。\n通过在后台与服务器进行少量数据交换，Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下，对网页的某部分进行更新。\u0026mdash;百度百科\n 请求报文 格式以及参数如下\n行 GET / HTTP/1.1 头 Host: fintinger.xyz Cookie: keyword=xyz Content-type: application/x-www-form-urlencoded User-agent: Chrome 83 空行 (必须有) 体 [GET]:空 [POST]:空/username=admin\u0026amp;password=admin \n响应报文 格式以及参数如下\n行 HTTP/1.1 200 Ok 头 Content-Type: text/html; charset=utf-8 Content-Length: 22 Content-encoding: gzip 空行 (必须有) 体 \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello!\u0026lt;/h1\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; \n\nExpress框架使用 //1.引入express const express=require(\u0026#39;express\u0026#39;) //2.创建应用对象 const app=express() //3.创建路由规则 /** * request 请求报文封装 * response 响应报文封装 */ app.get(\u0026#39;/\u0026#39;,(request,response)=\u0026gt;{ //设置响应  response.send(\u0026#39;\u0026lt;h1\u0026gt;Hello express\u0026lt;/h1\u0026gt;\u0026#39;) }) //4.监听端口启动服务 app.listen(8000,()=\u0026gt;{ console.log(\u0026#34;服务已经启动,8000端口监听中....\u0026#34;+\u0026#34;\\n\u0026#34;+\u0026#34;http://127.0.0.1:8000/\u0026#34;); })   运行\nnode express.js   结果\n\n\n  另外，\n  response.sendFile([path])//response.sendFile(__dirname + \u0026#39;/index.html\u0026#39;) 可以实现访问该路径打开指定网页的效果\n","permalink":"https://blog.archai.space/p/ajax%E5%9F%BA%E7%A1%80%E5%8F%8Aexpress%E6%A1%86%E6%9E%B6%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/","summary":"Baike  Ajax 即“AsynchronousJavascript And XML”（异步 JavaScript 和 XML），是指一种创建交互式、快速动态网页应用的网页开发技术，无需重新加载整个网页的情况下，能够更新部分网页的技术。\n通过在后台与服务器进行少量数据交换，Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下，对网页的某部分进行更新。\u0026mdash;百度百科\n 请求报文 格式以及参数如下\n行 GET / HTTP/1.1 头 Host: fintinger.xyz Cookie: keyword=xyz Content-type: application/x-www-form-urlencoded User-agent: Chrome 83 空行 (必须有) 体 [GET]:空 [POST]:空/username=admin\u0026amp;password=admin \n响应报文 格式以及参数如下\n行 HTTP/1.1 200 Ok 头 Content-Type: text/html; charset=utf-8 Content-Length: 22 Content-encoding: gzip 空行 (必须有) 体 \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello!\u0026lt;/h1\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; \n\nExpress框架使用 //1.引入express const express=require(\u0026#39;express\u0026#39;) //2.创建应用对象 const app=express() //3.创建路由规则 /** * request 请求报文封装 * response 响应报文封装 */ app.","title":"Ajax基础及Express框架基本使用"},{"content":"1. 关于元素显示与隐藏  # v-show=\u0026quot;name\u0026quot;  说明： 如果data中name为true就显示，否则该元素display:none   # v-if=\u0026quot;name\u0026quot;  说明： 如果data中name为true就显示，否则该元素变成一段注释\u0026lt;!----\u0026gt;，就被删掉了!   v-if 是“真正”的条件渲染，因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。\nv-if 也是惰性的：如果在初始渲染时条件为假，则什么也不做——直到条件第一次变为真时，才会开始渲染条件块。\n相比之下，v-show 就简单得多——不管初始条件是什么，元素总是会被渲染，并且只是简单地基于 CSS 进行切换。\n一般来说，v-if 有更高的切换开销，而 v-show 有更高的初始渲染开销。因此，如果需要非常频繁地切换，则使用 v-show 较好；如果在运行时条件很少改变，则使用 v-if 较好。\n 2. 访问data 　# v-model  说明： 与data中的值绑定，会随着data中的值得改变而改变，如果是input则data中的值会随着input中的值的改变而改变  # v-if   **说明：**用于条件性地渲染一块内容\n 在\u0026lt;template\u0026gt;元素上使用 v-if 条件渲染分组\n因为 v-if 是一个指令，所以必须将它添加到一个元素上。但是如果想切换(隐藏或显示)多个元素呢？此时可以把一个\u0026lt;template\u0026gt;元素当做不可见的包裹元素，并在上面使用 v-if。最终的渲染结果将不包含\u0026lt;template\u0026gt;元素。\n这样做的好处就是不用再写一个\u0026lt;div\u0026gt;将需要隐藏的元素包裹\n   # v-show  说明： 条件切换display属性  # v-for  说明： 在data中定义一个数组arr，然后用v-for=\u0026quot;val in arr\u0026quot;访问到  3. v-on  简写：@ 说明： v-on=\u0026quot;fn()\u0026quot;  如果函数fn没有参数，直接写fn即可 如果有参数，带括号传参即可 如果需要一次性绑定多个事件，用对象的形式，比如v-on=\u0026quot;{mouseenter:entereDiv,mouseleave:leaveDiv}\u0026quot; 这时候就不能简写了    4.全局组件和局部组件 全局组件 Vue.component(\u0026#39;my_web\u0026#39;, { template: \u0026#34;\u0026lt;a target=\u0026#39;_blank\u0026#39; href=\u0026#39;https://www.fintinger.xyz\u0026#39;\u0026gt;Fintinger\u0026#39;blog✍\u0026lt;/a\u0026gt;\u0026#34;, }); new Vue({ el: \u0026#34;#app\u0026#34;, })  注意为component，单数\n 局部组件 let surf_web = { template: \u0026#34;\u0026lt;a target=\u0026#39;_blank\u0026#39; href=\u0026#39;https://www.fintinger.xyz\u0026#39;\u0026gt;Fintinger\u0026#39;blog✍\u0026lt;/a\u0026gt;\u0026#34; } new Vue({ el: \u0026#34;#app\u0026#34;, components: { surf_web: surf_web } })  注意为components，复数\n Vue.extend( options )   参数：\n {Object} options    用法：\n使用基础 Vue 构造器，创建一个“子类”。参数是一个包含组件选项的对象。\n// 创建构造器 var Profile = Vue.extend({ template: \u0026#39;\u0026lt;p\u0026gt;{{firstName}} {{lastName}} aka {{alias}}\u0026lt;/p\u0026gt;\u0026#39;, data: function () { return { firstName: \u0026#39;Walter\u0026#39;, lastName: \u0026#39;White\u0026#39;, alias: \u0026#39;Heisenberg\u0026#39; } } }) // 创建 Profile 实例，并挂载到一个元素上。 new Profile().$mount(\u0026#39;#mount-point\u0026#39;) //result: \u0026lt;p\u0026gt;Walter White aka Heisenberg\u0026lt;/p\u0026gt;\t   Vue.extend可以利用$mount将构建的组件挂载到任意一个元素上，不一定是#app，但必须先实例化Profile,\n组件中的data要通过function方法返回出来，而且不能使用箭头函数\n 5.驼峰式的一些坑   在定义组件名的时候，不能用\u0026quot;驼峰式\u0026quot;，应该用_或者-\n  定义函数名的时候最好也不用驼峰式，我在使用this.$emit(fn,data)时，使用驼峰式出现错误\n  6.组件间的通信 父子通信(自定义属性传参)   **情形：**假设自定义一个eg-el组件，希望通过\u0026lt;eg-el username=\u0026quot;lsd\u0026quot;\u0026gt;\u0026lt;/eg-el\u0026gt;这种方式传入username的值给到template使用\n  实现：\n\u0026lt;!--html--\u0026gt; \u0026lt;div id=\u0026#34;app\u0026#34;\u0026gt; \u0026lt;eg-el username=\u0026#34;lsd\u0026#34;\u0026gt;\u0026lt;/eg-el\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;template id=\u0026#34;egEl\u0026#34;\u0026gt; \u0026lt;a :href=\u0026#34;\u0026#39;/user/\u0026#39;+username\u0026#34;\u0026gt;@{{username}}\u0026lt;/a\u0026gt; \u0026lt;/template\u0026gt; //javascript let egEl={ template:\u0026#34;#egEl\u0026#34;, props:[\u0026#39;username\u0026#39;],//这里使用到prop } new Vue({ el:\u0026#34;#app\u0026#34;, components:{ \u0026#34;eg-el\u0026#34;: egEl } })   子父通信(一个组间中包含另一个组件)   情形： 假定有两个组件\u0026lt;father\u0026gt;和\u0026lt;child\u0026gt;，而\u0026lt;father\u0026gt;中有\u0026lt;child\u0026gt;组件，且子组件事件的触发需要引起父组件中的某些变化！\n  实现：\n\u0026lt;!--只使用父组件，子组件已经包含在父组件中，无需单独使用--\u0026gt; \u0026lt;div id=\u0026#34;app\u0026#34;\u0026gt; \u0026lt;my-balance\u0026gt;\u0026lt;/my-balance\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!--子组件template--\u0026gt; \u0026lt;template id=\u0026#34;show_balance\u0026#34;\u0026gt; \u0026lt;button @click=\u0026#34;myClick\u0026#34;\u0026gt;我的余额\u0026lt;/button\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;!--父组件template，其中使用到子组件！--\u0026gt; \u0026lt;template id=\u0026#34;my_balance\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;show-balance @my_fn=\u0026#34;toShowMyBalance\u0026#34;\u0026gt;\u0026lt;/show-balance\u0026gt; \u0026lt;p v-if=\u0026#34;ToShow\u0026#34;\u0026gt;您的余额为9800\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt;  这里的@my_fn是子组件中通过this.$emit出来的方法\n /*注册父组件*/ Vue.component(\u0026#34;my-balance\u0026#34;,{ template: \u0026#34;#my_balance\u0026#34;, methods:{ toShowMyBalance:function (data) { this.ToShow=true console.log(data);//是子组件传入的数据  } }, data:function () { return{ ToShow:false } } }) /*注册子组件*/ Vue.component(\u0026#34;show-balance\u0026#34;,{ template: \u0026#34;#show_balance\u0026#34;, methods: { myClick: function () { this.$emit(\u0026#34;my_fn\u0026#34;, {a: 1, b: 2})//$emit触发当前实例上的事件。附加参数都会传给监听器回调。  } } }) new Vue({ el: \u0026#34;#app\u0026#34;, })   平行组件通信(任意两个不相干的组件)   情形： 假定有两个平行组件，需要一个组件能随着另一个组件的变化产生相应的变化！\n  实现：[思路]首先定义全局Vue实例let vm=new Vue(),通过vm.$emit把fn和data传入，另一组件在mounted的时候用vm.$on接收，其中数据用回调callback接收（不严谨，后面有解释）！\n\u0026lt;div id=\u0026#34;app\u0026#34;\u0026gt; \u0026lt;origin-input\u0026gt;\u0026lt;/origin-input\u0026gt; \u0026lt;deal-input\u0026gt;\u0026lt;/deal-input\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;template id=\u0026#34;originInp\u0026#34;\u0026gt; \u0026lt;div\u0026gt;\u0026lt;h3\u0026gt;originInput:\u0026lt;/h3\u0026gt;\u0026lt;input type=\u0026#34;text\u0026#34; v-model=\u0026#34;oText\u0026#34; @keyup=\u0026#34;o_input\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;template id=\u0026#34;dealInp\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;dealInp\u0026#34;\u0026gt;\u0026lt;h3\u0026gt;dealInp:\u0026lt;/h3\u0026gt;{{dText}}\u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; /** *\u0026lt;origin-input\u0026gt;输入，在\u0026lt;deal-input\u0026gt;中也显示出来 */ let vm=new Vue() Vue.component(\u0026#34;origin-input\u0026#34;,{ template:\u0026#34;#originInp\u0026#34;, data:function () { return{ oText:\u0026#34;\u0026#34; } }, methods:{ o_input:function () { vm.$emit(\u0026#34;d_input\u0026#34;,this.oText)//类似trigger,出入的数据为this.oText  } } }) Vue.component(\u0026#34;deal-input\u0026#34;,{ template: \u0026#34;#dealInp\u0026#34;, data:function () { return{ dText:\u0026#34;\u0026#34; } }, mounted:function () { vm.$on(\u0026#34;d_input\u0026#34;,(data)=\u0026gt;this.dText=data)//监听d_input事件的触发  } }) let app=new Vue({ el:\u0026#34;#app\u0026#34;, })  关于 vm.$on\n vm.$on会监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。\n因此本例中的js部分从后往前阅读，则先是用 vm.$on监听自定义事件d_input的触发并提供数据的处理回调，然后在上面用vm.$emit触发并传入数据\n   7.directive自定义指令   说明： 定义v-×××这种指令\n  用法：\nVue.directive(name,callback(el,biding))  name：指令的名称，并不用指定为v-×××的形式 el：调用的元素 binding：binding.value为v-×××后面赋的值    详细用法： GO\n  8.mixins混合   说明： 是一个类似于封装函数的东西，将组建中的data，methods等封装起来公用\n  用法：\n//定义一个base公用对象，其中的data，methods可以复用，即使不用也无妨，mixins会挑有用的使用 let base={ data:function () { return{ visible:false } }, methods:{ show:function () { this.visible=true }, hide:function () { this.visible=false }, toggle:function () { this.visible=!this.visible } } } Vue.component(\u0026#34;show-details\u0026#34;,{ template:\u0026#34;#hove_details\u0026#34;, mixins:[base] }) Vue.component(\u0026#34;more-details\u0026#34;,{ template:\u0026#34;#more_details\u0026#34;, mixins:[base] }) new Vue({ el:\u0026#34;#app\u0026#34;, })   9.slot插槽  说明： 插槽的引入会让同一个组件的内容具有可更改行 用法： 在template中用\u0026lt;slot name=\u0026quot;title\u0026quot;\u0026gt;Title\u0026lt;/slot\u0026gt;指定插槽，在#app中利用\u0026lt;div slot=\u0026quot;titl e\u0026quot;\u0026gt;Modified Title\u0026lt;/div\u0026gt; 使用插槽，其中slot中的内容为默认值，而div中的为修改值，可以不指定 具体用法： Go  skrskr~\n","permalink":"https://blog.archai.space/p/vue.js%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/","summary":"1. 关于元素显示与隐藏  # v-show=\u0026quot;name\u0026quot;  说明： 如果data中name为true就显示，否则该元素display:none   # v-if=\u0026quot;name\u0026quot;  说明： 如果data中name为true就显示，否则该元素变成一段注释\u0026lt;!----\u0026gt;，就被删掉了!   v-if 是“真正”的条件渲染，因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。\nv-if 也是惰性的：如果在初始渲染时条件为假，则什么也不做——直到条件第一次变为真时，才会开始渲染条件块。\n相比之下，v-show 就简单得多——不管初始条件是什么，元素总是会被渲染，并且只是简单地基于 CSS 进行切换。\n一般来说，v-if 有更高的切换开销，而 v-show 有更高的初始渲染开销。因此，如果需要非常频繁地切换，则使用 v-show 较好；如果在运行时条件很少改变，则使用 v-if 较好。\n 2. 访问data 　# v-model  说明： 与data中的值绑定，会随着data中的值得改变而改变，如果是input则data中的值会随着input中的值的改变而改变  # v-if   **说明：**用于条件性地渲染一块内容\n 在\u0026lt;template\u0026gt;元素上使用 v-if 条件渲染分组\n因为 v-if 是一个指令，所以必须将它添加到一个元素上。但是如果想切换(隐藏或显示)多个元素呢？此时可以把一个\u0026lt;template\u0026gt;元素当做不可见的包裹元素，并在上面使用 v-if。最终的渲染结果将不包含\u0026lt;template\u0026gt;元素。\n这样做的好处就是不用再写一个\u0026lt;div\u0026gt;将需要隐藏的元素包裹\n   # v-show  说明： 条件切换display属性  # v-for  说明： 在data中定义一个数组arr，然后用v-for=\u0026quot;val in arr\u0026quot;访问到  3.","title":"vue.js学习心得"},{"content":"该怎么去获得文件名字符串中的文件扩展名呢？\n一种较为强大的解决方法是利用String的slice、lastIndexOf方法：\nfunction getFileExtension(filename) { return filename.slice((filename.lastIndexOf(\u0026#34;.\u0026#34;) - 1 \u0026gt;\u0026gt;\u0026gt; 0) + 2); } console.log(getFileExtension3(\u0026#39;\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;filename\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;filename.txt\u0026#39;)); // \u0026#39;txt\u0026#39; console.log(getFileExtension3(\u0026#39;.hiddenfile\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;hiddenfile.\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;filename.with.many.dots.ext\u0026#39;)); // \u0026#39;ext\u0026#39; 说明 filename.slice((filename.lastIndexOf(\u0026quot;.\u0026quot;) - 1 \u0026gt;\u0026gt;\u0026gt; 0) + 2)\n 本方法中的\u0026gt;\u0026gt;\u0026gt;运算目的是\n找到最后一个.所在索引，然后索引-1+2，相当于+1，则slice方法返回.后面的后缀名\n.不会再开头，否则索引会是0，因为-1\u0026gt;\u0026gt;\u0026gt;0 =﻿﻿﻿﻿ 4294967295，所不会匹配到\n.出现在末尾，索引会等于length+1，也不会匹配到\n slice(start,end)  说明：start必须指定，负值为从后往前数，若只指定start值，则返回start位置及后面一直到数组结尾所有的元素 **注意：**区别于删除数组中的一段元素的方法 Array.splice()  lastIndexOf(searchvalue,fromindex)  **说明：**如果要检索的字符串值没有出现，则该方法返回 -1，该方法对大小写敏感 使用：  var str=\u0026#34;Hello world!\u0026#34; document.write(str.lastIndexOf(\u0026#34;Hello\u0026#34;) + \u0026#34;\u0026lt;br /\u0026gt;\u0026#34;)//0 document.write(str.lastIndexOf(\u0026#34;World\u0026#34;) + \u0026#34;\u0026lt;br /\u0026gt;\u0026#34;)//-1 document.write(str.lastIndexOf(\u0026#34;world\u0026#34;))//6 位运算符   **说明：**在执行位运算之前，JavaScript 将数字转换为 32 位有符号整数。执行按位操作后，结果将转换回 64 位 JavaScript 数。\n  使用：\n     操作 结果 等同于 结果     5 \u0026amp; 1 1 0101 \u0026amp; 0001 0001   5 | 1 5 0101 | 0001 0101   5 ^ 1 4 0101 ^ 0001 0100   ~ 5 10 ~0101 1010   5 \u0026laquo; 1 10 0101 \u0026laquo; 1 1010   5 \u0026raquo; 1 2 0101 \u0026raquo; 1 0010   5 \u0026raquo;\u0026gt; 1 2 0101 \u0026raquo;\u0026gt; 1 0010    ","permalink":"https://blog.archai.space/p/%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E6%89%A9%E5%B1%95%E5%90%8D/","summary":"该怎么去获得文件名字符串中的文件扩展名呢？\n一种较为强大的解决方法是利用String的slice、lastIndexOf方法：\nfunction getFileExtension(filename) { return filename.slice((filename.lastIndexOf(\u0026#34;.\u0026#34;) - 1 \u0026gt;\u0026gt;\u0026gt; 0) + 2); } console.log(getFileExtension3(\u0026#39;\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;filename\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;filename.txt\u0026#39;)); // \u0026#39;txt\u0026#39; console.log(getFileExtension3(\u0026#39;.hiddenfile\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;hiddenfile.\u0026#39;)); // \u0026#39;\u0026#39; console.log(getFileExtension3(\u0026#39;filename.with.many.dots.ext\u0026#39;)); // \u0026#39;ext\u0026#39; 说明 filename.slice((filename.lastIndexOf(\u0026quot;.\u0026quot;) - 1 \u0026gt;\u0026gt;\u0026gt; 0) + 2)\n 本方法中的\u0026gt;\u0026gt;\u0026gt;运算目的是\n找到最后一个.所在索引，然后索引-1+2，相当于+1，则slice方法返回.后面的后缀名\n.不会再开头，否则索引会是0，因为-1\u0026gt;\u0026gt;\u0026gt;0 =﻿﻿﻿﻿ 4294967295，所不会匹配到\n.出现在末尾，索引会等于length+1，也不会匹配到\n slice(start,end)  说明：start必须指定，负值为从后往前数，若只指定start值，则返回start位置及后面一直到数组结尾所有的元素 **注意：**区别于删除数组中的一段元素的方法 Array.splice()  lastIndexOf(searchvalue,fromindex)  **说明：**如果要检索的字符串值没有出现，则该方法返回 -1，该方法对大小写敏感 使用：  var str=\u0026#34;Hello world!\u0026#34; document.write(str.lastIndexOf(\u0026#34;Hello\u0026#34;) + \u0026#34;\u0026lt;br /\u0026gt;\u0026#34;)//0 document.write(str.lastIndexOf(\u0026#34;World\u0026#34;) + \u0026#34;\u0026lt;br /\u0026gt;\u0026#34;)//-1 document.","title":"获取文件扩展名"},{"content":"做网页轮播图，或者音乐播放列表时经常要用到一组循环的数组，到最后一项返回第一项，第一项返回最后一项\n利用两次判断 function doLoop( arr ){ arr.loop_idx = 0; // 返回当前的元素  arr.current = function(){ if( this.loop_idx \u0026lt; 0 ){// 第一次检查  this.loop_idx = this.length - 1;// 更新 loop_idx  } if( this.loop_idx \u0026gt;= this.length ){// 第二次检查  this.loop_idx = 0;// 更新 loop_idx  } return arr[ this.loop_idx ];//返回元素  }; // 增加 loop_idx 然后返回新的当前元素  arr.next = function(){ this.loop_idx++; return this.current(); }; // 减少 loop_idx 然后返回新的当前元素  arr.prev = function(){ this.loop_idx--; return this.current(); }; } 取余运算% function make_looper( arr ){ arr.loop_idx = 0; // return current item  arr.current = function(){ this.loop_idx = ( this.loop_idx ) % this.length;// 无需检查 !!  return arr[ this.loop_idx ]; }; // 增加 loop_idx 然后返回新的当前元素  arr.next = function(){ this.loop_idx++; return this.current(); }; // 减少 loop_idx 然后返回新的当前元素  arr.prev = function(){ this.loop_idx += this.length - 1; return this.current(); }; } 关于this.loop_idx += this.length - 1\n对于arr=[1,2,3,4]这样一个数组，执行arr.prev后，索引由0,1,2,3变成3,4,5,6，之后在prev()中执行current()索引又变成3,0,1,2,并不会出现索引超过length或者索引小于0的情况!\n","permalink":"https://blog.archai.space/p/%E4%B8%80%E7%BB%84%E5%BE%AA%E7%8E%AF%E6%95%B0%E7%BB%84/","summary":"做网页轮播图，或者音乐播放列表时经常要用到一组循环的数组，到最后一项返回第一项，第一项返回最后一项\n利用两次判断 function doLoop( arr ){ arr.loop_idx = 0; // 返回当前的元素  arr.current = function(){ if( this.loop_idx \u0026lt; 0 ){// 第一次检查  this.loop_idx = this.length - 1;// 更新 loop_idx  } if( this.loop_idx \u0026gt;= this.length ){// 第二次检查  this.loop_idx = 0;// 更新 loop_idx  } return arr[ this.loop_idx ];//返回元素  }; // 增加 loop_idx 然后返回新的当前元素  arr.next = function(){ this.loop_idx++; return this.current(); }; // 减少 loop_idx 然后返回新的当前元素  arr.prev = function(){ this.","title":"一组循环数组"},{"content":"经常会用到指定内容的复制粘贴问题，用到document的execCommand 方法,为此，我将这个功能封装为一个简单的函数:\nfunction doCopy($el, {deepCopy = false, copyTips = true, language = \u0026#34;Chinese\u0026#34;, bgColor = \u0026#34;#ff6666\u0026#34;, fontColor = \u0026#34;#fff\u0026#34;} = {}) { let tempEl = $(\u0026#34;\u0026lt;input id=\u0026#39;selectEl\u0026#39; type=\u0026#39;text\u0026#39; value=\u0026#39;\u0026#39;\u0026gt;\u0026#34;).val($el.text()) if (deepCopy) { tempEl.val($el.html()) } tempEl.appendTo($(\u0026#34;body\u0026#34;)) document.querySelector(\u0026#39;#selectEl\u0026#39;).select(); document.execCommand(\u0026#39;copy\u0026#39;); tempEl.remove() if (copyTips) { let tipEl = $(\u0026#34;\u0026lt;div class=\u0026#39;copyTips\u0026#39; \u0026gt;成功复制到剪切板\u0026lt;/div\u0026gt;\u0026#34;) tipEl.css({fontFamily: \u0026#34;\u0026#39;Microsoft YaHei\u0026#39;, sans-serif\u0026#34;,fontSize: \u0026#34;1.2rem\u0026#34;,position: \u0026#34;fixed\u0026#34;,top: \u0026#34;1rem\u0026#34;,textAlign: \u0026#34;center\u0026#34;, left: \u0026#34;50%\u0026#34;,fontWeight: \u0026#34;bolder\u0026#34;, borderRadius: \u0026#34;.5rem\u0026#34;,marginLeft: \u0026#34;-8rem\u0026#34;,width: \u0026#34;16rem\u0026#34;,height: \u0026#34;3rem\u0026#34;, lineHeight:\u0026#34;3rem\u0026#34;, background: bgColor,boxShadow: \u0026#34;0 6px 10px -8px #000\u0026#34;,color: fontColor, letterSpacing: \u0026#34;4px\u0026#34;,boxSizing: \u0026#34;border-box\u0026#34;,padding: \u0026#34;0 10px 0 10px\u0026#34;, display: \u0026#34;none\u0026#34;}) if (language === \u0026#34;English\u0026#34;) { tipEl.text(\u0026#39;Successfully copied!\u0026#39;).css({letterSpacing: \u0026#34;0\u0026#34;}) } tipEl.appendTo($(\u0026#34;body\u0026#34;)) $(\u0026#34;.copyTips\u0026#34;).fadeIn(800) setTimeout(function () { $(\u0026#34;.copyTips\u0026#34;).fadeOut(500) }, 1000) } } 使用(Usage)  这个方法依赖于jquery，目前最新的jquery为v3.5.1\n $el   类型：jquery DOM\n  默认值： 无默认值，必须指定\n  用法：\n  doCopy($(\u0026#34;.custom\u0026#34;)) deepCopy   类型：boolean\n  默认值：false\n  用法：\n  doCopy($(\u0026#34;.custom\u0026#34;),{ deepCopy:true })  说明： 是否复制指定元素下的DOM结构，false则为仅复制执行的jqueryDOM里面的文字  copyTips   类型：boolean\n  默认值：true\n  用法：\n  doCopy($(\u0026#34;.custom\u0026#34;),{ copyTips:true })  说明： 是否显示复制完成的提示框  language   类型：String\n  默认值:\u0026quot;Chinese\u0026quot;\n  用法：\n  doCopy($(\u0026#34;.custom\u0026#34;),{ language:\u0026#34;English\u0026#34; })  说明： 指定提示框的语言，两个值\u0026quot;Chinese\u0026quot;  和\u0026quot;English\u0026quot;  bgColor   类型：String\n  默认值:\u0026quot;#ff6666\u0026quot;\n  用法：\n  doCopy($(\u0026#34;.custom\u0026#34;),{ bgColor:\u0026#34;green\u0026#34; })  说明： 指定提示框的背景色  fontColor   类型：String\n  默认值:\u0026quot;#fff\u0026quot;\n  用法：\n  doCopy($(\u0026#34;.custom\u0026#34;),{ fontColor:\u0026#34;#000\u0026#34; })  说明： 指定提示框的字体颜色  ","permalink":"https://blog.archai.space/p/%E5%89%AA%E8%B4%B4%E6%9D%BF%E5%8A%9F%E8%83%BD%E5%AE%9E%E7%8E%B0/","summary":"经常会用到指定内容的复制粘贴问题，用到document的execCommand 方法,为此，我将这个功能封装为一个简单的函数:\nfunction doCopy($el, {deepCopy = false, copyTips = true, language = \u0026#34;Chinese\u0026#34;, bgColor = \u0026#34;#ff6666\u0026#34;, fontColor = \u0026#34;#fff\u0026#34;} = {}) { let tempEl = $(\u0026#34;\u0026lt;input id=\u0026#39;selectEl\u0026#39; type=\u0026#39;text\u0026#39; value=\u0026#39;\u0026#39;\u0026gt;\u0026#34;).val($el.text()) if (deepCopy) { tempEl.val($el.html()) } tempEl.appendTo($(\u0026#34;body\u0026#34;)) document.querySelector(\u0026#39;#selectEl\u0026#39;).select(); document.execCommand(\u0026#39;copy\u0026#39;); tempEl.remove() if (copyTips) { let tipEl = $(\u0026#34;\u0026lt;div class=\u0026#39;copyTips\u0026#39; \u0026gt;成功复制到剪切板\u0026lt;/div\u0026gt;\u0026#34;) tipEl.css({fontFamily: \u0026#34;\u0026#39;Microsoft YaHei\u0026#39;, sans-serif\u0026#34;,fontSize: \u0026#34;1.2rem\u0026#34;,position: \u0026#34;fixed\u0026#34;,top: \u0026#34;1rem\u0026#34;,textAlign: \u0026#34;center\u0026#34;, left: \u0026#34;50%\u0026#34;,fontWeight: \u0026#34;bolder\u0026#34;, borderRadius: \u0026#34;.5rem\u0026#34;,marginLeft: \u0026#34;-8rem\u0026#34;,width: \u0026#34;16rem\u0026#34;,height: \u0026#34;3rem\u0026#34;, lineHeight:\u0026#34;3rem\u0026#34;, background: bgColor,boxShadow: \u0026#34;0 6px 10px -8px #000\u0026#34;,color: fontColor, letterSpacing: \u0026#34;4px\u0026#34;,boxSizing: \u0026#34;border-box\u0026#34;,padding: \u0026#34;0 10px 0 10px\u0026#34;, display: \u0026#34;none\u0026#34;}) if (language === \u0026#34;English\u0026#34;) { tipEl.","title":"剪贴板功能实现"},{"content":"在javascript中，Array()经常用到，利用ES6中的一些新特性会让数组的处理更加方便快捷\n1.迭代空数组 直接创建一个数组\nconst arr=new Array(4); //Output:[undefined，undefined，undefined，undefined] 利用map方法，转化成新的数组，企图得到 [0,1,2,3] 数组\nconst arr=new Array(4); arr.map((ele,index) =\u0026gt; index); //Output:[undefined，undefined，undefined，undefined] 解决这个问题可以在创建数组时用到Array.apply\n apply与call类似，都是用来继承父类的方法的，不同之处是：\n  call() 方法分别接受参数。person.fullName.apply(person1, [\u0026quot;Oslo\u0026quot;, \u0026quot;Norway\u0026quot;]);\n  apply() 方法接受数组形式的参数.\tperson.fullName.call(person1, \u0026quot;Oslo\u0026quot;, \u0026quot;Norway\u0026quot;);\n  如果要使用数组而不是参数列表，则 apply() 方法非常方便。\n const arr = Array.apply(null, new Array(4)); arr.map((ele,index) =\u0026gt; index); //Output:[0,1,2,3] 由此，我们可以创建一个指定最大值、最小值、或者长度生成指定数列的方法\n/** * 生成自定义的连续数列 * @param{Number}min * @param{Number}max * @param{Number}len */ function newArr({min = null, max = null, len = null} = {}) { let newArray=[], skip = min if (len == null) {len = max - min + 1} if (min == null) {skip = -max} const arr = Array.apply(null, new Array(len)); newArray = arr.map((ele, index) =\u0026gt; { return Math.abs(index + skip) }).sort((a, b) =\u0026gt; a - b)//数组排序指定的方法  newArray = [...new Set(newArray)]//数组去重，return 0那里会有重复的0  } newArr({max: 10, len:200})   JS最为合理的设置多个默认值的方法,CSDN上Jonithan_具体讲解   function third({x = 1 ,y = 2} = {}) { return x+y }  数组排序方法sort(sortby)，sortby 可选。规定排序顺序,必须是函数。   function sortNumber(a,b){return a - b} let arr=[3,1,6,7,9,2,0] arr.sort(sortNumber) //Output [0,1,2,3,6,7,9]  箭头函数，Arrow function，=\u0026gt;   x =\u0026gt; x * x //上面的箭头函数相当于：  function (x) { return x * x; } 2. 给方法传一个空参数 如果调用一个方法，并不填其中一个参数，会报错\nfunction test(a,b,c) {console.log(\u0026#34;do something\u0026#34;)} test(1,,3) //Output Uncaught SyntaxError: Unexpected token 解决办法之一就是，改为传null 或 undefined\ntest(1,null,3) //Output do something 利用ES6中新增的展开语法， 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开；还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。\ntest(...[1,,3]) //Output do something  展开语法还可以用来做数组或对象的合并\narr1=[1,2,3] arr2=[4,5,6] obj1={name:\u0026#34;fintinger\u0026#34;,age:19,gender:\u0026#34;male\u0026#34;} obj2={realName:\u0026#34;李栓蛋\u0026#34;,hobby:\u0026#34;unknown\u0026#34;,gender:\u0026#34;confidential\u0026#34;} [...arr1,...arr2]; //Output [ 1, 2, 3, 4, 5, 6 ]  {...obj1,...obj2} //Output {name: \u0026#39;fintinger\u0026#39;, age: 19,gender: \u0026#39;confidential\u0026#39;,realName: \u0026#39;李栓蛋\u0026#39;,hobby:\u0026#39;unknown\u0026#39;} //对象合并的过程中，相同key，后面value会覆盖前面value 还可以用来将数组转为对象\narr1=[1,2,3] {...arr1} //Output { \u0026#39;0\u0026#39;: 1, \u0026#39;1\u0026#39;: 2, \u0026#39;2\u0026#39;: 3 }  由此，可以得到数组去重的新方法\nconst arr = [...new Set([1, 2, 3, 3])] //Output [1,2,3] 或者，下面代码也可以达到数组去重的效果，用到Array.from()方法\nconst arr=Array.from(new Set([1, 2, 3, 3])) //Output [1,2,3] 3.数组扁平化 对于一个数组arr = [1, [2, [3, 4]]],将其转化为[1,2,3,4],可以用到reduce，ES6中提供了更为简单的方法,flat(depth)，参数depth，代表展开嵌套数组的深度，默认是1\n reduce(function(total, currentValue, currentIndex, arr), initialValue)  let arr= [1, [2, [3, 4]]] function flatten(arr) { return arr.reduce(function (prev, next) { return prev.concat(Array.isArray(next) ? flatten(next) : next); }, []); } flatten(arrO) //Output [1,2,3,4]   flat(depth)\nlet arr= [1, [2, [3, 4]]] arr.flat(3); //Output [1,2,3,4]   4.截断数组 修改数组长度为某一固定值\nlet array = [0, 1, 2, 3, 4, 5]; array.length = 3; console.log(array); //Output: [0, 1, 2]; 5.获取数组最后一项 let arr = [0, 1, 2, 3, 4, 5]; const last = arr[arr.length - 1] //Output: 5; 或者，利用slice\nconst last = arr.slice(-1)[0] //Output: 5; ","permalink":"https://blog.archai.space/p/es6%E4%B8%AD%E6%96%B0%E5%A2%9E%E5%85%B3%E4%BA%8Earray%E7%9A%84%E6%96%B9%E6%B3%95/","summary":"在javascript中，Array()经常用到，利用ES6中的一些新特性会让数组的处理更加方便快捷\n1.迭代空数组 直接创建一个数组\nconst arr=new Array(4); //Output:[undefined，undefined，undefined，undefined] 利用map方法，转化成新的数组，企图得到 [0,1,2,3] 数组\nconst arr=new Array(4); arr.map((ele,index) =\u0026gt; index); //Output:[undefined，undefined，undefined，undefined] 解决这个问题可以在创建数组时用到Array.apply\n apply与call类似，都是用来继承父类的方法的，不同之处是：\n  call() 方法分别接受参数。person.fullName.apply(person1, [\u0026quot;Oslo\u0026quot;, \u0026quot;Norway\u0026quot;]);\n  apply() 方法接受数组形式的参数.\tperson.fullName.call(person1, \u0026quot;Oslo\u0026quot;, \u0026quot;Norway\u0026quot;);\n  如果要使用数组而不是参数列表，则 apply() 方法非常方便。\n const arr = Array.apply(null, new Array(4)); arr.map((ele,index) =\u0026gt; index); //Output:[0,1,2,3] 由此，我们可以创建一个指定最大值、最小值、或者长度生成指定数列的方法\n/** * 生成自定义的连续数列 * @param{Number}min * @param{Number}max * @param{Number}len */ function newArr({min = null, max = null, len = null} = {}) { let newArray=[], skip = min if (len == null) {len = max - min + 1} if (min == null) {skip = -max} const arr = Array.","title":"ES6中新增关于Array的方法"},{"content":"开发中有时会遇到多个条件，执行相同的语句，也就是多个||这种：\nif (status === \u0026#39;process\u0026#39; || status === \u0026#39;wait\u0026#39; || status === \u0026#39;fail\u0026#39;) { doSomething() } 这种写法语义性、可读性都不太好。可以通过switch case或includes这种进行改造。\nswitch case switch(status) { case \u0026#39;process\u0026#39;: case \u0026#39;wait\u0026#39;: case \u0026#39;fail\u0026#39;: doSomething() } includes const enum = [\u0026#39;process\u0026#39;, \u0026#39;wait\u0026#39;, \u0026#39;fail\u0026#39;] if (enum.includes(status)) { doSomething() ","permalink":"https://blog.archai.space/p/%E9%81%BF%E5%85%8D%E5%A4%9A%E6%9D%A1%E4%BB%B6%E5%B9%B6%E5%88%97/","summary":"开发中有时会遇到多个条件，执行相同的语句，也就是多个||这种：\nif (status === \u0026#39;process\u0026#39; || status === \u0026#39;wait\u0026#39; || status === \u0026#39;fail\u0026#39;) { doSomething() } 这种写法语义性、可读性都不太好。可以通过switch case或includes这种进行改造。\nswitch case switch(status) { case \u0026#39;process\u0026#39;: case \u0026#39;wait\u0026#39;: case \u0026#39;fail\u0026#39;: doSomething() } includes const enum = [\u0026#39;process\u0026#39;, \u0026#39;wait\u0026#39;, \u0026#39;fail\u0026#39;] if (enum.includes(status)) { doSomething() ","title":"避免多条件并列"},{"content":"在写小demo的过程中，经常需要把某个文件夹的图片文件的路径给引入，除非全部重命名成有序的数字，不然不好处理，这就用到了node中的fs和path模块，还没学\u0026hellip;\nconst path=require(\u0026#34;path\u0026#34;); const fs = require(\u0026#39;fs\u0026#39;); fs.stat(\u0026#39;../images\u0026#39;,(err)=\u0026gt;{//图片文件所在目录  if (err)return; var result=\u0026#39;{\u0026#39; fs.readdir(\u0026#34;../images\u0026#34;,(err,data)=\u0026gt;{//图片文件所在目录  for(var i=0;i\u0026lt;Object.keys(data).length;i++){ let ImgPath=\u0026#34;\\\u0026#34;images/\u0026#34;+data[i]+\u0026#34;\\\u0026#34;\u0026#34;; result+=\u0026#34;\\\u0026#34;\u0026#34;+i+\u0026#34;\\\u0026#34;:\u0026#34;+ImgPath+\u0026#34;,\u0026#34;; } result=result.substring(0,result.length-1); let length=\u0026#34;\\\u0026#34;\u0026#34;+\u0026#34;length\u0026#34;+\u0026#34;\\\u0026#34;\u0026#34;+\u0026#34;:\u0026#34;+\u0026#34;\\\u0026#34;\u0026#34;+Object.keys(data).length+\u0026#34;\\\u0026#34;\u0026#34;//文件数量  result+=\u0026#34;,\u0026#34;+length+\u0026#39;}\u0026#39; fs.writeFile(\u0026#34;../data/imgPath.json\u0026#34;,result,(err)=\u0026gt;{ if(err)return; console.log(\u0026#34;写入文件成功，一共\u0026#34;+Object.keys(data).length+\u0026#34;个文件\u0026#34;); }); }); }); 生成的文件大概就是这样的一个json数据\n{\u0026#34;0\u0026#34;:\u0026#34;images/1.jpg\u0026#34;,\u0026#34;1\u0026#34;:\u0026#34;images/10.jpg\u0026#34;,\u0026#34;2\u0026#34;:\u0026#34;images/100.jpg\u0026#34;,\u0026#34;3\u0026#34;:\u0026#34;images/101.jpg\u0026#34;,\u0026#34;4\u0026#34;:\u0026#34;images/102.jpg\u0026#34;,\u0026#34;5\u0026#34;:\u0026#34;images/103.jpg\u0026#34;,\u0026#34;6\u0026#34;:\u0026#34;images/104.jpg\u0026#34;,\u0026#34;7\u0026#34;:\u0026#34;images/105.jpg\u0026#34;,\u0026#34;8\u0026#34;:\u0026#34;images/106.jpg\u0026#34;,\u0026#34;9\u0026#34;:\u0026#34;images/107.jpg\u0026#34;,\u0026#34;10\u0026#34;:\u0026#34;images/108.jpg\u0026#34;,\u0026#34;length\u0026#34;:\u0026#34;109\u0026#34;} ","permalink":"https://blog.archai.space/p/javascript%E7%94%9F%E6%88%90%E5%9B%BE%E7%89%87%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84json/","summary":"在写小demo的过程中，经常需要把某个文件夹的图片文件的路径给引入，除非全部重命名成有序的数字，不然不好处理，这就用到了node中的fs和path模块，还没学\u0026hellip;\nconst path=require(\u0026#34;path\u0026#34;); const fs = require(\u0026#39;fs\u0026#39;); fs.stat(\u0026#39;../images\u0026#39;,(err)=\u0026gt;{//图片文件所在目录  if (err)return; var result=\u0026#39;{\u0026#39; fs.readdir(\u0026#34;../images\u0026#34;,(err,data)=\u0026gt;{//图片文件所在目录  for(var i=0;i\u0026lt;Object.keys(data).length;i++){ let ImgPath=\u0026#34;\\\u0026#34;images/\u0026#34;+data[i]+\u0026#34;\\\u0026#34;\u0026#34;; result+=\u0026#34;\\\u0026#34;\u0026#34;+i+\u0026#34;\\\u0026#34;:\u0026#34;+ImgPath+\u0026#34;,\u0026#34;; } result=result.substring(0,result.length-1); let length=\u0026#34;\\\u0026#34;\u0026#34;+\u0026#34;length\u0026#34;+\u0026#34;\\\u0026#34;\u0026#34;+\u0026#34;:\u0026#34;+\u0026#34;\\\u0026#34;\u0026#34;+Object.keys(data).length+\u0026#34;\\\u0026#34;\u0026#34;//文件数量  result+=\u0026#34;,\u0026#34;+length+\u0026#39;}\u0026#39; fs.writeFile(\u0026#34;../data/imgPath.json\u0026#34;,result,(err)=\u0026gt;{ if(err)return; console.log(\u0026#34;写入文件成功，一共\u0026#34;+Object.keys(data).length+\u0026#34;个文件\u0026#34;); }); }); }); 生成的文件大概就是这样的一个json数据\n{\u0026#34;0\u0026#34;:\u0026#34;images/1.jpg\u0026#34;,\u0026#34;1\u0026#34;:\u0026#34;images/10.jpg\u0026#34;,\u0026#34;2\u0026#34;:\u0026#34;images/100.jpg\u0026#34;,\u0026#34;3\u0026#34;:\u0026#34;images/101.jpg\u0026#34;,\u0026#34;4\u0026#34;:\u0026#34;images/102.jpg\u0026#34;,\u0026#34;5\u0026#34;:\u0026#34;images/103.jpg\u0026#34;,\u0026#34;6\u0026#34;:\u0026#34;images/104.jpg\u0026#34;,\u0026#34;7\u0026#34;:\u0026#34;images/105.jpg\u0026#34;,\u0026#34;8\u0026#34;:\u0026#34;images/106.jpg\u0026#34;,\u0026#34;9\u0026#34;:\u0026#34;images/107.jpg\u0026#34;,\u0026#34;10\u0026#34;:\u0026#34;images/108.jpg\u0026#34;,\u0026#34;length\u0026#34;:\u0026#34;109\u0026#34;} ","title":"JavaScript生成图片文件路径json"},{"content":"什么是瀑布流？ 一种图片在网页的布局方式，具体要求为：从页面第二行开始，后续图片跟在页面中最矮的那张图片后面\n\n用JavaScript怎么实现？ 为此我将整个过程封装为一个函数。\n/*HTML部分*/ \u0026lt;head\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;main.css\u0026#34;\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;div id=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;//container和img由js动态加入 \u0026lt;script src=\u0026#34;jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;loadFile.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;main.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; /*CSS部分*/ /*首先清空默认样式*/ #box{ position: relative; } .container{ float: left; } .container\u0026gt;.pic{ width: 200px; } .container\u0026gt;.pic\u0026gt;img{ width: 100%; } /*JS部分*/ /*loadFile.js(在window.onload之前执行，加载DOM树)*/\t$(function () { function addImg(imgNum,eleID) { let container for (let i = 1; i \u0026lt; imgNum; i++) { container=$(\u0026#34;\u0026lt;div class=\u0026#39;container\u0026#39;\u0026gt;\u0026lt;div class=\u0026#39;pic\u0026#39;\u0026gt;\u0026lt;img src=\u0026#39;images/\u0026#34;+i+\u0026#34;.jpg\u0026#39; alt=\u0026#39;\u0026#39;\u0026gt;\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt;\u0026#34;) $(eleID).append(container) } } addImg(109,\u0026#34;#box\u0026#34;); }) /*******************************************/ /*main.js(在$(function(){})之前执行，DOM树加载完毕 )*/ window.onload=function () { let waterFall=function (eleID) { let boxWidth = 0, colo = 0, $egEle = $(eleID).children().eq(0), boxArr = [], heightArr = [], minHeight = 0,minHeightInd = 0 /*1.父盒子居中*/ /*1.1盒子宽度*/ boxWidth = $egEle.outerWidth() /*1.2列数*/ colo = parseInt($(window).width() / boxWidth) /*1.3实现居中*/ $(eleID).css({ // width: boxWidth * colo + \u0026#34;px\u0026#34;,  margin: \u0026#34;0 auto\u0026#34; }) /*2.定位盒子*/ /*2.1找出第一行盒子最矮的一个*/ /*2.1.1 第一行盒子高度数列*/ boxArr = $egEle.prevObject // console.log(boxArr);  for (let i = 0; i \u0026lt; boxArr.length; i++) { if (i \u0026lt; colo) {//第一行盒子  heightArr.push(boxArr.eq(i).outerHeight()) } else { /*2.1.2 盒子中最矮的一个值及索引*/ minHeight = Math.min.apply(null, heightArr) minHeightInd = heightArr.indexOf(minHeight) /*2.2定位后续盒子*/ boxArr.eq(i).css({ position: \u0026#34;absolute\u0026#34;, top: minHeight + \u0026#34;px\u0026#34;, left: boxWidth * minHeightInd + \u0026#34;px\u0026#34; }) /*2.3 高度数列更新*/ heightArr[minHeightInd] += boxArr.eq(i).outerHeight() } } } /*执行*/ waterFall(\u0026#34;#box\u0026#34;); } 一些疑惑点   container之间无任何外边距，照片之间间距是由container的内边距造成的\n  用js加载图片文件后，获取元素高度出现了异常(所有盒子高度为一个奇怪的值)，是因为**$(function(){})**与**window.onload=function(){}**的区别造成的\n之前将文件的加载和后续样式的更改js全部写到了$(function(){})中\n后面将loadFile.js写到$(function(){})，将main.js写到window.onload=function(){}就解决了\n   **$(function(){})与window.onload=function(){}**的区别\n $(function(){})就是$(document).ready=function(){}，是在加载DOM树的时候执行 window.onload=function(){}则是在DOM树加载完成之后才执行 $(function(){})比window.onload=function(){}先执行   3.为了代码的易用性以及可维护性，将父盒子#box的居中用js动态来完成\n对于块级元素来说，居中使用到的是margin:0 auto，但前提是块级元素必须要有宽度\n而这个块级元素#box的宽度是动态的，Width = boxWidth * colo,即宽度为container宽度乘以列数, colo = parseInt($(window).width() / boxWidth),\n4.除第一行的盒子之外，后续盒子全部用绝对定位来排列\n4.1 怎么确定盒子在第一行？\n拿到盒子数列boxArr！\n利用到jQuery中的prevObject属性，\n jquery选择器在遍历的过程中都会找到一组元素（一个jQuery对象），然后jQuery会把这组元素推入到栈中，prevObject属性就指向这个对象栈中的前一个对象，通过这个属性就可以回溯到最初的DOM元素集，减少重复的查找和遍历操作\n 然后遍历boxArr，如果索引小于列数，即index \u0026lt; colo,则这些元素都属于第一行 ​\n4.2 后续盒子定位？ ​\n拿到第一行盒子的高度数列组heightArr ​\n第一行之后的第一个盒子定位在heightArr中的最小值盒子之后\n heightArr最小值? Math.min.apply(null,heightArr) ,\n 5.heightArr最小高度的更新☆\nheightArr[minHeightInd] += boxArr.eq(i).outerHeight()\n","permalink":"https://blog.archai.space/p/javascript%E7%80%91%E5%B8%83%E6%B5%81%E5%B8%83%E5%B1%80/","summary":"什么是瀑布流？ 一种图片在网页的布局方式，具体要求为：从页面第二行开始，后续图片跟在页面中最矮的那张图片后面\n\n用JavaScript怎么实现？ 为此我将整个过程封装为一个函数。\n/*HTML部分*/ \u0026lt;head\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;main.css\u0026#34;\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;div id=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;//container和img由js动态加入 \u0026lt;script src=\u0026#34;jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;loadFile.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;main.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; /*CSS部分*/ /*首先清空默认样式*/ #box{ position: relative; } .container{ float: left; } .container\u0026gt;.pic{ width: 200px; } .container\u0026gt;.pic\u0026gt;img{ width: 100%; } /*JS部分*/ /*loadFile.js(在window.onload之前执行，加载DOM树)*/\t$(function () { function addImg(imgNum,eleID) { let container for (let i = 1; i \u0026lt; imgNum; i++) { container=$(\u0026#34;\u0026lt;div class=\u0026#39;container\u0026#39;\u0026gt;\u0026lt;div class=\u0026#39;pic\u0026#39;\u0026gt;\u0026lt;img src=\u0026#39;images/\u0026#34;+i+\u0026#34;.jpg\u0026#39; alt=\u0026#39;\u0026#39;\u0026gt;\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt;\u0026#34;) $(eleID).append(container) } } addImg(109,\u0026#34;#box\u0026#34;); }) /*******************************************/ /*main.js(在$(function(){})之前执行，DOM树加载完毕 )*/ window.","title":"JavaScript瀑布流布局"},{"content":" 在python中，我们可以将那些在运行时可能会出现状况的代码放在try代码块中，在try代码块的后面可以跟上一个或多个except来捕获可能出现的异常状况。\n  FileNotFoundError，文件找不到 LookupError指定了未知的编码 UnicodeDecodeError读取文件时无法按指定方式解码  def main(): f = None try: f = open('致橡树.txt', 'r', encoding='utf-8') print(f.read()) except FileNotFoundError: print('无法打开指定的文件!') except LookupError: print('指定了未知的编码!') except UnicodeDecodeError: print('读取文件时解码错误!') finally: if f: f.close() if __name__ == '__main__': main()  finally块的代码不论程序正常还是异常都会执行到（甚至是调用了sys模块的exit函数退出Python环境，finally块都会被执行，因为exit函数实质上是引发了SystemExit异常），因此我们通常把finally块称为“总是执行代码块”，它最适合用来做释放外部资源的操作。\n 或者， with关键字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源\ndef main(): try: with open('致橡树.txt', 'r', encoding='utf-8') as f: print(f.read()) except FileNotFoundError: print('无法打开指定的文件!') except LookupError: print('指定了未知的编码!') except UnicodeDecodeError: print('读取文件时解码错误!') if __name__ == '__main__': main() ","permalink":"https://blog.archai.space/p/python%E4%B8%AD%E5%85%B3%E4%BA%8E%E6%96%87%E4%BB%B6%E7%9A%84%E7%A7%8D%E7%A7%8D%E9%97%AE%E9%A2%98/","summary":" 在python中，我们可以将那些在运行时可能会出现状况的代码放在try代码块中，在try代码块的后面可以跟上一个或多个except来捕获可能出现的异常状况。\n  FileNotFoundError，文件找不到 LookupError指定了未知的编码 UnicodeDecodeError读取文件时无法按指定方式解码  def main(): f = None try: f = open('致橡树.txt', 'r', encoding='utf-8') print(f.read()) except FileNotFoundError: print('无法打开指定的文件!') except LookupError: print('指定了未知的编码!') except UnicodeDecodeError: print('读取文件时解码错误!') finally: if f: f.close() if __name__ == '__main__': main()  finally块的代码不论程序正常还是异常都会执行到（甚至是调用了sys模块的exit函数退出Python环境，finally块都会被执行，因为exit函数实质上是引发了SystemExit异常），因此我们通常把finally块称为“总是执行代码块”，它最适合用来做释放外部资源的操作。\n 或者， with关键字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源\ndef main(): try: with open('致橡树.txt', 'r', encoding='utf-8') as f: print(f.read()) except FileNotFoundError: print('无法打开指定的文件!') except LookupError: print('指定了未知的编码!') except UnicodeDecodeError: print('读取文件时解码错误!') if __name__ == '__main__': main() ","title":"python中关于文件的种种问题"},{"content":"第一章 导 论 1.什么是统计学  统计学是收集、处理、分析、解释数据并从数据中得出结论的科学 数据分析所用的方法可分为描述统计方法和推断统计方法  2.参数和统计量   参数：总体特征，所关心的参数通常是总体平均数、总体标准差、总体比例等\n 由于总体数据通常是不知道的，所以参数是一个未知的常量-\n   统计量：个体特征，所关心的参数通常是样本平均数、样本标准差、样本比例等\n 由于样本是已经抽出来的，所以统计量总是知道的\n   第二章 数据的搜集 1.数据的来源  **间接来源（二手数据）**与研究内容有关的原信息已经存在，我们只是重新加工、整理，使之成为分析可用的数据，这称为间接来源的数据 **直接来源（一手数据）**通过调查方法获得的为调查数据，实验得到为实验数据，都是直接来源的数据  2.数据误差  抽样误差：由抽样的随机性引起的样本结果与总体真值之间的差异 非抽样误差：抽样框误差、回答误差、无回答误差、调查员误差、测量误差  第三章 数据的图表展示 1.数据预处理  原始数据：完整性、准确性 二手数据：适用性、时效性  2.分类数据图示 ​  1\u0026gt; 组数 5≤ K ≤15\n2\u0026gt; 组距 组距=(Max-Min)/K\n3\u0026gt; 为解决不重的问题，统计分组习惯上规定“上组限不在内”，即当相邻两组的上下限重叠时，恰好等于某一组上限的变量值不算在本组内，而计算在下一组内。即a≤ x \u0026lt;b\n 第四章 数据的概括性度量 1.集中趋势的度量 \n  众数不受极端值影响，具有不唯一性 中位数不受极端值影响，数据分布偏斜程度较大时 平均数易受极端值影响   2.离散程度的度量 \n   标准分数：也称标准化值或z分数（将数据平均值变为0，标准差为1） $$ z_i=\\frac{x_i-\\overline{x}}{s} $$\n  经验法则：对称分布\n  切比雪夫不等式：不是对称分布\n   离散系数 $$ v_s=\\frac{s}{\\overline{x}} $$\n 离散系数越大，数据离散程度越大\n 3.偏态与峰态   偏态 SK：数据对称性测度\n SK = 0 分布对称\nSK \u0026gt; 0 右偏\nSK \u0026lt; 0 左偏\nSK \u0026gt; 1或者SK \u0026lt; -1 高度偏态分布\nSK在0.5~1或-1~-0.5 之间，中等偏态分布\nSK越接近0，偏斜程度越小\n   峰态 K：数据分布平峰或尖峰程度的测量\n K \u0026gt; 0 尖峰分布，数据分布更集中\nK \u0026lt; 0 扁平分布，数据分布越分散\n   第五章 概率与概率分布 1.正态分布 X服从正态分布，记作X~N($\\mu,\\sigma^2$)\n\n $\\mu决定图形中心位置，\\sigma决定曲线陡峭程度$\n 2. 标准正态分布 当$\\mu=0，\\sigma=1$时 ，X~N(0,1)，即X服从标准正态分布\n第六章 统计量及其抽样分布 1.由正态分布导出的几个重要分布   卡方分布\n\n   E(Y) = n ,D(Y) = 2n\n   t分布\n\n   小样本方法\nn≥2，E(t) = 0\nn ≥ 3, D(t) = $v_s=\\frac{n}{n-2}$\n   F分布\n\n   　方差分析，回归方程的显著性检验\nn\u0026gt;2，$E(X)=\\frac{n}{n-2}$\nn\u0026gt;4，$D(X)=\\frac{2n^2(m+n-2)}{m(n-2)(n-4)}$\n  如果随机变量X服从t(n)分布，则$X^2$服从F(1,n)的F分布=\u0026gt;回归分析回归系数显著性检验\n 2.中心极限定理 定义：设从均值为$\\mu$、方差为$\\sigma^2$(有限)的任意一个总体中抽取样本量为n的样本，当n充分大时(n≥30)，样本均值$\\overline{x}$的抽样分布近似服从从均值为$\\mu$,方差为$\\frac{\\sigma^2}{n}$的正态分布\n第七章 参数估计 1.参数估计基本原理 置信区间：在区间估计中，由样本统计量所构造的总体参数的估计区间称为置信区间\n置信水平：如果将构造置信区间的步骤重复多次，置信区间中包含总体参数真值的次数所占的比例称为置信水平(置信度或置信系数)\n  如果用某种方法构造的所有区间中有95%的区间包含总体参数的真值，5%的区间不包含，那么，用该方法构造的区间称为置信水平为95%的置信区间\n  总体参数的真值是固定的、未知的，而样本构造的区间则是不固定的。因此，置信区间是一个随机区间，因样本的不同而不同\n  实际问题中，进行估计时往往只抽取一个样本。只是一个特定区间而不再是随机区间，所以无法知道这个样本所产生的区间是否包含总体参数的真值。\n比如，用95%的置信水平得到某班学生考试成绩的置信区间为60~80分，我们不能说60~80分这个区间以95%的概率包含全班学生平均考试成绩的真值，或者说全班学生的平均考试成绩以95%的概率落在60~80分之间，这类表述是错误的，因为总体均值p是一个常数，而不是一个随机变量。p要么落在这个范围内，要么不在这个范围内，这里并不涉及概率。我们只是知道在多次抽样中有95%的样本得到的区间包含全班学生平均考试成绩的真值。它的真正意义是如果做了100次抽样，大概有95次找到的区间包含真值，有5次找到的区间不包含真值。假定全班考试成绩平均数的真值为70分，60~80分这个区间一定包含真值，如果全班考试成绩平均数的真值为50分，那么区间60~80分就绝对不包含真值，无论做多少次试验。因此，这个概率不是用来描述某个特定的区间包含总体参数真值的可能性，而是针对随机区间而言的。一个特定的区间\u0026quot;总是包含\u0026quot;或\u0026quot;绝对不包含\u0026quot;参数的真值，不存在\u0026quot;以多大的概率包含总体参数\u0026quot;的问题。但是，用概率可以知道在多次抽样得到的区间中大概有多少个区间包含参数的真值。\n评价估计量的标准：无偏性、有效性、一致性\n  2.一个总体参数的区间估计   总体均值的区间估计\n“正态总体、方差已知或非正态总体、大样本”\n总体均值μ在1- α 置信水平下的置信区间为： $$ \\overline{x}±z_{α/2}\\frac{σ}{\\sqrt n} $$\n 置信水平1- α = 95%，$z_{α/2}$=1.96\n 例题：\n\n  总体比例的区间估计 $$ p±z_{α/2}\\sqrt {\\frac {p(1-p)}{n}} $$\n p是样本比例\nα 是显著性水平\n$z_{α/2}$ 是标准正态分布右侧面积为α/2是的z值\n$z_{α/2}\\sqrt {\\frac {p(1-p)}{n}}$是估计总体比例时的估计误差\n   例题：\n第八章 假设检验 1.假设检验的基本问题   原假设，备择假设：原假设与备择假设互斥\n  两类错误\n 第 Ⅰ类错误是原假设$H_0$为真却被拒绝，犯这种错误的概率用α 表示，也称α 错误或弃真错误 第Ⅱ类错误是原假设为伪却没有拒绝，犯这种错误的概率用 β表示也称β错误或取伪错误    假设检验流程\n  σ已知，大样本\n|z|\u0026lt;|$z_α/2$|，不拒绝$H0$\n|z|\u0026gt;|$z_α/2$|，拒绝$H0$\n  利用P值(事先给定α=0.05)\n 双侧检验：P\u0026gt;0.025不拒绝原假设，P\u0026lt; 0.025拒绝原假设 单侧检验：P\u0026gt;0.05不拒绝原假设，P\u0026lt; 0.05拒绝原假设      单侧检验\n一些情况下，我们关心的假设问题带有方向性\n 数值越大越好，使用寿命等，左单侧检验 数值越小越好，不合格率等，右单侧检验     正确选择双侧检验和单侧检验\n 2.一个总体参数的检验   总体均值的检验\n  样本量大\n|z| 与 |$z_α$|\n  小样本，σ已知\n|z| 与 |$z_α$| 或者 P值\n  小样本，σ未知\nt 与 $t_α/2$\n    总体比例的检验\n当α=0.05时，$z_α/2$=±1.96，比较|z| 与 |$z_{α/2}$|\n  第九章 分类数据分析 1.拟合优度检验 根据总体的分布状况，计算出分类变量中各类别的期望频数，与分布的观察频数进行对比，判断期望频数与观察频数是否有显著差异，从而达到对分类变量进行分析的目的。\n在泰坦尼克号的例子中，我们关注在这次海难中幸存者的性别是否有显著差异，当时船上共有2208人，其中男性1738人，女性470人。海难发生后，幸存者共718人，其中男性374人，女性344人。海难后存活比率为 718/2 208=0.325.如果是否活下来与性别没有关系，那么按照这个比率，在1738位男性中应该存活1738×0.325=565人，在470位女性中应该存活 470×0.325=153人。565和153就是期望频数，而实际存活结果就是观察频数。通过期望频数和观察频数的比较，能够从统计角度做出存活与性别是否有关的判断。\n 原假设：一致\n 2.独立性检验 独立性检验就是分析列联表中的行变量和列变量是否相互独立，是否存在依赖关系\n 原假设：不存在依赖关系\n 第十章 方差分析 1.单因素方差分析 方差分析(ANOVA)：通过检验各总体的均值是否相等来判断分类型自变量对数值型因变量是否有显著影响\n因素(因子)：方差分析中所要检验的对象\n水平(处理)：因素的不同表现\n单因素方差分析：只有一个因素的方差分析\n 例如，行业为因素，零售业、旅游业、家电制造业等属于水平\n 总平方和 SST(sum of squares for total)：全部观测值与总均值的误差平方和。\n组间平方和 SSA(sun of squares for factor A)：各组均值与总均值的误差平方和，反映个样本均值之间的差异程度，因此又称为因素平方和。\n组内平方和 SSE(sum of squares for error)：每个水平或组的各样本数据与其总均值的误差平方和，反映每个样本各观测值的离散状况，因此又称误差平方和。 $$ SST = SSA + SSE $$\n方差分析表\n   误差来源 平方和SS 自由度df 均方MS F值 P值 F临界值     组间（因素影响） SSA k-1 MSA MSA/MSE     组内（误差） SSE n-k MSE      总 和 SST n-1         n为全部观测值个数 ；k为因素水平(总体)的个数；MS=SS / df\n 2.双因素方差分析 第十一章 一元线性回归 相关系数：根据样本数据计算的度量两个变量之间线性关系强度的统计量\nρ：总体相关系数，根据总体全部数据计算的\nr：样本相关系数，根据样本数据计算的\n [-1,0) ==\u0026gt; 负线性相关\n(0,1] ==\u0026gt; 正线性相关\nr = -1==\u0026gt;完全负线性相关关系\nr=1 ==\u0026gt;完全正线性相关关系\n相关程度：\n 第十三章 时间序列分析和预测 1.时间序列及其分解 时间序列：同一现象在不同时间的相继观察值排列而成的序列，分为平稳序列和非平稳序列\n趋势：时间序列在长期内呈现出来的某种持续上升或持续下降的变动\n季节性（季节变动）：时间序列在一年内重复出现的周期性波动。\n2.增长率分析   增长率：也称增长速度，是时间序列中报告期观察值与基期观察值之比减1后的结果，用%表示\n 环比增长率：报告期观察值与前一时期观察值之比减1的结果，说明现象逐期增长变化的程度 $$ G_i=\\frac{Y_i-Y_{i-1}}{Y_{i-1}}=\\frac{Y_i}{Y_{i-1}}-1，i=1,2,\u0026hellip;,n $$    定基增长率：报告期观察值与某一固定时期观察值之比减1的结果，说明现象在整个观察期内总的增长变化程度 $$ G_i=\\frac{Y_i-Y_0}{Y_0}=\\frac{Y_i}{Y_0}-1，i=1,2,\u0026hellip;,n $$\n  平均增长率：也称平均增长速度，时间序列中逐期环比值（也称环比发展速度）的几何平均数减1后的结果 $$ \\overline{G}=\\sqrt[n]{\\frac{Y_n}{Y_0}-1} $$\n $\\overline{G}$表示平均增长率；n表示环比值的个数\n   第十四章 指数 1.简单指数 简单综合指数：将报告期的指数总和与基期的指标总和相对比的指数 $$ I_p=\\frac{\\sum p_1}{\\sum p_0} $$ $$ I_q=\\frac{\\sum q_1}{\\sum q_0} $$\n ｐ——质量指标 \u0026gt; \u0026gt; ｑ——数量指标 \u0026gt; \u0026gt; $I_p$——质量指标指数 \u0026gt; \u0026gt; $I_q$——数量指标指数 \u0026gt; \u0026gt; 下标1——报告期 \u0026gt; \u0026gt; 下标0——基期\n 简单平均指数： $$ I_p=\\frac{\\sum \\frac{p_1}{p_0}}{n} $$ $$ I_q=\\frac{\\sum \\frac{q_1}{q_0}}{n} $$\n2.加权指数 加权综合指数\n  拉氏指数：将作为权数的同度量因素固定在基期 $$ I_q=\\frac{\\sum q_1p_0}{\\sum q_0p_0} $$ $$ I_p=\\frac{\\sum q_0p_1}{\\sum q_0p_0} $$\n  帕氏指数：将作为权数的同度量因素固定在报告期 $$ I_q=\\frac{\\sum q_1p_1}{\\sum q_0p_1} $$\n$$ I_q=\\frac{\\sum q_1p_1}{\\sum q_1p_0} $$\n 大多数的看法是，计算数量指数（如生产量指数）时，权数（价格）应该定在基期，这样才能剔除价格变动的影响，准确反映生产量的变化，按不变价计算产量指数就是出于这个原因。计算质量指数（如价格指数）时，不同时期的权数含义不同：若权数定在基期，反映的是在基期商品（产品）结构下价格的整体变动，更能揭示价格变动的内容；若权数定在报告期，反映的是在现实商品（产品）结构下价格的整体变动，商品（产品）结构变化的影响会融入价格指数，更能揭示价格变动的实际影响。编制指数的目的不同，权数确定的时期就可以不同。\n   加权平均指数\n。。。\n指数计算 采用加权平均的方法 $$ I_p=\\frac{\\sum iW}{\\sum W} $$\n i为代表规格品个数指数或各层的类指数；\nW为相应的消费支出比重\n ","permalink":"https://blog.archai.space/p/%E7%BB%9F%E8%AE%A1%E5%AD%A6%E6%9C%9F%E6%9C%AB%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/","summary":"第一章 导 论 1.什么是统计学  统计学是收集、处理、分析、解释数据并从数据中得出结论的科学 数据分析所用的方法可分为描述统计方法和推断统计方法  2.参数和统计量   参数：总体特征，所关心的参数通常是总体平均数、总体标准差、总体比例等\n 由于总体数据通常是不知道的，所以参数是一个未知的常量-\n   统计量：个体特征，所关心的参数通常是样本平均数、样本标准差、样本比例等\n 由于样本是已经抽出来的，所以统计量总是知道的\n   第二章 数据的搜集 1.数据的来源  **间接来源（二手数据）**与研究内容有关的原信息已经存在，我们只是重新加工、整理，使之成为分析可用的数据，这称为间接来源的数据 **直接来源（一手数据）**通过调查方法获得的为调查数据，实验得到为实验数据，都是直接来源的数据  2.数据误差  抽样误差：由抽样的随机性引起的样本结果与总体真值之间的差异 非抽样误差：抽样框误差、回答误差、无回答误差、调查员误差、测量误差  第三章 数据的图表展示 1.数据预处理  原始数据：完整性、准确性 二手数据：适用性、时效性  2.分类数据图示 ​  1\u0026gt; 组数 5≤ K ≤15\n2\u0026gt; 组距 组距=(Max-Min)/K\n3\u0026gt; 为解决不重的问题，统计分组习惯上规定“上组限不在内”，即当相邻两组的上下限重叠时，恰好等于某一组上限的变量值不算在本组内，而计算在下一组内。即a≤ x \u0026lt;b\n 第四章 数据的概括性度量 1.集中趋势的度量 \n  众数不受极端值影响，具有不唯一性 中位数不受极端值影响，数据分布偏斜程度较大时 平均数易受极端值影响   2.离散程度的度量","title":"统计学期末知识总结"},{"content":"模块功能主要由两个命令构成：export和import。export命令用于规定模块的对外接口，import命令用于输入其他模块提供的功能。\n//profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export function sayName () { return firstName + lastName; } export default function () { console.log('foo'); } // main.js import { firstName, lastName , sayName} from './profile.js';  注意：在index.html中引入的时候需要给script加type=\u0026ldquo;module\u0026rdquo;,即\n \u0026lt;script type=\u0026quot;module\u0026quot; src=\u0026quot;./profiles.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script type=\u0026quot;module\u0026quot; src=\u0026quot;./main.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt; \n","permalink":"https://blog.archai.space/p/%E5%85%B3%E4%BA%8Ejavascript%E6%A8%A1%E5%9D%97%E5%8C%96/","summary":"模块功能主要由两个命令构成：export和import。export命令用于规定模块的对外接口，import命令用于输入其他模块提供的功能。\n//profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export function sayName () { return firstName + lastName; } export default function () { console.log('foo'); } // main.js import { firstName, lastName , sayName} from './profile.js';  注意：在index.html中引入的时候需要给script加type=\u0026ldquo;module\u0026rdquo;,即\n \u0026lt;script type=\u0026quot;module\u0026quot; src=\u0026quot;./profiles.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script type=\u0026quot;module\u0026quot; src=\u0026quot;./main.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;","title":"关于Javascript模块化"},{"content":"1. 网页出现横向滚动条？ \u0026lt;body style=`overflow:-Scroll;overflow-y:hidden \u0026gt; \u0026lt;/body\u0026gt; 让横条没有：\n \u0026lt;body style=`overflow:-Scroll;overflow-x:hidden\u0026gt; \u0026lt;/body\u0026gt; 两个都去掉？更简单了\n\u0026lt;body scroll=\u0026quot;no\u0026quot; \u0026gt; \u0026lt;/body\u0026gt; 火狐底部滚动条不显示：\n html { overflow:-moz-scrollbars-vertical; } 2. 网页图标？ \u0026lt;link rel=\u0026quot;shortcut icon \u0026quot; href=\u0026quot;images/favicon.ico\u0026quot;\u0026gt; 3. Height:100%失效？ \u0026lt;html style=\u0026quot;height: 100%;\u0026quot;\u0026gt; \u0026lt;body style=\u0026quot;height: 100%;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;height: 100%;\u0026quot;\u0026gt; \u0026lt;p\u0026gt; 这样这个div的高度就会100%了 \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 4. 设置一个元素不可见？ opacity 设为 0、将 visibility 设为 hidden、将 display 设为 none 或者将 position 设为 absolute\n注意：\n1\u0026gt; display=none元素消失，不占位置\n2\u0026gt; opacity =0、 visibility =hidden只是视觉上不可见，其实还在那里占位置\n5.input无法输入？ \n==\u0026gt; height:0; (将包裹提示的div高度设置为0，让其不再遮挡即可)\n6.input标签显示手势？ input{ cursor:pointer; } /* 鼠标移入按钮范围时出现手势 */* 7. JS中的一些语法 1\u0026gt; 注意书写其中的 \u0026ldquo;\u0026quot;（引号） ！！！！\ndocument.getElementById(\u0026quot;btn\u0026quot;); 2\u0026gt;onclick,.onload\u0026hellip;等事件都可以用这个，可以避免代码污染啥的（也不一定）\n.addEventListener('click',function f1() {}** 3\u0026gt;可以给一个标签中加入另一个标签\ncreateElement('')*** 8.addEventListener \u0026amp; onmouse×××的区别：  onmouseover   \u0026lt;!--CSS部分--\u0026gt; *{margin: 0;padding: 0;border: none;} ##progress{width: 1000px;height: 20px;line-height: 20px; /*background-color: ##e8e8e8;*/ margin: 100px auto;position: relative; } ##progress_bar{width: 900px;height: 100%;background-color: ##ccc;border-radius: 8px; position: relative;} ##progress_value{position: absolute;right: 30px;top: 0;} ##progress_bar_fg{width: 0;height: 100%;background-color: purple; border-top-left-radius: 8px; border-bottom-left-radius: 8px;} span{ width: 10px; height: 30px; background-color: purple; position: absolute; left: 0; top: -5px; border-radius: 5px; cursor: pointer; } \u0026lt;!--HTML部分--\u0026gt; \u0026lt;div id=\u0026quot;progress\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;progress_bar\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;progress_bar_fg\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;progress_value\u0026quot;\u0026gt;0%\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!--JS部分--\u0026gt; window.onload = function () { // 1. 获取需要的标签 var progress = document.getElementById(\u0026quot;progress\u0026quot;); var progress_bar = progress.children[0]; var progress_bar_fg = progress_bar.children[0]; var mask = progress_bar.children[1]; var progress_value = progress.children[1]; // 2. 监听鼠标按下 mask.onmousedown = function (event) { var e = event || window.event; // 2.1 获取初始位置 var offsetLeft = event.clientX - mask.offsetLeft; // 2.2 监听鼠标的移动 document.onmousemove = function (event) { var e = event || window.event; // 2.3 获取移动的位置 var x = e.clientX - offsetLeft; // 边界值处理 if(x \u0026lt; 0){ x = 0; }else if(x \u0026gt;= progress_bar.offsetWidth - mask.offsetWidth){ x = progress_bar.offsetWidth - mask.offsetWidth; } // 2.4 走起来 mask.style.left = x + 'px'; progress_bar_fg.style.width = x + 'px'; progress_value.innerHTML = parseInt(x / (progress_bar.offsetWidth - mask.offsetWidth) * 100) + '%'; return false; }; // 2.5 监听鼠标抬起 document.onmouseup = function () { document.onmousemove = null; } } }  addEventListener  \n\u0026lt;!--CSS部分--\u0026gt; *{margin: 0;padding: 0;border: none;} ##progress{width: 1000px;height: 20px;line-height: 20px; /*background-color: ##e8e8e8;*/ margin: 100px auto;position: relative; } ##progress_bar{width: 900px;height: 100%;background-color: ##ccc;border-radius: 8px; position: relative;} ##progress_value{position: absolute;right: 30px;top: 0;} ##progress_bar_fg{width: 0;height: 100%;background-color: purple; border-top-left-radius: 8px; border-bottom-left-radius: 8px;} span{ width: 10px; height: 30px; background-color: purple; position: absolute; left: 0; top: -5px; border-radius: 5px; cursor: pointer; } \u0026lt;!--HTML部分--\u0026gt; \u0026lt;div id=\u0026quot;progress\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;progress_bar\u0026quot;\u0026gt; \u0026lt;div id=\u0026quot;progress_bar_fg\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026quot;progress_value\u0026quot;\u0026gt;0%\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!--JS部分--\u0026gt; // addEventListener(); // removeEventListener(); window.addEventListener('load', function (ev) { // 1. 获取标签 var progress = document.getElementById('progress'); var progressBar = progress.children[0]; var progressBarFg= progressBar.children[0]; var mask= progressBar.children[1]; var progressValue= progress.children[1]; // 2. 监听鼠标在mask上面的按下 mask.addEventListener('mousedown', function (evt) { var e = evt || window.event; // 2.1 获取按下的坐标 var pointX = e.pageX - mask.offsetLeft; // 2.2 监听鼠标的移动 document.addEventListener('mousemove', function (ev1) { var e = ev1 || window.event; // 2.3 获取水平方向移动的距离 var x = e.pageX - pointX; if(x \u0026lt; 0){ x = 0; }else if(x \u0026gt; progressBar.offsetWidth - mask.offsetWidth){ x = progressBar.offsetWidth - mask.offsetWidth } // 2.4 走起来 mask.style.left = x + 'px'; progressBarFg.style.width = x + 'px'; progressValue.innerText = parseInt(x / (progressBar.offsetWidth - mask.offsetWidth)* 100) + '%'; return false; }, false) }, false); // 3. 监听鼠标松开 /* document.onmouseup = function (ev1) { document.addEventListener('mousemove', null); };*/ document.addEventListener('mouseup', function (ev1) { document.removeEventListener('mousemove', null, false); }, false); }); 9.手机端开发 务必写上\n\u0026lt;!-简写meta:vp 按Tab即可---\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; ","permalink":"https://blog.archai.space/p/%E5%B8%B8%E7%94%A8%E7%9A%84%E4%B8%80%E4%BA%9Bhtml%E5%B0%8F%E9%97%AE%E9%A2%98/","summary":"1. 网页出现横向滚动条？ \u0026lt;body style=`overflow:-Scroll;overflow-y:hidden \u0026gt; \u0026lt;/body\u0026gt; 让横条没有：\n \u0026lt;body style=`overflow:-Scroll;overflow-x:hidden\u0026gt; \u0026lt;/body\u0026gt; 两个都去掉？更简单了\n\u0026lt;body scroll=\u0026quot;no\u0026quot; \u0026gt; \u0026lt;/body\u0026gt; 火狐底部滚动条不显示：\n html { overflow:-moz-scrollbars-vertical; } 2. 网页图标？ \u0026lt;link rel=\u0026quot;shortcut icon \u0026quot; href=\u0026quot;images/favicon.ico\u0026quot;\u0026gt; 3. Height:100%失效？ \u0026lt;html style=\u0026quot;height: 100%;\u0026quot;\u0026gt; \u0026lt;body style=\u0026quot;height: 100%;\u0026quot;\u0026gt; \u0026lt;div style=\u0026quot;height: 100%;\u0026quot;\u0026gt; \u0026lt;p\u0026gt; 这样这个div的高度就会100%了 \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 4. 设置一个元素不可见？ opacity 设为 0、将 visibility 设为 hidden、将 display 设为 none 或者将 position 设为 absolute\n注意：\n1\u0026gt; display=none元素消失，不占位置\n2\u0026gt; opacity =0、 visibility =hidden只是视觉上不可见，其实还在那里占位置\n5.input无法输入？","title":"常用的一些html小问题"},{"content":"Jquery属性操作 1.属性 attr(attrName [,attrValue]) 操作所有属性（自定义和内置的）\nprop(attrName [,attrValue)) 操作HTML元素内置属性\nremoveAttr(attrNam)删除属性\nremoveProp(attrName) 并不能删除HMTL元素上的属性\n2.CSS类 addclass()添加一个class值\nremoveClass()删除一个class值\ntoggleClass()切换一个class值(有则删掉该class，没有则加上，其他class不动)\nhasClasss() 判断是否有指定class\n3.HTML代码/文本/值 html([html]) 相当于innerHTML\ntext([text)相当于innerText\nval([value]) 设置/获取表单控件的值\nJquery样式操作 1.CSS操作 css(atr,[value])设置/获取CSS值\n 参数可以是一个对象的形式css({atr: value,})\n 2.位置 offset()[.left/.top]元素在页面中的坐标\n 设置只需要传一个对象即可 {\u0026quot;left:num,top:num\u0026quot;}\n position()[.left/.top] 元素在第一个定位的祖先元素内的坐标 (只读！)\nscollTop \u0026hellip;\nscollLeft \u0026hellip;\n3.尺寸 width()/height()内容尺寸\ninnerwidth()/ innerHeight()内容尺寸+ padding\nouterWidth()/ outerHeight() 盒子的尺寸\n\nJquery筛选操作 1. 过滤操作 first ()\nlast() \neq()\nnot() \nfilter()\nslice ()\nhas()\n3.串联 add()把选中的元素加入当前集合\naddBack()把调用该方法的元素加入当前集合\nend()返回最后一次破坏性操作之前的DOM\ncontents()返回所有子节点的集合\n4 jQuery DOM对象操作 each()遍历\nmap()返回新的集合\nlength集合中元素的数量index（返回该元素在父元素中的索引位置）\nget([index])返回集合指定索引的dom对象，还可以把jquerydom集合转为纯数组没参数\n文档处理 1.内部插入 append(content|fn)\nappendTo(content)\nprepend(content|fn)\nprependTo(content)\n2.外部插入 after(content|fn)\nbefore(content|fn)\ninsertAfter(content)\ninsertBefore(content\n3.包裹 wrap(html|ele|fn)\nunwrap()\nwrapAll(html|ele)\nwrapInner(html|ele|fn)\n4.替换 replaceWith(content|fn)\nreplaceAll(selector)\n5.删除 empty()\nremove([expr])\ndetach([expr])\n6.复制 clone([Even[,deepEven]])\njQuery事件 1.事件绑定 on(event,fn) 标准的事件绑定方式\n on({}) 可以同时绑定多个事件\n one(event,fn) 只能绑定一次事件\n 把事件名作为对象\n 2.解除事件绑定 off([\u0026quot;event\u0026quot;]) 可以去除所有，也可以解除指定事件\n3.事件委派 on(event,selector,fn) 给父元素添加事件\n 给新添加的元素绑定与之前存在的元素相同的方法（利用冒泡）\n 4. 控制事件触发 trigger()\ntriggerHandler()\n ① trigger返回的是 jqDOM可以连贯操作 ② trigger可以触发元素自带的事件( input自带的事件，focus，submit\u0026hellip;) ③ trigger会触发集合中所有元素的事件；trigger值触发集合中第一个元素的事件\n 5.事件列表（新增） ready 页面中DOM加载完毕（不同于onload）\nfocusin 获取焦点，绑定给输入框的父元素\nfocusout 失去焦点 ，绑定给输入框的父元素\nmouseenter 代替mouseover\nmoouseleave 代替mouseout\nhover moouseleave和mouseenter集合\n6.事件对象 pageX 鼠标x坐标\npageY 鼠标y坐标\ntarget 当前触发事件的元素\nwhich 键盘按键的ASCII码\ntype 事件类型（事件名称）\npreventDefault阻止默认操作（例如a标签跳转，致使页面刷新） \nJquery动画 基本效果 hide([time,fn]) 隐藏\nshow([time,fn]) 显示\ntoggle([time,fn])\n 基本效果的CSS属性变化：透明度变化，元素大小相关的样式 padding, border width/height外边距\n  fn() 表示动画结束执行的函数\n 滑动效果 slidedown 显示\nslideup 隐藏\nslidetoggle \n 垂直方向的变化\n 淡入淡出效果 fadeOut() 隐藏\nfadeIn() 显示\nfadeToggle()\nfadeTo(time,opicity,fn)\n自定义动画  animate({},time,fn)   {}中为需要变化的值。 取值可以是toggle，可变化可恢复原样\n stop() 暂停动画\nfinish() 结束动画（提前完成）\ndelay(time) 延迟动画（动画连续调用时加在中间，会等time时间执行后面的）\n动画设置 jQuery. fx. off 清除所有动画，恢复为默认效果\njQuery. fx interval 动画帧数\n动画队列  所有的动画操作会加入到队列中，依次执行 其他操作不会加入动画队列  jQuery动画与CSS3动画  兼容性，CS53的动画和过渡需要E9+， jQuery可以使用版本的 CSS3的动画或者过渡必须给元素指定具体的CSS属性值  jQuery工具方法 1.数组对象方法 $.each（Aray,fn）遍历数组或类数组对象\n$.grep（Array, fn）过滤数组\n$.map（ Array, fn）从数组取出信息，返回新的数组\n$.makeArray(likeArray) 把类数组对象转化为纯数组\n$inArray(val，Array) 判断元素在数组中的位置，不存在返回-1\n$.merge() 合并数组\ntoArray() jQuery DOM方法，把jQueryDOM直接转化为纯数组\n2.函数方法 $.proxy() 改变函数中this指向\n3.类型判断  $.type()判断类型\n $.isFunction()判断是否是函数/方法\n $.isEmptyobject()判断是否是空的对象\n $.Plainobject()判断是否是纯的对象（构造函数是obeject)\n $.window()判断是否是 window对象\n $.isNumeric()判断是否是数字(NaN虽然是number类型，但这个是false)\n4.字符串 $.trim()取出两边的空格 $.param() 把对象序列化成字符串\n {name:\u0026quot;lili\u0026quot;,age:18} \u0026mdash;-\u0026gt;name=lili\u0026amp;age=18\n 5. 版本 $.fn.jquery\njQuery Ajax 1.快速请求方法   get\n$.get(url,callback,[,datatype])\n  post\n$.post(url,[,data],callback,[,datatype])`\n  2.ajax方法   ajax()\n$.ajax{ url:,//地址 type:\u0026quot;get\u0026quot;\u0026quot;post\u0026quot;, //请求方式 async:,//是否异步 data:,//发送的数据 对象或字符串(序列化) dataType:,//响应的内容格式 success://成功回调 error://失败回调 }   3.表单方法 serialize() 把表单中含有name属性的表单控件的值拼接成字符串(序列化表单)\njQuery插件 1. select2 下拉框搜索插件   官网 http://select.org/\n  Github http://github.com/select2/select2\n  用法\n$(dom).select2() $(dom).select2({ width:, data:, ajax:, ... })   2.datetimepicker 时间日期插件   github https://github.com/xdan/datetimepicker\n  用法\n//设置语言 $.datetimepicker.setLocal(\u0026quot;zh\u0026quot;); //调用插件 $(dom).datetimepicker({ datepicker:, timepicker:,//false||true format:\u0026quot;Y-m-d H:i\u0026quot;//H表示24小时制，h表示12小时制 value:, ... })   3.全屏滚动插件   官网 https://alvarotrigo.com/fullPage/zh/\n  Github说明 https://github.com/alvarotrigo/fullPage.js/tree/master/lang/chinese#fullpagejs\n  用法\n\u0026lt;!--HTML部分--\u0026gt; \u0026lt;div id=\u0026quot;fulPage\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;section\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;section\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;slide\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;slide\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;slide\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;section\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!--自定义的导航，写在包裹元素的外面--\u0026gt; \u0026lt;!--JS部分--\u0026gt; \u0026lt;script\u0026gt; $(\u0026quot;#fullPage\u0026quot;).fullpage({ navigation:true, sectionsColor:[] ... }) \u0026lt;/script\u0026gt;   4.lazeload 图片懒加载   官网 https://appelsiini.net/projects/lazyload/\n  Github https://github.com/tuupola/lazyload/tree/2.x\n  用法：\n   $(\u0026quot;#lazyWrapper img\u0026quot;).lazyload() 5.layer弹窗插件   官网 http://layer.layui.com/?alone\n  用法\nlayer.alert() layer.confirm() layer.msg() layer.load() layer.tips() layer.colse() layer.open({ type:, title:, content: ... }) ...   6.nice validator 表单验证   官网 https://validator.niceue.com/\n  使用\n$(\u0026quot;form\u0026quot;).validator   7.jQuery-easing   官网 http://gsgd.co.uk/sandbox/jquery/easing/\n  用法\n$(dom).hide(speed,easing,fn)   自定义插件   jQuery.fn.extend() 给jQuery扩展方法\n$.fn.extend({ 方法名:function(){} }) //或者 $.fn.方法名=function(){}   jQuery.extend() 给jQuery对象本身扩展方法\n$.extend({ 方法名:function(){} })   jQuery官网 jQuery UI 官网 https://jqueryui.com/\njQuery Mobile 官网 https://jquerymobile.com/\n教程 http://www.runoob.com/jquerymobile/jquerymobile-tutorial.html\nSizzle 官网 https://sizzlejs.com/\nZepto 官网 http://zeptojs.com\njquery区别：https://www.zhihu.com/question/25379207\n","permalink":"https://blog.archai.space/p/jquery%E4%B8%80%E4%BA%9B%E6%93%8D%E4%BD%9C/","summary":"Jquery属性操作 1.属性 attr(attrName [,attrValue]) 操作所有属性（自定义和内置的）\nprop(attrName [,attrValue)) 操作HTML元素内置属性\nremoveAttr(attrNam)删除属性\nremoveProp(attrName) 并不能删除HMTL元素上的属性\n2.CSS类 addclass()添加一个class值\nremoveClass()删除一个class值\ntoggleClass()切换一个class值(有则删掉该class，没有则加上，其他class不动)\nhasClasss() 判断是否有指定class\n3.HTML代码/文本/值 html([html]) 相当于innerHTML\ntext([text)相当于innerText\nval([value]) 设置/获取表单控件的值\nJquery样式操作 1.CSS操作 css(atr,[value])设置/获取CSS值\n 参数可以是一个对象的形式css({atr: value,})\n 2.位置 offset()[.left/.top]元素在页面中的坐标\n 设置只需要传一个对象即可 {\u0026quot;left:num,top:num\u0026quot;}\n position()[.left/.top] 元素在第一个定位的祖先元素内的坐标 (只读！)\nscollTop \u0026hellip;\nscollLeft \u0026hellip;\n3.尺寸 width()/height()内容尺寸\ninnerwidth()/ innerHeight()内容尺寸+ padding\nouterWidth()/ outerHeight() 盒子的尺寸\n\nJquery筛选操作 1. 过滤操作 first ()\nlast() \neq()\nnot() \nfilter()\nslice ()\nhas()\n3.串联 add()把选中的元素加入当前集合\naddBack()把调用该方法的元素加入当前集合\nend()返回最后一次破坏性操作之前的DOM","title":"Jquery一些操作"},{"content":"1.通过id获取元素 function $(id) { return typeof id === \u0026#39;string\u0026#39; ? document.getElementById(id): null; } 2.日期格式化 function formatDate(cDate) { //1.判断  if (!cDate instanceof Date){ return; } //2.转化  var year=cDate.getFullYear(); var month=cDate.getMonth(); var date=cDate.getDate(); var hour=cDate.getHours(); var minute=cDate.getMinutes(); var second=cDate.getSeconds(); //2.1 补0  month=month\u0026lt;10? \u0026#39;0\u0026#39;+month:month; date=date\u0026lt;10? \u0026#39;0\u0026#39;+date: date; minute=minute\u0026lt;10? \u0026#39;0\u0026#39;+minute:minute; second=second\u0026lt;10? \u0026#39;0\u0026#39;+second:second; return year + \u0026#39;-\u0026#39; + month + \u0026#39;-\u0026#39; + date + \u0026#39; \u0026#39; +hour+\u0026#39;:\u0026#39;+minute+\u0026#34;:\u0026#34;+second; } console.log(formatDate(new Date())); 3.匀速运动封装 /** * 设置一个盒子右边距匀速变化 * @param{string} btnId * @param{string} boxId * @param{number} step * @param{number}target */ function linearAnimation(btnId, boxId, step, target) { // 1. 获取需要的标签  var btn = document.getElementById(btnId); var box = document.getElementById(boxId); // 2. 定义变量  var timer = null, begin = 0; // 3. 监听按钮的点击  btn.onclick = function () { // 3.1 清除定时器  clearInterval(timer); // 3.2 设置定时器  timer = setInterval(function () { // 相加  begin += step; // 判断  if(begin \u0026gt;= target){ begin = target; clearInterval(timer); } // 动起来  box.style.marginLeft = begin + \u0026#39;px\u0026#39;; }, 100); } } 4.阻止冒泡 if(event \u0026amp;\u0026amp; event.stopPropagation){ // w3c标准  event.stopPropagation(); }else{ // IE系列 IE 678  event.cancelBubble = true; } 5.选中内容获取 var selectedText; if(window.getSelection){ // 标准模式 获取选中的文字  selectedText = window.getSelection().toString(); }else{ // IE 系列  selectedText = document.selection.createRange().text; } 6.JS继承  代码如下:\nfunction Temp(){}; Temp.prototype=Person.prototype; var stuProto=new Temp(); Student.prototype=stuProto.prototype; stuProto.constructor=Student; ","permalink":"https://blog.archai.space/p/javascript%E4%B8%80%E4%BA%9B%E6%93%8D%E4%BD%9C/","summary":"1.通过id获取元素 function $(id) { return typeof id === \u0026#39;string\u0026#39; ? document.getElementById(id): null; } 2.日期格式化 function formatDate(cDate) { //1.判断  if (!cDate instanceof Date){ return; } //2.转化  var year=cDate.getFullYear(); var month=cDate.getMonth(); var date=cDate.getDate(); var hour=cDate.getHours(); var minute=cDate.getMinutes(); var second=cDate.getSeconds(); //2.1 补0  month=month\u0026lt;10? \u0026#39;0\u0026#39;+month:month; date=date\u0026lt;10? \u0026#39;0\u0026#39;+date: date; minute=minute\u0026lt;10? \u0026#39;0\u0026#39;+minute:minute; second=second\u0026lt;10? \u0026#39;0\u0026#39;+second:second; return year + \u0026#39;-\u0026#39; + month + \u0026#39;-\u0026#39; + date + \u0026#39; \u0026#39; +hour+\u0026#39;:\u0026#39;+minute+\u0026#34;:\u0026#34;+second; } console.log(formatDate(new Date())); 3.匀速运动封装 /** * 设置一个盒子右边距匀速变化 * @param{string} btnId * @param{string} boxId * @param{number} step * @param{number}target */ function linearAnimation(btnId, boxId, step, target) { // 1.","title":"Javascript一些操作"},{"content":"1. 获取网页logo 京东 : www.jd.com\n 后面加 \u0026ldquo;/favicon.ico\u0026quot;即可\n 京东logo : www.jd.com/favicon.ico\n2. webstorm网页logo简易写法  link:favicon 按Tab键  3.meta标签  为搜索引擎提供的关键字列表( name=\u0026ldquo;keywords\u0026rdquo;)：   各关键词间用英文逗号“,”隔开。META的通常用处是指定搜索引擎用来提高搜索质量的关键词。\n \u0026lt;meta name=\u0026quot;keywords\u0026quot; content=\u0026quot;牙疼怎么办，智齿是什么，拔牙多少钱，矫正多少钱，九院医生，北大口腔，瑞尔口腔，拜耳口腔\u0026quot;\u0026gt;\n 用来告诉搜索引擎你的网站主要内容(name=\u0026ldquo;description\u0026rdquo;)：  \u0026lt;meta name=\u0026quot;description\u0026quot; content=\u0026quot;有牙齿问题,找河马牙医,儿童口腔，全国儿童口腔在线咨询，牙疼怎么办，拔牙多少钱，矫正多少钱，种植牙，有牙齿问题\u0026quot;\u0026gt;\n 优先以webkit内核渲染页面(\u0026ldquo;renderer\u0026rdquo;)：  \u0026lt;meta name=\u0026quot;renderer\u0026quot; content=\u0026quot;webkit\u0026quot; /\u0026gt;\n4.复合选择器 div#main{ color:green}\n 表示选中div中id为main的元素\n 5.标签居中  水平居中 行内标签/行内-块级标签: text-align:center  块级标签:\nmargin:0 auto\n2)垂直居中\n  行内标签/行内-块级标签:\n 设置行高为盒子高度line-hight:50px; 图片加文字时:vertical-align:middle;    块级标签:\n   设置“子绝父相” ==\u0026gt; top，left设置为50% ==\u0026gt; margin-left和nargin-top设置为负的盒子对应宽高的一半\n  .father{ background-color:red; height:150px; width:150px; position:relative; } .child{ background-color:yellow; height:50px; width:50px; position:absolute; left:50%; top:50%; margin-left:-25px; margin-top:-25px; } 6.text-transform属性 capitalize\t文本中的每个单词以大写字母开头。\nuppercase\t定义仅有大写字母。\nlowercase\t定义无大写字母，仅有小写字母。\n7.overflow属性 hidden\t内容会被修剪，并且其余内容是不可见的。\nscroll\t内容会被修剪，但是浏览器会显示滚动条以便查看其余的内容。\nauto 如果内容被修剪，则浏览器会显示滚动条以便查看其余的内容。\n8.box-sizing属性 border-box 设置边框和内边距盒子不会被撑大，即“向内挤”\n9. 高度塌陷的解决(+设置margin父元素被顶下来)  父元素的高度一旦塌陷, 所有标准流中元素的位置将会上移，导致整个页面的布局混乱\n 方案1：开启父元素的BFC 一般都是使用overflow:hidden来开启BFC，即父元素设置overflow:hidden\n方案2: 在塌陷的父元素的最后添加一个空白的div，然后对该div进行清除浮动 父元素之后加\u0026lt;div style=\u0026quot;clear:both\u0026quot;\u0026gt;\u0026lt;/div\u0026gt;\n方案3: 使用after伪类(注意设置content)，向父元素后添加一个块元素，并对其清除浮动 √ 添加CSS\n.clearfix:after{ content:\u0026quot;\u0026quot;; display:block; clear:both; }  子元素和父元素相邻的垂直外边距会发生重叠，子元素的外边距会传递给父元素，即给子元素设置margin父元素会被顶下来 解决方案： .clearfix::before{ content: ''; display: table; clear: both;}\n 最终，把要解决这两个问题的元素加class=\u0026quot;clearfix\u0026quot; ,然后设置\n.clearfix::before,.clearfix::after{ content: ''; display: table; clear: both; }  这是一个多功能的, 既可以解决高度塌陷，又可以确保父元素和子元素的垂直外边距不会重叠\n 10.双飞翼布局和圣杯布局 \n 双飞翼  \u0026lt;!--CSS部分--\u0026gt; *{ margin: 0; padding: 0; } .container{ min-width: 400px; background-color: #e91e63; height: 200px; } .main{ float: left; width: 100%; height: 200px; background-color: oldlace; } .left,.right{ /**************/ text-align: center; line-height: 200px; font-size: 100px; color: #fff; /*************/ height: 200px; width: 200px; background-color: lightgreen; float: left; } .left{ margin-left: -100%; } .right{ margin-left: -200px; } .main-content{ margin: 0 200px; } \u0026lt;!--HTML部分--\u0026gt; \u0026lt;div class=\u0026quot;container\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;main\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;main-content\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;left\u0026quot;\u0026gt;←\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;right\u0026quot;\u0026gt;→\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt;  圣杯  \u0026lt;!--CSS部分--\u0026gt; *{ margin: 0; padding: 0; } .container{ min-width: 400px; background-color: #e91e63; height: 200px; padding: 0 200px; } .main{ float: left; width: 100%; height: 200px; background-color: oldlace; } .left,.right{ /**************/ text-align: center; line-height: 200px; font-size: 100px; color: #fff; /*************/ float: left; height: 200px; width: 200px; background-color: lightgreen; } .left{ margin-left: -100%; position: relative; left: -200px; } .right{ margin-left: -200px; position: relative; right: -200px; } \u0026lt;!--HTML部分--\u0026gt; \u0026lt;div class=\u0026quot;container\u0026quot;\u0026gt; \u0026lt;div class=\u0026quot;main\u0026quot;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;left\u0026quot;\u0026gt;←\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026quot;right\u0026quot;\u0026gt;→\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; 11.溢出文字显示为省略号 overflow: hidden; text-overflow:ellipsis; white-space: nowrap; ","permalink":"https://blog.archai.space/p/%E5%B8%B8%E7%94%A8%E7%9A%84%E4%B8%80%E4%BA%9Bhtml%E4%BB%A3%E7%A0%81/","summary":"1. 获取网页logo 京东 : www.jd.com\n 后面加 \u0026ldquo;/favicon.ico\u0026quot;即可\n 京东logo : www.jd.com/favicon.ico\n2. webstorm网页logo简易写法  link:favicon 按Tab键  3.meta标签  为搜索引擎提供的关键字列表( name=\u0026ldquo;keywords\u0026rdquo;)：   各关键词间用英文逗号“,”隔开。META的通常用处是指定搜索引擎用来提高搜索质量的关键词。\n \u0026lt;meta name=\u0026quot;keywords\u0026quot; content=\u0026quot;牙疼怎么办，智齿是什么，拔牙多少钱，矫正多少钱，九院医生，北大口腔，瑞尔口腔，拜耳口腔\u0026quot;\u0026gt;\n 用来告诉搜索引擎你的网站主要内容(name=\u0026ldquo;description\u0026rdquo;)：  \u0026lt;meta name=\u0026quot;description\u0026quot; content=\u0026quot;有牙齿问题,找河马牙医,儿童口腔，全国儿童口腔在线咨询，牙疼怎么办，拔牙多少钱，矫正多少钱，种植牙，有牙齿问题\u0026quot;\u0026gt;\n 优先以webkit内核渲染页面(\u0026ldquo;renderer\u0026rdquo;)：  \u0026lt;meta name=\u0026quot;renderer\u0026quot; content=\u0026quot;webkit\u0026quot; /\u0026gt;\n4.复合选择器 div#main{ color:green}\n 表示选中div中id为main的元素\n 5.标签居中  水平居中 行内标签/行内-块级标签: text-align:center  块级标签:\nmargin:0 auto\n2)垂直居中\n  行内标签/行内-块级标签:\n 设置行高为盒子高度line-hight:50px; 图片加文字时:vertical-align:middle;    块级标签:\n   设置“子绝父相” ==\u0026gt; top，left设置为50% ==\u0026gt; margin-left和nargin-top设置为负的盒子对应宽高的一半","title":"常用的一些html代码"},{"content":"1.backgrouond简写  在background简写属性中指定background-sizing时，需要提供一个background-position值，而且要使用/作为分隔。  background: [background-color] [background-image] [background-repeat] [background-attachment] [background-position] / [ background-size] [background-origin] [background-clip]; \u0026mdash;\u0026ndash;P13\u0026mdash;\u0026ndash;\n2.background-clip 规定背景的绘制区域： 得到一个白色背景并且有白色边框的盒子：\nborder:10px solid hsla(0,0,100%,.5); background:white; background-clip:padding-box \n若不设置background-clip，背景会延伸至边框，边框的透明无法显示\n\u0026mdash;\u0026mdash;P18\u0026mdash;\u0026mdash;-\n3.双重边框  box-shadow   background:orange; box-shadow:0 0 010px #655 , 0 0 0 15px deeppink; /*可以加任意层数边框*/  outline  width:180px; height:90px; margin:30px auto; text-align:center; padding:10px; background:##655; outline: ##fff dashed 1px; outline-offset: -15px; /*设置outline向内缩进*/ border-radius:8px;  注意： outline可能会产生不贴合border-radius圆角的情况 \n\u0026mdash;-P20\u0026mdash;-\n4.box-shadow  只给下方设置阴影，（扩张半径为负的模糊半径）:  box-shadow: 0 5px 4px -4px black;  临边投影，(扩张半径为负的模糊半径的一半):  box-shadow: 3px 3px 6px -3px black; \u0026mdash;-P88\u0026mdash;-\n5.linear-gradient  取值 : (deg,color1 [%/num],color2[%/num]\u0026hellip;)\n 1\u0026gt;角度：表示渐变的方向 \n 0°表示从下到上，也可以是特殊的 : to top/bottom/left/right\n 2\u0026gt;颜色 3\u0026gt; 颜色后数字或取值\n 表示指某个颜色值距离起点的开始位置 : 50%,20px\u0026hellip;\u0026hellip;\n 注意：如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小，则该色标的位置值会被设置为它前面所有色标位置值的最大值。\n.box{ width:200px; height:150px; background-color:#58a; background:linear-gradient(-135deg,red 2em,#58a 0); } 结果\n6.设置文字段落效果(hyphens属性) text-align:justify; hyphens:auto; ","permalink":"https://blog.archai.space/p/css%E6%8F%AD%E7%A7%98%E4%B8%80%E4%B9%A6%E4%B8%AD%E7%9A%84%E7%9F%A5%E8%AF%86/","summary":"1.backgrouond简写  在background简写属性中指定background-sizing时，需要提供一个background-position值，而且要使用/作为分隔。  background: [background-color] [background-image] [background-repeat] [background-attachment] [background-position] / [ background-size] [background-origin] [background-clip]; \u0026mdash;\u0026ndash;P13\u0026mdash;\u0026ndash;\n2.background-clip 规定背景的绘制区域： 得到一个白色背景并且有白色边框的盒子：\nborder:10px solid hsla(0,0,100%,.5); background:white; background-clip:padding-box \n若不设置background-clip，背景会延伸至边框，边框的透明无法显示\n\u0026mdash;\u0026mdash;P18\u0026mdash;\u0026mdash;-\n3.双重边框  box-shadow   background:orange; box-shadow:0 0 010px #655 , 0 0 0 15px deeppink; /*可以加任意层数边框*/  outline  width:180px; height:90px; margin:30px auto; text-align:center; padding:10px; background:##655; outline: ##fff dashed 1px; outline-offset: -15px; /*设置outline向内缩进*/ border-radius:8px;  注意： outline可能会产生不贴合border-radius圆角的情况 \n\u0026mdash;-P20\u0026mdash;-\n4.box-shadow  只给下方设置阴影，（扩张半径为负的模糊半径）:  box-shadow: 0 5px 4px -4px black;  临边投影，(扩张半径为负的模糊半径的一半):  box-shadow: 3px 3px 6px -3px black; \u0026mdash;-P88\u0026mdash;-","title":"CSS揭秘一书中的知识"},{"content":"什么扁平和拟物设计？ 扁平化设计(Flat design) 完全属于二次元，这个概念最核心的地方就是放弃一切装饰效果，诸如阴影，透视，纹理，渐变等等能做出3D效果的元素一概不用。所有的元素的边界都干净利落，没有任何羽化，渐变，或者阴影。\n拟物设计(Skeuomorph) 正好相反，他需要加入各种元素的效果，通过不同的效果组合达到模拟要呈现物件外观的目的，以使新的外观让人感觉熟悉和亲切。\n扁平化与拟物化的优缺点:\n 扁平化设计单独设计起来更容易，突出内容主题，减弱各种渐变、阴影、高光等视觉效果对用户视线的干扰，让用户更加专注于内容本身，并且容易统一设计风格，调整设计方案更加方便。而现在手机平板电脑\u0026quot;横行\u0026quot;的今天，扁平化设计更能支持手势交互，而且它占用系统空间相对较少。 拟物化设计更适合初级人员了来做，这并不表示拟物化设计简单，而正是因为它的复杂所以更适合初级人员来做，可以让设计人员短时间内熟悉各种效果的实现，更好掌握色彩搭配等。并且拟物化设计认知和学习成本低，更能直接的表现出想表现的事物。   相对于它们的优点多多，它们的缺点却是一目了然。拟物化设计更倾向于视觉效果，功能实现很少。扁平化所能承载的信息量太少，会提高用户的学习成本。\n ","permalink":"https://blog.archai.space/p/%E6%89%81%E5%B9%B3%E8%AE%BE%E8%AE%A1%E5%92%8C%E6%8B%9F%E7%89%A9%E8%AE%BE%E8%AE%A1/","summary":"什么扁平和拟物设计？ 扁平化设计(Flat design) 完全属于二次元，这个概念最核心的地方就是放弃一切装饰效果，诸如阴影，透视，纹理，渐变等等能做出3D效果的元素一概不用。所有的元素的边界都干净利落，没有任何羽化，渐变，或者阴影。\n拟物设计(Skeuomorph) 正好相反，他需要加入各种元素的效果，通过不同的效果组合达到模拟要呈现物件外观的目的，以使新的外观让人感觉熟悉和亲切。\n扁平化与拟物化的优缺点:\n 扁平化设计单独设计起来更容易，突出内容主题，减弱各种渐变、阴影、高光等视觉效果对用户视线的干扰，让用户更加专注于内容本身，并且容易统一设计风格，调整设计方案更加方便。而现在手机平板电脑\u0026quot;横行\u0026quot;的今天，扁平化设计更能支持手势交互，而且它占用系统空间相对较少。 拟物化设计更适合初级人员了来做，这并不表示拟物化设计简单，而正是因为它的复杂所以更适合初级人员来做，可以让设计人员短时间内熟悉各种效果的实现，更好掌握色彩搭配等。并且拟物化设计认知和学习成本低，更能直接的表现出想表现的事物。   相对于它们的优点多多，它们的缺点却是一目了然。拟物化设计更倾向于视觉效果，功能实现很少。扁平化所能承载的信息量太少，会提高用户的学习成本。\n ","title":"扁平设计和拟物设计"},{"content":"About me Hello 陌生人，欢迎访问 Archai\u0026rsquo;s blog\n联系我   QQ : 2592030861\n  GitHub : https://github.com/fintinger\n  TODO √ 基本结构做完，带评论。\u0026lt; Wed Mar 9\u0026gt;\n√ 初步优化，首页修改，资源引入使用阿里云(不然慢😥)(暂时)。但是gotop莫名其妙报错😑。 \u0026lt;Fri Mar 18\u0026gt;\n√ 首页添加花里胡哨的随机线条功能，手机端不再设置不能显示主页，改成profile不再透明。 \u0026lt;Sat Mar 19\u0026gt;\n√ 更改首页video加载方式防止嗅探 \u0026lt;Mon Mar 21\u0026gt;\n√ 新增首页背景图用Blob加密，优化代码结构 \u0026lt;Thu Mar 31\u0026gt;\n√ 代码结构优化，取消背景图设定，用video标签的poster属性代替，新增几个个平台视频解析 \u0026lt;Mon Apr 04\u0026gt;\n bilibili：bilibili AV号或BV号 分P号 \u0026gt;}} tencent： tencent b31563j0jqw \u0026gt;}} 自定义视频：video src=\u0026quot;./video.mp4\u0026quot; autoplay=\u0026quot;true\u0026quot; poster=\u0026quot;./video-poster.png\u0026quot; \u0026gt;}}  √ 引入animate.min.css加入动效，评论区优化;引入font-awesome.min.css,优化图标展示，而不是用png格式的图标 \u0026lt;Tue Apr 05\u0026gt;\n√ 在文章列表页面，通过列表的footer尾注信息进行比较判断，筛选出pinned和newest文章，并为其添加相应样式 \u0026lt;Fri Apr 08\u0026gt;\n√ 黑暗模式适配✨ ✨ ！！\u0026lt;Sat Apr 09\u0026gt;\n√ 使用vercel实现自动化部署 \u0026lt;Thu Apr 28\u0026gt;\n□ 想做个音乐播放器组件😛\n□ Ajax请求是否会进行缓存，想利用本地缓存Blob对象，结果无法实现😑。因为Blob中存在function，所以无法JSON.stringify\n🥸  图片视频之类的都在static目录下，这个即为根目录，在config或者自定义css中都可以引用 通过下面的方式，使得assets/js/extended/路径下的js文件全部压缩后打包成一整个js文件  {{- $extendJS := (resources.Match \u0026#34;js/extended/*.js\u0026#34;) | resources.Concat \u0026#34;assets/js/extended/extend.js\u0026#34; | fingerprint | minify }} \u0026lt;script src=\u0026#34;{{ $extendJS.RelPermalink }}\u0026#34;\u0026gt;\u0026lt;/script\u0026gt;  对font-awesome.min.css做了略微修改，与官网不同的是，通过给class添加fab fa-xxx才能使得字体图标生效  ","permalink":"https://blog.archai.space/about/","summary":"About me Hello 陌生人，欢迎访问 Archai\u0026rsquo;s blog\n联系我   QQ : 2592030861\n  GitHub : https://github.com/fintinger\n  TODO √ 基本结构做完，带评论。\u0026lt; Wed Mar 9\u0026gt;\n√ 初步优化，首页修改，资源引入使用阿里云(不然慢😥)(暂时)。但是gotop莫名其妙报错😑。 \u0026lt;Fri Mar 18\u0026gt;\n√ 首页添加花里胡哨的随机线条功能，手机端不再设置不能显示主页，改成profile不再透明。 \u0026lt;Sat Mar 19\u0026gt;\n√ 更改首页video加载方式防止嗅探 \u0026lt;Mon Mar 21\u0026gt;\n√ 新增首页背景图用Blob加密，优化代码结构 \u0026lt;Thu Mar 31\u0026gt;\n√ 代码结构优化，取消背景图设定，用video标签的poster属性代替，新增几个个平台视频解析 \u0026lt;Mon Apr 04\u0026gt;\n bilibili：bilibili AV号或BV号 分P号 \u0026gt;}} tencent： tencent b31563j0jqw \u0026gt;}} 自定义视频：video src=\u0026quot;./video.mp4\u0026quot; autoplay=\u0026quot;true\u0026quot; poster=\u0026quot;./video-poster.png\u0026quot; \u0026gt;}}  √ 引入animate.min.css加入动效，评论区优化;引入font-awesome.min.css,优化图标展示，而不是用png格式的图标 \u0026lt;Tue Apr 05\u0026gt;","title":"About"}]