js 技巧

目录

  1. 类型检查小工具
  2. 检查是否为空
  3. 获取列表最后一项
  4. 带有范围的随机数生成器
  5. 随机 ID 生成器
  6. 创建一个范围内的数字
  7. 格式化 JSON 字符串,stringify 任何内容
  8. 顺序执行 promise
  9. 轮询数据
  10. 循环任何内容

     

 

1. 类型检查小工具

JavaScript 不是强类型语言,对此我推荐的最佳解决方案是 TypeScript。但有时你只是想要一个简单的类型检查,这种时候 JavaScript 允许你使用“typeof”关键字。

“typeof”的问题在于,将其用于某些原语和函数时效果很好,但对于数组和对象来说,由于它们都被视为“对象”,因此很难把握它们之间的区别。

const isOfType = (() => {
  // create a plain object with no prototype
  const type = Object.create(null);

  // check for null type
  type.null = x => x === null;
  // check for undefined type
  type.undefined = x => x === undefined;
  // check for nil type. Either null or undefined
  type.nil = x => type.null(x) || type.undefined(x);
  // check for strings and string literal type. e.g: 's', "s", `str`, new String()
  type.string = x => !type.nil(x) && (typeof x === 'string' || x instanceof String);
  // check for number or number literal type. e.g: 12, 30.5, new Number()
  type.number = x => !type.nil(x)
    && (// NaN & Infinity have typeof "number" and this excludes that
      (!isNaN(x) && isFinite(x)
      && typeof x === 'number'
    ) || x instanceof Number);
  // check for boolean or boolean literal type. e.g: true, false, new Boolean()
  type.boolean = x => !type.nil(x) && (typeof x === 'boolean' || x instanceof Boolean);
  // check for array type
  type.array = x => !type.nil(x) && Array.isArray(x);
  // check for object or object literal type. e.g: {}, new Object(), Object.create(null)
  type.object = x => ({}).toString.call(x) === '[object Object]';
  // check for provided type instance
  type.type = (x, X) => !type.nil(x) && x instanceof X;
  // check for set type
  type.set = x => type.type(x, Set);
  // check for map type
  type.map = x => type.type(x, Map);
  // check for date type
  type.date = x => type.type(x, Date);

  return type;
})();

 

2. 检查是否为空

有时你需要知道某些内容是否为空,并根据结果决定要使用的方法,例如检查长度、大小或是否包含任何子元素。下面这个工具打包了这些功能,你可以用它检查 String、Object、Array、Map 和 Set 的大小。

function isEmpty(x) {
  if(Array.isArray(x)
    || typeof x === 'string'
    || x instanceof String
   ) {
    return x.length === 0;
  }

  if(x instanceof Map || x instanceof Set) {
    return x.size === 0;
  }

  if(({}).toString.call(x) === '[object Object]') {
    return Object.keys(x).length === 0;
  }

  return false;
}

 

3. 获取列表最后一项

其他语言里这个功能被做成了可以在数组上调用的方法或函数,但在 JavaScript 里面,你得自己做点工作。

function lastItem(list) {
  if(Array.isArray(list)) {
    return list.slice(-1)[0];
  }

  if(list instanceof Set) {
    return Array.from(list).slice(-1)[0];
  }

  if(list instanceof Map) {
    return Array.from(list.values()).slice(-1)[0];
  }
}

 

4.带有范围的随机数生成器

 有时你需要生成随机数,但希望这些数字在一定范围内,那就可以用这个工具。

function randomNumber(max = 1, min = 0) {
  if(min >= max) {
    return max;
  }

  return Math.floor(Math.random() * (max - min) + min);
}

 

5. 随机 ID 生成器

有时你只是需要一些 ID?除非你要的是更复杂的 ID 生成器(例如 UUID),否则用不着为此安装什么新库,下面这个选项足够了。你可以从当前时间(以毫秒为单位)或特定的整数和增量开始生成,也可以从字母生成 ID。

