안녕하세요🐱🐉
자바스크립트 코딩 시 참고할 만한 팁에 대한 외국 글이 있어 번역본 포스팅합니다.
특히 스프레드신택스의 경우 구조 분해 할당 객체 복사 등 코드 양을 줄이는데 많은 도움이 되니 참고해보세요!~
1. 배열 선언과 동시에 값 채워 넣기
let array = Array(5).fill('');
console.log(array) // outputs : (5) ["", "", "", "", ""]
배열을 선언하면서 동시에 동일한 값을 채워 넣는 작업은 생각보다 흔히 쓰이지 않습니다. 특히 고정 길이 배열이 아닌 동적 길이 배열을 사용하는 자바스크립트에서는 더욱 그렇죠. 저는 차라리 배열로 for 루프를 없애는 코드가 유용해 보여요.
빈 배열로 for 루프 없애기
let sum = 0
for (let i = 0; i < 10; ++i) {
sum += i
}
최근, 함수형 프로그래밍이 인기를 얻으면서 for, while의 사용 빈도가 현저하게 줄어들고 있습니다. let 변수의 불완전성으로 인해 루프 안에서 상태가 달라질지 모른다는 두려움 때문입니다. (함수형 프로그래밍) 이미 존재하는 배열을 어떤 함수로 전달하여 원하는 동작을 취하는 것은 별로 어렵지 않습니다. 함수형으로 코딩을 하다 보면 자주 만나는 상황이니까요. 하지만 순수한 for 루프처럼 정해진 횟수만큼 수행해야 하는 작업이라면 좀 난감한데 async나 lodash의 times(n) 함수를 이용할 수는 있지만 가볍게 순수 자바스크립트로 작성하고 싶다면 아래 트릭을 사용할 수 있습니다.
const sum = [...Array(10)].reduce((acc, _, i) => acc += i, 0)
Array(n)으로 배열의 크기를 지정한 후 spread 연산자로 펼쳐주면 undefined의 아이템들이 뿌려지는데 이 것들을 []로 다시 묶어 배열로 활용하는 것이 핵심입니다. 보기에는 괜히 복잡해 보이는 점이 아쉽지만, 함수형 프로그래밍을 이어갈 수 있게 해주는 것이 너무 큰 장점입니다.
2. 유일 값 배열 구하기
const cars = [
'Mazda',
'Ford',
'Renault',
'Opel',
'Mazda'
]
const uniqueWithArrayFrom = Array.from(new Set(cars));
console.log(uniqueWithArrayFrom);
// outputs : ["Mazda", "Ford", "Renault", "Opel"]
const uniqueWithSpreadOperator = [...new Set(cars)];
console.log(uniqueWithSpreadOperator);
// outputs : ["Mazda", "Ford", "Renault", "Opel"]
Set 내장 객체를 이용하여 배열의 중복 값을 제거하는 트릭입니다. Array.from 함수가 arrayLike를 인자로 받기 때문에 가능한 코드입니다. arrayLike는 ‘유사 배열 객체’, ‘순회 가능한 객체’를 일컫는데 이들은 String, Set, Map, arguments와 같은 것들이 있습니다.
lodash를 이용한 유일 값 배열 구하기
import * as _ from 'lodash'
const cars = [
'Mazda',
'Ford',
'Renault',
'Opel',
'Mazda'
]
const uniqCars = _.uniq(cars)
console.log(uniqCars)
lodash의 uniq 함수를 이용하면 좀 더 직관적으로 배열의 아이템들이 유니크하다는 것을 잘 표현할 수 있지 않을까 생각합니다만, 1번 섹션처럼 순수 자바스크립트를 이용하려면 Array.from(new Set())을 이용하는 것도 좋아 보입니다.
3. spread 연산자를 이용한 객체 병합
이 예제는 두 가지 경우의 병합 방식을 보여줍니다.
첫 번째 예제는 두 오브젝트의 병합입니다. spread 연산자 예제에 자주 나오는 방식이므로 사용해보신 적이 있으실 겁니다. 이 연산자가 나오기 전에 주로 사용했던 방식으로 Object.assign이 있습니다. 하지만, 이 함수는 인자로 넘어가는 객체 자신을 변경시켜 버리기 때문에 함수형 프로그래밍의 콘셉트에 부합하지 않습니다.
// merging objects
const product = { name: 'Milk', packaging: 'Plastic', price: '5$' }
const manufacturer = { name: 'Company Name', address: 'The Company Address' }
const productManufacturer = { ...product, ...manufacturer };
console.log(productManufacturer);
// outputs : { name: "Company Name", packaging: "Plastic", price: "5$", address: "The Company Address" }
두 번째 예제는 객체들이 담긴 배열로부터 하나의 객체를 만듭니다. reduce 함수, spread 연산자 그리고 동적으로 객체 키를 설정하는 기술(Enhanced Object Literals)을 이용합니다. 이 코딩 기술은 json 객체를 조작할 때 굉장히 유용하게 사용될 수 있으니 익숙해지시면 좋습니다.
// merging an array of objects into one
const cities = [
{ name: 'Paris', visited: 'no' },
{ name: 'Lyon', visited: 'no' },
{ name: 'Marseille', visited: 'yes' },
{ name: 'Rome', visited: 'yes' },
{ name: 'Milan', visited: 'no' },
{ name: 'Palermo', visited: 'yes' },
{ name: 'Genoa', visited: 'yes' },
{ name: 'Berlin', visited: 'no' },
{ name: 'Hamburg', visited: 'yes' },
{ name: 'New York', visited: 'yes' }
];
const result = cities.reduce((accumulator, item) => {
return {
...accumulator,
[item.name]: item.visited
}
}, {});
console.log(result);
/* outputs :
Berlin: "no"
Genoa: "yes"
Hamburg: "yes"
Lyon: "no"
Marseille: "yes"
Milan: "no"
New York: "yes"
Palermo: "yes"
Paris: "no"
Rome: "yes"
*/
4. Array.map 없이 배열 맵핑하기
const cities = [
{ name: 'Paris', visited: 'no' },
{ name: 'Lyon', visited: 'no' },
{ name: 'Marseille', visited: 'yes' },
{ name: 'Rome', visited: 'yes' },
{ name: 'Milan', visited: 'no' },
{ name: 'Palermo', visited: 'yes' },
{ name: 'Genoa', visited: 'yes' },
{ name: 'Berlin', visited: 'no' },
{ name: 'Hamburg', visited: 'yes' },
{ name: 'New York', visited: 'yes' }
];
const cityNames = Array.from(cities, ({ name}) => name);
console.log(cityNames);
// outputs : ["Paris", "Lyon", "Marseille", "Rome", "Milan", "Palermo", "Genoa", "Berlin", "Hamburg", "New York"]
Array.map을 사용하지 않고, Array.from을 이용하여 새로운 배열을 만들어내는 예제 코드입니다. 처음 알게 된 트릭이지만, 코드가 크게 향상된 것 같지는 않아서 굳이 구분하여 사용할 필요가 있을까 생각합니다.
Array.map vs Array.from
혹시 성능의 이점이 있지 않을까 해서 간단히 테스트를 해 보았습니다. 10번을 수행해서 걸린 시간을 출력해봤는데, map이 from에 비해 근소하게 빠른 결과가 나왔네요. 꼭 필요하지 않다면 그냥 map을 쓰는 것이 좋아 보입니다.
const nums = array(100000).fill(1)
console.time('elapsed')
nums.map(v => v * 9)
console.timeend('elapsed')
/*
19.942ms
17.738ms
19.132ms
17.752ms
19.036ms
16.826ms
17.578ms
17.710ms
17.385ms
17.143ms
*/
const nums = array(100000).fill(1)
console.time('elapsed')
array.from(nums, v => v * 9)
console.timeend('elapsed')
/*
20.360ms
18.897ms
18.503ms
19.231ms
21.684ms
18.285ms
18.567ms
18.561ms
21.863ms
18.659ms
*/
5. 조건부 객체 속성
const getUser = (emailIncluded) => {
return {
name: 'John',
surname: 'Doe',
...emailIncluded && { email : 'john@doe.com' }
}
}
const user = getUser(true);
console.log(user); // outputs : { name: "John", surname: "Doe", email: "john@doe.com" }
const userWithoutEmail = getUser(false);
console.log(userWithoutEmail); // outputs : { name: "John", surname: "Doe" }
조건에 따라 객체의 인자가 달라져야 하는 경우, 겨우 이것 하나 때문에 객체를 두 벌 만들어서 골라야 하는가 하는 자괴감이 들었던 분들에게 유용한 예제입니다.
...emailIncluded && { email : 'john@doe.com' }
이 코드는 emailIncluded가 true라면 email key/value를 객체에 추가하고 false라면 추가하지 않도록 합니다. 여기서 주의해야 할 부분은 연산자 수행 순서입니다. ... 연산자로 펼쳐진 다음 &&를 만나는 것이 아니라, && 연산자로 AND 처리가 된 후에 그 결과에 대해 ... 연산자로 펼쳐지는 것입니다. 연산자 우선순위를 잘 모른다면 헷갈리는 코드가 되기 십상입니다. 제가 만약 이 코드를 프로덕션에 사용한다면 다음처럼 괄호를 포함하고 싶을 것 같습니다.
...(emailIncluded && { email : 'john@doe.com' })
이 트릭도 if 구문을 없애고 싶은 테크니션들의 절규와 같은 것인데요. 이 코드를 if가 있는 형태로 작성해볼까요?
const getUser = (emailIncluded) => {
const obj = {
name: 'John',
surname: 'Doe',
}
if (emailIncluded) {
obj.email = 'john@doe.com'
}
return obj
}
어떤가요? 코드가 살짝 길어지긴 했어도 이 예제가 더 보기 좋은가요? 사람마다 호불호가 많이 갈릴 것 같은 주제입니다. 다만, 유용함 때문에 코드 컨벤션을 파괴하는 예는 예전에도 있었죠.
const name = 'Reid'
const username = name || 'Guest'
console.log(username)
또는
const user = { name: 'Reid' }
// const user = undefined
const message = user && 'User exists'
if (message) {
console.log(message)
}
6. 데이터 destructing 하기
여기 이 예제는 매우 중요합니다. destructing을 자주 사용하는 사람 중에도 개별 변수가 아니라 object의 속성으로 분해/저장하는 방식은 익숙하지 않은 사람들이 있으니까요. 저처럼요.
const rawUser = {
name: 'John',
surname: 'Doe',
email: 'john@doe.com',
displayName: 'SuperCoolJohn',
joined: '2016-05-05',
image: 'path-to-the-image',
followers: 45
}
let user = {}, userDetails = {};
({ name: user.name, surname: user.surname, ...userDetails } = rawUser);
console.log(user); // outputs : { name: "John", surname: "Doe" }
console.log(userDetails); // outputs : { email: "john@doe.com", displayName: "SuperCoolJohn", joined: "2016-05-05", image: "path-to-the-image", followers: 45 }
가장 쉽고 자주 사용되는 destructing은 다음과 같습니다:
const rawUser = {
name: 'John',
surname: 'Doe',
email: 'john@doe.com',
displayName: 'SuperCoolJohn',
joined: '2016-05-05',
image: 'path-to-the-image',
followers: 45
}
const { name, email } = rawUser
rawUser 객체에서 원하는 객체만 pick 해서 새로운 변수를 만듭니다. 하지만, 실제로 코딩을 하다 보면 이렇게 개별 변수가 아니라 집어낸 몇몇 속성들로 새로운 객체를 만들어야 하는 경우가 더 많습니다. 그럴 때 이 기술을 이용해야 합니다.
const rawUser = {
name: 'John',
surname: 'Doe',
email: 'john@doe.com',
displayName: 'SuperCoolJohn',
joined: '2016-05-05',
image: 'path-to-the-image',
followers: 45
}
let user = {};
({ name: user.name, email: user.email } = rawUser)
console.log(user)
// results : { name: 'John', email: 'john@doe.com' }
lodash를 이용해서 좀 더 가독성 좋게 만드는 것도 추천합니다.
const user = _.pick(rawUser, ['name', 'email'])
7. 동적 속성 이름
const dynamic = 'email';
let user = {
name: 'John',
[dynamic]: 'john@doe.com'
}
console.log(user); // outputs : { name: "John", email: "john@doe.com" }
ES6에 추가된 유용한 기능 중 하나로 객체의 key를 동적으로 생성할 수 있습니다. 이런 방식이 없었던 시절에는 아래와 같은 처리가 불가피했습니다.
const dynamic = 'email';
let user = {
name: 'John'
}
user[dynamic] = 'john@doe.com'
console.log(user); // outputs : { name: "John", email: "john@doe.com" }
간단하지만, 굉장히 유용한 트릭입니다.
8. 문자열 템플릿
ES6에 포함된 신기술 중 가장 쉽고 널리 사용되는 것 중 하나가 아닐까 생각합니다. 문자열을 +로 더해가며 조합하던 방식과 비교하면 실수를 줄일 수 있고, 가독성도 살릴 수 있습니다.
const user = {
name: 'John',
surname: 'Doe',
details: {
email: 'john@doe.com',
displayName: 'SuperCoolJohn',
joined: '2016-05-05',
image: 'path-to-the-image',
followers: 45
}
}
const printUserInfo = (user) => {
const text = `The user is ${user.name} ${user.surname}. Email: ${user.details.email}. Display Name: ${user.details.displayName}. ${user.name} has ${user.details.followers} followers.`
console.log(text);
}
printUserInfo(user);
// outputs : 'The user is John Doe. Email: john@doe.com. Display Name: SuperCoolJohn. John has 45 followers.'
여러 줄에 걸쳐 문자열을 작성하는 방법도 간단합니다. 전체 문자열을 ``로 감싼 상태에서 그냥 엔터키를 쳐주면 됩니다.
const text = `The user is ${user.name} ${user.surname}.
Email: ${user.details.email}.
Display Name: ${user.details.displayName}.
${user.name} has ${user.details.followers} followers.`
/*
The user is John Doe.
Email: john@doe.com.
Display Name: SuperCoolJohn.
John has 45 followers.
*/
마치며
자바스크립트는 언어의 새로운 트렌드에 발맞춰 계속 발전하고 있습니다. 다른 언어에서 검증된 유용한 기술이나, 함수형 언어가 가져야 할 필수 조건들을 하나씩 장착하고 있죠. 앞으로도 자바스크립트의 변화는 계속될 것입니다.
이런 흐름을 무시하면 기술을 코드에 적용하는 것은 둘째 치고, 다른 사람이 작성한 코드를 이해하는 것부터 문제가 생깁니다. 결국 자주 사용하면서 몸에 익혀야 하는 수밖에 없습니다. 새로운 기술들을 모두 외워두고 필요할 때마다 사용하는 것은 어려운 일입니다. 이때 유념해두면 좋은 팁이 있습니다.
for, while 루프는 함수형으로 대체 가능한지 고민한다.
let 변수를 쓰지 않으려고 노력한다.
자바스크립트 내장 함수나 객체를 우선 살피고, 없는 경우 lodash나 underscore, async와 같은 외부 모듈을 확인한다.
ref. 원문 https://devinduct.com/blogpost/26/8-useful-javascript-tricks - Milos Protic
'JavaScript' 카테고리의 다른 글
[JavaScript] 함수 선언식과 함수 표현식, 차이와 장점 (0) | 2021.12.29 |
---|---|
[Javascript] 자바스크립트의 역사와 발전 과정에 대해! (0) | 2021.12.22 |
[JavaScript] 자바스크립트로 하는 전화번호 / 휴대폰 번호 유효성 검사 (정규식) (0) | 2021.11.05 |
[JavaScript] 이벤트를 취소하는 방법 세가지 (이벤트 전파 중단) (1) | 2021.07.18 |
[JavaScript] 키코드 정리 (JavaScript keyCode) (0) | 2021.07.17 |