
외부함수의 변수에 접근할 수 있는 내부함수 또는 이런 작동원리
function outerFn(){
let outerVar = 'outer';
console.log(outerVar);
function innerFn(){
// 이 함수가 클로저 함수
let innerVar = 'inner';
console.log(innerVar);
}
return innerFn; //함수자체를 return
}
outerFn(); // ???
클로저 함수 안에서는 지역변수, 외부함수의 변수, 전역변수 모두에게로 접근 가능하다.
출력결과
맨 위의 console.log에서 outer가 콘솔에 찍히고
아직 실행되지 않은 함수innerFn(){ };자체가 함께 리턴되어 나온다.
다음의 경우 각각 콘솔에 어떻게 찍힐까???
outerFn()(); //출력결과 1
let innerFn = outerFn(); //출력결과 2
innerFn(); //출력결과 3
출력결과 1 : outer inner
출력결과 2 : outerFn(); outer와 innerFn(){ }이므로 둘 다 변수에 할당된다
콘솔에서는 outer만 찍힌다
innerFn;로 변수를 조회해보면 outer와 실행되지 않은 함수가 나온다
출력결과 3 : inner
함수를 parameter로 받는다 ⇨ callback
함수자체를 return한다 ⇨ closure
함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어 각각 인자를 받게 하는 방법
function adder(x){ //함수를 생성하는 함수
return function(y){
return x + y;
}
}
adder(5); //일반함수를 리턴하고 아무런 숫자를 주지 않는다
adder(2)(3); // 2는 x의 인자로, 3은 y의 인자로 받아 5리턴
커링을 적용하려면 변수에 adder함수를 할당한다
let add100 = adder(100); //변수 add100은 x=100이지만 함수만 반환하는 변수
add100(2); // y값에 2를 지정한 후 실행하면 102리턴
add100(10); // 110
let add5= adder(5);
add5(2); // 7
클로저함수의 유용한 점
x의 값을 고정해놓고 재사용할 수 있다.
외부 함수의 변수를 내부 함수가 계속 쓰면서 템플릿처럼 사용할 수 있다.
클로저 모듈 패턴 : 변수를 scope 안쪽에 가두어 함수 밖으로 노출시키지 않는 방법
function makeCounter(){
let privateCounter = 0;
return { //리턴값이 객체 let obj={{},{},{}}; return obj;한 것과 같다
increment : function (){ //객체에 대한 key값을 function으로
privateCounter++; //외부함수의 변수를 내부함수에서 사용중
},
decrement : function (){
privateCounter--;
},
getValue : function (){
return privateCounter;
}
}
}
let counter1 = makeCounter();
//counter1 변수가 하나 생김
//counter1; 함수가 담겨있는 객체
counter1.increment();
counter1.iecrement();
counter1.getValue(); //???
// increment를 2번하면 privateCounter가 1씩 두번 늘어나면서 출력값 = 2
privateCounter라는 변수는 함수바깥에서 privateCounter; 이런식으로 호출하거나 접근하는 것이 불가능하다.
따라서 increment나 decrement같은 함수로 간접적으로 바꿀 수가 있다.
counter1.getValue();가 2일때
let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue(); //??? 출력값 1
counter1과 counter2는 서로 값에 영향을 주지않는다.
왜냐하면 둘은 각각 독립적으로 private counter를 가지고 있기 때문에 재사용 가능해진다. : 클로저 모듈 패턴
class를 바탕으로 instance를 만드는 프로그래밍 패턴
하나의 모델이 되는 청사진 = class
function Car (color){ }
그 청사진을 바탕으로 한 객체 = instance
let avante = new Car ('blue');
let mini = new Car ('cyan');
let beetles = new Car ('red');
prototype
= 원형객체(original form)
청사진을 만들 때 쓰는 객체가 prototype 객체
ex) Car.prototype.메소드
constructor
instance가 초기화될 때 실행하는 생성자 함수
ex)
this.brand = brand;
this.name = name;
this.color = color;
이 부분이 constructor에 해당한다.
this
함수 실행 시, 해당 scope마다 생성되는 고유한 실행 컨텍스트context
new 키워드로 instance를 생성했을 때에는 해당 instance가 바로 this의 값
ES5 vs ES6
함수이름의 첫글자를 대문자로
ES5
function Car(brand, name, color){
인스턴스가 만들어질 때 실행되는 코드
}
ES6
class Car(){
constructor (brand, name, color) {
}
}
ES6에서 사용하는 Rest parameter 및 ES5의 방법인 arguments 키워드를 이용할 수 있다
function Car(brand, name, color){
};
let avante = new Car ('hyundai', 'avante', 'blue');
let mini = new Car ('bmw', 'mini', 'cyan');
let beetles = new Car ('volkswagen', 'beetles', 'red');
avante; // Car{}
// constructor: ƒ Car(brand, name, color)
각각의 instance는 Car라는 class의 고유한 속성과 메소드를 갖는다. (상속)
class, 객체지향 javascript를 쓰는 이유는??
현실세계에 있는 것을 기반으로 프로그래밍 모델로 만들 때에 유용하기 때문이다.
객체지향에는 속성과 메소드가 있다.
속성과 메소드 : class에는 속성과 메소드를 정의만 하고 사용은 instance에서 한다.
function Car( brand, name, color ){
this.brand = brand; this.name = name;
this.color = color;
}
class Car() {
constructor( brand, name, color ) {
this.brand = brand;
this.name = name;
this.color = color;
}
}
this 키워드를 이용해서 this에 정의한 속성 brand,name,color을 집어넣는다.
let avante = new Car ('hyundai', 'avante', 'blue');
let mini = new Car ('bmw', 'mini', 'red')
avante.brand;하면 "hyundai"가 찍힌다.mini.color;하면 "red"Car가 갖는 성격들을 동일하게 갖고있지만 성격, 즉 속성값은 각각 다르다.
Car.prototype.drive = function(){
//drive라는 method는 avante,mini둘 다 갖는다
console.log(this.model + '출발!!!');
}
refuel(){ };
drive(){ };
mini.drive();
//mini출발!!! , drive는 함수이므로 ()를 써서 실행시킨다
avante.drive();
//avante출발!!!
해당하는 속성들을 갖고 위처럼 사용할 수 있다.
let avante = new Car ('hyundai', 'avante', 'blue');
avante.color; // 'blue'
avante.drive; // avante출발!!
let arr = ('code', 'states', 'pre');
//배열을 정의하는 것은 Array instance를 만들어내는 것과 동일
//따라서 let arr = new Array('code', 'states', 'pre'); 도 동일
arr.length; // 3
arr.push('course'); // 새 element추가
function getMaxNum(...nums){
console.log(nums);
// [3, 5, 8, 10] 배열이 찍힌다.
}
getMaxNum(3, 5, 8, 10);
function getMaxNum(){
console.log(arguments);
// {0:3, 1:5, 2:8, 3:10} 유사배열, 배열아님
}
getMaxNum(3, 5, 8, 10);
전달인자를 받은 arguments{ 0:3, 1:5, 2:8, 3:10 } 가 배열같아 보이지만 배열이 아니다.
유사배열, 배열 method 사용할 수 없다.
Default Parameter
Default Parameter의 값으로는 문자열/숫자/객체 등 어떤 타입이든 가능하다.
function getRoute(destination , departure='ICN'){
return '출발지: ' + departure + ', ' +'도착지: ' + destination;
}
getRoute('PEK'); //'출발지: ICN, 도착지: PEK'
function getRoute(destination='ICN' , departure){
return '출발지: ' + departure + ', ' +'도착지: ' + destination;
}
getRoute(undefined, 'PEK'); //'출발지: ICN, 도착지: PEK'
// 중간에 매개변수 기본값을 넣어주면 undefined를 넘겨줬을 때 기본값으로 처리