Javascript常用函数

164 阅读9分钟

常用的函数在手,代码写的飞快

1、连续最长不重复字符

// 连续最长不重复字符串
function getMaxLenStr(str) {
  var cur = [];
  var maxLenStr = '';
  for (var i = 0; i < str.length; i++) {
    if (!cur.includes(str[i])) {
      cur.push(str[i]);
    } else {
      cur = []; // 置为空
      cur.push(str[i]);
    }

    // 存储最大长度的字符串
    if (maxLenStr.length < cur.length) {
      maxLenStr = cur.join('');
    }
  }
  return maxLenStr;
}

getMaxLenStr('ababcabcde'); // abcde

将'3[a]2[bc]'变成aaabcbc

const str = '3[a]2[bc]';
function decodeString(str) {
  let Counts = str.split(/\[[a-zA-Z]+\]/); // [3,2]
  let Letters = str.match(/[a-zA-Z]+/g); // ["a","bc"]
  let newString = "";
  Letters.forEach((item, index) => {
    for (var n = 0; n < Counts[index]; n++) {
      newString += item;
    }
  })
  return newString;
}
decodeString(str) // aaabcbc

3、获取类型 typeof null //"object"

const getType = v =>
  v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();

var toString = Object.prototype.toString;
var isString = function (obj) {
  return toString.call(obj) == '[object string]';
};
var isFunction = function (obj) {
  return toString.call(obj) == '[object function]';
};
var isType = function (type) {
  return function (obj) {
    return toString.call(obj) == '[object' + type + ']';
  }
};

4、扁平化数组(递归)

function flat(arr) {
  return arr.reduce((prev, cur) => {
    return prev.concat(cur instanceof Array ? flat(cur) : cur)
  }, [])
}
flat([1,[2,3,[4]]]) // [1, 2, 3, 4]
const arr =  [1, 1, 2, [1, 2, 3]];
while (arr.some(Array.isArray)) {
 arr = [].concat(...arr);
}
console.log(arr) // [1, 1, 2, 1, 2, 3]
arr =  [1, 1, 2, [1, 2, 3]];
const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
deepFlatten(arr) // [1, 1, 2, 1, 2, 3]

5、自己定义的new方法

function myNew(foo, ...args) {
 /* 创建新对象,并继承构造方法的prototype属性, 这一步是为了
   把obj挂原型链上, 相当于obj.__proto__ = Foo.prototype */
  let obj = Object.create(foo.prototype)
  
  /* 执行构造方法, 并为其绑定新this, 这一步是为了让构造方法
    能进行this.name = name之类的操作, args是构造方法的入参,
    因为这里用myNew模拟, 所以入参从myNew传入 */
  let result = foo.apply(obj, args)
  
  /* 如果构造方法已经return了一个对象, 那么就返回该对象, 
   一般情况下,构造方法不会返回新实例,但使用者可以选择
   返回新实例来覆盖new创建的对象 否则返回myNew创建的新对象 */
  return typeof result === 'object' && result !== null ? result : obj
}

6、instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链,构造自己的instanceof

