每日一题 点滴进步~

644 阅读17分钟

每日一题 点滴进步~

2022.1.21

1.箭头函数与普通函数的区别

  • 箭头函数相比于普通函数更加简洁

  • 箭头函数不会改变this的指向

    • call() apply()bind() 都不会改变箭头函数this的指向
  • 箭头函数没有prototype 、 agruements

  • 箭头函数没有原型故不能作为构造函数

  • 箭头函数不能作Generator函数,不能使用yeild关键字

2.this指向哪⾥?

  • 对于箭头函数来说 , this的指向取决于箭头函数外部的执行上下文
  • 对于普通函数来说 ,this的指向取决于该函数运行时的执行上下文环境
  • 对于有bind()、apply()、call() 的函数this的指向取决于方法指定的this
  • 对于构造函数来说,this指向为新对象

3.扩展运算符的作用及使用场景

  • 对象扩展运算符 用来取出参数对象中所有的可遍历属性, 拷贝到当前对象中

    • let obj = {a: 1 , b: 2};
      let obj1 = {...obj} // {a: 1 , b: 2}
      
  • 数组扩展运算符

    • 任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组

      • 字符串转化为数组
      • 集合转化为数组
    • 合并数组

2022.1.22

1. JavaScript中对象继承的方式有哪些?

  • 原型式继承

    • // 原型式继承
      // Object()可以理解为对传入的对象进行一个浅复制
      let person = {
          name: 'szj',
          friends: ['zy', 'wjr', 'ghk']
      }
      ​
      let me = Object(person);
      me.friends.push('ssss')
      console.log(me);
      console.log(person)
      ​
      //{ name: 'szj', friends: [ 'zy', 'wjr', 'ghk', 'ssss' ] }
      //{ name: 'szj', friends: [ 'zy', 'wjr', 'ghk', 'ssss' ] }
      ​
      
  • 寄生式继承

    • // 寄生继承
      // 像是对原型继承的另一种升级 , 采用了工厂模式的方法 增加了对象的方法
      let jisheng = {
          name : 'sss',
          friends: ['ss' , 'www']
      }
      ​
      const js = (obj) =>{
            const clone = Object(obj);
            clone.sayHi = function (){
                console.log(clone.name);
            }
            return clone
      }
      ​
      const m = js(jisheng);
      m.sayHi();
      // sss
      
  • 组合继承

    • // 组合继承
      // 原型链 + 盗用构造函数
      function Super(name){
              this.name = name;
              this.friends = ['szj' , 'zy'];
      }
      Super.prototype.sayName  = function (){
          console.log(this.name);
      }
      ​
      function Sub(name){
              // 盗用构造函数
              Super.call(this , name);
      }
      // 原型链继承
      Sub.prototype = new Super;
      ​
      const bob  = new Sub('szj');
      bob.sayName()
      console.log(bob.friends);
      // szj
      // [ 'szj', 'zy' ]
      
  • 寄生式组合继承

    • // 寄生式组合继承
      // 最佳实践!! 相比于组合继承 减少了构造函数调用的次数
      function Superr (name){
          this.name = name;
          this.friends = ['ss' , 'www' , 'ppp'];
      }
      Superr.prototype.say = function (){
          console.log(this.name);
      }
      ​
      function Subb(name){
          Superr.call(this , name)
      }
      ​
      function inheritPrototype (sub , superr) {
          const prototype = Object(superr.prototype);
          // 别忘了原型对象的指向
          prototype.constructor = sub;
          sub.prototype = prototype;
      }
      ​
      inheritPrototype(Subb , Superr);
      const zy = new Subb('zy');
      zy.say()
      console.log(zy.friends);
          // zy
          // [ 'szj', 'zy' ]
      

    2. 实现异步加载方法的关键字有哪些?

    image.png

    • defer

    • async

    • 总结:

      • 两者都是用来异步加载外部脚本的关键字
      • defer 加载完成后 页面渲染后执行
      • async 加载完成后 立即执行
      • 多个 defer 按顺序依次执行 , 多个async执行顺序不定

    3. 什么是JS的事件循环 , 事件循环机制是什么?

    image

