[ 자바스크립트의 스코프와 클로저 | 이민규 ]
[ Learn JavaScript Closures in 6 Minutes | Yazeed Bzadough ]
클로저를 어렴풋이 이해하고 있던 탓에, 아래 함수 foo()
에서 내부함수가 func
에 전달된 인자 arguments
를 어떻게 읽을 수 있는지 이해하지 못했다. 그래서 체크해둔 글들을 다시 보며 클로저의 개념을 정리했다.
function foo(func) {
return function() { // 내부함수
return func(...arguments); // 어떻게 func의 인자들이 여기로 전달되는가?
}
};
function foo(func) { // 함수 A (외부함수)
return function() { // 함수 B (내부함수) -> 함수 A의 환경을 참조
return func(...arguments);
}
};
const add = (x, y) => x + y;
const testFoo = foo(add);
testFoo(1,2); // 함수 B를 통해 함수 B가 참조하는 함수 A의 값 func에 접근
개념을 더 자세히 살피기 위해 다른 함수를 작성했다. 값을 곱하여 반환하는 함수 withInnerFunc
는 내부 함수를 가지고 있다. 내부함수를 withInnerFunc
에 속한 변수 num
에 2
를 전달한 상태로 multiply
에 대입한다. multiply
에는 withInnerFunc
의 환경이 그 시점으로 고정된다.
function withInnerFunc(num) {
return function (x) {
num = num * x;
return num;
}
};
const multiply = withInnerFunc(2); // num = 2가 기억된다.
매개변수 x
에 값을 던지면, num
은 x
만큼 증가하고, 그대로 기억된다.
function withInnerFunc(num) {
console.log(`클로저에 처음 기억되는 num : ${num}`);
return function(x) {
num = num * x;
console.log(`num : ${num}`);
return num;
}
};
const multiply = withInnerFunc(2);
// 클로저에 처음 기억되는 num : 2
multiply(4) // x : 4
// num : 8
multiply(6) // x : 6
// num : 48
multiply(8) // x : 8
// num : 384
다시 의문을 가졌던 함수 foo
로 돌아온다. foo
는 인자로 어떤 함수를 전달받아 내부함수에서 그를 실행시킨다.
내부함수가 func
의 값을 콘솔에 띄우도록 코드를 변경하고 test
를 실행시키면, 함수 add
가 출력된다. add
가 인자로 전달된 시점의 foo
의 환경을 클로저가 기억하고 있다.
function foo(func) {
return function() {
console.log(`func : ${func}`);
}
};
const add = (x, y) => x + y;
const testFoo = foo(add);
testFoo();
// func : (x, y) => x + y
내부함수가 func(...arguments)
의 값을 리턴하도록 코드를 다시 변경하고 testFoo()
를 호출하면, 변수 func
와 arguments
를 기억된 환경 안에서 찾아 값을 얻어내고 실행한다. 현재 func
는 외부 함수인 foo
에서 add
로 정의되어 있다. 그리고 arguments
는 testFoo()
의 ()
안에 들어가는 인자다.
그러므로 testFoo(1,2)
처럼 testFoo
를 실행시킬 경우, 1,2는 arguments
가 된다.
function foo(func) {
return function() {
return func(...arguments);
}
};
const add = (x, y) => x + y;
const testFoo = foo(add);
// testFoo는 func에 (x, y) => x + y 를 기억(참조)한 상태
testFoo(1,2);
// 1, 2는 arguments에 대입된다
// 전개 구문을 통해 x와 y로 1과 2가 전달되어 (1, 2) => 1 + 2 가 실행된다.
[ Array.prototype.sort() | MDN ]
sort()
메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환한다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따른다. 정렬 속도와 복잡도는 각 구현방식에 따라 다를 수 있다. 새로운 배열을 반환하지 않고, 원 배열을 변형한다.
배열 arr
에 메서드 sort()
를 사용하려면 다음과 같이 코드를 작성한다.
arr.sort([compareFunction])
정렬하는 기준을 설정하는 함수 compareFunction
을 전달하지 않으면 배열 arr
의 요소는 문자열로 변환되고, 유니 코드 코드 포인트 순서로 오름차순 정렬된다. 'apple'은 'banana'보다 앞에 온다.
sort
는 차례로 인자 a
와 b
를 전달받아 비교한다. 함수의 리턴값이 양수라면 a
를 b
의 뒤로, 음수라면 a
를 b
의 앞으로 보낸다. 0일 경우 그대로 둔다. 리턴하는 값에 따라 a
와 b
의 위치가 달라지므로, 비교값과 리턴값을 설정해 원하는 대로 값을 정렬시킬 수 있다.
function compare(a, b) {
if (a와 b를 비교. 오름차순이라면 a < b, 내림차순이라면 a > b) {
return -1;
}
if (a와 b를 비교. 오름차순이라면 a > b, 내림차순이라면 a < b) {
return 1;
}
// a와 b가 같은 경우
return 0;
}
문자열끼리도 비교 연산자로 비교할 수 있다.
'apple' > 'banana'
// false
'apple' < 'banana'
// true
sort()
메서드는 숫자 역시 먼저 문자로 변환한다. 문자로 변환된 '100'은 유니 코드 순서에서 '2'앞에 온다.
'2' > '100'
// true
const arr = [1, 2, 3, 100, 255, 376];
// 숫자를 문자열로 변환 후 비교되므로 유니코드 순서대로 정렬된다.
arr.sort();
// [1, 100, 2, 255, 3, 376]
그래서 숫자를 크기 순서대로 정렬하려면 compareFunction
자리에 함수를 전달해야 한다.
숫자를 오름차순으로 정렬하고 싶다면, 두 수의 차를 리턴하면 된다. 5와 3을 비교한다면 5 - 3 = 2로 리턴값이 양수이므로 a
인 5가 b
인 3의 뒤에 위치하게 된다.
function compareNumbers(a, b) {
return a - b;
}
const arr = [1, 2, 3, 100, 255, 376];
arr.sort(compareNumbers);
// [1, 2, 3, 100, 255, 376]
조건부 삼항 연산자는 JavaScript에서 세 개의 피연산자를 취할 수 있는 유일한 연산자이다. 보통 if 명령문의 단축 형태로 쓰인다.
배열과 숫자를 인자로 받아, 숫자만큼 뒤에서부터 잘라낸 새 배열을 반환하는 함수 last
를 작성하려고 한다.
function last(array, n) {
if (n === 0) {
return [];
} else if (n === undefined) {
return array[array.length - 1];
}
return array.slice(-n);
function last(array, n) {
return n === 0 ? []
: n === undefined ? array[array.length - 1]
: array.slice(-n);
};
변수에 new
연산자를 사용하여 Date
를 생성하면 현재 시간을 대입할 수 있다. 기준이 되는 시간과, 그보다 나중에 다가오는 시간을 저장하여 차를 구하면 두 시점 사이 경과된 시간을 측정할 수 있다.
function timer() {
const startTime = new Date();
return function () {
const stopTime = new Date();
return Math.floor((stopTime - startTime) / 1000);
}
}
const getSeconds = timer() // 시작 시간이 startTime에 저장된다
getSeconds(); // getSeconds를 선언한 후에 getSeconds를 실행하기까지 경과한 시간(초)
// 7
getSeconds();
// 18
getSeconds();
// 31