클린코드 자바스크립트(5) - 함수
1. 함수 다루기
1) 함수, 메서드, 생성자
- 함수, 메서드, 생성자를 실전에서 자주 사용하면서도 막상 각 기능들을 정의하며 제대로 사용해볼 수 있는 여유는 없었던 것 같다.
1-1) 함수의 정의
- 1급 객체
- 변수나, 데이터에 담길 수 있음
- 매개변수로 전달 가능 (콜백 함수)
- ※ 콜백 함수 란, 다른 함수에 인자로 전달되는 함수를 말한다. 비동기적인 프로그래밍에서 유용하며, JS와 같은 언어에서 일반적인 패턴.
- 함수가 함수를 반환 할 수 있음 (고차 함수의 경우)
// 콜백 함수 예시
const numbers = [1, 2, 3];
// 함수 doubled 가 인자로 사용되고 있음. doubled를 콜백함수라고 함.
const doubled = numbers.map((number) => {
return number * 2;
})
console.log(doubled) // [2, 4, 6]
1-2) 메소드의 정의
- 객체에 의존성이 있는 함수, OOP 행동을 의미
- ※ OOP 란, 프로그램을 객체들의 집합으로 보고 이들 간의 상호작용으로 서술하는 접근법을 말함
// 메소드 예시
const obj = {
// method: (e) => {
// return 'helloWorld'
// }
// concise method 적용
method(e) {
return 'helloWorld'
}
}
console.log(obj.method());
2) argument & parameter
- argument(인자): 함수를 콜하는 곳에 위치, 실제로 사용되는 값.
- parameter: 함수를 정의하는 곳에 위치, 형식을 갖춘 변수.
const example = (parameter) => {
console.log(parameter);
}
const argument = 'foo';
example(argument);
3) 복잡한 인자 관리하기
- 함수를 정의할 때, 사용자 측에서 맥락을 이해하도록 매개변수의 이름을 명료하게 작성하는게 좋다.
// 예제 1
// 함수명과 매개변수명을 참고
const toggleDisplay = (isToggle) => {
// ... some code
}
const getRandomNumber = (min, miax) => {
// ... some code
}
const timer = (start, stop, end) => {
// ... some code
}
const genSquare = (top, right, bottom, left) => {
// ... some code
}
- 아래와 같이 매개변수를 구조분할로 작성하면, 사용자에게 필수 params를 알려주고 인자를 안전하게 사용할 수 있게 할 수 있다. (적극 추천)
// 예제 2
// 요구사항: 차량 소유자 확인에 필요한 정보(이름, 차량번호)와 옵션 정보(brand, type) 함수를 정의하라
const createCar = ({ name, carNumber, type, brand }) => {
if (!name) {
throw new Error('name is a required');
}
if (!carNumber) {
throw new Error('brand is a required');
}
return {
name,
carNumber,
type,
brand
}
}
console.log(createCar({ carNumber: 2810, type: "세단", brand: "K5" })) // Error: name is a required
console.log(createCar({ name: "이동규", carNumber: 2810, type: "세단", brand: "K5" })); // { name: '이동규', carNumber: 2810, type: '세단', brand: 'K5' }
4) Default Value
- Default Value를 포함하여 매개변수를 작성하면, 값이 undefined 되었을 때 발생할만한 문제를 방어할 수 있다.
- 그런데, Default Value를 ll 연산자로 지정하면 예제 1과 같이 코드 작성이 번거로울 수 있는데, 이를 예제 2와 같이 단축할 수도 있다.
// 예제 1
const createCarousel = (options) => {
options = options || {};
const margin = options.margin || 0;
const center = options.center || false;
const navElement = options.navElement || 'div';
return {
margin,
center,
navElement
}
}
// { margin: 0, center: false, navElement: 'div' }
console.log(createCarousel());
// 예제 2
// 매개변수가 객체로 들어옴
const createCarousel = (
{
// default value
margin = 0,
center = false,
navElement = 'div',
// '= {}' 는 'options = options || {};' 코드와 동일함
// '= {}' 가 없으면 코드 에러
} = {}) => {
return {
margin,
center,
navElement
}
}
// { margin: 0, center: false, navElement: 'div' }
console.log(createCarousel());
5) Rest Parameters
- 가변 인자가 들어오는 경우, 즉 몇 개의 인자가 사용될지 모르는 경우, 함수의 매개변수를 정의할 때 ‘Rest parameters(…)’를 사용하여 해결할 수 있다.
- 단, 여기서 ‘…‘는 스프레드 연산자와 다르니 주의하도록 하자.
- 이 때, Rest Parameters로 들어온 데이터의 타입은 ‘Array’이므로 배열 메소드를 사용할 수 있다는 점도 기억하자.
// 예제 1
const sumTotal = (...params) => {
// Rest Parameters의 타입은 Array
console.log(Array.isArray(params)); // true
// 배열 메소드 바로 사용 가능
return params.reduce(
(acc, curr) => acc + curr
);
}
console.log(sumTotal(1, 2, 3, 4, 5));
Rest Parameters에서 첫번째, 두번째 인자를 받아보고 싶은 경우, 아래와 같이 순서에 맞는 위치에 매개 변수를 지정하면 받아볼 수 있다.
- Rest Parameters에서 첫번째, 두번째 인자를 받아보고 싶은 경우, 아래와 같이 순서에 맞는 위치에 매개 변수를 지정하면 받아볼 수 있다.
// 예제 2
const sumTotal = (first, second, ...params) => {
return {
first,
second,
rest: params
}
}
console.log(sumTotal(1, 2, 3, 4, 5)); // { first: 1, second: 2, rest: [ 3, 4, 5 ] }
6) void & return
- JS의 함수에서 return이 없는 함수는 기본적으로 ‘undefined’를 리턴한다.
- 당연한 이야기지만, 함수 선언 시 불필요하게 return 을 사용하지 않는 편이 좋다.
// return 이 없는 함수를 호출하는 경우
const isAdult = (age) => {
const res = age > 19;
}
console.log(isAdult(20)); // undefined
JS에도 voide가 있는데, 때에 따라 아래와 같이 링크를 클릭했을 때, 어떠한 경로로 이동하지 않도록 할 때 사용할 수도 있다.
<a href="javascript:void(0)">Link</a>
7) 화살표 함수
- 실무에서 화살표 함수를 많이 쓰는데, 맹목적으로 사용하는 경우 문제가 발생할 수 있다.
- 리액트가 아닌 다른 개발 환경(Node.js)에서는 화살표 함수를 맹목적으로 사용하지 않도록 주의하자.
// 주의 1 - 객체의 메소드로 화살표 함수를 사용하는 경우
const user = {
name: '홍길동',
getName: () => {
return this.name;
}
};
console.log(user.getName()); // undefined, 오류
// 개선 1 - 객체의 메소드를 정의하는 경우, 화살표 함수 금지
const user = {
name: '홍길동',
getName() {
return this.name;
}
};
console.log(user.getName()); // 홍길동
// 주의 2 - 화살표 함수 형식으로 정의하면 생성자(new)를 사용할 수 없음
const Person = (name, city) => {
this.name = name;
this.city = city;
}
const person = new Person("홍길동", "서울"); // Person is not a constructor, 오류
8) 콜백 함수
- 콜백 함수는 Promise와 async, await으로 바뀌는 비동기로 제어하는 기법으로 볼 수 있다.
- 그리고 어떠한 함수의 실행권을 다른 함수로 넘겨주는 것으로 볼 수 있다.
const btnHandler = () => {
console.log("버튼이 눌렸습니다.")
}
// 콜백함수(btnHandler)의 실행권을 사용자에게 넘기는 것으로 볼 수 있음
window.addEventListener('click', btnHandler);
- 콜백 함수를 이용하면, 코드를 리펙토링할 때 도움이 될 수 있다.
- 주의) 콜백 함수를 argument로 사용하는 경우, 함수명만 호출하도록 한다. 일반적인 함수호출 방식(‘함수명 + ()’)의 경우가 아닌 함수의 이름을 그대로 받아 전달받게 해야한다.
// 예시 - 콜백 함수로 리팩토링 전
function register() {
const isConfirm = confirm(
"회원가입에 성공했습니다."
);
if (isConfirm) {
redirecUserInfoPage();
}
}
function login() {
const isConfirm = confirm(
"로그인에 성공했습니다."
)
if (isConfirm) {
redirectIndexPage();
}
}
// 개선 - 콜백 함수(confirmModal)로 리팩토링 후
// message가 참이면, 전달받은 함수를 실행
function confirmModal(message, cbFunc) {
const isConfirm = confirm(message);
if (isConfirm && cbFunc) {
cbFunc();
}
}
function register() {
confirmModal('회원가입에 성공했습니다.', redirecUserInfoPage)
// confirmModal('회원가입에 성공했습니다.', redirecUserInfoPage()) ← 콜백함수 호출시, 함수명 뒤에 () 붙이지 않게 주의
}
function login() {
confirmModal('로그인에 성공했습니다.', redirectIndexPage)
}
9) 순수 함수
- 함수를 정의할 때는 사이트 이펙트를 줄이기 위해, 함수를 ‘컨테이너화’ 해야한다.
- 그리고 이렇게 함수르 컨테이너화 하는 것을 ‘순수 함수’라고 한다.
// 예시 1 - 순수 함수가 아닌 예
// num1과 num2가 컨테이너화 되지 않아, 사이드 이펙트가 발생할 수 있음
let num1 = 10;
let num2 = 20;
function impurityFunction() {
return num1 + num2;
}
console.log(impurityFunction(num1, num2));
// 사이드 이펙트 발생
num1 = 30;
console.log(impurityFunction(num1, num2));
// 예시 1 - 순수 함수로 리팩토링 예
// 매개 변수로 정의하여, 사이드이펙트 방지
function impurityFunction(num1, num2) {
return num1 + num2;
}
console.log(impurityFunction(10, 20));
- reference type(배열, 객체)을 인자로 받는 경우, 새로운 배열, 객체를 반환하도록 한다.
- 왜냐하면, reference type은 옮겨 담을 때, 동일한 위치를 참조하기 때문에 사이드 이펙트가 발생할 수 있다.
// 예시 2 - 객체를 인자로 받아 수정 후, 새로운 객체로 리턴하지 않는 경우 발생하는 문제
const obj = { one: 1 };
function changeObj(targetObj) {
targetObj.one = 100;
return targetObj;
}
console.log(changeObj(obj)); // { one: 100 }, 예상 결과대로 나옴
console.log(obj); // { one: 100 }, 오류: 값이 같이 변경되어 버림
// 예시 2 - 객체를 인자로 받아 수정 후, 새로운 객체로 리턴하지 않는 경우 발생하는 문제
const obj = { one: 1 };
function changeObj(targetObj) {
// 스프레드 연산자로 새로운 객체를 리턴
return { ...targetObj, one: 100 };
}
console.log(changeObj(obj)); // { one: 100 }
console.log(obj); // { one: 1 }
다음에 다루게 될 것
- 추상화, 에러 다루기
댓글남기기