乐趣区

关于javascript:你应该了解的25个JS技巧

写代码的时候总有一些货色是会反复呈现的,次数多了你就会想找找捷径了。这类问题中有很大一部分解决起来甚至连库都不必装。上面就是我多年来收集的前 25 个捷径和小技巧。

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 任何内容

我在应用 NodeJs 将对象记录到控制台时常常应用这种办法。JSON.stringify 办法须要第三个参数,该参数必须有多个空格以缩进行。第二个参数能够为 null,但你能够用它来解决 function、Set、Map、Symbol 之类 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. 期待所有 promise 实现

这个算不上是代码解决方案,更多是对 Promise API 的强化。这个 API 在一直进化,以前我还为“allSettled”“race”和“any”做了代码实现,当初间接用 API 的就好了。

11. 替换数组值的地位

ES6 开始,从数组中的不同地位替换值变得容易多了。这个做起来不难,然而理解一下也不错,

12. 条件对象键

我最喜爱这条技巧了,我在应用 React 更新状态时常常用它。你能够将条件包装在括号中来有条件地将一个键插入一个 spread 对象。

13. 应用变量作为对象键

当你有一个字符串变量,并想将其用作对象中的键以设置一个值时能够用它。

14. 查看对象里的键

这是一个很好的技巧,能够帮忙你查看对象键。

15. 删除数组反复项

数组中常常有反复的值,你能够应用 Set 数据结构来打消它。它实用于许多数据类型,并且 set 有多种查看相等性的办法,很好用。对于不同实例或对象的状况,你还是能够应用 Set 来跟踪特定事物并过滤出反复的对象。

16. 在 ArrayforEach 中执行“break”和“continue”

我真的很喜爱应用数组“.forEach”办法,但有时我须要提前退出或持续进行下一个循环,而不想用 for…loop。你能够复制“continue”语句行为来提前返回,但如果要复制“break”行为,则须要应用数组“.some”办法。

17. 应用别名和默认值来销毁

Destructuring(销毁)是 JavaScript 最好用的性能之一,而且你能够应用“冒号”设置别名,并应用“等号”设置属性默认值。

18. 可选链和空值合并

深刻查看对象属性并解决 null 和 undefined 值时,你能够应用几个十分好用的 JavaScript 性能来解决常见的问题。

19. 用函数扩大类

我常常对他人讲,JavaScript 类只是构造函数和底层的原型,不是像 Java 中那样的实在概念。一个证据是,你能够只应用一个构造函数来扩大一个类。在公有内容里这个很好用,在类里“#”这些看着很奇怪,并且用于 babel 或 WebPack 时,编译进去的代码更少。

20. 扩大构造函数

类的一个问题是你只能扩大一个其余类。应用构造函数,你能够应用多个构造函数来形成一个函数,这样就会灵便多了。你能够应用函数原型的.apply 或.call 办法来实现。你甚至能够只扩大函数的一部分,只有它是一个对象即可。

21. 循环任何内容有时,你须要循环任何可迭代的内容(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;
  }
}

22. 使函数参数为 required

这是一种确保函数调用了实现工作所需内容的绝佳办法。你能够应用默认参数值的个性来调用函数,而后就会抛出一个谬误。如果调用该函数时带上了它须要的值,则该值将替换该函数,并且什么也不会产生。应用 undefined 调用也有雷同的成果。

function required(argName = 'param') {throw new Error(`"${argName}" is required`)
}
function iHaveRequiredOptions(arg1 = required('arg1'), arg2 = 10) {console.log(arg1, arg2)
}
iHaveRequiredOptions(); // throws "arg1" is required
iHaveRequiredOptions(12); // prints 12, 10
iHaveRequiredOptions(12, 24); // prints 12, 24
iHaveRequiredOptions(undefined, 24); // throws "arg1" is required

23. 创立模块或单例

很多时候,你须要在加载时初始化某些内容,设置它须要的各种事物,而后就能够在应用程序中到处应用它,而无需再做什么补充工作。你能够应用 IIFE 函数来做到这一点,这个函数太好用了。这种模块模式用来隔离事物十分好用,它能够只裸露须要交互的内容。

24. 深度克隆对象开发人员通常会装置一些相似“lodash”的库来执行这一操作,但应用纯 JavaScript 来实现的确也很容易。这是一个简略的递归函数:只有是一个对象,就应用函数的结构器将其从新初始化为一个克隆,而后对所有属性反复该过程。

const deepClone = obj => {
  let clone = obj;
  if (obj && typeof obj === "object") {clone = new obj.constructor();
    Object.getOwnPropertyNames(obj).forEach(prop => (clone[prop] = deepClone(obj[prop]))
    );
  }
  return clone;
};

25. 深度解冻对象

如果你喜爱不变性,那么这个工具你肯定要常备。

const deepClone = obj => {
  let clone = obj;
  if (obj && typeof obj === "object") {clone = new obj.constructor();
    Object.getOwnPropertyNames(obj).forEach(prop => (clone[prop] = deepClone(obj[prop]))
    );
  }
  return clone;
};

延长浏览
https://beforesemicolon.mediu…

退出移动版