常见的面试题。

柯里化

// 实现函数柯里化
function curry(fn) {
  // 返回一个新函数
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args); // 如果参数够了,就执行原函数,返回结果
    } else {
      //返回一个新函数,继续递归去进行柯里化,利用闭包,将当前已经传入的参数保存下来
      return function (...args2) {
        //递归调用 curried 函数
        return curried.apply(this, [...args, ...args2]); //新函数调用时会继续传参,拼接参数
      };
    }
  };
}
 
// 测试
function sum(a, b, c) {
  return a + b + c;
}
var curried = curry(sum);
console.log(curried(1, 2, 3)); //6
console.log(curried(1, 2)(3)); //6
console.log(curried(1)(2, 3)); //6
console.log(curried(1)(2)(3)); //6

深拷贝

function deepClone(obj) {
    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }
    let cloneObj = Array.isArray(obj) ? [] : {};
    for(let key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloneObj[key] = deepClone(obj[key]);
        }
    }
    return cloneObj;
    }

浅拷贝

function shallowCopy (obj){
    // 只拷贝对象,基本类型或null直接返回
      if(typeof obj !== 'object' || obj === null) {
         return obj;
       }
    // 判断是新建一个数组还是对象
     let newObj = Array.isArray(obj) ? []: {};
    //for…in会遍历对象的整个原型链,如果只考虑对象本身的属性,需要搭配hasOwnProperty
    for(let key in obj ){
        //hasOwnProperty判断是否是对象自身属性,会忽略从原型链上继承的属性
        if(obj.hasOwnProperty(key)){
            newObj[key] = obj[key];//只拷贝对象本身的属性
        }
    }
    return newObj;
}

数组的转化

  let primitiveArr = [
                         { year: '2010', time: '0909' },
                         { year: '2011', time: '0909' },
                         { year: '2012', time: '0909' },
                         { year: '2010', time: '1010' }
                        ]
    function changeArr(arr) {
        let getObj = {}
        for (let j = 0; j < primitiveArr.length; j++) {
            if (getObj[primitiveArr[j].year]) {
                getObj[primitiveArr[j].year].push(primitiveArr[j])
            } else {
                getObj[primitiveArr[j].year] = [primitiveArr[j]]
            }
        }
        //  console.log(getObj);
        let arr2 = []
        for (const key in getObj) {
            arr2.push(getObj[key])
        }
        return arr2
    }
    let text = changeArr(primitiveArr)
    console.log(text, '-11--');

求和

 function add(firstNum, secondNum) {
        let computedNum = firstNum + secondNum
        if (secondNum + 1 > 100) {
            return computedNum
        } else {
            return add(computedNum, secondNum + 1)
        }
    }
    let allNum = add(1, 2)
    console.log('和1-100值是', allNum);

字符次数出现最多的方法

 /**
     * 用途:
            for...in: 通常用于遍历对象的属性。
            for...of: 通常用于遍历数组或类似数组的对象,以及其他可迭代对象。
            迭代内容:
            for...in: 迭代的是属性名。
            for...of: 迭代的是值。
    */
    let str = 'aaaabbbccd'
    function maxNum(str) {
        let chatObj = {}
        //第一种方法
        // for (let index = 0; index < str.length; index++) {
        // !chatObj[str.charAt(index)] ?
        //     chatObj[str.charAt(index)] = 1
        //     : chatObj[str.charAt(index)] += 1
        // }
        //第二种方法
        for (let char of str) {
            chatObj[char] = (chatObj[char] || 0) + 1;
        }
        console.log(chatObj)
        let setName = ''
        let setValue = 1
        for (const key in chatObj) {
            console.log(key, '---in');
            if (chatObj[key] > setValue) {
                setName = key;
                setValue = chatObj[key]
            }

            return {
                name: setName,
                value: setValue
            }
        }
    }
    let getMaxNum = maxNum(str)
    console.log(getMaxNum, '----get');

