前端小白博客
  • 首页
  • 技术分享
  • 技术资讯
  • 文章归档
  • 生活感悟
  • 留言
  • 关于站长
技术分享
  • 所有
  • Vue
  • Javascript
  • CSS
  • React
  • ES6
  • html
  • mysql
  • node
  • TypeScript
  • nginx
  • seo
  • git
  • webpack
  • 其他
  • 算法
  • h5
  • uniapp
  • v8
  • python
  • 手写
  • http
  • JS中的遮蔽效应

    一:遮蔽效应是什么 在多层的嵌套作用域中可以定义同名的标识符,作用域查找会向上查找,找到第一个匹配的标识符时停止 抛开遮蔽效应,作用域的查找始终是从运行的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配标识符为止 说白一点,就是内部的标识符遮蔽住了外部的标识符 例子一 var val='global' function bar(){ console.log(val) //undefined var val='inner txt' console.log(val) // inner txt } bar() console.log(val) // global 例子二 var val='global' function bar(){ console.log(val) // global } function foo(){ var val='inner txt2' bar() console.log(val) //inner txt2 } foo() console.log(val) // global<br/> 例子一val值为undefined? 这里会涉及到变量提升,在块级作用域中,变量会提升到顶部,声明而未赋值 例子二val为啥为全局值? 作用域的查找是,不管函数在哪个作用域中执行,而查找看的是定义时的作用域,虽然在foo内部执行,但是作用域跟foo是平行块的关系,说白了就是看函数定义时所在位置,而不是调用时
  • 如何实现页面刷新后不定位到之前的滚动位置

    一、浏览器默认行为 无论是哪个浏览器,都有这样一个体验细节。 那就是,如果浏览器不是强制刷新(Ctrl + F5),而是普通刷新(点击刷新按钮,或者按下 F5 刷新),则页面重新载入完毕后大概率会调到之前访问的位置。 大多数时候,这种体验对用户是友好的。 但是,实际开发中,我们总会存在不希望用户刷新记住之前滚动位置的情况。 这个时候该怎么办呢? 曾几何时,我是在页面 load 完毕之后,在足够安全的时间之后设置页面的 scrollTop 为 0。 二、history.scrollRestoration 使用很简单,在页面的任意位置执行下面几行 JS 代码就可以了 if (history.scrollRestoration) { history.scrollRestoration = 'manual'; } 三、语法和兼容性 history.scrollRestoration 支持下面两个属性值: auto 默认值,表示滚动位置会被存储。 manual单词的意思就是手动。表示,滚动的位置不会被存储。 兼容性 兼容性很不错,现代浏览器很早就支持了,移动端放心使用(IE不支持)
  • vue中filters过滤器中不能使用this的解决方案

    vue中的过滤器更偏向于对文本数据的转化,而不能依赖this上下文,如果需要使用到上下文this我们应该使用computed计算属性的或者一个method方法 <template> <div id="app"> {{ a | dealSum}} </div> </template> <script> export default { name: 'App', data() { return { a: 10, b: 20 } }, filters:{ dealSum(val){ console.log(val) //10 console.log(this) //undefined } } } </script> 解决方案一:我们可以在data中定义that=this,通过过滤器传入进去 ,就可以获取到this对象以及参数了 <div id="app"> {{ a | dealSum(that)}} </div> data() { return { that:this, a: 10, b: 20 } }, filters:{ dealSum(val,that){ console.log(val) //10 console.log(that.a) //10 return val+that.b } } } 解决方案二:采用computed计算属性 <div id="app"> {{ dealSum}} </div> computed:{ dealSum(){ return this.a+this.b } } 解决方案三:采用method函数方式实现 <div id="app"> {{ dealSum()}} </div> methods:{ dealSum(){ return this.a+this.b } }
  • css 文本超出2行就隐藏并且显示省略号

    在实际开发过程中,总是会遇到显示省略号的需求 主要是采用overflow、text-overflow、white-space 主要用到来实现超过1行实现省略号,并且单行兼容ie overflow:hidden; //超出的文本隐藏 text-overflow:ellipsis; //溢出用省略号显示 white-space:nowrap; //溢出不换行 多行的话css3则采用弹性伸缩,内核原因,不支持ie display:-webkit-box; //将对象作为弹性伸缩盒子模型显示。 -webkit-box-orient:vertical; //从上到下垂直排列子元素(设置伸缩盒子的子元素排列方式) -webkit-line-clamp:2; //这个属性不是css的规范属性,需要组合上面两个属性,表示显示的行数。 -webkit-line-clamp 超过两行就出现省略号 -webkit-line-clamp 是一个 不规范的属性(unsupported WebKit property),它没有出现在 CSS 规范草案中 display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; 最后,集成代码为 overflow:hidden;  text-overflow:ellipsis; display:-webkit-box;  -webkit-box-orient:vertical; -webkit-line-clamp:2;  说明: 如果项目中使用了less -webkit-line-clamp: 2 //无效
  • CSS 去除两个span之间的默认间距

    在实际开发中,会遇到两个span标签横向排列时,会有几个px间隔,如图所示 a <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style> span{ display: inline-block; width: 100px; height: 30px; line-height: 30px; text-align: center; } span:first-child{ background: lightblue; } span:last-child{ background: salmon; } </style> </head> <body> <div> <span>1</span> <span>2</span> </div> </body> </html> 但是我们代码块没有编写间距。每个span元素间都会存在默认的空格,大楷4像素样子 第一种解决方案,将父元素font-size为0px,子元素重新设置字体可以解决 <style> div{ font-size: 0; } span{ font-size: 14px; display: inline-block; width: 100px; height: 30px; line-height: 30px; text-align: center; } span:first-child{ background: lightblue; } span:last-child{ background: salmon; } </style> a 第二种方案 采取移除掉掉text 空格方案 document.querySelector("div").childNodes.forEach(item=>{ if(item.nodeName=='#text'){item.remove()} })
  • js中常见判断空对象的几种方法

    在实际开发中,常用到对象,难免会判断空对象的情况,今天,整理一下常用的方式 第一种方式:JSON.stringify方式,判断字符串 let obj={a:1} let obj1={} console.log(JSON.stringify(obj)=='{}') console.log(JSON.stringify(obj1)=='{}') a 第二种方式:jquery中的封装方法$.isEmptyObject let obj={a:1} let obj1={} console.log($.isEmptyObject(obj)) console.log($.isEmptyObject(obj1)) a 第三种方式:Object.getOwnPropertyNames方式,转化成数组对象key,根据长度判断 let obj={a:1} let obj1={} console.log(Object.getOwnPropertyNames(obj)) console.log(Object.getOwnPropertyNames(obj1).length==0) a 第四种方式:采用ES6中Object.keys ,转化成数组对象key,根据长度判断 let obj={a:1} let obj1={} console.log(Object.keys(obj)) console.log(Object.keys(obj1).length==0) a 第五种方式:采用for in方式 根据循环内是否进入判断 let obj={a:1} let obj1={} let num=0 for(let val in obj){ num=1 } if(num!=1){ console.log('是空对象') }else{ console.log('不是空对象') }
  • vue中的混入mixin

    在实际开发中,部分数据,方法会被重复性使用,那么,我们就需要进行混入,官方提供两种方式,全局混入和局部混入, mixins它就是一个对象,这个对象里面可以包含Vue组件中的一些常见配置,如data、methods、created等等 今天,我们整理下vue2中混入和vue3中的混入方式 一:vue2版本 局部方式,通过mixins属性实现 export const mixins = { data() { return { msg: "我是小白", }; }, computed: {}, methods: { clickMe() { console.log("我是mixin中的点击事件"); }, }, }; import { mixins } from "./mixin/index"; mixins: [mixins], 全局方式,直接挂载在原型上,(会影响每个单独创建的 Vue 实例 (包括第三方组件)) import { mixins } from "./mixin/index"; Vue.mixin(mixins); 二:vue3版本,采用函数式编程,用js方式写 export default function getHomeMixin(source: string) { let name = ref<string>('小白'); const componentName = source const handleChangeName = (transmitName: string) => { name.value = transmitName } return { name, componentName, handleChangeName } } <script setup lang="ts"> import getHomeMixin from './homeMixin' const Mixin = getHomeMixin('我是A组件传的值'); </script> 三:优缺点 1:优点 A:提高代码复用性 B:无需传递状态 C:维护方便 2:缺点 A:命名冲突 B:滥用会导致维护困难 C:追溯源、排查问题麻烦 D:不能轻易重复代码 四:注意点 1:mixins 中的data数据与组件中的data数据冲突时,组件中的data数据会覆盖mixin中数据 2:默认合并策略下,先执行mixin中生命周期函数中的代码,再执行组件内部的代码(钩子函数不会覆盖,但是methods会覆盖)
  • vue中的$attrs和$listeners

    在使用vue框架中,经常会用到父子孙等多层次组件传参,如果采用props一层一层接收,就过于繁琐,官方提供$attrs和 $listeners来进行跨组件传参和方法处理 vue2版本 $attrs 主要是将父组件传过来的数据进行收集 ,如果props里面做了接收,那么$attrs则只收集没有接收的参数 inheritAttrs: false/ture,这个主要是是否将传递的参数显示在dom节点属性上 ,只会显示props没有接收的参数,如果props接收过的,则不在显示到节点 true:默认的显示 inheritAttrs: true, a false :则不显示 inheritAttrs: false, a <template> <div id="app"> <div>vue2</div> <father :info=info :info2=info2 v-on:eventOne="methodOne" ></father> <button @click="modityInfo()">修改info值</button> <button @click="modityInfo2()">修改info2值</button> </div> </template> <script> import father from './components/father.vue' export default { name: 'App', data(){ return{ info:{ name:'张三', id:1             }, info2:{ name:'李四', id:2             }         }     }, components:{ father     }, 我们从祖组件传递两个props对象 info 和info2 <template> <div> <p>Father组件</p> <son v-bind="$attrs" v-on="$listeners" ></son> <button @click="sendFather()">发送给父组件数据</button> </div> </template> <script> import son from './son.vue' export default { name:'FatherOne', inheritAttrs: false, // props:['info'], mounted() { console.log(this.$attrs) // console.log(this.$listeners)         }, 组件不适用props接收,则$attrs接收到全部参数 a 如果使用props接收全部或者某个,则$attrs就会排除接收的后参数, export default { name:'FatherOne', inheritAttrs: false, props:['info'], mounted() { console.log(this.$attrs) // console.log(this.$listeners)         }, a 如果要继续进行孙组件等等,则需要继续传递$attrs, <son v-bind="$attrs" v-on="$listeners" ></son> 接收原理一样 <template> <div> <p>Son组件组件</p> <button @click="sendAnce()">发送给祖组件数据</button> </div> </template> <script> export default{ name:'SonTwo', data(){ return{             }         }, props:{ info:{ default:()=>({})             }, info2:{ default:()=>({})             }         }, watch:{ info:{ handler(val){ console.log("监听info的值") console.log(val)                 }, deep:true, immediate:true             }, info2:{ handler(val){ console.log("监听info2的值") console.log(val)                 }, deep:true, immediate:true             }         }, 孙组件可采用props接收或者赋值使用 $listeners 官方的描述是事件监听器,主要采用v-on="$listeners",传入内部组件, 个人的理解,类似于中间件,把父级的方法传递给子孙组件使用,由他们进行一个触发,把参数通过函数调用或者$emit方式调用把数据传回给父组件 <father :info=info :info2=info2 v-on:eventOne="methodOne" @senddata="getSenddata" ></father> 最外层组件传递两个方法,on:eventOne和senddata mounted() { // console.log(this.$attrs) console.log(this.$listeners)         }, a 父组件接收到传递过来的方法 <template> <div> <p>Father组件</p> <son v-bind="$attrs" v-on="$listeners" ></son> <button @click="sendFather()">发送给父组件数据</button> </div> </template> <script> import son from './son.vue' export default { name:'FatherOne', inheritAttrs: true, mounted() { // console.log(this.$attrs) console.log(this.$listeners)         }, components:{ son         }, methods:{ sendFather(){ this.$listeners.eventOne('123') this.$emit('eventOne',123) this.$emit("senddata",456)             },         },     } </script> <style> </style> 可以通过两种种方式实现给外层组件传参 第一种直接调用方法 第二种通过$emit方式 如果还需要继续深层次传递外层的方法 则需要继续传递v-on="$listeners",如果当前组件继续添加事件,则会合并外层一起传递给子组件, 这里个人觉得不是笼统传递不便于区分,建议以对象方式区分外层,父组件方法,就看官方后期了 <son v-bind="$attrs" v-on="$listeners" v-on:eventThree="methodThree" ></son> 那么孙组件也可以相同方式给外层组件传参或者方法调用 a 就会收到合并后的一个事件监听器,则可采取同样模式进行传递给父,祖数据 总的来说 v-on="$listeners"将所有方法又绑定到组件相应标签 ,是绑定方法(.native除外) v-bind="$attrs" 将所有非props属性绑定到相应标签 ,是绑定属性(.class和style除外) vue3版本 移除了$listeners,合并到$attrs中,那么,属性和方法统一合并到$attrs
  • JS中常见对象合并方式

    在实际来发中,经常会处理到对象类型,今天我们就整理一下,常见的几种对象合并方式 第一种采用Object.assgin()方法 <script> /*JS中对象的合并方式*/ let obj={ name:'张三' } let obj1={ age:1 } let obj3=Object.assign(obj,obj1) console.log(obj3) </script> a 第二种方式采用es6的拓展运算符(...) let obj3={...obj,...obj1} a 第三种方式采用原始的递归赋值方法,就是循环 let obj3=obj for(let key in obj1){ if(obj1.hasOwnProperty(key)){ obj[key]=obj1[key] } } console.log(obj3) 第四种方式我们借用jquery库里面封装好的方法extend $(function(){ let obj3=$.extend(obj,obj1) let obj4=$.extend(true,obj,obj1) console.log(obj3)//浅拷贝 console.log(obj4)//深拷贝 }) 注意所有合并的新对象都是浅拷贝,如果修改了某个属性值,都会同步到其他属性上!!!
  • JS中数组的合并方式

    在实际来发中,经常会对数组进行操作,今天,整理一下数组合并的几种方式 第一种es6的扩展运算符(...) <script> /* JS中数组的合并方式 */ let arr=[1,2,3] let arr2=[4,5,6] let arr3=[...arr,...arr2] console.log(arr3) </script> 第二种方式采用concat进行数组的连接 let arr3=arr.concat(arr2) 第三种方式 采用for循环push for(let i of arr2){ arr.push(i) } console.log(arr) 第四种方式apply ()方法,有两个参数,第一个参数是上下文环境的对象,第二个参数是函数列表,支持数组形式传递,然后再把参数一个个push进行 arr.push.apply(arr,arr2)
  • JS中null、undefined、NaN的区别

    null 表示“没有对象”、“无”的对象,即该处不应该有值 ,原型链的终点 常见几种情况 一作为函数的参数,表示函数的参数为空 二作为对象原型链的终点 function getNum(a=null){ console.log(a) //null } getNum() console.log(Object.getPrototypeOf(Object.prototype)) //nul undefined表示“缺少值”,表示一个“无”的原始值,此处应该有一个值,只是现在还没有被定义 常见几种情况 一变量被申明了,但是没有赋值时。就是undefined 二调用函数时,应该提供的函数没有提供,就是undefined 三对象没有赋值的属性,该属性的值就是undefined 四 函数没有返回值时,默认就是undefined <script> let a; console.log(a) //undefined // console.log(getNum()) //Uncaught ReferenceError: getNum is not defined function getNum2(){ } let obj={ a } console.log(obj.a) //undefined console.log(getNum2()) //undefined </script> NaN 表示一个特殊的数字值,表示不是一个合法的数字,并且它与任何类型都不相等,包括自己 <script> var num=null var num1=undefined console.log(num==num1) //true console.log(num===num1) //false console.log(NaN==NaN) //false </script> 在进行数字运算过程中 null会被转化为0 而undefined转化为NaN let num=null let num1=undefined console.log(num+1) //1 console.log(num1+1) //NaN
  • vue中路由的三种模式

    主要有三种路由模式:hash、history、abstract 在开发中,会配置路由,路由模块的本质是建立起url和页面之间的映射关系,就会用到路由模式,一般我们默认的是hash模式,还有一种是histroy模式 mode: 'history', 一:hash模式 特点: 1:就是url地址栏上会出现#,当#号后面的路径发生变化时,浏览器不会重新发送请求,而会触发onhashchange事件,不会刷新页面,主要通过window.location.hash值变化来改变路由 2:hash值改变会在浏览器访问历史中增加记录,可以通过前进、后退进行hash值切换 3:hash不会修改浏览器历史记录栈 4:生成二维码、微信分享页面的时候都会自动过滤掉#后面的参数 优点:浏览器兼容性好,IE8都支持 缺点:路径在#号后边,比较丑,对seo不友好 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div> <a href="#/a">A页面</a> <a href="#/b">B页面</a> </div> <div id="app"> <div >111111</div> </div> </body> </html> <script> function render() { console.log('触发') app.innerHTML = window.location.hash } window.addEventListener('hashchange', render) render() </script> 利用a标签设置了两个路由导航,把app当做视图渲染容器,当切换路由的时候触发视图容器的更新,这其实就是大多数前端框架哈希路由的实现原理 二:histroy模式 特点: 1:H5提供的新特性,会触发popstate,是不会出现#,比hash模式好看, 2:history会修改浏览器历史记录栈,history.pushState用于在浏览历史中添加历史记录,history.replaceState修改浏览历史中当前纪录,不会触发页面刷新 3:history修改的url可以是同域的任意url 优点:路径比较正规,没得# 缺点:兼容性不如hash,且需要服务端支持,否则刷新404 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div> <a href="javascript:toA();">A页面</a> <a href="javascript:toB();">B页面</a> </div> <div id="app"> <div >111111</div> </div> </body> </html> <script> function toA() { history.pushState({},null,'/a') render() } function toB() { history.pushState({},null,'/b') render() } function render() { console.log('触发') app.innerHTML = window.location.pathname } window.addEventListener('popstate', render) </script> history 的 pushState 和 replaceState方法不能被监听到,像go,back,forward可以被监听到, 浏览器在刷新的时候,会按照路径发送真实的资源请求,如果这个路径是前端通过 history API 设置的 URL,那么在服务端往往不存在这个资源,于是就返回 404 了, 因此在线上部署基于 history API 的单页面应用的时候,一定要后端配合支持才行,否则会出现大量的 404。 以最常用的 Nginx 为例,只需要在配置的 location / 中增加下面一行即可: try_files $uri /index.html; 三:abstract(MemoryHistory) 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。缺点:没有URL,只对单机有效。前面两种都是把路径存到URL上面。memory模式不放在URL里面,前端一般放在localstorage里面。
  • vue中的插槽(slot)

    vue中提出了插槽(Slot)的概念 插槽的意思:决定将所携带的内容,插入到指定的某个位置、插槽既可以是默认插槽,又是作用域插槽,也可以是具名插槽 插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制 插槽分为:默认插槽、具名插槽、作用域插槽 一:默认插槽(#default) 是指没有名字的单个插槽,子组件未定义名字的插槽,父级将会把 未指定插槽的填充的内容填充到默认插槽中 <slot></slot> <MySlot> <h1>Outer Text</h1> </MySlot> 二:具名插槽 就是给插槽添加v-slot(#)/name属性,一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时根据名字把内容填充到对应插槽中 <slot name='header'></slot> <MySlot> <template v-slot:header> <h1>Outer Text</h1> </template> </MySlot> 三:作用域插槽 是指带数据(参数)的插槽,简单的来说就是子组件提供给父组件的参数,该参数仅限于插槽中使用,父组件可根据子组件传过来的插槽数据来填充插槽内容 <slot :outerUser='user' name='header'></slot> <template #header='slot01Data'> <h1>{{slot01Data.outerUser.name}}</h1> </template>
  • JS的数据类型

    js中数据类型分两类 一个是基础类型,一个是引用类型 一:基础类型 其中包括 Number、String、Boolean、BigInt、Symbol、Null、Undefined var str2 = "world"; var num2=520; var x=true; var a=null; var b=undefined js数组类型精度是有限的 console.log(0.1+0.2) //0.30000000000000004 console.log(parseFloat((0.1+0.2).toFixed(10))) //0.3 es6新推出的BigInt和Symbol 数据类型 BigInt类型主要是解决普通数据类型范围报错的问题,数据范围涵盖非常大,使用方式就是直接在数字后面加n,注意不能于Number类型共用 let sum=BigInt(9445455547887754564545877887787896567887n)+BigInt(944545556455644645665546456478878545567887n) console.log(sum.toString()) //953991012003532400230092334366666442135774 Symbol 的本质是表示一个唯一标识。每次创建一个Symbol,它所代表的值都不可能重复 主要用于创建唯一值场景,和对象的key有区别,可以同名,但是实际是唯一的,而对象的key不能同名,如果同名则后面会替换前面的 const name=Symbol('name') const name1=Symbol('name') const persons = {   [name]: '张三',    [name1]: '李四' } const personsobj={ name:'张三', name:'李四' } console.log(personsobj) //name: '李四'} console.log(Object.getOwnPropertySymbols(persons))//[Symbol(name), Symbol(name)] console.log(persons) //{Symbol(name): '张三', Symbol(name): '李四'} console.log(persons[name]) //张三 console.log(persons[name1]) //李四 基本类型存储是直接存储在栈中,占用空间少 二:引用类型 也叫复杂类型Object,像普通对象、数组、正则、日期、Math、函数都属于 引用类型是存储在堆中,占据空间大,在栈中存储了该指针,指针则指向该实体的起始地址, 当需要寻找引用值时,则检索栈中地址,取到栈中地址后再从堆中拿到实体
  • JS中数组扁平化处理方式

    在项目开发中,经常会用到数组的操作,今天,整理一下数组的扁平化处理 const arr = [1,2,[3,4,5,[6,7],8],9,10,[11,[12,13]]]; 方式一 采用ES2019新增属性,flat()方式,接收参数dept 需要扁平的层级,只能在支持 ES6 的浏览器或者环境中使用 console.log(arr.flat())//[1, 2, 3, 4, 5, [6, 7], 8, 9, 10, 11, [12, 13]] console.log(arr.flat(3))//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 方式二 采用toString() /join()方法把数组转成成字符串,然后再以,隔开,然后遍历把字符串数组转化为数字类型 console.log(arr.toString().split(',').map(item=>Number(item))) //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] console.log(arr.join(',').split(',').map(item=>Number(item)))//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 方式三 采用递归的方式,通过Array.isArray判断是否是数组,继续遍历调用下层,直到不是数组为止 function bpArr(arr){ let newarr=[] arr.map(item=>{ if(Array.isArray(item)){ // newarr=newarr.concat(bpArr(item)) newarr=[...newarr,...bpArr(item)] }else{ newarr.push(item) } }) return newarr } console.log(bpArr(arr)) //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 方式四 reduce 方法进行数组扁平化处理 function flatten(arr) { return arr.reduce((acc, item) => { if (Array.isArray(item)) { return acc.concat(flatten(item)); } else { return acc.concat(item); } }, []); } 方案五 迭代器实现数组扁平化,使用生成器函数将嵌套的数组转换为迭代器对象,然后使用 for...of 循环或者 Array.from() 方法将迭代器对象转换为一维数组 function* flatten(arr) { for (let item of arr) { if (Array.isArray(item)) { yield* flatten(item); } else { yield item; } } } const arr = [1, 2, [3, 4, [5, 6]]]; const flat = []; for (let item of flatten(arr)) { flat.push(item); } console.log(flat); // [1, 2, 3, 4, 5, 6] // 使用 Array.from() 方法 const arr = [1, 2, [3, 4, [5, 6]]]; const flat = Array.from(flatten(arr)); console.log(flat); // [1, 2, 3, 4, 5, 6] 方案六 ES6 的展开运算符进行数组扁平化处理 function flatten(arr) { while (arr.some(item => Array.isArray(item))) { arr = [].concat(...arr); } return arr; }
  • JS中some和every方法的区别

    在js内置方法中,every和some都是数组迭代的方法。不过 every()方法是对数组中每一项给定一个函数,如果每一项都为true,则才返回true some()也是对数组中每一项给定一个函数,只要有一项为true,则返回true console.log(arrdata.every((item)=>{ return item>2 })) //false console.log(arrdata.some((item)=>{ return item>2 })) //true some()一直查找符合条件的值,一旦找到,则不会继续迭代下去,说白了就是找到了就停止 every()从迭代开始。一旦有一个不符合条件。则不会进行迭代下去,说白了就是不符合就停止
  • JS中的强缓存和协商缓存

    浏览器访问网站,会加载html,css,jsimg....,如果每次都需要重新加载,对服务器压力,用户体验不好,这个时候我们都需要进行缓存。 强制缓存优先于协商缓存,若强制缓存(Expires 和Cache-Control)生效则直接使用缓存,否则则使用协商缓存(Last-Modified/If-Modified-Since/Etag/If-None-Match) 协商缓存则又根据服务器决定,若失败,则代表请求缓存失败,返回200,重新返回缓存资源和缓存标识,再存入浏览器中,生效则返回304,继续使用缓存 浏览器缓存执行图解 一:强制缓存 通过Expires和Cache-Control两个字段进行控制,都表示资源缓存的有效时间, 1:Expires:值是一个GMT格式时间字符串,比如:Expires:Mon,10 Oct 2022 20:50:50 GMT,在这个时间之前都认为命中缓存, 他是一个绝对时间,如果服务器和客户端时间相差很大,就会导致缓存混乱。 2:Cache-Control:常用max-age值来进行判断,单位值是秒,是一个相对时间, 比如max-age:3600表示资源有效期是3600秒,表示当前资源在Date~Date+3600都是有效的。当然也会出现服务器时间跟本地系统时间不同的情况, no-cache 不使用本地缓存。需要使用协商缓存。 no-store直接禁止浏览器缓存数据,每次请求资源都会向服务器要完整的资源。 public 可以被所有用户缓存。 private 只能被终端用户的浏览器缓存。 如果Cache-Control和Expires同时存在,Cache-Control优先级更高,Cache-Control > expires > Etag > Last-Modified 图解 a 二:协商缓存 主要是采用两种方式进行操作,来绝对是否命中缓存 1:Last-Modified/If-Modified-Since:都是GMT格式的时间字符串,Last-Modified表示最后文件修改时间, 下一次请求时头部带上If-Modified-Since:Last-Modified的值,服务器根据最后修改时间进行判断是否有变化,没变化则返回304 , 如果有变化 则返回新的资源内容,同时在response header返回新的Last-Modified. 2:Etag/If-None-Match:值是由服务器生成的资源id唯一字符串, 只要资源有变化值就会改变,服务器根据文件本身算出一个哈希值通过Etag返回给浏览器, 下一次请求时If-None-Match:Etag值,服务器通过比较判断是否改变,没改变返回304,有改变则重新返回Etag 推荐使用Etag,主要是Lat-Modified精确度只能到秒级、其次是某些服务器不能精确读取到最后修改时间、文件周期性的改变,内容相同也会被重新触发 图解 a Last-Modified/If-Modified-Since图解 a Etag/If-None-Match图解 a 缓存的优缺点: 优点: 1:减少重复数据请求,避免网络再次加载资源,节省流量。 2:减轻服务器压力,提升完整性能. 3:加快客户端加载网页速度,提升用户体验 缺点: 1:缓存了静态文件,如果没过期,则需要强制清除本地缓存,才能看到最新的发布版本 解决方案 1:每次手动添加版本号或者动态加上时间戳 2:每次对应静态文件里有内容改动的时候,自动加一段 hash 到静态文件名里
  • css中的盒子模型

    盒子组成分为四个部分: margin(外边距)、border(边框)、padding(内边距)、content(内容)。 盒子大小指的是盒子的宽度和高度,计算如下: 盒子的宽度 = 内容宽度 + 左填充 + 右填充 + 左边框 + 右边框 + 左边距 + 右边距 盒子的高度 = 内容高度 + 上填充 + 下填充 + 上边框 + 下边框 + 上边距 + 下边距 代码转化显示为 盒子的宽度 = width + padding-left + padding-right + border-left + border-right + margin-left + margin-right 盒子的高度 = height + padding-top + padding-bottom + border-top + border-bottom + margin-top + margin-bottom 特殊情况下 盒子的宽度和高度计算由box-sizing属性来控制 box-sizing:border-box/content-box/inherit 有三个属性值 content-box:默认值 ,也就是标准盒子模型,就是说将width和height应用到元素的内容框,在之外绘制元素的内边距、外边距、边框。 border-box:IE盒子模型,就是说width和height决定了元素的边框盒,在之内绘制元素的内边距、外边距、边框,拿到内容的宽高需要分别减去边框、内边距 inherit:从父元素基础box-sizing属性的值 标准盒子模型 .div{ box-sizing: content-box; width: 100px; height: 100px; background: gainsboro; border: 10px solid gold; padding: 10px; margin: 10px; } .div{ box-sizing: border-box; width: 100px; height: 100px; background: gainsboro; border: 10px solid gold; padding: 10px; margin: 10px; } IE盒子类型
  • CSS中的BFC

    BFC名为块级格式化上下文 官方的解释是:BFC决定了元素如何对其内容进行定位,以及于其他元素的关系和相互作用,html在这个环境中按照一定的规则进行布局 简单来说:就是一个完全独立的空间(布局环境),让空间内的子元素不会影响到外面的布局, 特性: 一:是一个块级元素,在垂直方向上排列 二:是一个独立的容器,内部元素不会影响到容器外部的元素 三:属于同一BFC的两个盒子,外边距会发生重叠,并且取得最大外边距 四:计算BFC的高度时,浮动元素也会参与计算 a 触发条件: 常用的有 overflow: hidden; disolay:flex; display: inline-flex; display: inline-block; position: absolute; position: fixed; 作用: 一:避免margin重叠 二:自适应两栏布局 三:清除浮动避免高度坍塌 重叠问题 .box .box1 { width: 100px; height: 100px; margin-bottom: 30px; background-color: gold; } 针对box1进行BFC行程一个块级,这样就避免了边距重叠 .box .box1 { display: inline-block; width: 100px; height: 100px; margin-bottom: 30px; background-color: gold; } a 自适应问题 .box .box1 { float: left; width: 100px; height: 100px; margin-bottom: 30px; background-color: wheat; } .box .box2 { height: 100px; margin-top: 60px; background-color: gold; } 针对box2进行设置成一个单独的BFC .box .box1 { float: left; width: 100px; height: 100px; margin-bottom: 30px; background-color: wheat; } .box .box2 { overflow: hidden; height: 100px; margin-top: 60px; background-color: gold; } a 高度坍塌问题 .box{ width: 600px; background-color: gainsboro; border: 5px solid red; } .box .box1 { float: left; width: 100px; height: 100px; margin-bottom: 30px; background-color: wheat; } .box .box2 { float: right; width: 100px; height: 100px; background-color: gold; } 针对外层盒子设置为BFC .box{ width: 600px; background-color: gainsboro; border: 5px solid red; display: inline-block; } a
  • ES6中set和map的区别

    Set 和 Map 主要的应用场景在于 数据重组 和 数据储存。 一:形式上 1:set:是一种“集合”数据结构,类似于数组,以【value,value】的形式存储元素,任何类型唯一的值,不允许重复,本身是一种构造函数,用来生成 Set 数据结构,初始值为一维数组 var s2=new Set([1,2,3]); 2:map是一种“字典”数据结构,以【key,value】的形式存储,是键值对的集合,记住插入顺序,任意值作为键,解决以往不能用对象做为键的问题,具有极快的查找速度,初始值为二维数组 const m=new map(['Kris',21],['Bob',19],['Lily',25],['Jack',27]); 二:方法上 1:set: 提供add、delete、has、clear等方法 add(value):添加某个值,返回 Set 结构本身(可以链式调用)。 delete(value):删除某个值,删除成功返回true,否则返回false。 has(value):返回一个布尔值,表示该值是否为Set的成员。 clear():清除所有成员,没有返回值。 var s=new Set([1,2,3,3]); s.add(4); // set{1,2,3,4} s.add(3); //set{1,2,3,4} s.size(); //4 s.has(3); //tru 2:map提供set、get、delete、has、clear等方法 set(key, val): 向Map中添加新元素 get(key): 通过键值查找特定的数值并返回 has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false delete(key): 通过键值从Map中移除对应的数据 clear(): 将这个Map中的所有元素删除 var m=new Map( ); //初始化一个空的 map m.set('Pluto',23); //添加新的key-value 值 m.has('Pluto'); //true 是否存在key ‘Pluto’ m.get('Pluto'); //23 m.delete('Pluto'); //删除key ‘Pluto ’ 三:相同点 1:都能通过迭代器进行for...of遍历 四:使用场景 1:set数组去重 let arr = [1, 2, 3, 4, 5, 6, 3, 2, 5, 3, 2]; console.log([...new Set(arr)]); // [1, 2, 3, 4, 5, 6] 2:map数据类型充当键、保证对象的顺序 let errors = new Map([ [400, 'InvalidParameter'], [404, 'Not found'], [500, 'InternalError'] ]); console.log(errors);
1•••2345•••23
热门文章
1JS中的遮蔽效应
2如何实现页面刷新后不定位到之前的滚动位置
3vue中filters过滤器中不能使用this的解决方案
4css 文本超出2行就隐藏并且显示省略号
5js中常见判断空对象的几种方法
6JS中常见对象合并方式

最近更新

站长推荐

Copyright © www.cqhxb.com All Rights Reserved | 渝公网安备50011302222502 | 渝ICP备17004821号 | | 互联网有害举报中心 | 管理员登录

郑重声明:本站部分图片来源互联网,如有侵权请联系删除