// create unique id starting from current time in milliseconds
// incrementing it by 1 everytime requested
const uniqueId = (() => {
  const id = (function*() {
    let mil = new Date().getTime();

    while (true)
      yield mil += 1;
  })();

  return () => id.next().value;
})();
// create unique incrementing id starting from provided value or zero
// good for temporary things or things that id resets
const uniqueIncrementingId = ((lastId = 0) => {
  const id = (function*() {
    let numb = lastId;

    while (true)
      yield numb += 1;
  })()

  return (length = 12) => `${id.next().value}`.padStart(length, '0');
})();
// create unique id from letters and numbers
const uniqueAlphaNumericId = (() => {
  const heyStack = '0123456789abcdefghijklmnopqrstuvwxyz';
  const randomInt = () => Math.floor(Math.random() * Math.floor(heyStack.length))

  return (length = 24) => Array.from({length}, () => heyStack[randomInt()]).join('');
})();

 

6. 创建一个范围内的数字

Python 里我很喜欢的一个功能是 range 函数,而在 JavaScript 里我经常需要自己写这个功能。下面是一个简单的实现,非常适合 for…of 循环以及需要特定范围内数字的情况。

function range(maxOrStart, end = null, step = null) {
  if(!end) {
    return Array.from({length: maxOrStart}, (_, i) => i)
  }

  if(end <= maxOrStart) {
    return [];
  }

  if(step !== null) {
    return Array.from(
      {length: Math.ceil(((end - maxOrStart) / step))},
      (_, i) => (i * step) + maxOrStart
    );
  }

  return Array.from(
    {length: Math.ceil((end - maxOrStart))},
    (_, i) => i + maxOrStart
  );
}

 

7. 格式化 JSON 字符串,stringify 任何内容

const stringify = (() => {
  const replacer = (key, val) => {
    if(typeof val === 'symbol') {
      return val.toString();
    }
    if(val instanceof Set) {
      return Array.from(val);
    }
    if(val instanceof Map) {
      return Array.from(val.entries());
    }
    if(typeof val === 'function') {
      return val.toString();
    }
    return val;
  }

  return (obj, spaces = 0) => JSON.stringify(obj, replacer, spaces)
})();

  

8. 顺序执行 promise

如果你有一堆异步或普通函数都返回 promise,要求你一个接一个地执行,这个工具就会很有用。它会获取函数或 promise 列表,并使用数组 reduce 方法按顺序解析它们。

const asyncSequentializer = (() => {
  const toPromise = (x) => {
    if(x instanceof Promise) { // if promise just return it
      return x;
    }

    if(typeof x === 'function') {
      // if function is not async this will turn its result into a promise
      // if it is async this will await for the result
      return (async () => await x())();
    }

    return Promise.resolve(x)
  }

  return (list) => {
    const results = [];

    return list
      .reduce((lastPromise, currentPromise) => {
        return lastPromise.then(res => {
          results.push(res); // collect the results
          return toPromise(currentPromise);
        });
      }, toPromise(list.shift()))
      // collect the final result and return the array of results as resolved promise
      .then(res => Promise.resolve([...results, res]));
  }
})();

  

9. 轮询数据

如果你需要持续检查数据更新,但系统中没有 WebSocket,则可以使用这个工具来执行操作。它非常适合上传文件时,想要持续检查文件是否已完成处理的情况,或者使用第三方 API(例如 dropbox 或 uber)并且想要持续检查过程是否完成或骑手是否到达目的地的情况。

async function poll(fn, validate, interval = 2500) {
  const resolver = async (resolve, reject) => {
    try { // catch any error thrown by the "fn" function
      const result = await fn(); // fn does not need to be asynchronous or return promise
      // call validator to see if the data is at the state to stop the polling
      const valid = validate(result);
      if (valid === true) {
        resolve(result);
      } else if (valid === false) {
        setTimeout(resolver, interval, resolve, reject);
      } // if validator returns anything other than "true" or "false" it stops polling
    } catch (e) {
      reject(e);
    }
  };
  return new Promise(resolver);
}

 

10.循环任何内容

有时,你需要循环任何可迭代的内容(Set、Map、Object、Array、String 等)。这个非常简单的 forEach 函数工具就可以做到这一点。如果回调返回 true,它将退出循环。

function forEach(list, callback) {
  const entries = Object.entries(list);
  let i = 0;
  const len = entries.length;

  for(;i < len; i++) {
    const res = callback(entries[i][1], entries[i][0], list);

    if(res === true) break;
  }
}

  

 
上一篇:Java关键字(一)——instanceof


下一篇:转帖 云和恩墨 http://www.eygle.com/archives/2015/06/sql_version_count.html