"외부 함수의 변수에 접근할 수 있는 내부 함수"
"자신이 선언 될때의 맥락을 기억하는 함수"
// case 1
const addNum = x => y => x+y;
addNum(3)(6); // 9
// case 2
const outer = function(x){
let outVar = 'hello ';
return function(y) {
return outVar+x+y;
}
}
outer('smith')('👋'); // 'hello smith👋'
/*
outer함수가 호출될때, 반환값인 이름없는 함수가 호출
반환 함수의 내부에 outVar와 x 변수에 대한 정보가 없으므로,
상위 스코프인 outer함수에 선언된 outVar와 전달받은 x에 접근하여 데이터를 받아옴
*/
함수의 리턴값으로 함수를 반환하는 형태를 가지며, 변수의 선언할 때의 스코프를 기준으로 내부 스코프는 외부 스코프의 데이터에 접근 가능하다는 개념을 활용
함수 실행이 끝난 후, 함수에 정의된 변수가 저장되어 사용 가능
: 스코프 범위에 의해 메모리가 저장되어 접근 가능한 상태
-> 현재 스코프에서 사용해야하는 변수에 대한 정보가 없다면 상위 스코프에서 변수에 대한 정보를 찾음(어휘적 환경을 기준으로 변수를 조회)
외부에서는 접근만 가능할 뿐 수정 불가 -> 캡슐화, 정보 은닉
cf.
함수 호출 환경 ≠ 함수 선언 환경(어휘적lexical 환경)
: 자바스크립트는 함수 선언할 때의 변수 범위를 바탕으로 호출할 때 인식
외부 함수의 실행이 종료되어도 클로저 함수(내부 함수)에 의해 외부함수의 변수 데이터가 유지되어 접근 가능
-> 메모리에 저장
호출된 환경의 스코프(외부 스코프A)에서는 외부 함수의 변수(내부 스코프A)에 접근 할 수 없지만, 내부 함수인 클로저(내부 스코프B)를 사용하면 외부 함수(외부 스코프B)에 접근하여 간접적으로 수정이 가능
-> 전역 변수 사용으로 인한 side effect 최소화 + 데이터 보호
/* 함수의 리턴값을 객체로 선언(객체의 벨류값은 함수) */
const makeCounter = () => {
let value = 0;
return {
add: () => {
value = value +1;
},
subtract: () => {
value = value -1;
},
current: () => value
}
}
const counter = makeCounter();
counter; // {add: ƒ, subtract: ƒ, current: ƒ}
새로운 변수에 외부함수를 할당하여 모듈처럼 사용할 수 있음
const counter1 = makeCounter();
counter1.add();
counter1.current(); // 1
const counter2 = makeCounter();
counter2.subtract();
counter2.subtract();
counter2.current(); // -2
/* counter1 과 counter2는 독립적인 형태 */
모듈화
: 함수를 하나의 독립적인 부품처럼 분리하는 개념
-> 데이터, 메서드를 각 모듈에 따로 저장
클로저 사용 예시
/* 태그 메이커 : 데이터 보존의 특성 활용 */
const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
const h1Maker = tagMaker('h1');
h1Maker('Closure') // '<h1>Closure</h1>'
클로저 사용 주의사항
남발할 경우 과도한 데이터 누적으로 퍼포먼스 저하가 발생
: 일반 함수의 경우 실행 종료 후 가비지 컬렉션에 의해 불필요한 메모리가 정리되는데, 클로저에 의해 참조되므로 정리대상에서 제외됨
cf. 자바스크립트 메모리 관리 : 참조가 되지 않는 객체는 가비지 컬렉션에 의해 메모리 할당이 해제
ECMAScript : JavaScript의 표준
cf. 최신버전은 ES2019이지만 ES6(2015년)에 가독성과 유지보수를 높이기 위한 문법이 많이 추가됨
ex. let, const 키워드 추가, 템플릿 리터럴 추가 등
전개 구문으로 배열이나 객체의 요소를 분해하여 전달하거나 추가할 때 사용
변수 앞에 ...
을 적용하여 표현
배열에 spread 사용하기
/* 배열 합치기 */
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = ['하나', '둘',...arr1, ...arr2, '셋'];
console.log(arr1); // ['하나', '둘', 0, 1, 2, 3, 4, 5, '셋']
/* 배열 복사 */
let arr1 = [1, 2, 3];
let arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4);
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]
/* 원본에 영향을 주지 않는 문법 */
객체에 spread 사용하기
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
let clonedObj = { ...obj1 };
let mergedObj = { ...obj1, ...obj2 };
console.log(clonedObj); // {foo: 'bar', x: 42}
console.log(mergedObj); // {foo: 'baz', x: 42, y: 13}
/* key가 동일할 때는 덮어씌워짐 */
스프레드 사용 예시
/* arr1을 [4,5,6,1,2,3]으로 만들기*/
let arr1 = [1,2,3];
let arr2 = [4,5,6];
// case 1. 스프레드 사용
arr1.unshift(...arr2);
console.log(arr1); // [4, 5, 6, 1, 2, 3]
// case 2. 배열의 forEach 사용
arr2.reverse().forEach((num) => {
arr1.unshift(num);
});
console.log(arr1); // [4, 5, 6, 1, 2, 3]
/*
arr2.forEach((num) => {arr1.unshift(num);});
console.log(arr1); // [6, 5, 4, 1, 2, 3]
*/
나머지 매개변수로 함수의 매개변수를 배열의 형태로 받아서 사용
마지막 매개변수 앞에 ...
적용
파라미터의 개수가 정해져있지 않을 때 활용
/* 매개변수와 나머지 매개변수 적용 : ...etc의 위치는 가장 마지막 */
function func(a,b,...etc){
console.log("a", a);
console.log("b", b);
console.log("etc", etc);
}
func(1,2,3,4,5,6,7,8,9);
// a 1
// b 2
// etc (7) [3, 4, 5, 6, 7, 8, 9]
/*
func(); 일때 etc는 빈배열 []로 출력
*/
/* 나머지 매개변수만 적용 */
function func1(...etc){
console.log(etc);
}
func1(1,2,3,4,5,6,7,8,9); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
자바스크립트는 정의된 parameter와 호출시 전달된 argument의 개수가 다르더라도 오류가 발생하지 않음
-> 나머지 매개변수 사용 권장function func(fst, second){ console.log(`첫번째 : ${fst}, 두번째 : ${second}`); } func('하나', '둘'); // 첫번째 : 하나, 두번째 : 둘 func('하나'); // 첫번째 : 하나, 두번째 : undefined func('하나','둘','셋','넷'); // 첫번째 : 하나, 두번째 : 둘
/* 나머지 매개변수 적용 */ function func(...nums){ console.log(nums); } func(); // [] func('하나', '둘'); // ['하나', '둘'] func('하나'); // ['하나'] func('하나','둘','셋','넷'); // ['하나', '둘', '셋', '넷']
cf. 나머지 매개변수 vs Arguments
구조분해할당으로 배열이나 객체의 요소를 분해하고 변수에 각각 할당하는 과정
-> 스프레드 문법으로 해체 + 할당
/* 선언과 함께 할당 */
let {a, b} = {a:1, b:2};
let [a, b] = [1, 2];
/* 선언 없는 할당 */
let a, b;
({a, b} = {a:1, b:2}); // 객체의 경우 -> (할당문);
[a, b] = [1, 2];
/*
a는 1이 할당되고 b는 2가 할당
*/
/* 배열 */
const [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(`a: ${a}, b: ${b}, rest : ${rest}`); // 'a: 10, b: 20, rest : 30,40,50'
console.log(rest); // [30, 40, 50]
/* 객체 */
const {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
console.log(`a: ${a}, b: ${b}, rest : ${rest}`); // `a: 10, b: 20, rest : [object Object]`
console.log(rest); // {c: 30, d: 40}
구조분해할당 예시
var a, b;
[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7
function f() {
return [1, 2, 3];
}
var [a, , b] = f();
console.log(a); // 1
console.log(b); // 3
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) // 'jdoe is John'
실행될 때의 환경을 기억하여, 실행이 종료된 외부함수의 변수로 접근할 수 있는 내부 함수를 의미
-> 함수를 리턴하는 것이 클로저 X
cf. 일반적인 함수의 내부에 선언된 변수는 함수 종료 후 사용할수 없음
cf. 고차함수 : 함수를 받아 인자로 받아 실행하거나 다른 함수를 리턴하는 함수
let outer = function(){
let otVar = {};
return function inner(val){
if(otVar[val]){ // otVar에 대한 정보 -> 외부 함수의 변수 사용
return true;
}
return false;
}
}
/* inner함수가 클로저 함수(outer함수 X) */
조건 ? (true시 실행할 부분) : (false시 실행할 부분)
box.style.display = !isShow ? 'none' : 'block'; isShow = !isShow;
/* 일반 조건문 */
if(!isShow){
box.style.display = "none";
isShow = true;
} else {
box.style.display = "block";
isShow = false;
}
let x;
typeof x; // 'undefined'
x = x + 20;
console.log(x); // NaN
typeof x; // 'number'
x= x+'20'
console.log(x); // 'NaN20'
typeof x; //'string'
입력받은 arguments 중에서 가장 큰 값을 반환하는 메서드
Math.max(num1, num2, ... , numN);
// number 타입의 매개변수 1개 이상
// Math.max();은 -Infinity 반환
arr = [1,4,2,7,10]
Math.max(arr); // NaN
Math.max(...arr); // 10
느낀점
오늘 javascript koans 과제하는데 긴장했던 것에 비해 수월하게 풀었다. 예전에 배열 이전의 코플릿 문제에서처럼 해결을 못하지 않을까 걱정했던것이 무색할 정도로 차근차근풀다보니 금방 해결되었다. 그리고 오늘 퀴즈나, 콘즈 푸는데 혼자 공부하는것이 얼마나 비효율적인지 알게 되었달까? 독학시기에 es6문법이나 클로저등 생활코딩에서도 보고 드림코딩엘리강의도 보긴했었더라... 강의 보고 아~ 그렇구나에서 끝내서 기억이 안났을 거다. 문제도 풀어보고, 내가 잘못 알고있던 부분은 없는지 체크해보지 못해서 기억이 유지가 안됐을터. 게다가 문제 풀고 왜 맞고 왜 틀렸는지 체크해보는 과정에서 더 개념이 확고해지는 것 같다. 아까 호이스팅 관련 문제도, 호이스팅 서치 잠시 해보고 문제 풀면서 살짝 했갈렸던 부분을 페어한테 설명해주다보니 적어두었던 궁금증도 해결이 되었다. 물론 더 알아봐야겠지만 확실히 상대에게 설명해주는 과정은 내 학습에도 엄청나게 도움이 된다.
개선점 및 리마인드
**