함수와 함수가 선언된 어휘적 환경의 조합.
클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
MDN은 클로저를 아래와 같이 정의.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). - mdn (2023)
"lexical" 이란 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지를 고려한다는 것을 의미.
function init() {
var name = "Mozilla" // name은 init에 의해 생성된 지역변수
function displayName() { // displayName() 은 내부 함수이며, 클로저다.
alert(name); // 부모 함수에서 선언된 변수를 사용
}
displayName();
}
init(); // "Mozilla"를 알림창으로 실행
displayName()함수가 부모함수인 init()함수의 지역변수 var name을 가져다 씀.
따라서 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다.
중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.
주로 배열을 풀어서 인자로 전달하거나, 배열을 풀어서 각각의 요소로 넣을 때에 사용.
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
sum(...numbers)
파라미터를 배열의 형태로 받아서 사용할 수 있다. 파라미터 개수가 가변적일 때 유용.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
sum(1,2,3)
sum(1,2,3,4)
let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
2.배열 복사
let arr = [1, 2, 3];
let arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4); // 참고: spread 문법은 기존 배열을 변경하지 않으므로(immutable), arr2를 수정한다고, arr이 바뀌지 않는다.
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
let clonedObj = { ...obj1 };
let mergedObj = { ...obj1, ...obj2 };
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a);
console.log("b", b);
console.log("manyMoreArgs", manyMoreArgs);
}
myFun("one", "two", "three", "four", "five", "six");
const [a, b, ...rest] = [10, 20, 30, 40, 50];
const {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
function whois({displayName: displayName, fullName: {firstName: name}}){
console.log(displayName + " is " + name);
}
let user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
whois(user)
화살표 함수는 ES6문법,
function 키워드 사용해서 함수를 만든 것보다 간단히 함수를 표현할 수 있다.
// 매개변수가 없는 경우
var foo = () => console.log('bar');
foo(); // bar
// 매개변수가 하나인 경우
var foo = x => x;
foo('bar'); // bar
// 매개변수가 여려개인 경우
var foo = (a, b) => a + b; // 간단하게 한줄로 표현할 땐 "{}" 없이 값이 반환.
foo(1, 2); // 3
var foo = (a, b) => { return a + b };
foo(1, 2); // 3
var foo = (a, b) => { a + b }; // "{}"를 사용했는데 return이 없을 때
foo(1, 2); // undefined
var foo = (a, b) => { // 여러줄 썼을 때
var c = 3;
return a + b + c;
}
foo(1, 2, 3) // 6
/*
"{}"를 사용하면 값을 반환할 때 return을 사용해야함.
"{}"를 사용하지 않으면 undefied를 반환.
"{}"을 사용할 때는 여러줄을 썼을 때 사용.
*/
// 객체를 반환할 때
var foo = () => ( { a: 1, b: 2, c: 3 } );
foo(); // { a: 1, b: 2, c: 3 };
콜백 함수에서도 사용할 수 있다.
// ES5
var numbers = [1, 4, 9];
var oddArr = numbers.filter(function (x) { return x % 2 !== 0;});
console.log(oddArr); // [1, 9]
// ES6
var numbers = [1, 4, 9];
var oddArr = numbers.filter( x => (x % 2) !== 0 );
console.log(oddArr); // [1, 9]
일반 함수와 화살표 함수의 차이점이 있다. 일반함수가 전역 컨텍스트에서 실행될 때 this가 정의, 화살표함수는 this를 정의하지 않는다.
함수의 내부함수, 콜백함수에 사용되는 this는 window이다.