function myInstanceof(left, right) {
  // 基本数据类型直接返回false
  if (typeof left !== 'object' || left === null) return false;
  // getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
  let proto = Object.getPrototypeOf(left);
  while (true) {
    // 查找到尽头,还没找到
    if (proto == null) return false;
    // 找到相同的原型对象
    if (proto == right.prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
}
function instance_of(L, R) {
  var O = R.prototype; //取 R 的显示原型
  L = L.__proto__; //取 L 的隐式原型
  while (true) {
    if (L === null) return false;
    if (O === L)
      // 这里重点 :当 O 严格等于 L 时,返回 true
      return true;
    L = L.__proto__;
  }
}
instance_of({}, Object) // true
myInstanceof({}, Object) // true

7、获取最长的公共前缀

function longestCommonPrefix(strs) {
  // 如果strs没有数据则返回""
  if (strs.length == 0) {
    return "";
  }

  // 获取最短字符串的长度值
  let minLength = strs[0].length;
  for (let str of strs) {
    minLength = Math.min(minLength, str.length);
  }

  // 二维数组的遍历,调整内外循环先后顺序即可得到不同的效果
  for (let j = 0; j < minLength; j++) {
    for (let str of strs) {
      // 遍历,只需要和第一个字符串strs[0]的比较即可,不用一一对比
      if (strs[0].charAt(j) != str.charAt(j)) {
        return strs[0].substring(0, j);
      }
    }
  }
  // 如果遍历过后没有找到遍历结束出口,则表示最短的字符串就是公共前缀
  return strs[0].substring(0, minLength);
}
longestCommonPrefix(['sstr','sstwwwwww']) // sst

8、对一个数组基于n次去重

functiondeleteNth(arr, n) {
  let newArr = arr.map(item => {
    return item;
  }) // 原始数据副本             
  let newArr1 = []; // 处理后的数据             
  for (var i = 0; i < newArr.length; i++) { // 遍历数据
    if (newArr1.indexOf(newArr[i]) < 0) { // 处理后的数据没有则push
      newArr1.push(newArr[i]);
    } else if (newArr1.indexOf(newArr[i]) > -1) { // 处理后的数据已经存在
      let hasIndexArr = []; // 用于存放相匹配的项的索引                     
      for (let j = 0; j < newArr1.length; j++) {  // 遍历处理后的数据
        if (newArr1[j] == newArr[i]) { // 处理后的数据中含有当前项
          hasIndexArr.push(j); // 将匹配的项的索引push进hasIndexArr 
        }
      }
      if (hasIndexArr.length < n) { // 如果数量还不满足n,push进去                         
        newArr1.push(newArr[i]);
      } //如果数量已经满足,则跳过                 
    }
  }
  return newArr1;
}
var arr = [1, 1, 2, 5, 23, 23, 1, 1];
console.log(deleteNth(arr, 2))  // [1, 1, 2, 5, 23, 23]
console.log(deleteNth(arr, 1))  // [1, 2, 5, 23]

9、去重

// 利用对象的特性去重
function unique(array) {
  var obj = {};
  return array.filter(function (item, index, array) {
    return obj.hasOwnProperty(item) ? false : (obj[item] = true)
  })
}
unique([1,2,3,4,4,4]) // [1, 2, 3, 4]
// map
Array.prototype.unique = function () {
  const tmp = new Map();
  return this.filter(item => {
    return !tmp.has(item) && tmp.set(item, i)
  })
}

Array.prototype.unique = function () {
  return this.sort().reduce((init, current) => {
    if (init.length === 0 || init[init.length - 1] !== current) {
      init.push(current)
    }
    return init
  }, [])
}

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (!newArray.includes(item)) {
      newArray.push(item)
    }
  });
  return newArray;
}
const unique = (arr, key) => {
  return [...new Map(arr.map(item => [item[key], item])).values()]
}

10、如果让你手写一个forEach

function forEach(array, iteratee) {
  let index = -1;
  const length = array.length;
  while (++index < length) {
    iteratee(array[index], index);
  }
  return array;
}

11、

findPath = (tree, func, path = []) => {
  if (!tree) return [];
  for (const data of tree) {
    // 这里按照需求来存放最后返回的内容
    path.push(data.name);
    // 符合条件则返回数组
    if (func(data)) return path;
    // 不符合条件且有子元素就继续往下找
    if (data.children) {
      const findChildren = findPath(data.children, func, path);
      if (findChildren.length) return findChildren;
    }
    // 不符合以上条件删除此元素
    path.pop();
  }
  return [];
}

12、比较两个变量是否一致,可以用来表单数据对象不重复

const equals = (a, b) => {
  if (a === b) return true;
  if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
  if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b;
  if (a.prototype !== b.prototype) return false;
  let keys = Object.keys(a);
  if (keys.length !== Object.keys(b).length) return false;
  return keys.every(k => equals(a[k], b[k]));
};
console.log(equals({a:{a:1}},{a:{a:1}})) // true