如图: JS是单线程的 , 在代码执行时,会将代码压入执行栈中保证函数的有序执行 , 遇到异步任务会将 任务抛给webapi进行处理 , 处理之后将回调函数推到任务队列(宏任务队列 , 微任务队列) , 当执行栈为空时 , 先执行微任务队列中的函数 , 如果需要渲染页面 , 则会渲染页面 , 最后执行宏任务队列中的函数。

Event Loop 执行顺序如下所示:

  • 首先执行同步代码,这属于宏任务
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 当执行完所有微任务后,如有必要会渲染页面
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码

宏任务 微任务

  • 微任务包括: promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。
  • 宏任务包括: script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲染等。

2022.1.23

1. let、const、var区别

  • 作用域

    • let 、 const 为块级作用域
    • var为函数作用域
  • 是否可以重复声明

    • let var 可以重复声明
    • const 不可以重复声明
  • 变量提升

    • var 具有变量提升
  • 暂时性死区

    • let 、const 具有暂时性死区 , 未声明之后不能使用
  • 给全局添加属性

    • var 可以给全局添加属性 let const 不会
  • 初始值设置

    • var let 声明时可以不设置初始值
    • const 必须设置初始值
  • 指针的指向

    • let 可以修改指针的指向(重新赋值) , const 不能够修改指针的指向不可以重新赋值,但是引用数据类型的属性可以改变
区别varletconst
是否有块级作用域×✔️✔️
是否存在变量提升✔️××
是否添加全局属性✔️××
能否重复声明变量✔️××
是否存在暂时性死区×✔️✔️
是否必须设置初始值××✔️
能否改变指针指向✔️✔️×

2. 数组去重的方法有哪些?

1. 数组元素比较型
  • 双层for循环

    • // 双层for循环
      // 前一个跟之后所有进行比较 , 重复了删除掉
      function uniq(arr){
          for(let i = 0 ; i < arr.length - 1 ; i++){
              for(let j = i + 1 ; j < arr.length; j++){
                  if(arr[i] === arr[j]){
                      arr.splice(i , 1);
                      // 删除后下表移动到原位置
                      j--;
                  }
              }
          }
          return arr;
      }
      ​
      
  • 排序后 相邻位置进行比较

    • // 排序进行后进行相邻比较
      function sortQ(arr){
          // 排序后
          // 没参数 如果没有指明 compareFunction ,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。
          arr.sort();
          for(let i = 0 ; i < arr.length - 1 ; i++){
              if(arr[i] === arr[i + 1]){
                  arr.splice(i , 1);
                  i--;
              }
          }
      ​
      }
      
2.查找元素位置型
  • indexOf 查找元素并返回其第一个索引值

    • function uniq(arr){
          const res = [];
          for(let i = 0 ; i < arr.length ; i++){
              if(arr.indexOf(arr[i]) === i){
                  res.push(arr[i])
              }
          }
          return res;
      }
      
  • findIndex 返回数组中第一个满足测试函数的元素的索引

    • function uniq(arr){
          const res = [];
          for(let i = 0 ; i < arr.length ; i++){
              if(arr.findIndex(item => item === arr[i]) === i ){
                  res.push(arr[i]);
              }
          }
          return res;
      }
      
3. 查找元素存在型
  • includes

    • // 查找元素存在型
      function uniq(arr){
          const res = [];
          for(let i = 0 ; i < arr.length ; i++){
              if(!res.includes(arr[i])){
                  res.push(arr[i])
              }
          }
          return res;
      }
      
4. 利用数据结构类型
  • set

    • // set
      function uniq(arr){
          return [...new Set(arr)]
      }
      
  • map

    • function uniq(arr){
          const map = new Map();
          arr.forEach(item =>{
              map.set(item , true)
          })
          // 返回键值 Object.keys(key)
          return[...map.keys()]
      }
      
5. 总结

在简单的测试用例大概 2000 万条数据下,indexOf 的方案速度相对最快

img

3. vue双向绑定的原理

  • Mvc 模式 到 mvvm模式 的转变