数组降序

 const numbers = [1, 2, 34, 5, 6, 7, 8, 0];
    // 使用 sort() 方法进行降序排序
    numbers.sort((a, b) => b - a);
    console.log(numbers);  // 输出: [34, 9, 8, 7, 6, 5, 2, 1]
    function insertionSortDescending(arr) {
        for (let i = 1; i < arr.length; i++) {
            let key = arr[i];
            let j = i - 1;
            // 移动元素
            while (j >= 0 && arr[j] < key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;
        }
        return arr;
    }
    const sortedNumbers = insertionSortDescending(numbers);
    console.log(sortedNumbers);  // 输出: [34, 9, 8, 7, 6, 5, 2, 1]

数组升序

 const numbers = [1, 2, 34, 5, 6, 7, 8, 0];
    numbers.sort((a, b) => a - b);
    console.log(numbers);  // 输出: [1, 2, 5, 6, 7, 8, 9, 34]

    function insertionSort(arr) {
        for (let i = 1; i < arr.length; i++) {
            let key = arr[i];
            let j = i - 1;

            // 移动元素
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;
        }
        return arr;
    }
    // const sortedNumbers = insertionSort(numbers);
    // console.log(sortedNumbers);  // 输出: [1, 2, 5, 6, 7, 8, 9, 34]

数组扁平化

//flat方式
const arr = [1,[2,[3,[4,5]]],6]
//  arr.flat([depth]) flat的参数代表的是需要展开几层,如果是Infinity的话,就是不管嵌套几层,全部都展开
console.log(arr.flat(Infinity)) //[1,2,3,4,5,6]

//递归方式
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    //如果当前元素还是一个数组
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));//递归拼接
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}
console.log(flatten(arr)); //  [1, 2, 3, 4]

数组去重

const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log([...new Set(arr)]); //[ 1, 2, 3, 5, 9, 8 ]
console.log(Array.from(new Set(arr))); //[ 1, 2, 3, 5, 9, 8 ]

<---------------------或者------------------------>
function unique(arr) {
  return arr.filter((item, index, array) => {
    return array.indexOf(item) === index;
  });
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]

手写类型判断函数

function getType(value) {
  // 判断数据是 null 的情况
  if (value === null) {
    return String(value);
  }
  // 判断数据是基本数据类型的情况和函数的情况,使用typeof
  if (typeof value !== "object") {
    return typeof value;
  } else {
    // 判断数据是引用类型的情况
    let valueClass = Object.prototype.toString.call(value); //"[object Date]"
    type = valueClass.split(" ")[1].split("");
    type.pop(); 
    return type.join("").toLowerCase(); 
  }
}
 

防抖

确保在指定的时间间隔内,无论连续触发了多少次事件,只有最后一次事件会在该间隔结束后执行。(触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。)
工作原理是,当事件持续触发时,只有在事件停止触发n秒后,才会执行事件函数。如果在n秒内事件被重新触发,那么之前的计时会被重置。这种技术通常用于搜索框输入、按钮点击等场景,避免短时间内的频繁请求。


//fn是需要防抖的函数,delay是等待时间
function debounce(fn, delay = 500) {
    let timer = null;
    // 这里返回的函数是每次用户实际调用的防抖函数
    return function(...args) {    //...args是es6的剩余参数语法,将多余的参数放入数组,用来代替arguments对象
        // 如果已经设定过定时器了就清空上一次的定时器
        if(timer) {
            clearTimeout(timer);    
        }
        // 开始一个新的定时器,延迟执行用户传入的方法;注:定时器的返回值是一个数值,作为定时器的编号,可以传入clearTimeout来取消定时器
        timer = setTimeout(() => {  //这里必须是箭头函数,不然this指向window,要让this就指向fn的调用者
            fn.apply(this, args);   
        }, delay)    
    }
}
// 测试
function task() {
  console.log('run task')
}
const debounceTask = debounce(task, 1000)
window.addEventListener('scroll', debounceTask)