13、深克隆

// 深克隆Object和Array
const deepClone = source => {
  // 判断复制的目标是数组还是对象
  const targetObj = source.constructor === Array ? [] : {}; 
  for (let keys in source) { // 遍历目标
    if (source.hasOwnProperty(keys)) {
      if (source[keys] && typeof source[keys] === 'object') { 
        // 如果值是对象,就递归一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      } else { // 如果不是,就直接赋值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}
// 深克隆
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
  if (hash.has(obj)) return hash.get(obj);
  let type = [Date, RegExp, Set, Map, WeakMap, WeakSet];
  // 如果成环了,参数obj=obj.loop=最初的obj 会在WeakMap中找到第一次放入的obj提前返回第一次放入WeakMap的cloneObj
  if (type.includes(obj.constructor)) return new obj.constructor(obj);
  // 遍历传入参数的所有键的特性
  let allDesc = Object.getOwnPropertyDescriptors(obj);
  // 继承原型
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
  hash.set(obj, cloneObj);
  // Reflect.ownKeys(obj)可以拷贝不可枚举属性和符号类型
  for (let key of Reflect.ownKeys(obj)) {
    // 如果值是引用类型(非函数)则递归调用deepClone
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ?
      deepClone(obj[key], hash) : obj[key];
  }
  return cloneObj;
}

14、获取Offset

function getViewportOffset() {
  if (window.innerWidth) {
    return {
      w: window.innerWidth,
      h: window.innerHeight
    }
  } else {
    // ie8及其以下
    if (document.compatMode === "BackCompat") {
      // 怪异模式
      return {
        w: document.body.clientWidth,
        h: document.body.clientHeight
      }
    } else {
      // 标准模式
      return {
        w: document.documentElement.clientWidth,
        h: document.documentElement.clientHeight
      }
    }
  }
}

15、base64ToFile

base64ToFile = (base64, filename) => {
  let arr = base64.split(',')
  let mime = arr[0].match(/难过.*?);/)[1]
  let suffix = mime.split('/')[1] // 图片后缀
  let bstr = atob(arr[1])
  let n = bstr.length
  let u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], `${filename}.${suffix}`, {
    type: mime
  })
}

16、base64ToBlob

base64ToBlob = base64 => {
  let arr = base64.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
};

17、字典类型

class Dictionary {
  constructor() {
    this.items = {}
  }
  set(key, value) {
    this.items[key] = value
  }
  get(key) {
    return this.items[key]
  }
  remove(key) {
    delete this.items[key]
  }
  get keys() {
    return Object.keys(this.items)
  }
  get values() {
    return Object.keys(this.items).reduce((r, c, i) => {
      r.push(this.items[c]);
      return r
    }, [])
  }
}

18、子定义setMyInterval

function setMyInterval(callback, interval) {
  let timer;
  const now = Date.now;
  let startTime = now();
  let endTime = startTime
  const loop = () => {
    timer = window.requestAnimationFrame(loop)
    endTime = now();
    if (endTime - startTime >= interval) {
      startTime = endTime = now();
      callback(timer);
    }
  }
  timer = window.requestAnimationFrame(loop);
  return timer;
}
function clearMyInterval(timer) {
  cancelAnimationFrame(timer);
}

19、获取请求路径中的字段

function getQueryString(name) {
  const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
      const url = window.location.href
  const search = url.substring(url.lastIndexOf('?') + 1)
      const r = search.match(reg)
  if (r != null) return unescape(r[2])
  return null
}

20、阶乘

factorial = num => {
  let count = 1;
  for (let i = 1; i <= num; i++) {
    count *= i;
  }
  return count;
}

21、求和

function all() {
  let sum=0;
  for(let i=0;i<arguments.length;i++) {
    sum+=arguments[i]
  }
  return sum;
}

22、柯里化

function curry(fn, ...args) {
  const len = fn.length;
  return function(...innerArgs) {
    const newArgs = [...args, ...innerArgs];
    if (newArgs.length < len) {
      return curry.call(this, fn, ...newArgs);
    }
    return fn.apply(this, newArgs);
  }
}

