Jinwen Xie

一边工作,一边学习;写写代码,看看书,追追剧,走走世界!

Javascript常用算法集

16 Mar 2020 »

1.函数防抖和函数节流

函数防抖:

debounce-函数防抖:将一个弹簧按下,继续加压,继续按下,只会在最后放手的一瞬反弹。即我们希望函数只会调用一次,即使在这之前反复调用它,最终也只会调用一次而已。

某些代码不可以在没有间断的情况下连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作(清除定时器)就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。

 function debounce(fn, delay) {
    let timer = null;
    return function () {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        fn.apply(this, arguments);
      }, delay || 500);
    }
  }
函数节流:

一个水龙头在滴水,可能一次性会滴很多滴,但是我们只希望它每隔 500ms 滴一滴水,保持这个频率。即我们希望函数在以一个可以接受的频率重复调用。

 function throttle(fn, cycle) {
    let timer, now, start = Date.now();
    return function () {
      now = Date.now();
      clearTimeout(timer);
      if (now - start >= cycle) {
        fn.apply(this, arguments);
        start = now;
      } else {
        timer = setTimeout(() => {
          fn.apply(this, arguments);
        }, cycle);
      }
    }
  }

2.手写一个v-module方法?

  // html
  <input type="text" id="ipt" />
  <div id="box"></div>

  // js
  let oIpt = document.querySelector('#Jipt');
  let oBox = document.querySelector('#Jbox');
  let obj = {};

  /**
  * Object.defineProperty(obj, prop, descriptor)
  * obj: 被定义或修改属性的对象
  * prop: 要定义或修改的属性名称
  * descriptor: 对属性的描述
  */

  Object.defineProperty(obj, 'name', {
    get: function() {
      return val;
    },
    set: function(newVal) {
      oIpt.value = newVal;
      oBox.innerHTML = newVal;
    }
  });
  oIpt.addEventListener('input', event => {
    obj.name = event.target.value;
  });
  obj.name = 'xiejinwen';

3.如何实现数组的随机排序?

let arr = [1,2,3,4,5,6,7,8,9,10];
function randomSort(arr) {
  if (!arr) return false;	
  for(let i=0, len=arr.length; i<len; i++) {
    let index = parseInt(Math.random() * len);
    let temp = arr[index];
    arr[index] = arr[i];
    arr[i] = temp;
  }
  return arr;
}
console.log(randomSort(arr))

4.递归

递归就是自己调自己,递归在前端里面算是一种比较常用的算法。假设现在有一堆数据要处理,要实现上一次请求完成了,才能去调下一个请求。但是有时候并不想引入Promise,能简单处理先简单处理。这个时候就可以用递归,如下代码所示:

let ids = [34112, 98325, 68125];
(function sendRequest() {
  let id = ids.shift();
  if (id) {
    // 加入ajax请求
    console.log(1);
    sendRequest();
  } else {
    console.log('finished');
  }
})();
用递归算法实现,数组长度为5且元素的随机数在2-32间不重复的值
let arr = [];
function randomNum() {
  return Math.floor(Math.random() * 31 + 2);
}
function randomArr(arr, num) {
  if (arr.length >= 5) return arr;
  if (!arr.includes(num)) arr.push(num);
  return randomArr(arr, randomNum()); 
}
randomArr(arr, randomNum());
console.log(arr); 

5.数组排序

方法一(冒泡排序):

// 冒泡排序的原理如下,从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到 length - 1 的位置。
let arr = [5,8,9,7,6,3,2,1,10,4];
function bubbleSort(arr = []) {
  let len =arr.length
  if (len === 0) return
  for (let i = len - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      if (arr[j] > arr[i]) {
        // [arr[i], arr[j]] = [arr[j], arr[i]]
        arr[i] = [arr[j], [arr[j] = arr[i]]][0]
      }
    }
  }
  return arr
}
// console.log(bubbleSort(arr));

方法二(sort方法):

function sortArr(arr = []) {
  if (arr.length === 0) return
  return arr.sort((item1, item2) => {
    return item1 - item2
  })
}
// console.log(sortArr(arr));

6、图片懒加载(监听滚动事件以及使用函数节流的方法)

将页面中的img标签src指向一张小图片或者src为空,然后定义data-src(这个属性可以自定义命名,我才用data-src)属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求。可以指向loading的地址。
当载入页面时,先把可视区域内的img标签的data-src属性值负给src,然后监听滚动事件,把用户即将看到的图片加载。这样便实现了懒加载。

// 简单的节流函数
//fun 要执行的函数
//delay 延迟
//time  在time时间内必须执行一次
function throttle(fn, delay, time) {
  let timer;
  let curTime;
  let startTime = new Date();

  return function() {
    curTime = new Date();
    clearTimeout(timer);
    if (curTime - startTime >= time) { // 如果达到了规定的触发时间间隔,触发 handler
      fn.apply(this, arguments);
      startTime = curTime;
    } else { // 没达到触发间隔,重新设定定时器
      timer = setTimeout(() => {
        fn.apply(this, arguments);
      }, delay || 500); 
    }
  };
};
// 实际想绑定在 scroll 事件上的 handler
function lazyload(event) {
  for (var i = n; i < imgNum; i++) {
    if (img.eq(i).offset().top < parseInt($(window).height()) + parseInt($(window).scrollTop())) {
      if (img.eq(i).attr("src") == "default.jpg") {
        var src = img.eq(i).attr("data-src");
        img.eq(i).attr("src", src);
        n = i + 1;
      }
    }
  }
}
// 采用了节流函数
window.addEventListener('scroll',throttle(lazyload,500,1000));

7.写一个方法去掉字符串中的空格

方法一:

  let str = ' abc de  g ';
  function trim(str = '') {
    if (str.length === 0) return;
    let reg = /\s+/g;
    str = str.replace(reg,'');
    return str;
  }
  console.log(trim(str)); 

方法二:

let str = ' abc de  g ';
// filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
// 注意: filter() 不会对空数组进行检测。
// 注意: filter() 不会改变原始数组。
function trimString(str = '') {
  if (str.length === 0) return
  let arr = str.split('')
  arr = arr.filter(item => {
    return item != ' '
  })
  return arr.join('')
}
console.log(trimString(str));

8.获取url地址上的参数

   /**
    *   window.location.search // ?id=123554&name=xiejinwen
    *   window.location.search.substr(1) // id=123554&name=xiejinwen
    *   ^	匹配字符串的开始
    *   $	匹配字符串的结束
    *   [^&]* 表示匹配字符直到遇到 & 符号为止
    *   ([^&]*) 表示捕获匹配到的结果
    *   unescape() 函数可对通过 escape() 编码的字符串进行解码。
    */

    function getUrlAgt(agtName) {
      if (!agtName) return false;
      let reg = new RegExp('(^|&)' + agtName + '=([^&]*)(&|$)');
      let arr = window.location.search.substr(1).match(reg);
      if (arr) return unescape(arr[2]);
      return false;
    }
    console.log(getUrlAgt('id'));

9.数组去重

方法一:

let arr = [1,2,3,3,4,5,4,5,6,7,8,4,7,9]
function reviewSort(arr = []) {
  let len = arr.length
  if (len === 0) return
  let newArr = []
  for (let i = 0; i < len; i++) {
    if (!newArr.includes(arr[i])) newArr.push(arr[i])
  }
  return newArr
}
console.log(reviewSort(arr))

方法二(es6方法):

let arr = [1,2,20,3,3,4,5,4,5,6,7,8,4,7,9]
let newArr = [...new Set(arr)]
console.log(newArr)