자바스크립트에서 전역공간이란 최상위 객체를 뜻한다.
최상위 객체는 브라우저와 Node.js 환경에 따라서 조금 다른데,
브라우저에서는 window가 최상위 객체이며,
Node.js 환경에서는 global이 최상위 객체이다.
전역공간의 사용을 최소화 해야 하는 이유는
어느 디렉토리에서나 접근이 가능하기 때문이다.
다음과 같은 예시를 들 수 있다.
// index.js
var setTimeout = "⏰"
// index2.js
setTimeout(()=>{console.log('5초 이후에 실행될 코드')},5000)
// setTimeout is not a function
setTimeout은 전역객체에 내장된 메소드 중 하나이다.
그런데 이를 var의 변수로 선언해버리면 setTimeout 메소드를 덮어 씌워버린다.
단순하게 전역에 내장된 메소드로 간단하게 실험했지만
협업시 혹은 오랜기간 유지보수를 하는 긴 코드의 경우 큰 문제가 야기될 수 있다.
결론적으로는,
일단 global, window 처럼 최상위 객체에 직접적인 조작을 가하지 않아야하며,
const, let을 이용한 블록 레벨 스코프 지역변수를 만드는 것이 좋다.
또한 이를 해결하기 위한 방법으로는 IIFE, Module, closure 가 있다.
아래와 같은 함수의 예시를 보자.
const getElements = () => {
const result = {};
result.title = document.querySelector(".title");
result.text = document.querySelector(".text");
result.value = document.querySelector(".value");
return result;
};
querySelector로 html 엘리먼트를 객체로 묶어 반환하는 함수이다.
이 함수는 단지 반환을 목적으로 하는 함수이기 때문에 함수의 생명주기가 간단하다.
const result 같은 임시 객체를 만들지 않고도
const getElements = () => {
return (result = {
title: document.querySelector(".title"),
text: document.querySelector(".text"),
value: document.querySelector(".value"),
});
};
명시적으로 return 단 한가지의 역할만 할 수 있도록 리펙토링 할 수 있다.
유틸함수로 보다 정확한 문제점을 확인해볼 수 있다.
const getDateTime = (targetDate) => {
let month = targetDate.getMonth();
let day = targetDate.getDate();
let hour = targetDate.Hours();
month = month >= 10 ? month : "0" + month;
day = day >= 10 ? day : "0" + day;
hour = hour >= 10 ? hour : "0" + hour;
return {
month,
day,
hour,
};
};
겉으로는 별 문제가 없는 것 처럼 보인다.
그러나 이 함수의 문제는 추가적인 스펙이 요구됐을 때 생길 수 있다.
함수에 대한 추가적인 요구사항이 생겼을 때,
함수를 추가로 만들거나, 함수를 유지보수하여 수정하는 두가지의 방법으로 해결할 수 있다.
함수를 수정하여 사용하는 판단이 때로는 옳은 선택이 될 수 있지만,
만약 유틸 함수의 사용처가 적게는 10곳, 많으면 100곳에서 함께 사용하고 있다면 어떨까 ?
함수를 수정했을 때 이 함수가 모든 케이스에 적용되는 함수 인지 확인해야한다.
예를들어 위의 함수에 "~달 전", "~분 전" 이라는 문자열이 추가된 함수를 구현하려고 한다면
위의 함수를 고쳐 사용하면 문자열이 추가로 필요하지 않은 곳에서는 해당함수를 사용할 수 없거나,
추가적인 조치가 필요하다.
따라서 위의 함수는 이러한 방식으로 리펙토링 할 수 있다.
const getDateTime = (targetDate) => {
let month = targetDate.getMonth();
let day = targetDate.getDate();
let hour = targetDate.Hours();
return {
month: (month = month >= 10 ? month : "0" + month),
day: (day = day >= 10 ? day : "0" + day),
hour: (hour = hour >= 10 ? hour : "0" + hour),
};
};
마찬가지로 임시변수를 만들지 않고 리턴을 명시하는 함수로 리펙토링 한 뒤,
const getDateTimeWithString = () => {
const currentDateTime = getDateTime(new Date());
return {
month: currentDateTime.month + "달 전",
day: currentDateTime.day + "분 전",
hour: currentDateTime.hour + "시간 전",
};
};
추가적인 요구사항을 만족하는 새로운 함수를 만든다.
추상화 된 함수를 재사용 하는 방법으로 함수 설계를 하는 방향이다.
요약하자면,
단 하나의 역할만 할 수 있는 함수라는 것이 이러한 의미라는 것.
값을 리턴하는 로직으로 귀결되는 함수라면 임시변수를 제거하고 값을 바로 리턴하는 식으로 설계하는 것이 바람직하다.