23、对象转为对象数组

objToArrObj = obj=> {
  let arr = []
  for(let i in obj){
    arr.push({[i]:obj[i]})
  }
  return arr
}
objToArrObj({a:1,b:2,c:3}) // [{a:1},{b:2},{c:3}]

24、返回最大值与最小值之差

function MaxMinPlus(arr) {
  // 返回最大值与最小值之差
  return Array.isArray(arr) ? Math.max.apply(Math, arr) 
    - Math.min.apply(Math, arr) : console.log('传入的不是数组亦或者未能解决的错误')
}

25、

Array.prototype.extraChar = function () {
  var cacheExtraChar = []; // 缓存重复出现的字符
  var that = this; // 缓存 this;
  this.map(function (item, index) {
    // 怎么理解这段代码呢?
    // 就是向前往后查找一遍和从后往前查找一遍,不等就是没有重复
    // 为什么还要判断一遍缓存,是过滤缓存数组内多次写入
    (that.indexOf(item) !== that.lastIndexOf(item)) && cacheExtraChar.indexOf(item) === -1 ? cacheExtraChar
      .push(item) : -1;
  });
  return cacheExtraChar;
}
var testArr = [1,6,8,3,7,9,2,7,2,4,4,3,3,1,5,3];
testArr.extraChar(); // [1, 3, 7, 2, 4]

26、去掉头尾部空字符串

String.prototype.emuTrim = function () {
  // 这条正则很好理解,就是把头部尾部多余的空格字符去除
  return this.replace(/(^\s*)|(\s*$)/g, '');
}

27、浅克隆

function shallowClone(sourceObj) {
  // 先判断传入的是否为对象类型
  if (!sourceObj || typeof sourceObj !== 'object') {
    console.log('您传入的不是对象!!')
  }
  // 判断传入的 Obj是类型,然后给予对应的赋值
  var targetObj = sourceObj.constructor === Array ? [] : {};
  
  // 遍历所有 key
  for (var keys in sourceObj) {
    // 判断所有属于自身原型链上的 key,而非继承(上游 )那些
    if (sourceObj.hasOwnProperty(keys)) {
      // 一一复制过来
      targetObj[keys] = sourceObj[keys];
    }
  }
  return targetObj;
}

28、获取ID

function getIdChain(data, id, idkey = "id", childrenKey = "children") {
  if (check(data, id)) {
    return [];
  }
  loop.chain = [];
  loop(data, id);
  return loop.chain;
  function check(data, id) {
    return !Array.isArray(data) || !data.length || (!id && id !== 0);
  }
  function loop(arr, v) {
    if (check(arr, v)) {
      return false;
    }
    return arr.some(i => {
      return i[idkey] === v || (i[childrenKey] && loop(i[childrenKey], v))
        ? (loop.chain.unshift(i[idkey]), true)
        : false;
    });
  }
}

29、扁平的数组构造成树结构

let list = [
  { id: 1, name: '部门A', parentId: 0 },
  { id: 2, name: '部门B', parentId: 0 },
  { id: 3, name: '部门C', parentId: 1 },
  { id: 4, name: '部门D', parentId: 1 },
  { id: 5, name: '部门E', parentId: 2 },
  { id: 6, name: '部门F', parentId: 3 },
  { id: 7, name: '部门G', parentId: 2 },
  { id: 8, name: '部门H', parentId: 4 }
];

function convert(array) {
  let reslutArray = array.filter((item) => {
    let children = array.filter((child) => {
      return item.id === child.parentId
    })   
    item.children = children       
    return item.parentId === 0
  })
  return reslutArray
}

function convert(list, parentId = 0) {
  let result = [];
  for (let i = 0; i < list.length; i++) {
    let item = list[i];
    if (item.parentId === parentId) {
      let newItem = {
        ...item,
        children: convert(list, item.id)
      };
      result.push(newItem);
    }
  }
  return result;
}
  
