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 的对象不会被垃圾回收