25 JavaScript Tricks You Need To Know About


25 JavaScript Tricks You Need To Know About


Including some useful bits of code and utilities

While coding, you will notice that there are some things you stumble upon over and over, and normally they beg for that quick solution. A lot of these things do not even need you to install a library to solve it. These are my top 25 collected over the years.

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

1. Type check util

JavaScript is not a strongly typed language and to get that your best solution is TypeScript which I recommend but, sometimes all you need is a simple type check which JavaScript allows you to do with the “typeof” keyword.

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

The problem with “typeof” is that if you use it for some primitives and function it works great but for array and objects it becomes hard to know the difference since they are both considered “objects”

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

Javascript Type checker Code on Github

Javascript Type checker Code on Github

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;
})();

Real-world usage

2. Check for empty

Sometimes you just need to know if something is empty and depending on what you are checking, you need to use different methods like, checking the length, size, or if it contains any child elements. This util unifies all of that allowing you to check the size of String, Object, Array, Map, and Set.

有时你需要知道某些内容是否为空,并根据结果决定要使用的方法,例如检查长度、大小或是否包含任何子元素.下面这个工具打包了这些功能,你可以用它检查 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. Get any list last item

Other languages make this a method or function you can call on the array, but for JavaScript, you need to do some work.

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

Last Item Getter Code on Github

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. Random number generator with a range

生成范围随机数

If sometimes you need to generate random numbers but you want these numbers to be in a range, you need this small utility.

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

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

  return Math.floor(Math.random() * (max - min + 1) + min);
}
console.log(randomNumber(2, 1)); // returns a random integer from 1 to 2
console.log(randomNumber(10, 1)); // returns a random integer from 1 to 10

Note: Math.random() returns a random number between 0 (inclusive), and 1 (exclusive)

5. Random id generator

Just need some id? No need to install a new library for that unless you need some more complex ID generators like UUID. Check these options which may be enough to get going. You can start from the current time in milliseconds or a specific Integer and increment, or you can generate an ID from alphanumeric characters.

有时你只是需要一些 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. Create a range of numbers

One thing I love in Python is the range function and I often need it coding in JavaScript. Here is a simple implementation of it that is perfect for for…of loop and whenever you need numbers in a specific range.

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. Format JSON string and stringify anything

I find myself using this a lot when working on something with NodeJs to log objects to the console. The JSON.stringify takes a third parameter that must be a number of spaces to indent the line with. The second parameter can be null but you can use it to handle things like function, Set, Map, and Symbol that the JSON.stringify method does not handle or simply ignores.

我在使用 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. Execute promise sequentially

If you have a bunch of asynchronous or normal function that returns a promise that you need to execute one after the other, this utility can help a lot. It takes a list of functions or promises and sequentially resolves them using the array reduce method.

如果你有一堆异步或普通函数都返回 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 => [...results, res])
    );
  };
})();

9. Polling data

If you ever need to keep checking for data updates but don’t have WebSocket available in your system, this util lets you do just that. It is great for cases where you upload a file and you want to keep checking if the file is done processing, or you are working with a third-party API like dropbox or uber and you want to keep checking whether the process is complete or the ride has reached the destination.

如果你需要持续检查数据更新,但系统中没有 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. Wait for all Promises to complete

This is not quite a code solution but a good to know about the Promise API. It is constantly evolving and I used to have a code solution for the now available methods “allSettled“, “race“ and “any“ but I am glad just to tell you about them instead of sharing a code implementation of them.

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

11. Swap array values place

交换赋值

ES6 made it easy to swap values from places in the array. Not that this is a hard thing to do but it is a good one to know about.

ES6 开始,从数组中的不同位置交换值变得容易多了.这个做起来不难,但是了解一下也不错,

12. Conditional Object key

This trick is my favorite and I find myself using it a lot when working with React updating states. It simply lets you conditionally insert a key into a spread object by wrapping the condition in parenthesis.

我最喜欢这条技巧了,我在使用 React 更新状态时经常用它.你可以将条件包装在括号中来有条件地将一个键插入一个 spread 对象.

13. Use variables as the object key

Another one that comes handy is for cases when you have a string variable and want to use it as a key in the object to set a value.

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

14. Check for key in object

This one can be problematic but this is a good trick to know of which helps you check for object keys.

这是一个很好的技巧,可以帮助你检查对象键.

15. Remove Array duplicates

It’s common to have duplicated value in an array which you can get rid of with the Set data structure. It works great with many data types and set has a different way to check equality which helps a lot. For cases where you may have different instances or objects, you can still use Set to track a specific thing and filter duplicated objects out.

数组中经常有重复的值,你可以使用 Set 数据结构来消除它.它适用于许多数据类型,并且 set 有多种检查相等性的方法,很好用.对于不同实例或对象的情况,你还是可以使用 Set 来跟踪特定事物并过滤出重复的对象.

const numberArrays = [
  undefined,
  Infinity,
  12,
  NaN,
  false,
  5,
  7,
  null,
  12,
  false,
  5,
  undefined,
  89,
  9,
  null,
  Infinity,
  5,
  NaN,
];

const objArrays = [{ id: 1 }, { id: 4 }, { id: 1 }, { id: 5 }, { id: 4 }];

console.log(
  //prints [undefined, Infinity, 12, NaN, false, 5, 7, null, 89, 9];
  Array.from(new Set(numberArrays)),
  // prints [ { id: 1 }, { id: 4 }, { id: 1 }, { id: 5 }, { id: 4 } ]
  //nothing changes because even though the ids repeat in some objects
  //the objects are different instances, different objects
  Array.from(new Set(objArrays)),
);