function convert (list, topid = 0) {
  const result = [];
  let map = {};
  list.forEach(item => {
    const { id, parentId } = item;
    if (parentId === topid) {
      result.push(item);
    } else {
      map[parentId] = map[parentId] || [];
      map[parentId].push(item);
    }
    item.children = item.children || map[id] || (map[id] = [])
  })
  map = null;
  return result;
}

30、查找一个树多有第一个节点,深度遍历

// 查找一个树多有第一个节点,深度遍历
function getFirstNode (tree) {
  var temp = []
  var forFn = function (arr) {
    if (arr && arr.length > 0) {
      temp.push(arr[0])
      if (arr[0].children) {
        forFn(arr[0].children)
      }
    }
  }
  forFn(tree)
  return temp
}

31、

findPath = (tree, func, path = []) => {
  if (!tree) return [];
  for (const data of tree) {
    // 这里按照需求来存放最后返回的内容
    path.push(data.name);
    // 符合条件则返回数组
    if (func(data)) return path;
    // 不符合条件且有子元素就继续往下找
    if (data.children) {
      const findChildren = findPath(data.children, func, path);
      if (findChildren.length) return findChildren;
    }
    // 不符合以上条件删除此元素
    path.pop();
  }
  return [];
}

32、{ a: { b: { c: 1 } }, d: 1 }); => { 'a.b.c': 1, d: 1 }

const flattenObject = (obj, prefix = '') =>
  Object.keys(obj).reduce((acc, k) => {
    const pre = prefix.length ? prefix + '.' : '';
    if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k));
    else acc[pre + k] = obj[k];
    return acc;
  }, {});

33、['beep', 'boop', 'foo', 'bar'], [true, true, false, true] => [ ['beep', 'boop', 'bar'], ['foo'] ]

const bifurcate = (arr, filter) =>
  arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [
    [],
    []
  ]);
bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]);
// [ ['beep', 'boop', 'bar'], ['foo'] ]

34、 查找当前ID的所有父节点的ID

const findId = (data, tarId, parentId = []) =>
  data.reduce((acc, item) =>
    acc.concat(item.id === tarId ?
      [...parentId, item.id] :
      item.children ?
      findId(item.children, tarId, [...parentId, item.id]) :
      []), [])
const cityData = [{
  id: '1',
  name: '广东省',
  children: [
    {
      id: '11',
      name: '深圳市',
      children: [
        {
          id: '111',
          name: '南山区'
        },
        {
          id: '112',
          name: '福田区',
          children: [{
            id: '1121',
            name: 'A街道'
          }]
        }
      ]
    },
    {
      id: '12',
      name: '东莞市',
      children: [
        {
          id: '121',
          name: 'A区'
        },
        {
          id: '122',
          name: 'B区',
        }
      ]
    }
  ]
}];
findId(cityData,'122') // ["1", "12", "122"]

35、自定义call

Function.prototype.myCall = function(thisArg = window) {
  // thisArg.fn 指向当前函数 fn (fn.myCall)
  thisArg.fn = this;
  // 第一个参数为 this,所以要取剩下的参数
  const args = [...arguments].slice(1);
  // 执行函数
  const result = thisArg.fn(...args);
  // thisArg上并不存在fn,所以需要移除
  delete thisArg.fn;
  return result;
}

36、

// 浅复制
$ = {
  extend: function (target, options) {
    for (name in options) {
      target[name] = options[name];
    }
    return target;
  }
};
// 深复制
$ = {
  extend: function (deep, target, options) {
    for (name in options) {
      copy = options[name];
      if (deep && copy instanceof Array) {
        target[name] = $.extend(deep, [], copy);
      } else if (deep && copy instanceof Object) {

        target[name] = $.extend(deep, {}, copy);
      } else {
        target[name] = options[name];
      }
    }
    return target;
  }
};

37、查询当前数组中的某一个值的所有index

const arr = [1, 2, 3, 1, 1];
const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);
indexOfAll(arr,1) // [0, 3, 4]

38、

const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1);
intersectionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1.5, 3, 0]