日常复习
1. vue3中 ref 和reactive的区别 当我们调用 ref 方法来定义响应式数据时,当参数为对象类型时,其实里面用的是 reactive 方法。.value 事实上是 reactive 方法创造出来的。 reactive 能做的ref也能做, reactive 不支持对基本类型数据响应式,也就是说基本类型数据不能直接作为 reactive 的参数来使用。
2. js中new 到底做了什么 1、创建了一个空的js对象(即{}) 2、将空对象的原型prototype指向构造函数的原型 3、将空对象作为构造函数的上下文(改变this指向) 4、对构造函数有返回值的判断
// 判断数组对象特定属性是否相同 function isEqualObj(arr, key) { const newArr = arr.map(item => item[key]) const arrSet = new Set(newArr) return arrSet.size !== newArr.length }
// js 继承有四种 原型链继承, 构造函数继承, 组合继承,寄生继承 // 原型继承,改了其中一个实例会影响其它的,构造函数继承不到父类的属性和方法 //组合继承 function chidren(name) { this.name = name } chidren.prototype.getName = function() { return this.name } function inhert() { // 构造函数继承 chidren.call(this,'王二') } inhert.prototype = new chidren() // inhert.prototype = Object.create(chidren.prototype) //将指向父类实例改为指向父类原型 为寄生继承 inhert.prototype.constructor = inhert // 使用 const inhert1 = new inhert() inhert1.name // ['王二']
// 数组去重 function removeDuplicate(arr, key) { let obj = {} const curArr = arr.reduce((pre, cur)=> { obj[pre[key]] ? '' : obj[pre[key]] = true && pre.push(cur) return pre },[]) return curArr }
// 实现sleep(1000) const sleep = time => { return new Promise((resolve) => { resolve(setTimeout(() => { console.log('time'); }, time)) }) } // 使用 sleep(1000).then(res => { console.log(res); })
// 手动实现bind Function.prototype.myBind = function() { // 不能使用箭头函数,应为this的指向不同 // arguments 可以获取函数的所有参数,是一个伪数组, 使用Array.from() 将 伪数组转换成数组 const arg = Array.from(arguments) // 获取this 指向取出数组的第一项,数组剩余的就是传递的参数 const self = this return () => { return self.apply(_this,arg) } } // 使用 function fn1(a, b, c) { console.log('this',this); console.log(a,b,c); } const fn2 = fn1.myBind(10,20,30) console.log(fn2);
// 手动实现call Function.prototype.myCall = function() { const fn = Symbol('fn') // 声明一个独有的symbol属性,防止fn覆盖已有属性 thisArg = thisArg || window // 如没有传入this,默认绑定到window thisArg[fn] = this // this 指向调用call的对象,即我们要改变this指向的函数 const result = thisArg[fn](...args) // 指向当前函数 delete thisArg[fn] // 删除声明的fn属性 return result }
// 并集,交集,差集 let set1 = new Set([1,2,3]) let set2 = new Set([2,3,4]) // 并集 let union = new Set([...set1, ...set2]) // 交集 let interset = new Set([...set1].filter(x => set2.has(x))) // 差集 let difference = new Set([...set1].filter(x=> !set2.has(x)))
// 克隆 function deepClone(obj) { if (!obj || typeof obj !== 'object') return const data = Array.isArray(obj) ? [] : {} for (let i in obj) { data[i] = typeof(obj[i]) === 'object' ? deepClone(obj[i]) : obj[i] } return data } // 去重 function removeRepeat(arr, key) { let obj = {} const cur = arr.reduce((pre,cur) => { obj[cur[key]] ? '' : obj[cur[key]] = true && pre.push(cur) return pre },[]) return cur } // 求和 function sum(arr) { let data = arr.reduce((pre,cur) => { return pre + cur },0) }
// 防抖是在事件触发后n秒再去执行回调,如果n秒内再次被触发,则重新计算,最常用 搜锁框输入查询,表单提交 function debouce(fn, wait) { let time = null return function() { time && clearTimeout(time) time = setTimeout(() => { fn.apply(this, arguments) }, wait); } } // 节流 是指如果频繁触发一个事件,则n秒后执行一次 function throttle(fn, wait) { let flag = true return function() { if (!flag) return flag = false setTimeout(() => { fn.apply(this, arguments) flag = true }, wait); } }
// Set 和Map的区别是,Set类似于数组,每一个值是唯一的,Map类似于对象,是键值对的集合 // AMD require 是运行时调用,可以用在任何地方,是赋值的过程可以是数组,对象,字符串 // CMD import 是编译是调用,所以必须用于开头,是解构的过程
// 通常缓存,分为协商缓存和强制缓存 // 强制缓存: 1.浏览器控制 2.后端把过期时间和资源发过来,浏览器每次获取资源看一下过期时间到了没 // 协商缓存: 1.服务器控制 2.后端把资源标识,和资源发送过来,浏览器每次用的时候,就会拿标识和后端存储的对比一下不一样就更新 // 强制缓存使用时间来判断,时间分为相对时间和绝对时间 // 绝对时间 expires字段 // 相对时间 后端给有效时长,浏览器自己倒计时,对应着请求的cach-control字段,同时存在。cach-control优先级更高
// js垃圾回收,就是标记清除和引用计数 // js中 ! 和 !! !将变量转换成boolen类型,!!常常用来做类型判断,在第一步!之后再做逻辑取反运算, ??只有在左侧为null和undefiend时,才会返回右侧数据
// 渐进式图片加载 antimoderate
// aligin-self: baseline
// for of 无法遍历对象,forEach和map都只能遍历数组,不能终端,前者没有返回值,后者有
// JS中 宏任务一般包含 scpirt setTimeout setInterval 微任务: promise preocess.nextTick , promise 是同步任务 then是异步任务
// 什么是事件循环: js是单线程语言,同个时间执行一件事(同步),但它可以有一个异步队列,遇到异步操作,(如axios这种阻塞时间久的),
把它放到异步队列中,并继续往下执行,当同步任务执行 // 完成,就会去异步队列中找刚刚存放的事件依次执行(异步任务包含宏任务和微任务,微任务优先于宏任务)
// var 可以重复声明,无法限制修改,没有块级作用域, let 和const 不能重复声明,const 常量不能修改
const只能在声明的时候被初始化一次,之后不能讲全新的值赋值给它,但是,任然可以修改const常量已经存在的值,只是不能重写
因为对象是引用类型,const 中保存的仅仅是对象的指针,const只保证指针不改变,对象的属性是可以修改的
// vue中定义props类型,Proptype
// [1,2,3].map(parseInt) 结果时[1,NAN,NAN] 原因是,最终会转换成 arr.map((value,index, array) => parseInt(value,index))
// flex:1 对应这flex-grow(拉伸占比) flex-shrink(收缩规则,0 为不收缩) flex-basis(主轴方向初始大小) // 回流,当元素尺寸,位置发生变化需要重新计算。 重绘: 当元素DOM样式发生变化,没有影响到DOM的几何尺寸
// rem相对于根元素 如果font-size = 16px,那 // 监听屏幕大小 window.onresize = function() { // 获取屏幕宽度 let w = document.documentElement.clientWidth || document.body.clientWidth
// 根据设计图设置大小 HTML.style.fontSize = 16 * (w / 750) + 'px' }
// iphonex 适配当viewport-fit = contain 时,env() 是不起作用的 必须配合 viewport-fit-cover // env() 和 constant() 需要同时存在,且顺序不能换 paading - bottom: containsPoint(safe - area -inset -bottom); paading - bottom: event(safe -area -inset - bottom)
// GET和POST 请求的区别:
1. get请求在url上,post在请求体中
2.get请求有长度限制(浏览器限制的)
3.post请求相对安全点,get请求在url上,且有历史记录 4. get请求能缓存,post不能
// HTTPS使用443端口,HTTP使用80端口 2. http是超文本传输协议,是明文传输,
https是经过SSL加密协议,传输更安全,不过要申请证书
3. https比http慢因为,除了tcp握手的三个包,还有ssl加密的9个包 // http1.1和http2.0区别: 1.1使用基于文本的格式传输,2.0使用二进制传输,
1.1不支持header数据压缩,2.0使用hpack算法对header进行数据压缩,体积更小,传输更快 // http 请求状态码总结 // 1** 服务器接收请求,需要请求者接着操作 // 2** 请求成功 // 3** 重定向,需要进一步操作 // 4** 客户端错误,请求包含语法错误,无法完成请求 // 5** 服务器错误 // 从浏览器输入url页面发生了什么? // 1. DNS查询,解析url中对应的ip地址 2.tcp链接 3.发送http请求
4.server处理http请求,并返回http保文 5. 浏览器解析,render页面 6. http断开链接
// VUE中diff算法: // VUE根据真实dom生成一个virtural DOM ,当virtral DOM 的某个节点数据改变后会生成一个新的Vnode,
然后Vnode和 oldVnode做对比,发现不一样就直接更新dom,然后oldVnode的职位Vnode, // 来实现节点更新。 具体是: 1.同级比较再比较字节点
2. 先判断一方是否有子节点,如果新的的没有子节点,就将旧的字节点移除掉 3.都有子节点的情况,递归比较子节点
// VUE中key的作用,尽可能的复用dom元素,新旧节点如果只有顺序不同,最佳是通过移动位置达到更新目的,需要再节点保持映射关系,key就是节点中的唯一标识
// VUE中template的编译原理 1. 将模板字符串转换成element Asts解析器 2. 对ast进行静态节点标记,主要用来虚拟dom的优化 3.使用element ast生成render函数代码字符串
// .stop禁止事件冒泡, .prevert 阻止默认事件, .self 只有点击元素本身才会执行 .capture事件触发,会先执行大盒子的事件再执行小盒子的事件,.once只触发一次
// data为什么是一个函数,对象再栈中存放的都是对象的地址,函数的作用,就是属性私有化,保证组件修改自身的属性不会影响到其它组件
// VUE中keep-alive实现原理和缓存策略: // 1.获取keep-alive的第一个组件 2. 根据include和 exclude名单进行匹配,决定是否缓存,不匹配直接返回组件实例,匹配进行第3步
3. 根据组件id和tag生成缓存组件的key,再去判断cache中是否存在这个key,如果存在,则用缓存的实例代替Vnode的 // 的实例,然后更新key再keys的位置(LRU置换策略),如果没有命中就缓存起来,如果超出最大缓存数,就删除cache中的第一项
4. keep-alive是一个抽象组件:自身不会渲染DOM元素,也不会出现再父组件链中 5.LRU算法:根据 // 数据的历史访问记录来进行数据淘汰,其实就是访问过的,以后访问率会高
6. LRU实现,新数据插入链表头部,每当缓存命中,则将数据移至链表头部,当链表满的时候,则将链表尾部数据丢弃
// vue-router的实现原理:单页应用只有一个页面,所有的页面切换,其实是组件之间的显示和隐藏,所有的页面都只在一个html页面上, // vue-router是通过对window.location.hash和windown.history进行封装,来实现hash或url变化映射组件的变化 // vue-router 前端路由的核心就是改变视图的同时不会向后端发送请求,而是加载对应的而组件, 有两种模式 hash和history。
hash模式在url后面带有#,hash值会出现在#后面,但是不包含在http请求中,hash改变会 // 触发hashChage事件,能控制浏览器的前进后退,兼容性好 缺点是: 包好#不美观,
设置的值必须与原来的值不一样才会触发hashChange, 每次url改变不属于一次http请求,不利于seo优化 // history 基于pushState()和replaceState()来实现改变页面路径,histoty是浏览器提供的历史记录接口,
可以通过back go forward读取历史记录,history模式下,需要后端配置相应的路径,不然会出现404
// VUE中nextTick原理: 因为vue采用的异步更新策略,当监听到数变化时,不会立即更新dom,而是开启一个任务队列,
缓存同一事件所有的数据变更,在这个事件循环之后依次执行队列中所有任务, 好处是,可以将多次更新合并成一次,减少dom操作,Vue 不止使用
setTimeout 实现nextTick;会判断promise是否存在,选择任务类型
// VUE中vue.use的作用,和Vue.prototype.$xxx的关系 vue.use就是要运行插件里面的install函数,vue的插件是一个对象,就像常用的element框架一样,插件对象必须要有一个install函数,初始化插件通过vue.use 为什么Vue.prototype.$xxx 其实只不过是js中函数的原型特性,函数的原型上的方法活属性,
在函数实例化之后,可以在任意实例上读取,一个是封装vue插件的写法,一个是在实例上挂载 vue3中因为使用createApp这个api返回一个应用实例,可以链条的方式继续调用 app.config.globalPropertier.$xxx = xxx
// VUE3自定义v-model // 父组件中使用 <test v-model:title ="value" /> //子组件中定义 <div :title="title" @update:title="title = $event">{{title}}</div> emit('update:title',t.value) // vue2自定义v-model mode: { event: 'change', prop: 'check' }
// 前端常见的内存泄露
内存泄露是指:用动态存储分配函数内存空间,在使用完毕后未释放,导致一直占据该内存单元。直到程序结束。指任何对象在你不再拥有或需要它之后仍然存在。
1. 意外的全局变量挂在在window上的,解决办法把变量设置为null
2. 未清理的dom元素, 虽然移出了,但是变量a的引用一直存在 解决 a= null
let a = document.getElementById('a')
document.body.removeChild(a)
3. 定时器,页面关闭的时候没有销毁
4. 闭包会造成对象引用的生命周期脱离当前函数的上下文
function test() {
var name = 'li';
var age = 18;
function person() {
console.log(name);
console.log(age);
}
return person;
}
var fn = test();
fn();
5. console.log 的对象不会被垃圾回收