img

  • Mvc 模式 controler 层要大量的控制dom
  • Mvvm 模式 是真正做到了数据与视图的分离, view 和 model 改变时 , vm层自动进行数据和视图的同步
  • vue.js 采用数据劫持结合发布者-订阅者模式的方式 , 通过Object.defineProperty()来劫持各个属性的setter、getter,在数据监听时发布消息给订阅者 , 触发响应的监听回调

  • 发布 订阅者模式让双向绑定更有效率(一对多)

  • 实现一个数据监听器 Observer

    • 核心是 Object.defineProperty() , 将Observe的数据对象进行递归遍历 , 包括子属性的对象加上setter getter方法 , 赋值时就会调用setter方法,就监听到了数据变化
    • 通知订阅者
  • 实现Compile

    • 解析模板的指令 , 将模板中的变量替换成数据 ,
    • 初始化页面渲染 ,
    • 并绑定更新函数 , 添加监听数据的订阅者 , 一但数据有变化 , 更新视图 --绑定更新函数
  • 实现watcher (解析 compile 和 observe 的桥梁)

    • 实例化在订阅者添加自己
    • 自己有一个update()方法 --添加订阅者
    • 待属性变动 , 接受通知,调用自身的update() , 并触发compile中的回调 --》更新视图
  • 整合形成一个mvvm

img

4.70. 爬楼梯 - 力扣(LeetCode) (leetcode-cn.com)

  • 经典dp
​
var climbStairs = function(n) {
    const dp = [];
    dp[0] = 1;
    dp[1] = 1
    for(let i = 2 ; i <= n ; i++){
        dp[i] = dp[i - 1] + dp[i - 2];
    } 
    return dp[n]
​
};

2022.1.24

1.字面量创建对象和 new 创建对象有什么区别,new 内部都实现了什么, 手写一个 new

  • 字面量创建对象 简洁 比较易读 不需要作用域解析 , 速度更快

  • new的过程

    • 开辟一个新内存
    • 将新对象的[[prototype]]指针指向构造函数的变量对象
    • this指向新对象
    • 执行构造函数内部的代码
    • 如果构造函数返回一个对象返回该对象 , 否则返回建立的对象
  • 手写一个new

    • function reNew (obj , ...rest){
          //  原型
          const newObj = Object.create(obj.prototype);
          // 属性 + 原型
          const result = obj.call(newObj , rest);
          // 如果结果为一个对象 返回 不是的话返回继承原型之后的
          return typeof result=== 'object'? result : newObj;
      }
      

2.== 和 === 的区别

  • 本质区别是 比较的时候是否会进行数据类型的转换转换 。
  • === 严格意义上的相等 不会进行数据类型的转换 直接比较值
  • == 通常会转化为相同数据类型进行值得比较

3. 在 JS中为什么 0.2 + 0.1 > 0.3

  • JS中只有一种数字类型 Number , 遵循IEEE规则 ,双精度浮点数 64位 1位符号位 11位指数位 52位尾数
  • 计算机是通过二进制的方式进行存储数据 , 由于尾数有限 只能记录52位 因此加上之后转化为十进制 为0.300000004

4. 那为什么 0.2 + 0.3 == 0.5 呢

  • 还是因为52位尾数 二进制加完之后 53位有数字 但是只要52 位 且没有进位

2022.1.25

1.三拦布局,有多少种?思路和代码

  • flex布局 父级元素弹性布局 左右盒子固定宽度 中间flex:1

    •     .container{
              display: flex;
          }
          .left{
              width: 100px;
              background-color: antiquewhite;
          }
          .right{
              width: 100px;
              background-color: aqua;
          }
          .center{
              flex: 1;
              background-color: aquamarine;
          }
      
  • 浮动

    • 父级盒子BFC 清除浮动
    • 将盒子的位置重新放置 左右中
    • 左右分别 左右浮动 中间 margin进行
    •     .container{
              overflow: hidden;
          }
          .left{
              float: left;
              height: 500px;
              width: 100px;
              background-color: antiquewhite;
          }
          .right{
              float: right;
              height: 500px;
              width: 100px;
              background-color: aquamarine;
          }
          .center{
              margin-left: 100px;
              margin-right: 100px;
              background-color: blue;
              height: 500px;
          }
          
          <body>
          <div class="container">
              <div class="left"></div>
              <div class="right"></div>
              <div class="center"></div>
          </div>
      </body>
      
  • 绝对定位

    • 左右绝对定位
    • 中间 margin
    •     .container{
              position: relative;
          }
          .left{
              position: absolute;
              left: 0;
              width: 100px;
              height: 500px;
              background-color: aquamarine;
          }
          .right{
              position: absolute;
              right: 0;
              width: 100px;
              height: 500px;
              background-color: blueviolet;
          }
          .center{
              margin-right: 100px;
              height: 500px;
              margin-left: 100px;
              background-color: aqua;
          }
      
  • 总结:

    • 最优解 flex 但是兼容性不能兼容ie8以下
    • 浮动和绝对定位都会导致 盒子脱离文档流 因此 最好将浮动或绝对定位的盒子 放到中间盒子的前面

