함수는 자신의 내부 슬롯 [[Environment]]에 자신의 정의된 환경, 상위 스코프의 참조를 저장함
const increase = (function () {
let num = 0;
// 클로저
return function () {
return ++num;
}
}());
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
const counter = (function() {
let num = 0;
// 객체 리터럴은 스코프를 만들지 않음
return {
increase() {
return ++num;
}
decrease() {
return --num;
}
}
}());
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0
const Counter = (function() {
let num = 0;
funtion Counter() {}
Counter.prototype.increase = function () {
return ++num;
}
Counter.prototype.decrease = function () {
return num > 0 ? --num : 0;
}
return Counter;
}());
const conter = new Counter();
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0
// 함수를 인수로 전달받아 함수를 반환하는 고차 함수
function makeCounter(aux) {
let counter = 0;
// 클로저
return function () {
counter = aux(counter):
return counter;
};
}
function inrease(n) {
return ++n;
}
function derease(n) {
return --n;
}
const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2
// decreaser는 increaser 함수와는 독립적인 렉시컬 환경을 가짐
const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2
function Person(name, age) {
this.name = name; // public
let _age = age; // private
// 인스턴스 메서드
this.sayHi = function () {
console.log(`Hi, My name is ${this.name}. I am ${_age}.`);
};
}
const me = new Person('Lee', 20);
me.sayHi(); // Hi, My name is Lee. I am 20.
console.log(me.name); // Lee
console.log(me._age); // undefined
const you = new Person('Kim', 30);
you.sayHi(); // Hi, My name is Kim. I am 30.
console.log(you.name); // Kim
console.log(you._age); // undefined
const Person = (function () {
let _age = 0; // private
// 생성자 함수
function Person(name, age) {
this.name = name; // public
_age = age;
}
// 프로토타입 메서드
Person.prototype.sayHi = function () {
console.log(`Hi, My name is ${this.name}. I am ${_age}.`);
};
// 생성자 함수를 반환
return Person;
}());
const me = new Person('Lee', 20);
me.sayHi(); // Hi, My name is Lee. I am 20.
console.log(me.name); // Lee
console.log(me._age); // undefined
const you = new Person('Kim', 30);
you.sayHi(); // Hi, My name is Kim. I am 30.
console.log(you.name); // Kim
console.log(you._age); // undefined
// _age 변수 값이 변경됨
me.sayHi(); // Hi, My name is Lee. I am 30.
// 클로저를 사용할 때 자주 발생하는 실수
// (var 키워드로 인해 발생하는 문제)
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function () { return i; };
}
for (var j = 0; j < funcs.length; j++) {
console.log(funcs[j]); // 3 3 3
}
var funcs = [];
for (var i = 0; i < 3; i++) {
// 매개변수 id를 중첩 함수(클로저)에서 참조함
funcs[i] = (function (id) {
// 클로저
return function () {
return id;
};
}(i));
}
for (var j = 0; j < funcs.length; j++) {
console.log(funcs[j]); // 0 1 2
}
// let 키워드로 선언한 변수는 블록 레벨 스코프를 가짐
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function () { return i; };
}
for (var j = 0; j < funcs.length; j++) {
console.log(funcs[j]); // 3 3 3
}
// 배열 요소로 추가된 함수들은 모두 클로저임
const funcs = Array.from(new Array(3), (_,i) => () => i); // [f, f, f]
// 배열의 요소로 추가된 함수들을 순차적으로 호출함
funcs.forEach(f => console.log(f())); // 0 1 2