Logo

dev-resources.site

for different kinds of informations.

Introducing Our New JavaScript Standard Library

Published at
4/12/2024
Categories
webdev
javascript
typescript
standard
Author
Thanga Ganapathy
Introducing Our New JavaScript Standard Library

js-std playground demoWelcome,

I would like to introduce our new JavaScript Standard Library @opentf/std, please keep on reading.

Docs: https://js-std.pages.dev/

Why a Standard Library Matters

Over the past few years, JavaScript has evolved at breakneck speed. The language has seen a proliferation of features, modules, and packages. The JavaScript projects with dependencies crisscrossing like a tangled web.

The Quest for Simplicity

As developers, our primary goal is to find the simplest solution to a problem. Simplicity means functionality, performance, readability, and efficiency. We walk a fine line between complexity and elegance.

The Package Proliferation Problem

Many NPM packages are one-linersβ€”tiny snippets of code packaged up for convenience. But is this proliferation of trivial code a good thing? Not always. In the real world, it leads to issues. If a package is removed, chaos ensues. Dependencies become a house of cards, and maintaining sanity becomes a challenge.

Features

  • Simple & Familiar API with some differences
  • Practical Default Options
  • Cross-Environment Compatibility: Execute seamlessly in browsers, Node.js, Bun, Deno, etc.
  • Clean Code: We’ve followed best practices to ensure readability and maintainability.
  • Documentation: Clear explanations and examples for every function.
  • TypeScript Support

Usage Examples:

Let’s explore some of the library’s capabilities:

Checking if a Value is Numeric:

import { isNum } from "@opentf/std";

isNum(NaN); //=> false

Converting Strings to Pascal Case:

import { pascalCase } from "@opentf/std";

pascalCase("pascal case"); //=> PascalCase

Sorting an Array in Descending Order:

import { sort } from "@opentf/std";

sort([1, 10, 21, 2], "desc"); //=> [21, 10, 2, 1]

Deep Cloning an Object:

import { clone } from "@opentf/std";

const obj = { a: 1, b: "abc", c: new Map([["key", "val"]]) };
clone(obj); // Returns deeply cloned value

Checking Equality of Objects & Arrays:

import { isEql, isEqlArr } from "@opentf/std";

const mapA = new Map([["a", 1], ["b", 2]]);
const mapB = new Map([["b", 2], ["a", 1]]);
isEql(mapA, mapB); //=> false

isEqlArr([1, 2, 3], [2, 3, 1]); //=> true

Adding a Delay (1 second) with sleep:

import { sleep } from "@opentf/std";

await sleep(1000); // Suspends execution for 1 second.

Functions composition using pipe & compose functions:

import { pipe, compose } from "@opentf/std";

pipe(
  1,
  (x) => x + 1,
  (x) => x * 5
); //=> 10

compose(
  1,
  (x) => x + 1,
  (x) => x * 5
); //=> 6

*You can try out these examples on the Playground.

Benchmarks

Some benchmark outputs are shown here for reference.

*Note: Our priorities are reliability and accuracy rather than performance.

clone:
β”Œβ”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   β”‚ Task Name                β”‚ ops/sec β”‚ Average Time (ns)  β”‚ Margin β”‚ Samples β”‚
β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0 β”‚ structuredClone (Native) β”‚ 276,824 β”‚ 3612.3959469709525 β”‚ Β±1.29% β”‚ 27683   β”‚
β”‚ 1 β”‚ _.cloneDeep (Lodash)     β”‚ 216,965 β”‚ 4609.032953864744  β”‚ Β±2.41% β”‚ 21697   β”‚
β”‚ 2 β”‚ R.clone (ramda)          β”‚ 174,567 β”‚ 5728.439422580611  β”‚ Β±1.92% β”‚ 17457   β”‚
β”‚ 3 β”‚ R2.clone (remeda)        β”‚ 310,268 β”‚ 3223.0154703960834 β”‚ Β±2.40% β”‚ 31027   β”‚
β”‚ 4 β”‚ cloneDeep (clone-deep)   β”‚ 468,908 β”‚ 2132.611673882092  β”‚ Β±1.70% β”‚ 46891   β”‚
β”‚ 5 β”‚ copy (fast-copy)         β”‚ 486,179 β”‚ 2056.852050680814  β”‚ Β±1.91% β”‚ 48618   β”‚
+ 6 β”‚ clone                    β”‚ 535,302 β”‚ 1868.1028376072306 β”‚ Β±2.07% β”‚ 53531   β”‚
β””β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*Note:
    - Here the lodash does not support errors, sparse arrays & objects in map keys.

    - Here the ramda & remeda does not support cloning Map & Set.

    - The fast-copy does not clone objects within Map, buffers in TypedArray, sparse arrays.

    - The clone-deep does not handle circular refs, does not clone objects within map,
    sparse arrays, internal refs within the object, TypedArray buffers & DataView.