2. css中定位有多少个值,每个值有什么特点

属性值概述
absolute绝对定位 , 元素会被移除文档流 , 寻找最近的非static的父元素进行偏移
relative相对定位 , 相对于本身自己的位置移动 。 可以配合absolute进行 相对于指定元素的定位
fixed绝对定位 , 相对于屏幕视口进行定位
static默认值 没有定位 元素会存在于文档流中 , 会忽略 top, bottom, left, right 或者 z-index 声明
inherit继承父元素的position

3.对BFC的理解,如何创建BFC

  • BFC也叫块级格式化上下文 , 其本质上是指 盒子内部的元素不会影响外部元素的一个布局

  • 如何创建

    • float 不为none
    • overflow 不为 visiblie
    • position absolution
    • display flex
  • 作用

    • 解决margin重叠的问题
    • 解决 浮动元素 高度塌陷的问题

4. 算法:剑指 Offer 30. 包含min函数的栈 - 力扣(LeetCode) (leetcode-cn.com)

  • 最小栈 利用两个栈
/**
 * initialize your data structure here.
 */
var MinStack = function() {
    this.stack = [];
    this.minS = [];
​
};
​
/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
    this.stack.push(x);
    if(this.minS.length){
        if(this.minS[this.minS.length - 1] > x){
            this.minS.push(x)
        }else{
            this.minS.push(this.minS[this.minS.length - 1])
        }
    }else{
        this.minS.push(x)
    }
​
};
​
/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    this.stack.pop();
    this.minS.pop();
​
};
​
/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    const c = this.stack.pop();
    this.stack.push(c);
    return c
​
​
};
​
/**
 * @return {number}
 */
MinStack.prototype.min = function() {
    return this.minS[this.minS.length - 1]
};
​
/**
 * Your MinStack object will be instantiated and called as such:
 * var obj = new MinStack()
 * obj.push(x)
 * obj.pop()
 * var param_3 = obj.top()
 * var param_4 = obj.min()
 */

2022.1.26

1. 你了解CDN吗?

  • CDN也叫内容分发网络技术 , 目的是为了提高传输内容的传输速度和稳定性,避开网络上的拥塞和不稳定的节点 , 寻找最近的网络节点获取资源

  • cdn上的内容分为静态和动态内容

    • 静态:提前备份到cdn服务器
    • 动态:可能会在cdn服务器实现一些接口
  • 如何转发:任播 -- 服务器对外拥有一个相同的ip地址 , 请求会被距离最近的cdn服务器接受

2. Https 加密算法

https 分为三种加密加密算法

  • 摘要算法 : 输入一系列子串 输入长度一定 , 常见的MD5
  • 非对称加密:公钥加密 私钥解密 (公钥会传输 私钥不会传输)
  • 对称加密:用同一个密钥加密 并且解密 异或的思想

1418a9c3-66d8-43da-b53c-c3ecce2c96d8.png 拓展:

https的传输过程(4次握手)

  • 客 : 发送协议的版本 可以支持的加密算法 以及一个随机数
  • 服 : 确定加密算法 , 发送公钥 , 发送数字证书 以及一个随机数
  • 客 : 将公钥加密后的随机数发送
  • 服 : 将随机数用私钥解密
  • 至此 , 将 第一随机数 第二随机数 加公钥加密后的随机数 生成 会话密钥 (对称加密)

3. JS如何进行垃圾回收的?如果回收出现循环引用会导致什么?

垃圾回收的策略

  • 标记清理

    • 将内存中的所有变量或者函数都进行标记 , 调用后去除标记 , 一段时间后 , 剩下还有标记的变量就会被回收
  • 引用计数

    • 跟踪每一个值被引用的次数 , 赋值加一 被覆盖减一 当为零的时候可以清理
  • 循环引用

    • 当出现两个值相互引用的现象 , 利用引用计数进行垃圾回收 , 会导致循环引用 , 永远不会释放 , 因此得手动解除引用

      • 例如
      • obj1.a = obj2
        obj2.a = obj1
        //引用次数都为2
        ​
        // 手动解除引用
        obj1.a =  null obj2.a =  null
        

4. Vue中组件传值得方法有哪些?

  • 父子组件

    • 父组件传子组件

      • props
    • 子组件向父组件传值

      • $emit()
      • 子组件中定义一个方法 this.$eimt('父组件中的函数名' , 传的值的名字)
      • 父组件中接受 @子组件中定义的名字 = ‘’函数名称‘ 直接以参数的形式接受
  • eventsBus事件总线

    • 创建eventsBus.js

    • 发送数据

      • EventBus.$emit('addition' , { num : this.num++})
    • 接受数据

      • EventBus.$on('addition' , pramas =>{ console.log(paramas.num)})
  • ref/$refs

    • ref属性在子组件上 ,他的引用就指向子组件的实例 , 便可以访问子组件内部的方法
  • 依赖注入(project / inject)

    • 这种方式就是Vue中的依赖注入,该方法用于父子组件之间的通信。当然这里所说的父子不一定是真正的父子,也可以是祖孙组件,在层数很深的情况下,可以使用这种方法来进行传值。就不用一层一层的传递了。

      project / inject是Vue提供的两个钩子,和datamethods是同级的。并且project的书写形式和data一样。

      • project 钩子用来发送数据或方法
      • inject钩子用来接收数据或方法

      在父组件中:

      provide() {
       return {
          num: this.num
        };
      }
      

      在子组件中:

      inject: ['num']
      

      还可以这样写,这样写就可以访问父组件中的所有属性:

      provide() {
       return {
          app: this
        };
      }
      data() {
       return {
          num: 1
        };
      }
      ​
      inject: ['app']
      console.log(this.app.num)
      

      注意: 依赖注入所提供的属性是非响应式的。

5.算法 5. 最长回文子串 - 力扣(LeetCode) (leetcode-cn.com)

  • 动态规划
/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function longestPalindrome(s){
    let n = s.length;
    let res = '';
    let dp = Array.from(new Array(n),() => new Array(n).fill(false));//初始化二维数组dp[n][n]
​
    for(let i = n-1; i >= 0; i--){//循环字符串
        for(let j = i; j < n; j++){
          //dp[i][j]表示子串i~j是否是回文子串
​
          //回文子串必须满足s[i],s[j]相等。并且向外扩展一个字符也相等,即dp[i+1][j-1]也是回文子串
          //j - i < 3表示子串之间剩余的也必然是回文串(ij的差值为0表示ij指向同个下标,
          //1表示ij为相邻下标,2表示ij为相隔一个的下标)
            dp[i][j] = s[i] == s[j] && (j - i < 3 || dp[i+1][j-1]);
            
            if(dp[i][j] && j - i + 1 > res.length){//当前回文子串比之前的大,更新最大长度
                res = s.substring(i,j+1);
            }
        }
    }
    return res;
};

2022.1.27

  1. 如何判断两个链表是否相交?
  2. 对vue的响应式原理有了解么,可以简单的说一下么?
  3. cookie是为了解决什么问题,session, localStorage, sessionStorage,这三者的区别是啥呢?
  4. 给你一棵树,树上的每条边权值都是1,求树上两个节点的距离,使得路径最大

1. 如何判断两个链表是否相交?

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
​
/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
//  创建一个计算长度的函数
 const countLen = (head) =>{
     let count = 0;
     while(head){
         head = head.next;
         count++;
     }
     return count;
 }
var getIntersectionNode = function(headA, headB) {
    let lenA = countLen(headA);
    let lenB = countLen(headB);
    let p1 = headA;
    let p2 = headB;
    if(lenA > lenB){
        [lenA  , lenB] = [lenB , lenA];
        [p1 , p2] = [p2 , p1];
    }
​
    let i = lenB - lenA;
    while(i--){
        p2 = p2.next;
    }
    while(p1 && p2){
        if(p1 === p2){
            return p1
        }
        p1 = p1.next;
        p2 = p2.next;
    }
    return null
};

2.对vue的响应式原理有了解么,可以简单的说一下么?

整体思路是数据劫持+观察者模式

对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。

相关代码如下

