Skip to content

2024-04-15

作者:guo-zi-xin
更新于:2 个月前
字数统计:2.1k 字
阅读时长:8 分钟
创建一个递增的数字数组
javascript
const nums = 100
const arr = Array.from(Array(nums).keys())

reduce方法的使用

  • 给定数组arr, 执行以下操作后最后打印结果
typescript
const arr = [2, 4, 6, 8]

arr.map(n=>n/2).filter(n=>n>1).reduce((a,b)=>b-a)
// 打印结果为3

第一步: 通过map方法给元素每一项执行 n / 2的回调函数, 得到结果为数组[1, 2, 3, 4]

第二步: 再调用filter方法过滤出来 n > 1的值并且返回这个新数组[2, 3, 4]

第三步: 新数组开始执行reduce方法, 目标reduce方法中没有传入第二个参数, 所以是默认从0开始计算的, 运行顺序为下面的顺序:

reduce中回调函数有两个参数, 第一个是累加器(accumulator), 第二个是当前值(currentValue)

|-|累加器acc|当前值cur|index|返回值(cur - acc)| |第一次调用|0|2|0|2| |第二次调用|2|3|1|1| |第一次调用|1|4|2|3|

综上 输出最终结果为 3

JS事件循环机制

javascript
console.log(1);
setTimeout(function () { console.log(2); }, 0);
new Promise(resolve => {
    console.log(3);
    resolve();
    console.log(4);
}).then(() => {
    console.log(5);
})
console.log(6);

打印结果为 1 -> 3 -> 4 -> 6 -> 5 -> 2

JavaScript中事件执行顺序是 先同步任务形成执行栈, 执行同步任务的过程中遇到异步任务会把异步任务先放到任务队列中,等同步任务执行完成后开始自行异步任务队列.

题中 属于同步任务的是 console.log(1)console.log(6) new Promise(),

属于异步任务的是setTimeout(function () { console.log(2); }, 0) .then()函数

所以先打印的项按顺序为 1 -> 3 -> 4 -> 6

在异步任务中,分为宏任务和微任务, 微任务先执行, 属于微任务的是 .then() resolve() 方法 属于宏任务的是定时器setTimemout 所以一步任务中先执行 .then, 之后执行 定时器,

所以先打印的项按顺序为 5 -> 2

综上 打印顺序为 1 -> 3 -> 4 -> 6 -> 5 -> 2

补充

属于宏任务的有:

  • setTimeout
  • setInterval
  • I/O
  • setImmediate(NodeJs中使用 浏览器中已经废弃)
  • 事件
  • requestAnimationFrame

属于微任务的有

  • Promise.then()
  • async/await
  • MutationObserver
  • process.nextTick(NodeJS)

封装一个多维数组转一维数组的方法

typescript
const arr = [1, 2, 3, [4, 5, [6]]]

const myFlat = <T>(arr:T[]): T[] => {
  let resultArr:T[] = []
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      resultArr = resultArr.concat(myflat(item))
    } else {
      resultArr.push(item)
    }
  })
  return resultArr
}

解析URL参数的函数

  • eg:
javascript
const url =  'http://www.baidu.com?k=1&k=1&kk=2#c=3'
return {
    query: {
        k: [1, 2]
        kk: 2
    },
    hash: {
        c: 3
    }
}
typescript
type ParseUrl = {
  query: Record<string, string| string[]>,
  hash: Record<string, string>
}

const parseUrl = (url: string): ParseUrl => {
  const parsedUrl = new URL(url)
  // 处理query
  const query: Record<string, string | string[]> = {}
  parsedUrl.searchParams.forEach((value, key) => {
    if (query.hasOwnProperty(key)) {
      if (Array.isArray(query[key])){
        query[key].push(value)
      } else {
        query[key] = [query[key] as string, value]
      }
    } else {
      query[key] = value
    }
  })

  // 处理Hash
  const hash:Record<string, string> = {}
  parsedUrl.hash.slice(1).split('&').forEach((item, index) => {
    const [key, value] = item.split('=')
    if (hash.hasOwnProperty(key)) {
      if (Array.isArray(hash[key])) {
        hash[key].push(value)
      } else {
        hash[key] = [hash[key] as string, value]
      }
    } else {
      hash[key] = value
    }
  })

  return { query, hash }
}
const url = 'http://www.baidu.com?k=1&k=1&kk=2#c=3&d=4';
console.log(parseUrl(url));

option请求

HTTP中的OPTIONS请求是用于确定服务器支持哪些HTTP方法和信息,它是一种预检请求, 通常用于跨域请求和CORS(跨资源共享)机制

一般是通过复杂跨域请求时才会发送OPTIONS请求

复杂跨域请求

复杂跨域请求满足以下任意条件就会触发

  1. 请求方法不是GET/POST/HEAD
  2. Content-Type并非application/x-www-form-urlencoded, multipart/form-data, 或text/plain
  3. 请求设置了自定义的header字段
用途
  1. 检查服务器支持的方法

    OPTIONS请求可以告诉客户端服务器支持哪些 HTTP 方法(如 GETPOSTPUTDELETE 等)。

  2. CORS预检

    在进行跨域请求时,浏览器会自动发送OPTIONS请求,以确定实际请求(如 POSTGET)是否安全。

  3. 访问控制

