[Javascript] 알면 좋은 25가지 JS Tricks

Dongmin Jang
8 min readNov 30, 2020

아래 글에 있는 예시 코드들을 저장하기 위한 글입니다.

  1. Type check util
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. Check for empty

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

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

function randomNumber(max = 1, min = 0) {
if(min >= max) {
return max;
}
return Math.floor(Math.random() * (max - min) + min);
}

5. Random id generator

// 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 resetsconst 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

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

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

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. Polling data

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

const prom1 = Promise.reject(12);
const prom2 = Promise.resolve(24);
const prom3 = Promise.resolve(48);
const prom4 = Promise.resolve('error');
Promise.all([prom1, prom2, prom3, prom4])
.then(res => console.log('all', res))
.catch(err => console.log('all failed', err))
Promise.allSetteld([prom1, prom2, prom3, prom4])
.then()
.catch()
Promise.any([prom1, prom2, prom3, prom4])
.then()
.catch()
Promise.race([prom1, prom2, prom3, prom4])
.then()
.catch()

11. Swap array values place

const array = [12, 24, 48];const swapOldWay = (arr, i ,j) => {
const arrayCopy = [...arr];

let temp = arrayCopy[i];
arrayCopy[i] = arrayCopy[j];
arrayCopy[j] = temp;
return arrayCopy;
}

const swapNewWay = (arr, i, j) => {
const arrayCopy = [...arr];
[arrayCopy[i], arrayCopy[j]] = [arrayCopy[j], arrayCopy[i]];
return arrayCopy;
}
swapOldWay(array, 0, 2); // [48, 24, 12]
swapNewWay(array, 0, 2); // [48, 24, 12]

12. Conditional Object key

let condition = true;const man = {
someProperty: "some value",
// the parenthesis will execute the ternary that will
// result in the object with the property you want to insert
// or an empty object. Then its content is spreaded in the wrapper object
...(condition === true ? { newProperty: "value" } : {})
}

13. Use variables as the object key

let property = 'newValidProp';const man = {
someProperty: "some value",
// the "square bracket" notation is a valid way to access object key
// like object[prop] but it is used inside to assign a property as well
// using the 'backtick' to first change it into a string
// but it is optional
[`${property}`]: 'value'
}

14. Check for key in object

const sample = {
prop: 'value'
}
// using the "in" keyword will still consider proptotype keys
// which makes it unsafe and one of the issues with "for...in" loot
console.log('prop' in sample) // prints "true"
console.log('toString' in sample) // prints "true"
// using the "hasOwnProperty" methods is safer
console.log(sample.hasOwnProperty('prop')) // prints "true"
console.log(sample.hasOwnProperty('toString')) // prints "false"

15. Remove Array duplicates

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 repreat in some objects
// the objects are different insatnces, 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

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 enxt item
}
if (number > 5) {
break; // stop the loop
}
console.log(number)
}
// logs 1, 3 and 5
number.some(number => {
if(number % 2 === 0) {
return false; // skip to the next item
}
if (number > 5) {
return true; // stop the loop
}
console.log(number)
})

17. Destructuring with alias name and default values

function demo1({dt: data}) {
// rename "dt" to "data"
console.log(dta); // prints {name: 'sample', id:
}
function demo2({ dt: {name, id = 10}}) {
// deep destruct "dt" and if no "id" use 10 as default
console.log(name, id)
}
demo1({
dt: {name: 'sample', id: 50},
});
demo2({
dt: {name: 'sample'}m
});

18. Optional Chaining and nullish coalescing

const obj = {
data: {
container: {
name: {
value: 'sample'
},
int: {
value: 0
}
}
}
}
console.log(
// event 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'
// 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"
// 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'
// using optional chaining "?" is better
(obj?.data?.wrapper?.name) || 'no name' // prints 'no name'
)

19. Extend class with functions

function Paraent() {
const privateProp = 12;
const pribateMethod = () => privateProp + 10;
this.publicMethods = (x = 0) => privateMethod() + x;
this.publicProp = 10;
}
class Child extends Parent {
myProp = 20;
}
const child = new Child();console.log(
child.myProp, // prints 20
child.publicProp, // prints 10
child.publicMethods(40) // prints 62
child.privateProp, // prints undefined
child.privateMethod(), // throws "child.privateMethods is not a function"
)

20. Extend constructor functions

function Employee() {
this.profession = 'Software Engineer';
this.salary = '$150000';
}
function DeveloperFreelancer() {
this.programmingLanguages = ['Javascript', 'Python', 'Swift'];
this.avgPerHour = '$100';
}
function Engineer(name) {
this.name = name;
this.freelancer = {};
Employee.apply(this);
DeveloperFreelancer.apply(this.freelancer);
}
const john = new Engineer('John Doe');console.log(
john.name, // prints "John Doe
john.profession, // prints "Software Engineer"
john.salary,// prints "$150000"
john.freelancer // prints { programmingLanguages: ['Javascript', 'Python', 'Swift'], avgPerHour:'$100'}
)

21. Loop anything

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;
}
}
forEach([1, 2, 3], console.log)
forEach(new Set([1, 2, 3]), console.log)
forEach(new Map([1, 1], [2, 2], [3, 3]), console.log)
forEach('123', console.log)
forEach({a: 1, b: 2, c: 3}, console.log)

22. Make function argument required

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

class Server {
name = 'service'
}
const service = (function (S) {
// do something here like preparing data that you can use to initialize service;
const service = new S();
return () => service;
})(Service)
const element = (function (S) {
const element = document.createElement('DIV');
// do something here to grab something on the dom
// or create elements with javascript setting it all up
// than to return it
return () => element;
})()

24. Deep clone object

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

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

--

--