class Observer {
  // 观测值
  constructor(value) {
    this.walk(value);
  }
  walk(data) {
    // 对象上的所有属性依次进行观测
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value);
    }
  }
}
// Object.defineProperty数据劫持核心 兼容性在ie9以及以上
function defineReactive(data, key, value) {
  observe(value); // 递归关键
  // --如果value还是一个对象会继续走一遍odefineReactive 层层遍历一直到value不是对象才停止
  //   思考?如果Vue数据嵌套层级过深 >>性能会受影响
  Object.defineProperty(data, key, {
    get() {
      console.log("获取值");
​
      //需要做依赖收集过程 这里代码没写出来
      return value;
    },
    set(newValue) {
      if (newValue === value) return;
      console.log("设置值");
      //需要做派发更新过程 这里代码没写出来
      value = newValue;
    },
  });
}
export function observe(value) {
  // 如果传过来的是对象或者数组 进行属性劫持
  if (
    Object.prototype.toString.call(value) === "[object Object]" ||
    Array.isArray(value)
  ) {
    return new Observer(value);
  }
}
复制代码

响应式数据原理详解 传送门

3.cookie是为了解决什么问题,session, localStorage, sessionStorage,这三者的区别是啥呢?

  • cookie (服务器设置 , 客户端储存 , 同源请求携带) 常用来识别用户 , 与session一起跟踪用户的状态。 最多储存4k
  • session 保存在服务端 , 用来跟踪用户的状态 , 与cookie一起使用
  • sessionStorage : HTML5 提供的一种浏览器本地存储的方法 , 类似于session , 代表了一次会话所保存的数据 , 页面关闭后失效
  • loaclStorage:HTML5 提供的一种浏览器本地存储的方法 , 保存在本地 , 不删除就会永久的储存。

4.给你一棵树,树上的每条边权值都是1,求树上两个节点的距离,使得路径最大

543. 二叉树的直径 - 力扣(LeetCode) (leetcode-cn.com)

  • 思路 : 递归 (深度遍历)
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
​
//  深度优先遍历
​
var diameterOfBinaryTree = function(root) {
    let res = 1; 
    const deep = (n) =>{
        if(!n) return 0;
        let l = deep(n.left);
        let r = deep(n.right);
        //  最大访问节点数
        res = Math.max(res , l + r +1)
        //  返回每一次的最长节点树
        return Math.max(r , l) + 1;
}
    deep(root);
    return res - 1;
​
};

2022.1.18

1.力扣3 无重复字符的最长子串 2.css优先级是怎么计算的 3.typeof和instanceof的区别 4.v-if和v-show区别

1. css优先级如何计算

  • css优先级是通过权重计算得到的
  • 选择器权重
    内联样式1000
    id选择器100
    类选择器、属性选择器、伪类选择器10
    标签选择器、伪元素选择器1
  • 另外 !inportant 优先级最高!!

2. typeof和instanceof的区别

  • 相同点:两者都是操作符 用来判断类型

  • 不同点

    • typeof: string number symbol bigInt boolean function object , 可以判断七种类型 只能判断对象 , 无法判读哪种对象
    • instanceof : 可以判断 是哪种object 但是不能判断 原始数据类型

3. v-if和v-show区别

  • 原理:v-if 是利用动态的向DOM树内添加或者删除DOM , v-show 是通过设置DOM元素的display属性控制显隐
  • 编译过程:v-if 切换有一个局部编译/卸载的过程 , 切换过程中合适的销毁和重建内部的事件监听和子组件 , v-show只是进行一个css样式的切换
  • 编译条件:v-if 是惰性的 , 当初是条件为假 , 不会进行一个编译 , 只有到条件为真的时候才开始真正的编译 , v-show 无论条件是否为真都会进行一个编译
  • 性能消耗:v-if 涉及到编译卸载的过程 比较高的切换消耗, v-show 有比较高的初始渲染消耗
  • 使用场景:频繁切换用v-show ,条件不大可能改变用v-if

4.3. 无重复字符的最长子串 - 力扣(LeetCode) (leetcode-cn.com)

  • 思路:

    • map + 快慢指针
    • 慢指针 每次遇到重复的元素 就向重复元素之后移动
  • var lengthOfLongestSubstring = function(s) {
        // 思路:快慢指针 
        if(!s) return 0;
        const arr = [...s];
        const map = new Map();
        let l = 0;
        let res = 0;
        for(let r = 0 ; r < arr.length ; r++){
            const c = arr[r];
            if(map.has(c) && l <= map.get(c)  ){
                l = map.get(c) + 1;
            }
                res = Math.max(res , r -l + 1 );
            map.set(c , r);
        }
        return res
    };
    