const idSet = new Set();

console.log(
  // prints [ { id: 1 }, { id: 4 }, { id: 5 } ] using id to track id uniqueness
  objArrays.filter(obj => {
    const existingId = idSet.has(obj.id);
    idSet.add(obj.id);
    return !existingId;
  }),
);

16. Do “break” and “continue” in Array forEach

I really like using the array .forEach method but sometimes I need to quit early or continue to the next loop and don’t want to use the for…loop for it. You can replicate the “continue” statement behavior with an early return but if you want to replicate the “break” behavior you need to use the array “.some” method instead.

我真的很喜欢使用数组 “.forEach” 方法,但有时我需要提早退出或继续进行下一个循环,而不想用 for…loop.你可以复制 “continue” 语句行为来提前返回,但如果要复制 “break” 行为,则需要使用数组 “.some” 方法.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//logs 1, 3 and 5
for (const number of numbers) {
  if (number % 2 === 0) {
    continue; //skip to the next item
  }
  if (number > 5) {
    break; // stops the Loop
  }
  console.log(number);
}

//logs 1, 3 and 5
numbers.some(number => {
  if (number % 2 === 0) {
    return false; //skip to the next item
  }
  if (number > 5) {
    return true; //stops the Loop
  }
  console.log(number);
});

17. Destructuring with alias name and default values

Destructuring is one of the best JavaScript features and the cool thing is you can set alias using “colon” and default values using the “equal” sign for the properties.

Destructuring(解构)是 JavaScript 最好用的功能之一,而且你可以使用 “冒号” 设置别名,并使用 “等号” 设置属性默认值.

18. Optional Chaining and nullish coalescing

A couple of amazing and very anticipated JavaScript features you must start using right now to solve commonly used hacks when it comes to deep checking object property and dealing with null and undefined values.

深入检查对象属性并处理 null 和 undefined 值时,你可以使用几个非常好用的 JavaScript 功能来解决常见的问题.

const obj = {
  data: {
    container: {
      name: {
        value: 'sample',
      },
      int: {
        value: 0,
      },
    },
  },
};

console.log(
  // even though the " int.value " exists ,it is falsy therefore fails to be printed
  obj.data.container.int.value || 'no int value', // prints  no int value
);
console.log(
  // the ?? make sure to fallback to the right side only if left is null or undefined
  obj.data.container.int.value ?? 'no int value', // prints 0
);
console.log(
  // "wrapper" does not exist inside " data"
  obj.data.wrapper.name.value, // throws " Cannot )read property 'name' of undefined
);
console.log(
  // this is better but can be a problem if object is deep
  (obj && obj.data && obj.data.wrapper && obj.data.wrapper.name) || 'no name', //  prints ' no name'
);
console.log(
  // using optional chaining " ? "is better
  obj?.data?.wrapper?.name || 'no name', // prints ' no name'
);

19. Extend class with functions

I always tell people, JavaScript classes are just constructor functions and prototypes under the hood. Is not a real concept like in Java. One way to prove it to just use a constructor function to extend a class. It comes in handy for private stuff which in class is weird with the whole “#” thing and it compiles to less code when used with babel or WebPack.

我经常对别人讲,JavaScript 类只是构造函数和底层的原型,不是像 Java 中那样的真实概念.一个证据是,你可以只使用一个构造函数来扩展一个类.在私有内容里这个很好用,在类里 “#” 这些看着很奇怪,并且用于 babel 或 WebPack 时,编译出来的代码更少.

20. Extend constructor functions

The thing with class is you can only extend a single other class. With constructor function, you can compose a function using multiple other constructor functions making it much more flexible to work with at times. You do that by either using the .apply or the .call method of the function prototype. You can even extend only parts of the function as well as long as it is an object.

类的一个问题是你只能扩展一个其他类.使用构造函数,你可以使用多个构造函数来构成一个函数,这样就会灵活多了.你可以使用函数原型的. apply 或. call 方法来实现.你甚至可以只扩展函数的一部分,只要它是一个对象即可.

21. Loop anything

Sometimes you just want to loop anything, Set, Map, Object, Array, String, etc, as long as it is iterable. This very simple forEach function utility allows you to do just that. If the callback returns true it will just break out of the loop.

有时,你需要循环任何可迭代的内容(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. Make function argument required

This is an awesome way to make sure a function is called with what it need to do the work. You can use the feature of the default argument value to call a function that simply throws an error. If you call the function with what it needs, the value will replace that function and nothing happens. Calling with undefined has the same effect as well.

这是一种确保函数调用了完成工作所需内容的绝佳方法.你可以使用默认参数值的特性来调用函数,然后就会抛出一个错误.如果调用该函数时带上了它需要的值,则该值将替换该函数,并且什么也不会发生.使用 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. Create modules or singletons

There are often times that you need to initialize something on load, set it up with everything it needs, and then use it everywhere in the application without ever needing to do anything to it. You can use the power of the IIFE function to do just that and it will change your life. This is the module pattern very useful to isolate things and only expose what you will need to interact with.

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

24. Deep clone object

Here are something devs often install some libraries like “lodash“ to be able to do this, but it is actually effortless to do in plain JavaScript. It is a simple recursive function that as long as it is an object, re-initializes it as a clone using its constructor, then repeats the process for all properties.

开发人员通常会安装一些类似 “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. Deep freeze object

If you love immutability, this is a must-have utility but immutability is a big topic so make sure to follow Before Semicolon here on Medium and on Youtube for us to address these things.

如果你喜欢不变性,那么这个工具你一定要常备.

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;
};

文章作者: Elson Correia
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Elson Correia !
  目录