자바스크립트의 세계에서는 특별한 대우를 받는 것들이 존재하며 이를 일급 객체라고 한다.
그 중 하나가 바로 함수(function)이다.
일급 객체의 세 가지 특징은 다음과 같다.
이는 함수를 데이터(string
, number
, boolean
, array
, object
)를 다루듯이 다룰 수 있다는 걸 의미한다. 변수에 저장할 수 있기 때문에 배열의 요소나 객체의 속성값으로 저장하는 것도 가능함.
아래의 함수 표현식(function expression: 함수를 변수에 저장하는 방법)은 함수 선언식(function declaration)과 다르게 호이스팅(hoisting)이 적용되지 않는다.
// 아래는 변수 square에 함수를 할당하는 함수 표현식.
// 자바스크립트에서 함수는 일급 객체이기 때문에 변수에 저장할 수 있음.
// 함수 표현식은 할당 전에 사용할 수 없음.
// square(7); // --> ReferenceError: Can't find variable: square
const square = function (num) {
return num * num;
};
// square에는 함수가 저장되어 있으므로 (일급 객체), 함수 호출 연산자 '()'를 사용할 수 있다.
output = square(7);
console.log(output); // --> 49
함수 선언식의 호이스팅에 지나치게 의존하는 것은 코드의 유지 보수 측면에서 좋지 않다. 코드 리뷰나 디버깅을 할 때 코드를 위 아래로 왔다 갔다하게 될 수 있는데, 함수 선언식의 경우, 어느 위치에나 함수를 선언할 수 있고 함수의 실행 위치도 중요하지 않기 때문이다. 반면, 함수 표현식은 함수의 할당과 실행의 위치가 중요하기 때문에 코드의 위치가 어느 정도 예측 가능하다.
호이스팅을 제외하면 함수 선언식과 함수 표현식의 차이는 크게 없어 보이고, 실제로도 그렇다. 다만 함수 표현식의 경우, 함수가 변수에 저장될 수 있다는 사실을 좀 더 분명하게 드러낼 수 있다.
한편, 변수에 저장된 데이터는 함수의 인자로 전달되거나 함수 내에서 리턴값으로 사용될 수 있다. 앞서 함수가 변수에 저장될 수 있다는 사실로부터, 함수 역시 다른 함수의 인자로 전달되거나 다른 함수 내에서 리턴될 수 있다는 것을 알 수 있다.(고차 함수, higher order function)
고차 함수(higher order function)는 함수를 인자(argument)로 받거나 함수를 리턴하는 함수를 말한다.
이 때 다른 함수(caller)의 인자(argument)로 전달되는 함수를 콜백 함수(callback function)라고 한다.
콜백 함수를 전달받은 함수는 이 콜백 함수를 호출(invoke)할 수 있다. caller는 조건에 따라 콜백 함수의 실행 여부를 결정할 수도 있고, 심지어 여러 번 실행할 수도 있습니다. 특히 콜백 함수는 어떤 작업이 완료되었을 때 호출되는 경우가 많아서 답신 전화를 뜻하는 콜백이라는 이름이 붙여졌다고 한다.
'함수를 리턴하는 함수'만을 일컫는 용어로서, 이런 함수를 이를 고안해 낸 논리학자 하스켈 커리(Haskell Curry)의 이름을 따라 커리 함수라고 한다. 하지만 엄밀한 의미에서 고차 함수는 커리 함수를 포하므로, 본 포스팅에서는 고차 함수로 용어를 통일하도록 한다.
function double(num) {
return num * 2;
}
function doubleNum(func, num) {
return func(num);
}
// 1) 함수 doubleNum은 다른 함수를 인자로 받는 고차 함수다.
// 2) 함수 doubleNum의 첫 번째 인자 func에 함수가 들어올 경우,
// 함수 func는 함수 doubleNum의 콜백 함수다.
// 3) 아래와 같은 경우, 함수 double은 함수 doubleNum의 콜백 함수입니다.
let output = doubleNum(double, 4);
console.log(output); // -> 8
function adder(added) {
return function (num) {
return num + added;
};
}
// 함수 adder는 다른 함수를 리턴하는 고차 함수다.
// adder는 인자 한 개를 입력받아서 함수(익명 함수)를 리턴한다.
// 리턴되는 익명 함수는 인자 한 개를 받아서 added와 더한 값을 리턴한다.
// 아래 adder(5)는 함수이므로 함수 호출 연산자 '()'를 사용할 수 있다.
let output = adder(5)(3); // -> 8
// adder가 리턴하는 함수를 변수에 저장할 수 있다.
// javascript에서 함수는 일급 객체이기 때문.
const add3 = adder(3);
output = add3(2);
console.log(output); // -> 5
function double(num) {
return num * 2;
}
function doubleAdder(added, func) {
const doubled = func(added);
return function (num) {
return num + doubled;
};
}
// 함수 doubleAdder는 고차 함수다.
// 함수 doubleAdder의 인자 func는 함수 doubleAdder의 콜백 함수다.
// 함수 double은 함수 doubleAdder의 콜백으로 전달된다.
// doubleAdder(5, double)는 함수이므로 함수 호출 기호 '()'를 사용할 수 있다.
doubleAdder(5, double)(3); // -> 13
// doubleAdder가 리턴하는 함수를 변수에 저장할 수 있다. (일급 객체)
const addTwice3 = doubleAdder(3, double);
addTwice3(2); // --> 8
자바스크립트에는 기본적으로 내장(built-in)되어 있는 고차 함수들이 있다. 바로 배열 메소드들 중 일부가 고차 함수에 해당한다.
배열의 요소 중 특정 조건을 만족하는 요소들만을 걸러내는(filter) 메소드
true
) 또는 거짓(false
)을 리턴해야 한다. // ex1
const isEven = function (num) {
return num % 2 === 0;
};
let arr = [1, 2, 3, 4];
// let output = arr.filter(짝수);
// '짝수'를 판별하는 함수가 조건으로서 filter 메소드의 인자로 전달된다.
let output = arr.filter(isEven);
console.log(output); // ->> [2, 4]
// ex2
const isLteFive = function (str) {
// Lte = less then equal
return str.length <= 5;
};
arr = ['hello', 'code', 'states', 'happy', 'hacking'];
// output = arr.filter(길이 5 이하)
// '길이 5 이하'를 판별하는 함수가 조건으로서 filter 메소드의 인자로 전달된다.
let output = arr.filter(isLteFive);
console.log(output); // ->> ['hello', 'code', 'happy']
해당 배열의 각 요소(element)에 대해 함수(콜백 함수)를 한 번씩 호출하는 메소드(method)
// JS Code forEach 변경
const fruits = ['Apple', 'Orange', 'Kiwi'];
fruits.forEach(function(element, index){
console.log(`fruits[${index}] : ${element}`);
}
//fruits[0] : Apple
//fruits[1] : Orange
//fruits[2] : Kiwi
forEach는 this 를 매개변수로 제공하기 때문에, callback 을 전달받은 this의 값을 사용할 수 있다.
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(function(entry){
this.sum += entry
++this.count
}, this); //this 를 작성해 주어야 됩니다.
}
const obj = new Counter();
obj.add([24,87,1]);
console.log('count : ' + obj.count); // count : 3
console.log('sum : ' + obj.sum); // sum : 112
map은 하나의 데이터를 다른 데이터로 맵핑(mapping) 할 때 사용된다.
map이 필요한 경우,
const cartoons = [
{
id: 1,
bookType: 'cartoon',
title: '식객',
subtitle: '어머니의 쌀',
createdAt: '2003-09-09',
genre: '요리',
artist: '허영만',
averageScore: 9.66,
},
{
id: 2,
// .. 이하 생략
},
// ... 이하 생략
]; // 만화책의 모음
const findSubtitle = function (cartoon) {
return cartoon.subtitle;
}; // 만화책 한 권의 제목을 리턴하는 로직(함수)
const subtitles = cartoons.map(findSubtitle); // 부제의 모음
reduce는 초기값을 정할 수 있는데 값을 정하지 않는다면 배열의 가장 첫번째 값이 초기값이 된다.
이 초기값은 누적값의 기반이된다.
그 다음 요소부터 현재값이 되는데 이 둘은 누적값이 되며 반환된다.
만약 초기값을 정한다면, 배열의 첫번째요소가 바로 현재값이 된다.
reduce는 '만화책 식객 27권의 정보가 배열에 담겨있습니다. 각 단행본의 평점의 평균을 리턴하세요.'와 같이 여러 데이터를 하나의 데이터로 응축(reduce)할 때 사용된다.
reduce가 필요한 경우,
const cartoons = [
{
id: 1,
bookType: 'cartoon',
title: '식객',
subtitle: '어머니의 쌀',
createdAt: '2003-09-09',
genre: '요리',
artist: '허영만',
averageScore: 9.66,
},
{
id: 2,
// .. 이하 생략
},
// ... 이하 생략
]; // 단행본의 모음
const scoreReducer = function (sum, cartoon) {
return sum + cartoon.averageScore;
}; // 단행본 한 권의 평점을 누적값에 더한다.
let initialValue = 0 // 숫자의 형태로 평점을 누적한다.
const cartoonsAvgScore = cartoons.reduce(scoreReducer, initialValue) / cartoons.length;
// 모든 책의 평점을 누적한 평균을 구한다.
수도 코드
function joinName(resultStr, user) {
resultStr = resultStr + user.name + ', ';
return resultStr;
}
let users = [
{ name: 'Tim', age: 40 },
{ name: 'Satya', age: 30 },
{ name: 'Sundar', age: 50 }
];
users.reduce(joinName, '');
수도 코드
function makeAddressBook(addressBook, user) {
let firstLetter = user.name[0];
if(firstLetter in addressBook) {
addressBook[firstLetter].push(user);
} else {
addressBook[firstLetter] = [];
addressBook[firstLetter].push(user);
}
return addressBook;
}
let users = [
{ name: 'Tim', age: 40 },
{ name: 'Satya', age: 30 },
{ name: 'Sundar', age: 50 }
];
users.reduce(makeAddressBook, {});
(a,b) 형식으로 지정한 두 인자를 차례로 비교하여 배열을 정렬하는 메소드
내가 설정한 조건에 맞아떨어지는 것이 배열 안에 들어있는지를 확인할 수 있다.
return시킬 값을 설정하지 않으면, some()의 괄호 안에 넣어준 함수는 그 배열의 끝까지 계속해서 실행된다.
내가 설정한 조건에 대해서, 배열 안에 들어있는 것들이 전부 다 맞아떨어지는지를 확인할 수 있다.
return시킬 값을 설정하지 않으면, every()의 괄호 안에 넣어준 함수는 딱 한번만 실행된다.
본 세션의 이해를 돕기 위해 해당 부분은
코드스테이츠
의 기술 내용을 그대로 가져온다.
출처: Codestates Pre-Course
컴퓨터 공학의 근간을 이루는 여러 개념 중에 추상화(abstraction)가 있습니다. 추상화의 다른 말은 요약입니다. 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것이 추상화입니다. 사실 이 세상은 추상화로 가득차 있습니다. 인간은 추상화를 매우 좋아하고 그렇게 사고하는 것이 효율적이고 편하기 때문입니다.
예를 들어, 여러분들은 브라우저 창에 주소를 입력했을 때 정확히 무슨 일이 일어나는 지 전부 알지 못합니다. 그런 복잡한 것들은 가려져 있고 우리는 그저 주소창에 올바른 주소를 입력하면 브라우저가 해당 사이트를 보여준다는 것만 알고 있습니다. 여러분들이 스마트폰으로 카카오톡이나 페이스북 메신저를 통해 친구에게 'ㅇㅇ'이란 메세지를 보내면 그 순간 여러분들의 스마트폰은 기지국과 약 20개의 메세지를 주고 받습니다. 하지만 우린 이런 것들은 알지 못합니다. 알 필요도 없습니다. 하지만 입력창에 메세지를 입력하고 전송 버튼을 누르기만 하면 내 친구가 메세지를 받는다는 사실을 알고 있습니다. 자동차의 시동 버튼, 엑셀도 추상화고 지하철/버스 카드도 추상화의 결과입니다. 추상화가 아닌 것을 찾아보기 힘들 정도입니다.
자바스크립트(를 비롯한 많은 프로그래밍 언어) 역시 추상화의 결과입니다. 컴퓨터를 구성하는 장치(CPU)는 0과 1만 이해합니다. 크롬 개발자 도구의 콘솔(console)탭에서 아래 코드를 입력했을 때, 어떤 과정을 거쳐 10이 출력되는지 몰라도 10을 출력할 수 있습니다. 그런 복잡한 것들은 크롬의 자바스크립트 해석기(엔진)가 대신 해주기 때문입니다.
function sum(num1, num2) {
return num1 + num2;
}
const output = sum(3, 7);
console.log(output); // --> 10
컴퓨터의 내부 구조에 대한 고민이 해결되었기 때문에, 자바스크립의 문법(syntax)을 올바르게 사용하는 것만으로 다양한 프로그램을 (자바스크립트가 없었을 때) 보다 쉽게 작성할 수 있습니다. 이처럼 고민거리가 줄어들고, 그래서 문제의 해결이 더 쉬워지는 것이 추상화의 이점입니다.
추상화 = 생산성(productivity)의 향상
한편 프로그램을 작성할 때 자주 반복되어 사용되는 로직은 별도의 함수로 작성하기도 합니다. 이 역시 추상화의 좋은 사례입니다. 추상화의 관점에서 함수를 바라보면, 함수는 사고(thought) 또는 논리(logic)의 묶음입니다.
아래의 getAverage
함수는 수(number
)를 요소로 갖는 배열을 입력받아 요소의 평균값을 리턴합니다. 앞으로는 수를 요소로 갖는 배열을 인자로 전달하기만 하면 (조금? 복잡한 로직은 신경쓰지 않아도) 언제나 평균값을 얻게 됩니다.
function getAverage(data) {
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum = sum + data[i];
}
return sum / data.length;
}
let output = getAverage([1, 2, 3]);
console.log(output); // --> 2
output = getAverage([4, 2, 3, 6, 5, 4]);
console.log(output); // --> 4
함수를 통해 얻은 추상화의 단계를 한단계 더 높인 것이 고차 함수입니다. getAverage
함수는 값(배열)을 전달 받아, 이 값을 가지고 복잡한 작업을 수행합니다. 이는 값 수준에서의 추상화입니다.
함수 = 값을 전달 받아 값을 리턴한다 = 값에 대한 복잡한 로직은 감추어져 있다 = 값 수준에서의 추상화
고차 함수는 이 추상화의 수준을 사고의 추상화 수준으로 끌어 올립니다.
값 수준의 추상화: 단순히 값(value)을 전달 받아 처리하는 수준
사고의 추상화: 함수(사고의 묶음)를 전달 받아 처리하는 수준
즉, 고차 함수를 통해 보다 높은 수준(higher order)에서 생각할 수 있습니다.
고차함수 = 함수를 전달 받거나 함수를 리턴한다 = 사고(함수)에 대한 복잡한 로직은 감추어져 있다 = 사고 수준에서의 추상화
추상화의 수준이 높아진만큼 생산성도 비약적으로 상승합니다. 이를 예시를 통해 살펴 보겠습니다.
const data = [
{
gender: 'male',
age: 24,
},
{
gender: 'male',
age: 25,
},
{
gender: 'female',
age: 27,
},
{
gender: 'female',
age: 22,
},
{
gender: 'male',
age: 29,
},
];
위와 같이 주어진 데이터에 대해서 순차적으로 처리하고 싶은 작업들이 있을 때, 모든 작업을 하나의 함수로 작성할 수도 있습니다. 예를 들어 남성들의 평균 나이를 구한다고 할 때, 아래와 같은 함수를 작성할 수 있습니다.
function getAverageAgeOfMaleAtOnce(data) {
const onlyMales = data.filter(function (d) {
// data.filter는 배열의 각 요소에 인자로 전달 받은 함수를 적용하고,
// 그 결과가 true인 요소만을 갖는 배열을 리턴합니다.
return d.gender === 'male';
});
const numOfMales = onlyMales.length;
const onlyMaleAges = onlyMales.map(function (d) {
// onlyMales.map는 배열의 각 요소에 인자로 전달 받은 함수를 적용하고,
// 각 결과를 요소로 갖는 배열을 리턴합니다.
return d.age;
});
const sumOfAges = onlyMaleAges.reduce(function (acc, cur) {
// onlyMaleAges.reduce는 배열의 각 요소에 인자로 전달 받은 함수를 적용하고,
// 각 결과를 두 번째 인자로 전달 받은 초기값(0)에 누적한 결과를 리턴합니다.
return acc + cur;
}, 0);
return sumOfAges / numOfMales;
}
위에 제시된 getAverageAgeOfMaleAtOnce
함수는 배열 메소드를 적절하게 사용하여 순차적으로 원하는 작업을 수행합니다. 꽤 괜찮은 코드이지만, '남성'의 '평균 나이'만 구하는 작업에만 국한된 함수입니다. 개선점을 찾아보자면, 'male'을 매개변수화(parameterization)하여 조금 더 일반적인(generic) 함수로 변경할 수도 있습니다. 그래도 어디까지나 '남성' 또는 '여성'의 '평균 나이'를 구하는 작업에 그칩니다.
한편, filter
, map
, reduce
등의 배열 메소드는 다른 목적을 위해서 사용될 수도 있습니다. 예를 들어 '남성' 중 '최연소 나이'를 구하거나, '여성' 중 '최연소 나이와 최연장 나이의 차이'를 구할 때 이미 작성된 로직을 그대로 쓸 수 있습니다.
고차 함수를 통해 이를 쉽게 달성할 수 있습니다. 아래의 compose
함수는 입력받은 함수들을 순차적으로 결합하는 고차 함수입니다. 각각의 작업(filter, map, reduce)들은 별도의 함수로 분리되어, compose
의 인자로 전달되는 콜백 함수가 됩니다.
function getOnlyMales(data) {
return data.filter(function (d) {
return d.gender === 'male';
});
}
function getOnlyAges(data) {
return data.map(function (d) {
return d.age;
});
}
function getAverage(data) {
const sum = data.reduce(function (acc, cur) {
return acc + cur;
}, 0);
return sum / data.length;
}
function compose(...funcArgs) {
// compose는 여러 개의 함수를 인자로 전달받아 함수를 리턴하는 고차 함수입니다.
// compose가 리턴하는 함수(익명 함수)는 임의의 타입의 data를 입력받아,
return function (data) {
// funcArgs의 요소인 함수들을 차례대로 적용(apply)시킨 결과를 리턴합니다.
let result = data;
for (let i = 0; i < funcArgs.length; i++) {
result = funcArgs[i](result);
}
return result;
};
}
// compose를 통해 함수들이 순서대로 적용된다는 것이 직관적으로 드러납니다.
// 각각의 함수는 다른 목적을 위해 재사용(reuse)될 수 있습니다.
const getAverageAgeOfMale = compose(
getOnlyMales, // 배열을 입력받아 배열을 리턴하는 함수
getOnlyAges, // 배열을 입력받아 배열을 리턴하는 함수
getAverage // 배열을 입력받아 `number` 타입을 리턴하는 함수
);
const result = getAverageAgeOfMale(data);
console.log(result); // --> 26
이처럼 고차 함수를 통해 사고 수준에서의 추상화를 달성할 수 있습니다. 각각의 작업들은 다른 목적을 위해 재사용될 수 있습니다. 다른 함수를 작성하고, 새로운 작업들의 조합을 만들어 보시기 바랍니다.
고차함수의 사용에 대해 많은 깨우침을 준 강의 내용!
앞으로 코드를 짤 때 일회성에 그치는 코드가 아닌 재사용성과 generic한 스타일로 코드를 짤 수 있도록 노력해야겠다.
맵리듀스(MapReduce) 프레임워크는 대용량 데이터를 분산 처리하기 위한 목적으로 개발된 프로그래밍 모델이다. Google에 의해 고안된 맵리듀스 기술은 대표적인 대용량 데이터 처리를 위한 병렬 처리 기법의 하나로 최근까지 많은 주목을 받고 있다. 그림 1과 같이 맵리듀스는 임의의 순서로 정렬된 데이터를 분산 처리(Map)하고 이를 다시 합치(Reduce)는 과정을 거친다. 하둡(Hadoop)은 맵리듀스를 핵심 기반으로 하는 플랫폼 중 하나이다.
맵 리듀스는 2개의 복사본을 가지기 때문에 작업 당 장애를 마주하더라도 복사본을 사용하여 작업이 중지되지 않아 높은 내고장성을 지닌다.
클로저는 local scope, parental scope 및 global scope의 변수에 액세스할 수 있는 함수일 뿐이다. 클로저는 관심사를 분리하고 DRY한 코드를 유지하는 좋은 방법인데, 내부 환경이 포함되어 있어 특정 목적에 맞는 기능을 만들 수 있다.
반면에 커링은 f(a,b,c)를 f(a)(b)(c) 와 같이 다중 callable 프로세스 형태로 변환하는 기술이다. 보통 자바스크립트에서의 커링되어진 함수는 평소처럼 호출도 하고 만약에 인수들이 충분하지 않을 때에는 partial을 반환한다. 커링을 적용하면 인수 세 개의 범용 함수 log(date, importance, message)를 log(date)같이 인수가 하나인 형태나 log(date, importance)처럼 인수가 두 개인 형태로 호출할 수 있다.
또한, 커링을 이용하여 클로저로 만들어진 외부 함수의 인자를 재사용할 수도 있다.
// Imperative
const arrayContainsAnotherArray = (needle, haystack) => {
for(let i = 0; i < needle.length; i++) {
if(haystack.indexOf(needle[i]) === -1)
return false;
}
return true;
}
장점
단점
bugs
가 낄 수 있는 요소가 많다.(명시적이거나 시각적이지 못함)null
or undefined
vs -1)// Declarative
const arrayContainsOtherArray = (needle=[], haystack=[]) =>
needle.every(el => haystack.includes(el));
장점
true
, false
결과임을 바로 알 수 있다. 또한 반복문의 구조를 특별하게 명시할 필요가 없다.true
, false
결과임을 바로 알 수 있다. 또한 다른 것과 비교할 필요가 없다.단점
선언형 프로그래밍은 단점보다 장점이 더 많으며, 가독성이 향상될 것으로 예상된다.
다만, 성능에 미치는 영향은 또 다른 질문이며 특히 많은 데이터가 처리되는 경우 반드시 고려해야 한다. 성능에 큰 영향이 없다면 더 읽기 쉬운 코드베이스가 더 관리하기 쉬운 코드베이스라고 볼 수 있다..
함수의 조합은 기존의 함수를 재활용하기 때문에 유지보수 측면에서 뛰어나다.
function add2(num) {
return num+2;
}
function multiply3(num) {
return num*3;
multiply3(add2(5)); // 21
예시 코드와 같은 '함수의 조합'을 활용하여 위
Lesson - 왜 꼭 고차함수를 써야할까?(feat. Codestates)
중 추상화의 예시 코드가 한 단계 높은 차원의 코드를 나타내고 있으므로
는 관련 내용으로 본 세션을 대신한다.
boolean
이 할당된 변수는 is
혹은 are
을 붙여서 참 혹은 거짓임을 분명히 표현한다.class Anical() {
// ES6 부터 사용 가능한 class가 할당되는 변수는 첫 글자를 대문자로 적는다.
}
function Animal() {
// class 생성자 함수가 될 함수임으로 대문자로 적는다.
// new 키워드를 사용한 함수에 한해서 대문자를 쓰기도 함. class 역할을 하기 때문.
}
const MAX_ITEMS_IN_QUEUE = 100;
{}
는 생략하지 않는다.'
를 권장함."
와 구분하기 위함)\n
금지);
을 사용alert('hi');
if
, for
, while
문의 끝에는 세미콜론;
을 사용하지 않음.{}
로 끝나는 Statement는 이미 종료가 암시되어 있기 때문)===
, !==
사용?
)는 가독성이 좋고 간결한 경우에만 사용!
는 바로 앞에 스페이스 없이 붙여서 사용if(!isEqual){
}
||
사용// Good
return charSet.size > text.length;
// Bad
if(charSet.size < text.length) {
return false;
}
return true;
// 비교문은 무조건 true 혹은 false로 평가되기 때문에 장황하게 조건문을 작성하지 않음
// Good
alert('I chose to put no visual padding around this string');
// 괄호와 따옴표 사이 공백이 없음
alert( 'I chose to put visual padding around this string' );
// 괄호와 따옴표 사이 공백이 일관됨
// Bad
alert( 'I only put visual padding on one side of this string');
// 한 쪽에는 공백이 있고, 한 쪽에는 공백이 있음
// Good
let firstItem = getFirst();
let secondItem = getSecond();
let thirteenthItem = getThirteenth();
// Bad
let firstItem = getFirst();
let secondItem = getSecond();
let thirteenthItem = getThirteenth();
,
사이는 한 칸 띄어쓰기+
, -
, ===
등은 한 칸 띄어쓰기const MAX_ITEMS_IN_QUEUE = 100;