sortBy:
β”Œβ”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   β”‚ Task Name          β”‚ ops/sec   β”‚ Average Time (ns) β”‚ Margin β”‚ Samples β”‚
β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0 β”‚ _.orderBy (Lodash) β”‚ 1,231,295 β”‚ 812.1529684071648 β”‚ Β±3.09% β”‚ 123130  β”‚
β”‚ 1 β”‚ R.sortWith (Ramda) β”‚ 1,279,200 β”‚ 781.7380570822326 β”‚ Β±2.27% β”‚ 127921  β”‚
β”‚ 2 β”‚ R2.sortBy (Remeda) β”‚ 1,419,707 β”‚ 704.3703291518029 β”‚ Β±2.81% β”‚ 141971  β”‚
β”‚ 3 β”‚ sort (Moderndash)  β”‚ 2,697,568 β”‚ 370.7042634668106 β”‚ Β±1.82% β”‚ 269757  β”‚
+ 4 β”‚ sortBy             β”‚ 2,728,366 β”‚ 366.5196435965459 β”‚ Β±2.19% β”‚ 272837  β”‚
β””β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

*Note: Here the Moderndash does not support passing object prop as string.

isEql:
β”Œβ”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   β”‚ Task Name                           β”‚ ops/sec β”‚ Average Time (ns)  β”‚ Margin β”‚ Samples β”‚
β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0 β”‚ deepStrictEqual (Native)            β”‚ 950,686 β”‚ 1051.871609041841  β”‚ Β±0.24% β”‚ 95069   β”‚
β”‚ 1 β”‚ fastDeepEqual (fast-deep-equal/es6) β”‚ 652,611 β”‚ 1532.3058134904193 β”‚ Β±1.49% β”‚ 65262   β”‚
β”‚ 2 β”‚ dequal                              β”‚ 120,791 β”‚ 8278.7573675501    β”‚ Β±0.74% β”‚ 12080   β”‚
β”‚ 3 β”‚ _.isEqual (Lodash)                  β”‚ 152,075 β”‚ 6575.660376117521  β”‚ Β±2.02% β”‚ 15208   β”‚
β”‚ 4 β”‚ R.equals (Ramda)                    β”‚ 51,496  β”‚ 19418.976504855284 β”‚ Β±1.70% β”‚ 5150    β”‚
+ 5 β”‚ isEql                               β”‚ 104,355 β”‚ 9582.655710998957  β”‚ Β±1.13% β”‚ 10436   β”‚
β””β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

*Note:

  - The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
  - The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
  - The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
  - The ramda `equals` does not check `Map` ordering & symbols.
  - The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.

Conclusion:

We invite you to be part of our journey. The world of JavaScript development is ever-evolving, and together, we can shape it for the better. Here’s what we’d like you to take away:

Collaboration Matters:

Open-source projects thrive on collaboration. Whether you’re contributing code, reporting issues, or sharing ideas, your voice matters. Join us on GitHub and let’s build something remarkable.

Simplicity Wins:

In a sea of complexity, simplicity stands out. Our library aims to simplify your coding life, one function at a time. Embrace the elegance of clean code and watch your productivity soar.

Keep Learning:

JavaScript surprises us daily. Stay curious, explore new ideas, and never stop learning. The next breakthrough might be just a line of code away.

Visit our docs website for more info.

πŸ™ Thanks for reading.

Featured ones: