JavaScript
20210704 JavaSciprt DeepDive 02 : 타입 변환, 단축평가, 옵셔널 체이닝(?.), null 병합 연산자(??), 객체 리터럴, 프로퍼티, 원시값 vs 객체, 복사, 함수(즉시 실행 함수, 재귀함수, 중첩함수, 콜백함수, 고차..
JavaScript Deep Dive 02
용어 및 중요사항 정리
타입 변환
타입 변환
: 기존 원시 값을 사용해 다른 타입의 새로운 원시값을 생성하는 것- 명시적 타입변환, 암묵적 타입 변환 둘다, 원시값은 변경 불가능한 값(imutable value)이라서 기존 원시값을 직접 변경하는 것은 아님 (부수 효과X, 재할당X)
암묵적 타입 변환(implicit coercion)
,타입 강제 변환(type coercion)
: 표현식을 평가하는 도중 자바스크립트 엔진에 의해 암묵적으로 변환되는 것
문자열 타입으로 암묵적 변환
- 문자열 연결 연산자로 (+)가 작용 하는 경우, 문자열 이외의 것들 문자열 타입으로 변환
- Type + '' -> 'Type'
- Symbol 타입의 경우에만 타입 에러 발생
- 템플릿 리터럴의 표현식 삽입의 경우, 삽입하는 표현식이 암묵적으로 문자열 타입으로 변환
- 문자열 연결 연산자로 (+)가 작용 하는 경우, 문자열 이외의 것들 문자열 타입으로 변환
숫자 타입으로 암묵적 변환
- 산술 연산자가 작용하는 경우, 피연산자는 모두 숫자이어야 함으로 암묵적으로 숫자타입으로 변환 (숫자로 변환 불가한 경우 NaN 반환)
- 비교 연산자가 작용하는 경우, 피연산자는 모두 숫자이어야 함으로 암묵적으로 숫자타입으로 변환
- 단항 연산자가 작용하는 경우, 피연산자를 숫자 타입값으로 암묵적으로 변환
- NaN을 반환 : 문자열, undefined, {}, [1, 2], function(){}
- type error를 반환 : symbol
불리언 타입으로 암묵적 변환
- 제어문(if, for 등..), 삼항 조건 연산자의 경우, 조건식의 평가 결과가 모두 불리언 타입으로 변환
- Truthy, Falsy : 불리언이 아닌 값을 참, 거짓으로 평가 되는 것
- Truthy: true, 'string', {}, []
- Falsy: false, undefined, null, 0, -0, NaN, ''
명시적 타입 변환
,타입 캐스팅
: 개발자가 의도적으로 값의 타입을 변환하는 것- 표준 빌트인 생성자 함수 호출 방법 (new 없이)
- 빌트인 메서드 사용하는 방법
문자열 타입으로 명시적 변환
:- String 생성자 함수 호출(new 없이)
- Object.prototype.toString 메서드 사용
- 문자열 열결 연산자 사용
// String 생성자 함수 String(1); // Object.prototype.toString (1).toString(); // 문자열 열결 연산자 1 + "";
숫자 타입으로 명시적 변환
:- Number 생성자 함수 (new없이)
- parseInt, parseFloat 함수(문자열만 가능)
- +단항 산술 연산자
- *산술연산자(이항)
// Number 생성자 함수 Number("0"); // parseInt parseInt("0") + "0"; // 단항 연산자 +"0"; // 산술 연산자 "0" * 1;
불리언 타입으로 명시적 변환
:- Boolean 생성자 함수 (new 없이)
- ! 부정 논리 연산자를 두번 사용하는 방법
// Boolean 생성자 함수 Boolean("a"); // !! 사용 (반대로 됬다 다시 반대로 되서 자신으로 돌아옴) !!"a";
단축 평가
단축평가
: 표현식 평가 도중에 평과 결과 확정시 나머지 평가 과정 생략하는 것논리연산자를 활용한 단축평가
: 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반환 하는 것true || anything; // true (바로, 확정나는 것이면 그 뒤로 평가 안함) false || anything; // anything (결정을 확정 하는 곳은 평가를 안하고 그대로 나옴) 1 || anything; // 1 true && anything; // anything false && anything; // false 0 && anything; // 0
논리연산자를 활용한 단축 평가 활용
- 단축 평가를 활용하여 if문을 대체 가능
- Truthy 값은 &&, Falsy값은 || 으로 if문 대체
- 단축 평가를 활용하여 if문을 대체 가능
- null, undefined인 자료가 객체인줄 알고 프로퍼티가 있다고 생각하고 참조하는 경우, error 발생 예방
- 주의) 단축평가의 경우 0 ,''은 객체로 평가 될 떄도 있음 (대체하여 옵셔널 체이닝 사용 가능)
var elem = null;
var value = elem && elem.value; // null
함수 매개변수에 기본값 설정
- 함수 호출시 인수 전달하지 않는 경우 해당 함수 실행시 발생할 에러 방지
// 논리연산자 단축 평가 function getStringLength(str) { str = str || ""; return str.length; } // es6 방식 function getStringLength(str = "") { return str.length; }
옵셔널 체이닝을 활용한 단축평가
옵셔널 체이닝 연산자(?.)
: 좌항의 피연산자가 null 또는 undefined인 경우 undefined를 반환, 그렇지 않으면 우항의 프로퍼티 참조를 이어감- 객체를 가르키기 기대하는 변수가 null, undefined 체크하고 프로퍼티 참조시 유용 (옵셔널 체이닝 or 단축평가)
- 0, ''의 경우에도 체크가 가능함
var elem = null;
// 옵셔널 체이닝 단축평가
var value = elem?.value;
console.log(value); // undefined
// 논리 연산자 단축평가
var value = elem && elem.value;
null 병합 연산자(??)
: 좌항의 피연산자가 null 또는 undefined인 경우 우항의 피연산자를 반환, 그렇지 않으면 좌항의 피연산자 반환- 변수에 기본값 설정시 유용
- null 병합 연산자의 경우 좌항 피연산자 falsy말고 정확히 null, undefined이어야 함
// null 병합 단축평가 var foo = null ?? "default string"; console.log(foo); // "default string" // 논리 연산자 단축평가 var foo = "" || "default string";
객체 리터럴
객체 타입
: 다양한 타입의 값을 하나의 단위로 구성한 복합적인 자료구조- 객체 타입은 변경 가능한 값임 (mutable value)
- 원시타입은 변경 불가 값임(immutable value)
- 0개 이상의 프로퍼티로 구성됨
- 프로퍼티: 객체의 상태를 나타내는 값으로, key(프로퍼티 키) - value(프로퍼티 값)로 구성됨
- 프로퍼티 값: 자바스크립의 모든 값이 가능함(일급 객체인 함수도 가능)
- 메서드(method): 프로퍼티 참조하고 조작할 수 있는 동작으로, 프로퍼티 값이 함수인 경우의 프로퍼티를 말함
- 자바스크립트에서 객체와 함수는 떨어 질수 없는 개념임
객체지향 프로그래밍의 객체
: 클래스와 인스턴스를 포함한 개념인스턴스
: 클래스에 의해 생성되어 메모리에 저장된 실체클래스
: 인스턴스를 생성하기 위한 템플릿
클래스 기반 객체 지향 언어
: new 연산자와 함께 생성자(constructor)를 호출하여 인스턴스 생성방식으로 객체 생성프로토타입 기반 객체 지향 언어(javascript)
: 다양한 객체 생성방법을 지원- 객체 리터럴, Object 생성자 함수, 생성자 함수, Object.create 메서드, 클래스(ES6)
객체 리터럴
: 객체 생성 방법중 가장 일반적이고 간단한 방법, { }사용, 변수할당 시점에 객체 리터럴 해석해서 객체 생성- 코드블록과 다르게 객체 리터럴은 값으로 평가되는 표현식이므로, 세미콜론을 붙임
- 객체를 생성하고, 동적으로 추가도 가능
프로퍼티
: 객체는 프로퍼티의 집합, 프로퍼티는 키-값으로 구성- , 쉼표로 구분하여 프로퍼티 나열
프로퍼티 키
: 프로퍼티 값에 접근할 수 있는 식별자로, 빈 문자열을 포함하는 모든 문자열 또는 심벌 값을 사용할 수 있음- 식별자 네이밍 규칙에 유효한 이름인 경우 따옴표 생략 가능, 유효하지 않으면 따옴표로 묶어야 함
- 프로퍼티 키를 동적으로 생성하는 경우 key 표현식에 [] 대괄호로 묶어야 함
- 문자열, 심벌 값 외의 값은 암묵적으로 문자열로 타입 변환됨
- 중복된 프로퍼티 키인 경우 덮어 씀
프로퍼티 값
: JS의 모든 값
프로퍼티 접근
:- 마침표 표기법 : 객체 표현식.프로퍼티 키
- 대괄호 표기법 : 객체 표현식['프로퍼터 키']
- 대괄호 표기법시, 프로퍼티 키는 무조건 따옴표로 감싼 문자열
- 숫자로 이뤄진 문자열 따옴표 생략 가능
- 식별자 네이밍 규칙 준수 하면 둘다 사용 가능, 준수 하지 않으면 대괄호 표기법만 가능
- 존재하지 않는 프로퍼티에 접근시 undefined 반환
프로퍼티 값 갱신
: 이미 있던 프로퍼티에 값을 할당하면 갱신됨프로퍼티 동적 생성
: 없는 프로퍼티에 값 할당시 동적으로 프로퍼티 생성 되고 값도 할당됨프로퍼티 삭제
: delete 연산자로 특정 프로퍼티 삭제 가능 (delete 프로퍼티 접근 표현식)- 없는 값을 삭제하면, 에러 없이 무시됨
객체 리터럴 확장 기능
:프로퍼티 축약 표현
: 프로퍼티 값으로 변수 사용하는 경우 변수와 프로퍼티 키가 동일한 이름일 경우 프로퍼티 키 생략 가능
let x = 1, y = 2; const obj = { x, y }; // {x: 1, y: 2}
계산된 프로퍼티 이름(computed property name)
:- ES5는 외부에서만, ES6에서는 객체 리터럴 내부에서도 계산된 프로퍼티 이름으로 프로퍼티 키 동적 생성 가능
메서드 축약 표현
:- 프로퍼티 값으로 함수를 넣어 메서드를 만드는 경우, function 키워드 생략한 축약 표현 사용 가능
const obj = { name: "kim", hi() { console.log("hi!" + this.name); }, };
원시 값과 객체 비교
차이점 | 원시 | 객체 |
---|---|---|
값 변경 | 변경 불가 값 | 변경 가능 값 |
값 저장 | 실제 값 저장 | 참조값 저장 |
값 복사 전달 | 값에 의한 전달 | 참조에 의한 전달 |
변경 불가능한 값
: 한번 생성된 원시 값은 읽기전용 값으로 변경 불가하다(변수 값 변경(상수, 변수) 이야기가 아님, 값 자체를 이야기 함)- 재할당시, 새로운 메모리 확보 -> 값을 할당 -> 변수의 메모리 참조 주소 변경
문자열
: 원시 타입이며 문자의 집합으로 문자(character) 1개당 2바이트의 공간이 필요함- 유사 배열 객체로 이터러블이라서 배열과 유사하게 각문자에 접근 가능함
- 하지만, 문자에 접근해서 해당 문자만 다른 값으로 할당하여 변경 불가하다. (변수에 새로운 문자열 재할당은 가능)
값에 의한 전달
: 원시 값을 갖는 변수(A)를 다른 변수(B)에 할당하면, 할당 받는 변수(B)에 원시 값이 복사되어 전달됨 (새로운 메모리에 값만 복사됨, 새로운 메모리 주소를 사용하게 됨)- A, B는 다른 메모리 공간에 저장된 별개의 값임 ->A, B의 변화는 서로에게 영향을 주지 않음
- 파이썬의 경우에는 새로운 메모리 주소가 아닌 같은 메모리 주소를 사용하며 한쪽에 다른 값을 재할당 했을 때, 새로운 메모리 공간에 재할당 되어 비로서 새로운 메모리 주소를 사용하게 됨
객체
: 객체의 경우 내부 구성요소를 동적으로 변화 시킬 수 있기 때문에, 메모리 공간 크기를 사전에 정해 둘 수 없음 -> 프로퍼티 접근이 비효율 적임 -> V8 엔진은 히든 클래스 방식으로 개선함해시테이블, 연관배열, map, dictionary, lookup table
: key-value 구조의 자료 구조
변경 가능한 값
: 객체 타입의 값이며, 재할당 없이 객체를 직접 변경할 수 있음참조 값
: 생성된 객체가 저장된 메모리 공간의 주소- 식별자(객체 저장 공간 주소가 저장된 메모리 주소) -> 참조값(실제 객체가 저장된 메모리 공간 주소) -> 객체가 저장된 메모리
- 객체를 할당한 변수를 참조하면 메모리에 저장된 참조 값을 통해 실제 객체에 접근
- 여러개의 식별자가 하나의 객체를 공유 가능함
얕은 복사
: 객체의 한단계 까지만 복사하는 것 (서로 다른 메모리를 사용하게 하기 위해)깊은 복사
: 객체에 중첩되어 있는 객체까지 모두 복사하는 것 (서로 다른 메모리를 사용하기 위해서), 원시타입의 값이 복사 되는 것을 이야기 하기도 함(원시타입의 경우 얕은 복사가 발생하지 않음으로)- lodash의 cloneDeep을 이용하여 가능함
참조에 의한 전달
: 객체를 가르키는 변수를 다른 변수에 할당하는 경우 참조 값이 복사되어 전달- 메모리 주소는 다르지만, 같은 참조 값을 가짐 (두개의 실별자가 하나의 객체를 공유)
- 참조값은 그대로 이고, 객체가 실제 저장된 곳의 데이터가 변경됨으로 공유하게 됨(서로 영향을 주고 받음)
- 참조에 의한 전달, 값에 의한 전달 모두 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 것은 동일 -> 값에 의한 전달임
- 다른 언어에는 pointer가 존재하기 때문에 참조에 의한 전달이 딱 맞게 떨어지지만,자바스크립트의 경우에는 pointer가 없어 살짝 다른 개념임
- 값의 비교하는 비교 연산자의 경우 정확하게 메모리가 아닌 값을 보고 비교함
var person1 = {
name: "kim",
};
var person2 = {
name: "kim",
};
console.log(person1 === person2); // false 참조값(메모리 주소)이 다르므로
console.log(person1.name === person2.name); // true kim, kim 으로 값이 같으므로
함수
함수(function)
: 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것- 함수 정의를 통해 함수를 생성함 -> 함수 리터럴도 평가되어 객체 값을 생성함 (함수 = 호출 가능한 객체)
- 함수의 사용 -> 코드의 재사용성 향상, 유지보수 평의성 향상, 코드의 신뢰성 향상
매개변수(parameter)
: 함수 내부로 입력을 전달 받는 변수인수(argument)
: 함수 내부로 받는 입력반환값(return value)
: 함수를 거쳐 외부로의 출력하는 값, 함수 실행뒤 반환되는 값함수호출(function call/invoke)
: 인수를 매개변수로 함수에 전달하여 함수를 명시적으로 실행지시하는 것 (함수 이름으로 호출하는 것이 아닌 함수 객체를 가르키는 식별자로 호출)
함수 리터럴
: 함수를 생성하기 위한 기호 방식- function키워드
- 함수이름 : 식별자(네이밍 규칙 준수), 함수 몸체 내에서만 참조 가능한 식별자, 생략 가능함(기명, 무기명 함수)
- 매개 변수 목록 : 소괄호로 감싸고 쉼표로 구분, 매개 변수 목록은 순서의미가 있음, 몸체내에서 변수와 동일 취급(네이밍 규칙 준수)
- 함수 몸체: 함수 호출시 일괄 실행되는 문들이 있는 코드블록, 함수 호출시 실행
함수 정의
: 함수 호출하기 전 매개변수, 실행문, 반환할 값 등을 지정하는 것함수 선언문
- 표현식이 아닌 문
- 함수 이름 생략 불가
- 함수 호이스팅 O (런타임 전에 식별자 암묵적 생성 -> 함수 객체 할당)
- 함수 선언문과 함수표현식의 형태는 똑같음 하지만 변수에 할당하냐 안하냐에 차이 (자바스크립트 엔진이 상황에 따라서 문인지 표현식인지 알아서 해석)
- 선언문의 경우 암묵적으로 외부에서 함수 식별자를 몸체 내에서만 사용할 수 있는 함수 이름으로 함
function add(x, y) { return x + y; } add(1, 2); // 3 (암묵적)식별자 add , 함수 이름 add
함수 표현식
- 표현식인 문
일급 객체
: 함수를 값처럼 자유럽게 사용가능- 변수에 할당되는 값이 함수 리터럴인 문
- 함수 이름생략 가능
- 함수 호이스팅 X, 변수 호이스팅O (함수 리터럴은 런타임에 할당문 실행 시점에 평가됨)
- 리터럴의 경우 함수 이름은 몸체 내에서만 참조 가능한 식별자
- 외부에서는 함수를 가르키는 식별자가 없음
const add = function foo(x, y) { return x + y; }; add(1, 2); // 3 식별자로 호출 foo(1, 2); // Reference Error 함수 이름으로 호출 시도 했기 때문 (function bar() { console.log("bar"); }); bar(); // Reference Error 리터럴이므로 외부에서는 함수를 가르키는 식별자가 없음 // 식별자 없음 , 함수 이름 bar
변수 호이스팅 vs 함수 호이스팅
- 런타임전 둘다 식별자 생성하고 초기화가 다름
- 변수는 undefined로, 함수는 함수 객체로 초기화
생성자 함수
: 객체를 생성하는 함수
Fucntion 생성자 함수
- 일반적이지 않은 함수 생성 방법
- 다른 함수 생성방식에 의한 함수와 다르게 동작함 (클로저를 생성하지 않음)
const add = new Function("x", "y", "return x+y");
화살표 함수
- function 키워드 대신 화살표(=>)를 사용해 간략한 방법으로 함수 선언(항상 익명 함수)
- 생성자 함수로 사용 불가
- this 바인딩 방식이 다르고
- prototype 프로퍼티가 없음
- arguments 객체를 생성하지 않음
const add = (x, y) => x + y;
함수 호출
: 함수 식별좌와 한쌍의 소괄호인 함수 호출 연산자로 호출- 함수 호출시 실행 흐름을 중단하고 호출된 함수로 실행 흐름을 옮김
인수
: 함수를 호출 할때 지정, 표현식이어야 함, 개수와 타입 제한 없음매개변수
: 함수 정의할 때 선언, 몸체 내부에서 변수와 동일 취급 (마찬가지로 undefined로 초기화 된 이후 인수가 순서대로 할당), 스코프는 함수 내부- 매개변수는 적을 수록 좋고 함수는 하는일이 가급적 작게 만들어야 함
- 매개변수 3개 사용 권장, 많이 들어가게 되면 1개의 인수를 객체로 넣게 하자
- 인수의 개수와 매개변수 개수가 일치하지 않아도 에러가 발생하지 않음
- 매개변수 개수 > 인수개수 인경우, 할당 되지 않은 매개변수는 undefined
- 매개변수 개수 < 인수개수 인경우, 넘치는 인수는 암묵적으로 arguments 객체 프로퍼티로 보관됨
arguments 객체
: 매개변수 개수를 확정 할 수없는 가변 인수 함수를 구현시 사용인수 확인
: 인수의 타입, 인수의 전달 개수 확인 필요 -> 잘못된 사용 방지- typeof를 활용한 조건식으로 에러처리 또는 typescript로 컴파일 에러
- arguments 객체로 인수 전달 개수 확인 가능
- 인수가 전달되지 않은 경우 기본값 할당 (단축 평가 방식, 매개변수 기본값 방식)
반환문
: return 키워드 및 표현식으로 이루어진 반환문을 사용해 실행 결과를 외부로 반환 가능- 함수 호출은 표현식으로 함수 내부 반환문의 실행 결과로 평가되어 나타남
- 역할1) 함수 실행 중단하고 몸체를 빠져 나옴 (이후의 코드는 실행 X)
- 역할2) return 키워드 뒤에 오는 표현식을 평가해 반환 (표현식이 없는 경우, undefined가 반환됨)
- 반환문 생략시 암묵적으로 undefined를 반환
- 전역에서 반환문 사용시 SyntaxError발생 (nodeJS환경에서는 에러 발생X)
매개변수에 값을 전달하는 방식
:값에 의한 호출(값에 의한 전달)
: 인수가 primitive인 경우, 값 자체가 복사 되어 원본 훼손X (부수 효과X)참조에 의한 호출(참조에 의한 전달)
: 인수가 obj인 경우, 참조값이 복사 되어 원본 훼손O (부수 효과O)- 외부의 값을 변경하기 때문에 외부 상태가 변하는지 상태 확인이 어려움
- ->
옵저버 패턴
또는불변 객체
활용하여 해결
옵저버 패턴
: 객체가 변경되었을 때 객체를 참조를 공유하는 모든 이들에게 변경사실 통지불변 객체
: 객체를 변경 불가능하게 만들어 상태 변경을 봉쇄- 상태변경 필요시, 객체의 방어적 복사 (깊은 복사)를 통해서 새로운 객체를 생성하고 재할당 해서 교체
순수 함수
: 외부 상태에 의존하지 않고, 변경하지 않는 함수 (부수효과X)- 동일 인수 전달시 언제나 동일 값 반환
- 반환한 결과를 재할당을 해줘야 상태가 변경되어 상태 변경 추적이 좋음
비순수 함수
: 외부 상태에 의존하고, 변경하는 함수 (부수 효과O)- 외부 상태에 따라 반환값이 달라지는 함수
- 외부 상태를 직접 참조하거나, 객체를 전달 받은 경우 비순수 함수임
- 실행하면 상태가 변경 되기 때문에, 언제 실행 했는지 잘 모르면 변경 추적이 힘듦
함수형 프로그래밍
: 순수 함수와 보조함수의 조합을 통해 부수 효과 최대한 억제하여 안정성, 불변성(immutability)을 높이는 프로그래밍
즉시 실행 함수(IIFE)
: 함수 정의와 동시에 즉시 호출되는 함수(단 한번 호출되고 재호출 불가)- 익명함수 활용하여 뒤에 () 호출 연산자 붙여줌
- 무조건 그룹 연산자로 감싸야 함(감싸지 않으면 syntax Error)
- 그룹 안에서는 기명함수를 쓰면 error
- 그룹 안쓰고 밖에서 익명함수 쓰면 함수 선언문으로 인식해서 error
- 그룹 안쓰고 밖에서 ()호출 연산자 붙이면 구룹 연산자로 인식해서 error
- 그룹 연산자로 함수 리터럴을 묶으면 함수리터럴을 먼저 평가하여 함수 객체를 생성하기 때문에 이외의 연산자를 사용할 수 있음
- 일반 함수 처럼, 값 반환, 인수 전달도 됨
var res = (function () { var a = 3; var b = 5; return a * b; })(); console.log(res); // 15 res = (function (a, b) { return a * b; })(3, 5); console.log(res); // 15
재귀함수
: 함수 자기 자신을 호출(재귀 호출)하는 함수- 반복 처리를 위해 사용함
- 함수 표현식의 경우 함수 이름, 함수 식별자로도 재귀 호출 가능
- 외부에서 함수 호출은 무조건 함수 식별자로 해야함
탈출 조건
(탈출 조건 없으면 스택 오버플로 에러 발생)
외부함수(outer function)
: 중첩함수를 포함하는 함수중첩함수/내부 함수(nested function/inner function)
: 함수 내부에 정의된 함수- 중첩 함수는 외부 함수 내부에서만 호출 가능
- 자신을 포함하는 외부 함수를 돕는
헬퍼 함수(helper function)
역할
ES6 함수 정의 위치
: 문이 위치할 수 있는 문맥 어디든지 (최상위, 다른 함수 내부, if, for 등의 코드블럭 안에서)- 단, if, for문에서 사용하는 것은 바람직 하지 않음
콜백 함수(Callback function)
: 변경되는 함수의 로직을 구현하기 위해서, 함수의 매개변수를 통해 다른 함수의 내부로 전달 되는 함수- 고차함수를 돕는 헬퍼함수 역할
- 고차함수에 의해 호출됨
- 비동기 처리, 배열 고차함수에 활용되는 중요한 패턴임
고차함수(Higher-Order Function)
: 매개변수를 통해 함수의 외부에서 콜백함수를 전달받은 함수- 콜백함수를 자신의 일부분으로 합성함
- 필요에 따라 콜백함수에 인수를 전달 할 수 있음
- 주의) 전달시 함수 자체를 전달해야함, 호출을 전달하는 것이 아니라
- 콜백함수의 형태는 보통 익명함수로 전달(해당 함수가 많이 반복 된다면, 미리 callback을 외부에서 만들어 전달)
- 콜백함수는 고차함수가 호출 될때마다 생성됨