1.一次性请求10万条
1.分堆,用requestAnimationFrame 逐个渲染
const arr = [{},{},{}] 十万条
分成1万个堆 分批次渲染,借助requestAnimation
const averageFn = (arr)=>{
let i = 0;
let res = []
while(i < arr.length){
res.push(arr.splice(i,i+10))
i = i+10
}
return res
}
const res = averageFn(arr)
定义一个渲染函数
const renderArr = (page = 0)=>{
requestAnimationFrame(()=>{
this.arr = [...this.arr,res[page]]
page++;
renderArr(page)
})
}
rederArr()
2.用表格的滚动加载,分堆后一个一个取。取了之后shift删除堆。
3.虚拟列表 vxetable
2.手动实现call,bind
let name = 'tom'
let person = {
getName(a,b){
console.log(a,b)
return this.name
}
}
Function.prototype.mycall = function(context){
//执行.mycall前面的函数,同时将this指向man
const arg = Array.prototype.slice(arguments,1)
context.fn = this
return context.fn(...arg)
}
person.getName.mycall(man,1,2)
Function.prototype.mybind = function(context){
const arg = Array.prototype.slice(arguments,1)
const fn = this
return fn.call(context)
}
3.如何翻转一个字符串,
var str = '123'
str.split('').reverse().join() str.split()可替换成[...str]或者Array.from(str)
for(let i = str.length;i--){
res.push(str[i)
}
4 实现一个map
Array.prototype._map = function(callback){
var newArr = []
for(let i = 0;i<this.length;i++){
newArr.push(callback(this[i]),i)
}
}
5.实现一个filter
Array.prototype._filter = function(callback){
var newArr = []
for(let i = 0;i<this.length;i++){
if(callback(this[i])){
newArr.push(this[i])
}
}
}
6.获取symbol的遍历
var person= {
name:'123',
age::12,
[Symbol('level')]:'A'
}
console.log(Reflect.ownKeys(person)) 获取所有的属性名
应用场景:如果函数里有关于对象的条件运算,可以拿
7. xss攻击
脚本攻击。讲用户输入的文本进行合适的过滤,在get请求里没被服务器过滤渲染在页面上,浏览器输入(反射性),发出评论没被服务器过滤(存储型)还有dom型。比如image标签出错的钩子
预防:防止HTML出现注入,防止js执行执行恶意代码。在一些敏感数据进行加密或者转义。a标签进行白名单过滤。
预防存储型和反射型XSS攻击,服务端在提交表单时进行转义。改为纯前端渲染
8.写一个函数求和,参数不确定
function sum(){
let result = 0
for(lei i = 0;i<arguments.length;i++){
if(isNaN(arguments[i])){
result+=arguments[i]
}
}
}
9.js扁平化
function flattern(arr){
while(arr.some(item=>isArray(item))){
arr = [].concat(...arr)
}
}
function flattern(arr,result = []){
for(let i = 0;i<arr.length;i++){
if(Array.isArray(arr[i])){
flattern(arr[i],result)
}else{
result.push(arr[i])
}
}
return result
}
function flattern(arr){
return arr.tostring().join(',').map(item=>v=>+v)
}
10 递归求和1到100
function add(num1,num2){
let res = num1+num2
if(num2+1===100){
return res
}else{
return add(res,num2+1)
}
}
11 广度优先遍历和深度优先遍历
广度bfs:取key值 一层一层取(队列)
深度dfs:每个分支取到底取不到了再去其他分支取(递归)
const obj = {
a:{
b:{
z:{},
x:{}
},
c:{
d:{
f:{
g:1,
h:2
}
}
}
}
}
function bfs(target){
const queue = [target]
const res = []
while(queue.length){
const _target = queue.shift()
for(const key in _target){
if(typeof(_target[key]) === 'object'){
queue.push(_target[key])
}
res.push(key)
}
}
return res
}
console.log(bfs(obj)) // 输出结果 a b c z x d f g h
function dfs(target,res=[]){
for(const key in target){
res.push(key)
if(typeof(target[key]) === 'object'){
dfs(target[key],res)
}
}
return res
}
12 数组转tree
const data = [
{ id: 1, parentId: null, name: "根节点" },
{ id: 2, parentId: 1, name: "子节点1" },
{ id: 3, parentId: 1, name: "子节点2" },
{ id: 4, parentId: 2, name: "子节点1-1" },
{ id: 5, parentId: 2, name: "子节点1-2" },
{ id: 6, parentId: 3, name: "子节点2-1" },
];
// fun1 广度优先遍历实现
// 解题思路:数组转tree,先遍历一层数组找到根节点,push到res和queue队列中。由于push的是根节点的指针,所以后续对queue循环操作也操作了res。while 循环queue队列,找到当前节点的子节点push到当前的children中,找到了再将子节点push到queqe队列中,由于一次会把所有的子节点找到,所以是广度优先,后续返回res
function arrayToTreeBfs(target,parentId = null){
const res = []
const queue = []
// 先遍历一层找根节点,放入队列和tree中
target.forEach(item=>{
if(item.parentId === parentId){
res.push(item)
queue.push(item)
}
})
while(queue.length){
// 取出队列第一项 找到他的子节点放进children中,
const _target = queue.shift()
target.forEach(item=>{
// 找到当前队列的所有子节点,然后将子节点也放入队列
if(item.parentId === _target.id){
(_target.children??=[]).push(item)
queue.push(item)
}
})
}
return res
}
// fun2 找到根节点放入list中,再循环源数据找到每一项的父节点,放入其中
function arrayToTree(target,parentId = null){
const res = []
// 先遍历一层找根节点,放入队列和tree中
target.forEach(item=>{
if(item.parentId === parentId){
res.push(item)
}
})
// 遍历平铺的数据源每一项,找到对应的父级
target.forEach(item=>{
const parent = target.find(el => el.id === item.parentId)
if(parent){
(parent.children??=[]).push(item)
}
})
return res
}
// fun3 先用map把每一项存起来,循环数据源,在map中找父节点,找到了则push
function arrToTreeByMap(target,parentId = {}){
const res = []
const map = new Map()
target.forEach(item => {
map.set(item.id,item)
})
target.forEach(item=>{
if(item.parentId === parentId){
res.push(item)
}else{
const parent = map.get(item.parentId)
if(parent){
(parent.children ??= []).push(item)
}
}
})
return res
}
13.请求并发的调度问题,限制最大并发数(浏览器限制6并发,不能占满,多图片上传时可用)
// 需求:实现一个任务调度器,要求最大并发数n,依次执行
// 变量:最大并发数,当前执行任务数,执行的任务队列.核心思路 调方法时将任务推进一个队列中,任务返回一个promise,保证执行完再开启一个任务.执行函数
class TaskScheduler{
#maxCount
#runCount
#queue
constructor(maxCount = 2){
this.#maxCount = maxCount
this.#queue = []
this.#runCount = 0
}
addTask(delay,value){
const task = ()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
console.log(value)
resolve()
},delay)
})
}
this.#queue.push(task)
this.runTask()
}
runTask(){
if(this.#runCount >= this.#maxCount || this.#queue.length === 0) return
const task = this.#queue.shift()
this.#runCount++
task().finally(()=>{
this.#runCount--
this.runTask()
})
this.runTask()
}
}
const scheduler = new TaskScheduler(2)
scheduler.addTask(10000, 1) // 10000ms 后输出1
scheduler.addTask(5000, 2) // 5000ms 后输出2
scheduler.addTask(3000, 3) // 8000ms 后输出3
scheduler.addTask(4000, 4) // 12000ms 后输出4
scheduler.addTask(5000, 5) // 15000ms 后输出5
工作中使用调度器控制并发
// task是一个函数返回promise(()=>接口请求)
addTask(fn){
const task = ()=> {
return new Promise((resolve,reject)=>{
fn().then(res=>resove(res))
})
}
this.#queue.push(task)
this.runTask()
}
//高级写法
思路:runTask中要调task()的finally,所以task是一个函数需要返回promise将fn的promise状态传递给下一个promise,
所以 可以直接写一个promise,将task变成一个回调 借助promise.then也返回一个promise,将resolve和reject传入到then中传递给finally
addTask(fn){
return new Promise((...args)=>{
this.#queue.push(()=>fn().then(...args))
this.runTask()
})
}
14.实现flow的函数效果,
flow(fn1,fn2,fn3,fn4)
const fn1 = (n) => n + 1;
const fn2 = (n) => n + 2;
const fn3 = (n) => n + 3;
const fn4 = (n) => n + 4;
const add = flow(fn1, fn2, fn3, fn4);
console.log(add(1)); 11
// 思路 传入的函数的返回值作为下个函数的参数
function flow(...fns){
return (num)=>{
return fns.reduce((prev,cur)=>{
return cur(prev)
},num)
}
}
// 此时返回的函数只接收了一个参数,lodash中的flow是接收多个参数的
function flow(...fns){
return (...args)=>{
return fns.reduce((prev,cur)=>{
return
},args)
}
}
15.闭包的运用。保存私有化变量的一种手段
- setup就是闭包,setup函数内的变量被render函数引用。vue2的data
export default {
setup(){
const count = ref()
return {
count
}
},
template:`{{count}}`
}
2.多个promise执行时,前一个执行完才能执行下一个,不影响其他同步任务执行
const asyncFn = createSyncFn(()=>{
return new Promise(relove => {
setTimeout(()=>{
relove(1)
},1000)
})
})
const asyncFn = ()=> {
let p = Promise.resove()
return new Promise((resolve,reject)=>{
p =
})
}
16 针对数组id相同的数据进行去重
- 方案一 使用map 时间复杂度为O(2n) 循环了两次不推荐
const arr = [
{id:1,name:'张三'},
{id:2,name:'李四'},
{id:3,name:'王五'},
{id:2,name:'蒸五'},
]
function unique(arr,key){
let map = new Map()
for(let item of arr){
if(!map.has(item[key])){
map.set(item[key],item)
}
}
return [...map.values()]
}
- 方案二 使用filter 只循环一次,业务上可用
function uniqueByKey(arr,key){
const set = new Set()
return arr.filter(item=>{
if(!set.has(item[key])){
set.add(item[key])
return true
}
return false
})
}
- 方案三 将参数包装成一个回调,支持更多场景,最终版
function uniqueByKey(arr,getKey){
const set = new Set()
return arr.filter(item=>{
const key = typeof getKey === 'function'?getKey(item):getKey
if(!set.has(key)){
set.add(key)
return true
}
return false
})
}
console.log(uniqueByKey(arr,(item)=>item.id))
console.log(uniqueByKey(arr,(item)=>`${name}-${age}`)) // age和name都相同才会被去重
ts版本
type KeyType = string | number | symbol
function uniqueArray<T>(
arr: T[],
getKey: ((item: T) => KeyType) | keyof T
): T[] {
const seen = new Set<KeyType>()
return arr.filter((item) => {
const key = typeof getKey === 'function' ? getKey(item) : item[getKey] as KeyType
if (!seen.has(key)) {
seen.add(key)
return true
}
return false
})
}
tags = uniqueArray<Tag>(tags, (item) => item.text)
17 实现loadImg函数
promse调用
loadImg('www.xxx.img').then().catch
function loadImg(url){
return new Promise((resolve,reject) => {
const image = new Image(url)
imgae.url = url
image.onsuccess = resolve
image.onerror = reject
})
}
参数调用
loadImg('www.xxx.img',{
onSuccess(){},
onError(){}
})
function loadImg(url,{onSuccess,onError}){
const image = new Image(url)
imgae.url = url
image.onsuccess = onSuccess
image.onerror = onError
}
18 实现memorize函数,相同引用的参数多次调用只执行第一次的结果 后面不执行
// 一个参数同个引用
const fn = memorize(arr => {
console.count('count')
return arr.map(item => item + 1)
})
const num1 = fn([1, 2, 3])
const num2 = fn([1, 2, 3])
console.log('num1,num2 :>> ', num1, num2);
function memorize(fn){
const cache = new Map()
return (args)=>{
if(cache.has(args))return cache.get(args)
const res = fn(args)
cache.set(args,res)
return res
}
}
// 多参数处理 因为要收集参数,只能判断参数的equal 不能做引用相等判断
function memorize(fn){
const cache = new Map()
return (...args)=>{
for(const key,value of cache){
if(_.isEqual(key,args)) return cache.get(cache)
}
const res = fn(...args)
cache.set(args,res)
return res
}
}