클린코드
const arr = [1,2,3]
arr[3] = 'test';
arr['property'] = 'string value';
arr['obj'] = {};
arr[{}] = [1,2,3];
arr['func'] = function () {
return 'hello';
}
for(let i=0; i<arr.length; i++){
console.log(arr[i]); // 1 , 2, 3
}
이게 1,2,3만 찍히는 이유는 위에 숫자가 아닌 값들이 찍혀있기 때문에다 그러면 다르게 보면 어떻게 될까?
const arr = [1,2,3]
arr[3] = 'test';
arr['property'] = 'string value';
arr['obj'] = {};
arr[{}] = [1,2,3];
arr['func'] = function () {
return 'hello';
}
console.log(arr) // [1, 2, 3, 'test', property:'string value'...]
우리가 평소에 보던 느낌이 아닌 것 같다. 인덱스가 이상하게 느껴지면서 배열이 객체와 똑같이 보인다.
심지어 배열에 있는 함수또한 동작한다.
그렇기 때문에 타입을 검사할 때 매우 조심하자
const arr = [1,2,3]
if(arr.length){}
if(typeof arr === 'object'){}
if(arr instanceof Array){}
위와같이 검사하기도 하는데 이는 좋지 않은 상황이 생길 수 있으니 최근에 나온 폴리필인
Array.isArray(arr);
과 같은 문법을 사용하자
const arr = [1, 2, 3];
console.log(arr.length); // 3
arr.length = 10
console.log(arr.length); // 10
console.log(arr) // [1, 2, 3, , , , , , , ]
length는 배열의 길이보단 배열의 마지막 인덱스에 가깞다 보기 쉽다
length
를 0으로 줄경우 초기화가 되거나 3의 길이를 가진 배열에 10을 주면 빈칸이 숭숭 뚫린 배열이 된다거나 한다.
항상 주의해서 작성하자
배열에 접근할 때 [0]
와 같은 인덱스를 사용하여 암호에 접근하듯이 하는 것 보다 1개만 있는 것도 구조분해 할당을 통해 좀 더 꺼내려는 값을 명확하게 해주면 좋다.
function head(arr){
return arr[0] ?? ''
}
와 같이 배열의 앞 가격만 꺼내는 함수를 만들 수 있다.
const arrayLikeObject = {
0: 'HELLO',
1: 'WORLD',
length: 2,
}
const arr = Array.form(arrayLikeObject)
console.log(arr) // ['HELLO','WORLD']
console.log(Array.isArray(arr)) // true
console.log(Array.isArray(arrayLikeObject)) // false
이게 정상적으로 동작하는 건데 객체의 length
마저 아주 잘 들어간 것을 확인할 수 있다.
하지만 이 유사 배열 객체로 위험할 수 있는데
function generatePriceList() {
return arguments.map((arg) => arg + '원')
}
generatePriceList(100, 200, 300, 400, 500, 600)
가변적인 값들을 매개변수로 던져 인자로 선언하지 않았음에도 함수 내부에서 argument
라는 유사배열 객체로 다룰수가 있다
그래서 for
문을 만들어서 출력할수도 있다. 그래서 이걸 '아! 배열이구나' 하고 생각할 수 있지만 유사배열 객체인 것이다.
그 이유를 생각하자면 Array.isArray
로 확인하면 false
가 나온다.
그러다 보니 map,filter ...
등과 같은 고차함수를 사용할 경우 정상 동작하지 않는다. 그래서 사용하고 싶다면 Array.form
을 사용하여 변환 시켜야한다.
불변성을 지키기 위해선
for문을 통한 배열을 복사할 때 고차함수를 사용하면 더욱쉽게 사용할 수 있다.
이 부분은 JS를 공부하다보면 더욱 쉽게 이해하는 부분이다.
이때 for문이나 for...in 등 여러가지 반복문이 있지만 map,filter,find...
와 같은 명시적인 이름이 있는 함수를 사용하는 것이 훨씬 코드가 깔끔하며 사람이 읽기에 알기 쉽다고 생각한다.
배열 메서드를 활용했을 때도 계속해서 로직들이 늘어날 경우 굉장히 복잡해질 수도 있다.
메서드 체이닝을 활용하면 좋다.
function getWonPrice(priceList){
const filterList = priceList.filter(callbackFunc)
const sortedList = filterList.sort(callbackFunc)
return sortedList.map(callbackFunc)
}
// =>
function function getWonPrice(priceList){
return priceList
.filter(callbackFunc) //filter를 통한 조건
.sort(callbackFunc) // sort를 통한 정렬
.map(callbackFunc) // map을 활용한 배열 반환
}
이와 같이 메서드 체이닝을 활용하여 결과값을 명확하고 선언적으로 사용할 수 있다.
정확한 map
과 forEach
의 차이를 보자면 return
값의 반환 유무를 통해 알 수 있다.
map
은 변환된 새로운 배열을 반환시킨다.(forEach
는 undefined
)
그저 외부의 함수를 실행하는 정도등은 forEach
를 사용하는 등 함수의 명확한 사용을 구분하여 쓴다면 코드적으로 볼 때 훨씬 좋아진다.
기본 for
문을 사용할 땐 continue
와 break
를 썼었지만 고차 함수부턴 사용이 되지 않는다 (문법적으로 불가능)
이때는 try...catch
문을 사용하여 에러를 던질수도 있지만 for
문을 그냥 사용하거나 for..of
,for..in
등을 사용하자
하지만 다른 좋은 방법들도 있는데 이는
Array.prototype.every()
(&&연산자)나 Array.prototype.some()
(||연산자),Array.prototype.find()
,Array.prototype.findIndex()
등을 활용할 수 있다.
유익한 글 잘 봤습니다, 감사합니다.