2022.1.29

  1. null和undefined的区别
  2. 判断this指向的几种方法
  3. vue中key的作用,为什么不建议用index作为key
  4. promise.all实现

1. null 和 undefined的区别

  • undefined 意为未定义 , 常常是变量声明并没有赋值
  • null 空对象 , 常用来赋值给一个可能返回对象的变量

2. 判断this指向的几种方法?

  • 箭头函数类别

    • this指向为箭头函数外部的执行上下文

    • 非箭头函数

      • this指向该函数运行时的函数的执行上下文
    • 构造函数

      • this指向新对象
    • call、apply、bind this指向指定this

3. vue中key 的作用,为什么不建议用index作为key

  • 作用

    • 标识一个独立的元素

      • 在 v-if中 , 由于vue会尽可能高效的渲染元素 , 会优先复用相同类型的元素而不是重头渲染 , 因此为了防止复用相同的元素(input用户输入不会被清除掉),使用key的元素就不会复用
    • 更高效的更新DOM

      • v-for中利用key来跟踪元素 , 如果发生了元素的顺序改变 , 没有必要移动dom 来匹配顺序 ,而是利用key值直接复用此处的元素。
    • 总结: 更准确 , 更快速

  • 为什么不建议?

    • index 仅仅是标记了数组的一个索引值 , 当数组中的元素顺序发生改变后 , 索引值并不发生改变 , 并不能提供便捷。

4. promise.all

const promiseAll = (_promises) =>{
    return new Promise ((resolve , rejected) =>{
        const promises = [..._promises];
        const agr = agruements;
        const result = [];
        for(let i = 0 ; i < promises.length ; i++){
            Promise.resolve(promises[i]).then((res) =>{
                result.push(res)
                if (i === promises.length - 1){
                    resolve(result)
                }
            } , (err) =>{
                rejected(err)
            })
        }
    })
​
}

2022.1.30

1.水平垂直居中的方法 2.ES6有哪些新特性? 3.宏任务和微任务都有哪些?有什么区别? 4.力扣:1.两数之和

1.水平垂直居中的方法

水平居中
  • flex 布局

    • display:flex ;justify-content : center
      
  • 块级元素

    • 设置宽度 margin : 0 auto
  • 行内元素

    • text-align : center
  • 绝对定位

    • position:absolute;
      left: 50%;
      transform : translateX(-50%) 
      // 或者
      margin-left : - width/2 ;
      
垂直居中
  • flex布局

    • display:flex;
      justify-content : center;
      alsign-item : center
      
  • 绝对定位

    • position:absolute;
      left: 50%;
      right: 50%
      transform : translate(-50% , -50%) 
      
    • position: absolute;
      left: 50%;
      top: 50%;
      margin-left :- width/2;
      margin-bottom: -top/2;
      
    • position:absolute;
      left:0;
      right:0;
      top:0;
      bottom:0;
      margin : auto;
      
  • 行内元素

    • line-height:height;
      text-align:center;
      

2.ES6有哪些新特性?

  • 解决原有语法上的不足

    • let const块级作用域
  • 对原有语法进行增强

    • 解构

      • 数组解构
      • 对象解构
    • 参数

      • 默认参数
      • 剩余参数
    • 模板字面量

  • 全新的数据结构和数据类型

    • symbol
    • bigInt
    • map
    • set
  • 全新的方法、函数、对象

    • promise对象
    • proxy 代理对象
    • Object.assign() Object.is()
    • 箭头函数

3.宏任务和微任务都有哪些?有什么区别?

1. 宏任务
  • I/O
  • setTimeout
  • setInterval
  • setImmediate
2. 微任务
  • Promise
3. 区别
  • 执行顺序

    • 当执行栈为空时 , 先执行微任务 , 后执行宏任务
  • 进程的切换为宏任务 , 线程的切换为微任务。

4.两数之和

var twoSum = function(nums, target) {
    const map = new Map();
    const res = [];
    nums.forEach((item , index) =>{
        if(map.has(target - item)){
            res.push(index , map.get(target - item))
        }
        map.set(item , index)
    } )
    return res
};