节流(定时器版本)

确保在指定的时间间隔内,无论触发了多少次事件,只有一次事件会被执行,后续事件在这个间隔内都不会执行。(连续触发事件但是在 n 秒中只执行第一次触发函数) 是指在一定时间内,无论函数被触发多少次,函数只会在固定的时间间隔内执行一次。如果在时间间隔内有多次触发事件,只会执行最后一次。节流技术常用于滚动事件、鼠标移动等场景,限制函数的执行频率。

function throttle(fn, delay = 500) {
    let timer = null;
    return function(...args) {
        // 当前有任务了,直接返回
        if(timer) {
            return;
        }
        timer = setTimeout(() => {
            fn.apply(this, args);
            //执行完后,需重置定时器,不然timer一直有值,无法开启下一个定时器
            timer = null;    
        }, delay)
    }
}
// 测试

function task() {
console.log('run task')
}

const throttleTask = throttle(task, 1000)
window.addEventListener('scroll', throttleTask)

节流(时间戳版本)


function throttle(fn, delay = 500) {
  let prev = Date.now();// 上一次执行该函数的时间
  return function(...args) {
    let now = Date.now();//返回从UTC到当前时间的毫秒数
    // 如果差值大于等于设置的等待时间就执行函数
    if (now - prev >= delay) {
      fn.apply(this, args);
      prev = Date.now();
    }
  };
}
// 测试

function task() {
  console.log('run task')
}

const throttleTask = throttle(task, 1000)
window.addEventListener('scroll', throttleTask)

手写异步控制并发数

function limitRequest(urls = [], limit = 3) {
  return new Promise((resolve, reject) => {
    const len = urls.length
    let count = 0
 
    // 同步启动limit个任务
    while (limit > 0) {
      start()
      limit -= 1
    }
 
    function start() {
      const url = urls.shift() // 从数组中拿取第一个任务
      if (url) {
        axios.post(url).finally(() => {
          if (count == len - 1) {
            // 最后一个任务完成
            resolve()
          } else {
            // 完成之后,启动下一个任务
            count++
            start()
          }
        })
      }
    }
 
  })
}
 
// 测试
limitRequest(['http://aaa', 'http://aaa1', 'http://aaa2', 'http://aaa3', 'http://aaa4'])

懒加载图片实现

const imgs = document.getElementsByTagName('img');
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
 
let num = 0;
 
function lazyLoad() {
  for (let i = 0; i < imgs.length; i++) {
    let distance = viewHeight - imgs[i].getBoundingClientRect().top;
    if(distance >= 0) {
      imgs[i].src = imgs[i].getAttribute('data-src');
      num = i+1;
    }
  }
}
window.addEventListener('scroll', lazyLoad, false);

数组转换树形结构

function composeTree(list = [], mainMatches = 'id', viceMatches = 'pid') { 
    const data = JSON.parse(JSON.stringify(list)) // 浅拷贝不改变源数据 
    const result = []  
    if (!Array.isArray(data)) { 
      return result
     } 
     data.forEach((item) => {  
       delete item.children  
      })  
     const map = {} 
    data.forEach((item) => {  
      map[item[mainMatches]] = item
    }) 
   data.forEach((item) => {
       const parent = map[item[viceMatches]] 
          if (parent) {   
               (parent.children || (parent.children = [])).push(item)  
            } else {  
                result.push(item)  
                  }})  
        return result
    }

树形结构转换数组

// 将树数据转化为平铺数据
flatTreeData(treeData, childKey = 'children') {
  const arr = [];
  const expanded = (data) => {
    if (data && data.length > 0) {
      data
        .filter((d) => d)
        .forEach((e) => {
          arr.push(e);
          expanded(e[childKey] || []);
        });
    }
  };
  expanded(treeData);
  return arr;
}

  • 19
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值