js常见题

1、事件戳排序

 // 输入数据
var data= [
     {
          l:[{id:1,ts:'2022-09-10'},{id:2,ts:'2022-09-19'}]
     },
     {
          l:[{id:3,ts:'2022-01-19'},{id:4,ts:'2022-03-20'},{id:5,ts:'2021-08-22'}]
     },
     {
          l:[{id:7,ts:'2009-11-01'}]
     },
]
// 先找的每行最老的时间戳,然后再排序从老到新 
var newarr = []
function sort(data) {
     data.forEach(item => {
          var minIndex = 0
          for(var i = 1; i < item.l.length ; i++) {
               if( item.l[minIndex].ts >=item.l[i].ts) {
                    minIndex = i
               }
          }
          newarr.push(item.l[minIndex])
     })
     return newarr.sort((a,b) =>{
          return new Date(a.ts) - new Date(b.ts)
     })
}
console.log( sort( data ) );

2、求斐波那契数列的第n项的值

1、动态规划优化
	动态规划是一种将复杂问题分解成小问题来解决的算法。在斐波那契数列中,可以使用动态规划来避免重复计算。具体实现如下:
function fibonacci(n) {
  if (n === 0) return 0;
  if (n === 1) return 1;
  const dp = [0, 1];
  for (let i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}
2、循环优化
	使用循环来实现斐波那契数列。这种方法不需要使用递归,也不需要使用额外的空间来存储数组。具体实现如下:
function fibonacci(n) {
  let a = 0, b = 1;
  while (n > 0) {
    const c = a + b;
    a = b;
    b = c;
    n--;
  }
  return a;
}
3、递归优化
var arr=[]
function fbnq(n){
     if(n==1||n==2){
          return 1
     }else{
          if(!arr[n-2]){
               arr[n-2]=fbnq(n-1)
          }
          if(!arr[n-3]){
               arr[n-3]=fbnq(n-2)
          }
          return arr[n-3]+arr[n-2]
     }
}

3、灯泡状态切换

<template>
    <!-- <img src="http://d1.realmaster.com/img/download/bulb_on.png"/> -->
    <!-- <img src="http://d1.realmaster.com/img/download/bulb_off.png"/> -->
    <div class="bulb">
        <!-- 通过改变图片的路径来显示不同的图片  两张图片地址不同的地方通过三目运算符判断自定义的数组中的元素的布尔值来进行返回不同的字符串 -->
        <img v-for="(item, index) in arr" :key="index" @click="change(index)"
          :src="`http://d1.realmaster.com/img/download/bulb_${item ? 'on' : 'off'}.png`">
        <div class="countBulb"> {{ '有' + count + '个灯泡是亮的' }} </div>
    </div>
</template>
<script setup>
  import { onMounted, ref } from 'vue'
  // 让灯泡产生  并且返回高亮灯泡的个数     组件一挂载(页面一打开)立马执行此函数
   // 思路:根据效果图显示      
          // 1.先产生n个img标签
          // 2.在进行每一轮的操作:利用双层for循环 外层控制循环轮数  内层根据灯泡状态切换的当前轮下每个灯泡的状态的变化
          //3.统计灯泡高亮的个数
var count = ref(0)
var arr = ref([])
var add=(n,r)=>{
     for(var i=0;i<n;i++){
          arr.value.push(false)
     }
     for(var i=1;i<=r;i++){
          for(var j=1;j<=n;j++){
               if(j%i==0){
                    arr.value[j-1]=!arr.value[j-1]
               }
          }
     }
     return arr.value.filter(item=>item).length
}
var change=(index)=>{
     arr.value[index]=!arr.value[index]
}
onMounted(()=>{
     count.value=add(100,3)
})
</script>
<style scoped>
.bulb img {
  width: 50px;
}

.countBulb {
  width: 400px;
  height: 50px;
  text-align: center;
  line-height: 50px;
  font-size: 20px;
  margin:  20px auto;
  background-color: pink;
}
</style>

4、最大数

	给定一组非负整数nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

function largestNumber(nums) {
  const sortedArr = nums.map(num => num.toString())
                       .sort((a, b) => (b + a) - (a + b));
  return sortedArr[0] === '0' ? '0' : sortedArr.join('');
}

const nums = [10, 2, 5, 23, 98];
const maxNum = largestNumber(nums);
console.log(maxNum); // 985523210

5、字符串大小写转换

函数内部使用for循环遍历字符串中的每个字符,判断字符的大小写并进行互换操作。最终将互换后的字符串返回。
function swapCase(str) {
  let result = '';
  for (let i = 0; i < str.length; i++) {
    const char = str.charAt(i);
    if (char === char.toUpperCase()) {
      result += char.toLowerCase();
    } else {
      result += char.toUpperCase();
    }
  }
  return result;
}
const str = "Hello World";
const swappedStr = swapCase(str);
console.log(swappedStr); // "hELLO wORLD"

6、给定字符串通过字符出现频率排序

	给定一个字符串s,根据字符出现的频率对其进行降序排序。一个字符出现的频率是它出现在字符串中的次数。
返回已排序的字符串。如果有多个答案,返回其中任何一个。 
var str = 'aaabbcccddee'
 var frequencySort = function (s) {
      const freq = new Map();
      for (let i = 0; i < s.length; i++) {
           //通过map集合得到每一个字符出现的次数
           freq.set(s[i], (freq.get(s[i]) || 0) + 1);
      }
      //[...freq.entries()]将map集合转化为[key,value]的形式,通过sort比较值的大小排序
      const sortedFreq = [...freq.entries()].sort((a, b) => b[1] - a[1]);
      //0:['a', 3] 1:['c', 3] 2:['b', 2] 3:['d', 2] 4:['e', 2]
      let res = '';
      for (let i = 0; i < sortedFreq.length; i++) {
           //repeat将指定字符重复sortedFreq[i][1]次并拼接到res中
           res += sortedFreq[i][0].repeat(sortedFreq[i][1]);
      }
      return res;
 };
frequencySort(str)

7、使用map集合统计字符串中每个字符出现的次数

 var str = 'abaabbnnaan'
    function strCount(str) {
        const map = new Map();
        var newStr = '';
        for (let i = 0; i < str.length; i++) {
            map.set(str[i], (map.get(str[i])||0) + 1);
        }
        for (let key of map) {
            newStr += key[0] + ':' + key[1] + '次,'
        }
        return newStr.slice(0, newStr.length - 1)
    }
    console.log(strCount(str));

8、编写函数对字符串进行排列组合, 得到所有字符的全排列组合(假设所有字符不重复)

    function permute(str) {
        let result = [];
        //如果字符长度等于1,则直接返回
        if (str.length == 1) {
            return [str];
        }
        //遍历字符串,将每一项字符都当作首位
        for (let i = 0; i < str.length; i++) {
            let firstChar = str[i];
            //截取除首位外的字符
            let charsLeft = str.slice(0, i) + str.slice(i + 1);
            //递归截取后的字符,每一项都为首位,依次类推
            let innerPermutations = permute(charsLeft);
            //遍历递归返回的数组,与第一项拼接起来
            for (let j = 0; j < innerPermutations.length; j++) {
                result.push(firstChar + innerPermutations[j]);
            }
        }
        return result;
    }

    var str = 'abc'
    console.log(permute(str));

9、不同路径问题

9.1、解法一

    //公式:db[i][j] = db[i - 1][j] + db[i][j - 1]
    //到当前位置的路径数 = 上方位置的路径数 + 左侧位置的路径数 
    function uniquePaths(m, n) {
        var db = []
        for (let i = 0; i < m; i++) {
            db[i] = []
            for (let j = 0; j < n; j++) {
                //边界判断,只可以向下或向右走,所以第一行和第一列路径数都为1
                if (i == 0 || j == 0) {
                    //dp[i][j] 表示到达 (i,j) 的不同路径数	
                    db[i][j] = 1
                } else {
                    db[i][j] = db[i - 1][j] + db[i][j - 1]
                }
            }
        }
        //右下角的数组则为总路径次数
        return db[m - 1][n - 1]
    }
    console.log(uniquePaths(3, 7));

9.2、解法二

 function uniquePaths(m, n) {
      //通过fill方法填充数组,渲染出结构
	var db = new Array(m).fill(1).map(() => new Array(n).fill(1))
     //通过双层循环改变其值
	for (var i = 1; i < m; i++) {
		for (var j = 1; j < n; j++) {
			arr[i][j] = arr[i - 1][j] + arr[i][j - 1]
		}
	}
    return arr[m-1][n-1]
}

10、为什么arr.map(parseInt)结果为[1, NaN, NaN]

arr = ['1', '2', '3'];
console.log(arr.map(parseInt));//[1, NaN, NaN]

map在执行回调函数会传递三个参数:
parseInt函数用于字符串转数字,只能接收两个参数:
	第一个参数为需要被转换的数
	第二个参数为需要被转换的数的进制数//写几则表示需要被转换的数是几进制(省略或0默认为10进制)
进制有:2进制,8进制,10进制,16进制
以上代码可以转化为:
arr.map((item, index, origin) => {
	console.log(parseInt(item, index));
})
第一次为:parseInt('1', 0); // 1
第二次为:parseInt('2', 1); // NaN,没有1进制,不合法
第三次为:parseInt('3', 2); // NaN,2进制只能1和0,不能为3

11、事件轮询机制

11.1、事件轮询机制概念:

JavaScript 事件轮询机制(Event Loop)是指 JavaScript 在单线程上运行时,通过不断地循环查看任务队列中是否有任务来实现异步编程的一种机制。
事件轮询机制的基本流程如下:

首先,执行同步代码,将异步代码加入到任务队列中。

当异步事件触发时,将任务加入到任务队列中。

当任务队列中的任务都执行完毕后,JavaScript 引擎会查看任务队列中是否有任务需要执行。

如果有,则取出任务并执行;如果没有,则等待新的任务加入到任务队列中。

循环执行以上步骤,直到程序结束。
在事件轮询机制中,任务队列分为两种:宏任务(Macro Task)和微任务(Micro Task)。宏任务包括 setTimeout、setInterval、setImmediate、I/O 操作等,而微任务包括 Promise、process.nextTick 等。
在任务执行的过程中,如果遇到微任务,它们会被优先执行,直到微任务队列为空。而在执行宏任务的过程中,如果遇到了新的宏任务,它们会被加入到队列的末尾,等待下一次轮询。
需要注意的是,由于 JavaScript 是单线程执行的,因此如果某个任务的执行时间过长,会阻塞后续任务的执行,导致页面卡顿,因此在编写代码时应该尽量避免出现过长的任务。

11.2、通过案例解释

	//1、创建了一个promise对象,传递进入了一个函数,并立即执行了,打印了Promise1,通过resolve将第一个promise对象状态改为成功
	//2、调用.then方法注册了一个回调函数,存放在了promise对象内部(会等待promise对象状态为成功时,加入到异步队列的微任务队列里),此时promise对象状态是成功,所以加入到了微任务对列里
    const promisel = new Promise((resolve, reject) => {
        console.log('Promise1');               //1
        resolve();
    })
    promisel.then(() => {
        console.log(3)                    //5
    })

    //3、打印1
    console.log(1);                           //2
    //4、创建了一个函数,函数内部返回一个promise对象
    const fn = () => {
        return new Promise((resolve, reject) => {
            console.log(2);                   //3
            resolve("success");
        });
    };
    //5、调用该函数,得到promise对象,打印2,将promise对象状态改为成功,并传入参数success
    //通过.then调用注册第二个回调函数,先存放在promise对象内部,promise对象状态为成功,所以加入微任务队列
    fn().then((res) => {
        console.log(res);                  //6
    });
    //6、打印start
    console.log('start');                  //4

    //7、所有同步任务执行完毕,开始执行异步任务,执行微任务队列的任务,依次打印3和success

12、选择题数据结构

[
     {
     	题目1,
     	选项:[
     		{
     			text:选项1
     			flag:true
     		}{
     			text:选项2
     			flag:true
     		}
     	]
     }{
     	题目2,
     	选项:[
     		{
     			text:选项1
     			flag:true
     		}
     	]
     }]

13、组件通信eventBus的简单封装

class EventBus{
  constructor(){
    this.events={}
  }
  //绑定事件
  on(name,callback){
    if(!this.events[name]){
      this.events[name]=[]
    }
    this.events[name].push(callback)
  }
  //解绑事件
  off(name){
    delete this.events[name];
  }
  //触发事件
  trigger(name,data){
    const callbacks =this.events[name];
    if(callbacks){
      for(let i=0;i<callbacks.length;i++){
        callbacks[i](data)
      }
    }
  }
}
//使用示例
const event=new EventBus()
event.on("click",function(data){
  console.log(`clicked ${data}!`);
})
event.trigger("click","button");
event.off('click');                                                                                                                                          
event.trigger("click","button");

14、写一个函数 parseQueryString,它的用途是把 URL 参数解析为一个对象

function parseQueryString(url) {
  var params = {};
  var queryString = url.split("?")[1];
  if (queryString) {
    var pairs = queryString.split("&");
    for (var i = 0; i < pairs.length; i++) {
      var pair = pairs[i].split("=");
      var key = decodeURIComponent(pair[0]);
      var value = decodeURIComponent(pair[1]);
      // 如果该参数已经存在,则将它转换为数组
      if (params[key]) {
        if (!Array.isArray(params[key])) {
          params[key] = [params[key]];
        }
        params[key].push(value);
      } else {
        params[key] = value;
      }
    }
  }
  return params;
}

15、列表结构转树形结构

方式一: 
var arr = [
        { "name": "分类一", "id": 1, "pid": 0 },
        { "name": "分类二", "id": 2, "pid": 0 },
        { "name": "子分类一", "id": 3, "pid": 1 },
        { "name": "子分类二", "id": 4, "pid": 1 },
        { "name": "子分类三", "id": 5, "pid": 2 },
        { "name": "子分类四", "id": 6, "pid": 2 },
        { "name": "子分类五", "id": 7, "pid": 3 }
    ];
      function listToTree(arr) {
        var map = [];
        var roots = {};
        arr.forEach(item => {
            map[item.id] = { ...item, child: [] };
        })
        map.forEach(item => {
            if (item.parentId) {
                map[item.parentId].child.push(item);
            } else {
                roots[item.id] = item;
            }
            delete item.parentId
        })
        if (Object.keys(roots).length <= 1) {
            roots = roots[Object.keys(roots)[0]]
        }
        return roots;
    }
    console.log(listToTree(arr))

    // 打印结果用json字符串的形式打印出来,并且使用每级2个空格进行缩进
    // 第一个参数是要打印的对象 tree;
    // 第二个参数是一个可选的转换函数,将会被应用在输出结果中每个属性上。因为这里没有需要进行特殊处理的属性,所以这个参数传入了 null;
    // 第三个参数是一个可选参数,指定缩进字符串的数目,这里传入了2,代表使用两个空格作为缩进。
方式二:
function arrayToTree(arr, pid = null) {
        let result = [];
        arr.forEach(item => {
            // pid=null的时候进行树形转化
            if (item.pid === pid) {
                // 继续调用函数,进行下一个判断(递归)
                let children = arrayToTree(arr, item.id);
                //判断当前节点是否存在子节点,如果存在子节点,就将子节点数组赋值给当前节点的children属性,构成嵌套的树形结构
                if (children.length) {
                    item.children = children;
                }
                delete item.pid;
                result.push(item);
            }
        });
        return result;
    }
    const data = [
        { id: 1, pid: null, name: 'a' },
        { id: 2, pid: 1, name: 'b' },
        { id: 3, pid: 1, name: 'c' },
        { id: 4, pid: 2, name: 'd' },
        { id: 5, pid: 2, name: 'e' },
        { id: 6, pid: 3, name: 'f' },
        { id: 7, pid: 4, name: 'f' },
        { id: 8, pid: 4, name: 'f' },

    ];
    // console.log(JSON.stringify(arrayToTree(data), null, 2));

16、用户权限——菜单过滤

//参数一:菜单,参数二:用户权限
export function getAuthority(menu:any,permissions:any){
    if(!permissions.length) return menu;
    var newPermissions = JSON.parse(JSON.stringify(permissions));
    //补齐权限缺失部分
    newPermissions.map((item:any)=>{
        if(!newPermissions.includes(item.split('-')[0])){
            //如果权限里不包含一级菜单,则添加一级菜单
            newPermissions.push(item.split('-')[0])
            return true;
        }
    })
    
    //根据权限总体匹配用户权限
    var quanxian = (menu:any)=>{
        // 遍历菜单
        return menu.filter((item:any)=>{
            // 如果菜单项没有子菜单
            if(!item.children){
                // 判断菜单项是否在权限数组中
                return newPermissions.includes(item.key);
            }
            // 如果菜单项有子菜单,则递归调用该函数过滤子菜单
            item.children = quanxian(item.children);
            // 如果过滤后子菜单不为空,则返回包含该子菜单的菜单项
            return item.children.length > 0;
            
        })
    }
    return quanxian(menu);
}

17、数组扁平化

1、利用递归:
function flattenArray(arr) {
        //定义空数组,用来存放处理后一维数组
        var flattened = [];
        // 首先遍历外层数组
        for (var i = 0; i < arr.length; i++) {
            //首先判断每一项是否为数组
            if (Array.isArray(arr[i])) {
                //如果是数组,利用递归在处理一遍,和新数组进行拼接
                flattened = flattened.concat(flattenArray(arr[i]));
            } else {
                // 如果不是数组就直接push到新数组
                flattened.push(arr[i]);
            }
        }
        return flattened;
    }
2、利用render方法迭代数组,将数组所有元素按指定规则合并
 function flattenArray(arr) {
        return arr.reduce((result, item) => {
            //如果item是数组则递归,不是则拼接如空数组
            return result.concat(Array.isArray(item) ? flattenArray(item) : item)
        }, [])//定义初始值为空数组
    }
3、利用扩展运算符和while循环
function flattenArray(arr) {
        //将传入的数组展开到新数组里
        var flattened = [...arr]
        //循环判断,只要还有子数组,就进入循环
        while (flattened.some(Array.isArray)) {
            //将数组展开为一维数组,并拼接到空数组,赋值给flattened
            flattened = [].concat(...flattened)
        }
        //如果还有子数组,会一直循环,直到只有一维数组
        return flattened
    }

18、排序算法

18.1、快排

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr; // 递归终止条件
  }
  var pivotIndex = Math.floor(arr.length / 2); // 选择中间元素作为基准
  var pivot = arr.splice(pivotIndex, 1)[0]; // 取出基准,并将其从原数组中删除
  var left = [];
  var right = [];
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]); // 小于基准的元素放入左侧数组
    } else {
      right.push(arr[i]); // 大于等于基准的元素放入右侧数组
    }
  }
  // 递归调用
  return quickSort(left).concat([pivot], quickSort(right));
}

18.2、插入排序

function insertionSort(arr) {
  for (var i = 1; i < arr.length; i++) {
    var current = arr[i]; // 当前待插入的元素
    var j = i - 1; // 已排序部分的最后一个元素的下标
    while (j >= 0 && arr[j] > current) {
      arr[j + 1] = arr[j]; // 将已排序部分中比当前元素大的元素向右移动一位
      j--; // 继续向前遍历已排序部分
    }
    arr[j + 1] = current; // 将当前元素插入到正确的位置上
  }
  return arr;
}

19、数组加一

	给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
函数名: plusOne
参数: 非负整数数组
返回值:1以后的非负整数数组
例如: plusOne([1,2,3]) 返回 [1,2,4]
例如: plusOne([9,9]) 返回 [1,0,0]

 function plusOne(arr) {
        var newArr = arr.slice(0);
        for (let i = newArr.length - 1; i >= 0; i--) {
            if (newArr[i] < 9) {
                newArr[i]++;
                return newArr;
            } else {
                newArr[i] = 0;
            }
        }
        newArr.unshift(1);
        return newArr;
    }
    console.log(plusOne([9, 9, 9]));

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值