OPTIONS请求也可以用于访问控制,服务器可以根据请求头信息(如 OriginAccess-Control-Request-MethodAccess-Control-Request-Headers 等)来决定是否允许请求。

封装一个判断是否为数字类型的方法,返回布尔值

typescript

const isNumber = <T>(num: T): boolean => {
  if (isNaN(num)) return false
  if (typeof num === 'number') {
    return true
  } else {
    return false
  }
}

箭头函数指向

箭头函数的this指向 指向的是由其所属函数或全局作用域决定的

javascript
let config = {a: 1, b: () => {console.log(this)}}
let configs = {a: 1, b () {console.log(this)}}
config.b()
configs.b()
let config = {a: 1, b: () => {console.log(this)}}
let other = {c: 1}
other.b = config.b()
other.b()

Promise类方法

Promise.all()

Promise.all的作用是将多个Promise包裹在一起形成一个新的Promise,并且这个新的Promise的状态是由包裹的Promise的状态共同决定的:

  • 当所有的Promise的状态变成fullfilled,新的Promise的状态变为fullfilled,并将所有promise的返回值组成一个数组

  • 当有一个Promise的状态变成reject,新的Promise的状态会变成reject,并且会将第一个rejectPromise的返回值作为参数

Promise.resolve

Promise.resolve(res)方法返回一个以给定值解析后的Promise对象,有时候我们已经有一个现成的值希望将其转换成Promise可以使用该类方法

javascript
  Promise.resolve('这是一个Promise')
  //等价于
  new Promise(resolve=>resolve('这是一个Promise'))
Promise.race

如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法

Promise.any

any方法是ES12中新增的方法,和race方法是类似的,不同的是any方法会等到一个fulfilled状态,才会决定新Promise的状态,如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态。

如果所有的Promise都是reject的,那么会报一个AggregateError的错误 All promises were rejected

Promise.allSettled

all方法有一个缺陷:当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态。那么对于resolved的,以及依然处于pending状态的Promise,我们是获取不到对应的结果的 在ES11(ES2020)中,添加了新的API Promise.allSettled,该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是rejected时,才会有最终的状态

Typescript问题

typescript
interface TreeData {
  username: string,
  userid: string,
  children?: TreeData[]
}

const orgTreeData: TreeData[] = [
  {
    username: '0-0',
    userid: '0-0',
    children: [
      {
        username: '0-0-0',
        userid: '0-0-0',
        children: [
          { username: '0-0-0-0', userid: '0-0-0-0' },
          { username: '0-0-0-1', userid: '0-0-0-1' },
          { username: '郭子鑫', userid: '0-0-0-2', }
        ]
      },
      { username: '0-0-1', userid: '0-0-1' },
      { username: '0-0-2', userid: '0-0-2' },
    ]
  },

  {
    username: '0-1',
    userid: '0-1',
    children: [
      { username: '0-1-0-0', userid: '0-1-0-0' },
      { username: '0-1-0-1', userid: '0-1-0-1' },
      { username: '0-1-0-2', userid: '0-1-0-2' },
    ],
  },

  {
    username: '0-2',
    userid: '0-2',
  }
]
// 问题1: ts 描述 treeData 数据结构

interface TreeData {
  username: string,
  userid: string,
  children?: TreeData[]
}

// 问题2: 写一个根据用户名称,查找userid的方法, 并且找到自己的姓名对应的userid ts来实现

const getUserId = (treeData: TreeData[], userName: string): string => {
  // 递归遍历treeData
  for (const item of treeData) {
    // 查找children节点上是否包含目标用户
    if (item.children) {
      const deepUserId = getUserId(item.children, userName)
      if (deepUserId !== undefined) {
        return deepUserId
      };
    };
    // 设置边界条件, 找到目标用户名称后终止, 返回对应的userId
    if (item.username === userName) {
      return `${userName}的用户ID为:${item.userid}`
    }
  };
  // 如果未找到匹配的用户名,返回 undefined
  return '未匹配到目标用户';
};

const targetUserId: string = getUserId(orgTreeData, '郭子鑫');
const emptyUserId: string = getUserId(orgTreeData, '测试不存在用户');

console.log('test1', targetUserId)

console.log('test2', emptyUserId)


// 问题3: 获取最长路径, 如 ['0-0', '0-0-0', '0-0-0-0']格式 由userid组成,多个最长返回任意一个 ts来实现
const getLongPath = (treeData: TreeData[], path: string[]): string[] => {
  let longPath: string[] = [];
  // 遍历树结构
  for (const item of treeData) {
    // 收集用户ID
    const newPath: string[] = [...path, item.userid];
    // 遍历children节点, 继续递归收集用户ID
    if (item.children && item.children.length > 0) {
      const deepLongPath: string[] = getLongPath(item.children, newPath)
      // 这里直接比较收集的数组的长度, 将最长的数组重新赋值给longPath
      longPath = longPath.length > deepLongPath.length ? longPath : deepLongPath
    } else {
      //设置边界条件 当节点不再存在children, 比较收集的数组的长度, 将最长的数组重新赋值给longPath
      longPath = longPath.length > newPath.length ? longPath : newPath
    };
  };
  return longPath;
};

const longPath: string[] = getLongPath(orgTreeData, []);
console.log(longPath);

人生没有捷径,就像到二仙桥必须要走成华大道。