함수 정의하기
함수 호출하기
함수의 인수
재귀 함수
프로그램의 평가와 실행과정
클로저
이름 공간
객체로서의 함수
고차 함수
콜백 함수
ES6부터 추가된 함수 기능
function square(x) {return x*x;}
var square = function(x) {return x*x;}
Function()
생성자로 정의 var square = new Function("x", "return x*x");
var square = x => x*x;
Function
생성자, 화살표 함수 표현식으로 정의한 함수는 그 함수의 참조를 할당해야만 사용할 수 있음.신기한 점은 자신을 둘러 싼 외부 함수의 인수와 지역 변수에 접근할 수 있다는 점이다. 이 규칙은 클로저의 핵심적인 구성요소가 된다.
function func1(arg1) {
var a = 1;
function innerFunc() {
console.log(arg); // 외부 함수의 인수에 접근 가능
console.log(a); // 외부 함수의 변수에 접근 가능
}
}
함수 호출
var s = square(5);
메서드 호출
obj.m = function() {};
obj.m();
new
키워드를 추가하면 함수가 생성자로 동작한다. var obj = new Object();
call
, apply
를 사용한 간접 호출 익명함수의 뒤에 인수를 넘기며 곧바로 실행
(function(a,b) {console.log(a,b)})(1,2);
반대로 함수 정의시에 작성된 인자 개수보다 더 많은 개수의 인수를 넘길 수도 있다.
// 생략된 인수는 undefined
function f(x, y) {
console.log(x, y);
}
f(2); // x=2, y=undefined
// 인자 초기값 설정하는 방법
function multiply(a, b) {
b = b || 1;
return a * b;
}
multiply(2, 3); // -> 6
multiply(2); // -> 2
||
는 왼쪽 피연산자가 true
로 평가되면 왼쪽 피연산자를 반환하고, 왼쪽 피연산자가 false
로 평가되면 오른쪽 피연산자를 반환한다.b
가 undefined
가 되고, false
이므로 b=1
이 된다.=
연산자를 통해 기본값을 지정한다.(파이썬의 그것과 동일)arguments
변수가 지역변수로 있다.arguments
변수의 값은 Arguments
객체이다. (배열 객체가 아닌 유사 배열 객체)argments
에 저장된다.length(인수 개수)
, callee(현재 실행되고 있는 함수의 참조)
프로퍼티를 가지고 있다.arguments 의 요소의 값을 바꾸면 함수의 입력된 값이 바뀐다.
function f(a, b) {
arguments[1] = 3;
console.log(a, b);
}
f(1, 2); // -> x = 1, y = 3
arguments 변수를 활용하면 인수 개수가 일정하지 않은 가변 인수 함수를 정의할 수 있다.
function myConcat(separator) {
var s = "";
for(var i=1; i<arguments.length; i++){
s += arguments[i];
if(i<arguments.length-1) s += separator;
}
return s;
}
console.log(myConcat("/", "apple","orange","peach"));
eval()
함수를 말함.this
가 된다.자바스크립트 코드의 유효 범위 안에 있는 식별자와 그 식별자가 가리키는 값을 키와 값의 쌍으로 묶어(바인드하여) 렉시컬 환경 컴포넌트에 기록한다.
환경 레코드 (Environment Record)
window
를 생성한 다음, 전역 환경의 객체 환경 레코드에 전역 객체의 참조를 대입한다.이를 의사 코드로 표현시 다음과 같다.
// 전역 환경
전역 환경 = {
객체 환경 레코드: {
bindObject: window
},
외부 렉시컬 환경 참조: null
}
// 전역 실행 문맥
실행 문맥 = {
렉시컬 환경 컴포넌트: 전역 환경,
디스 바인딩 컴포넌트: window
}
// 따라서 전역의 this는 (클라이언트측 자바스크립트의 전역 객체)
this === window // -> true
Window
객체가 전역 객체 이므로 객체 환경 레코드의 bindObejct
프로퍼티에는 Window
의 참조가 할당되어, 전역 환경의 변수와 함수를 Window 안에서 검색하게 된다.Window
의 참조가 할당되어 this
가 Window
를 가리키게 되고, this
의 프로퍼티는 window
의 프로퍼티가 된다.추가적으로, window 객체는 다음과 같은 프로퍼티를 기본적으로 가지고 있다.
분류 | 프로퍼티 |
---|---|
전역 프로퍼티 | undefined , NaN , Infinity |
생성자 | Object() , String() , Number() 등 |
전역 함수 | parseInt() , parseFloat() , isNaN() 등 |
내장 객체 | Math , JSON , Refect , console 등 |
var
를 이용해 선언한 전역 변수들은 전역 환경의 객체 환경 레코드의 프로퍼티로 추가된다.undefined
가 된다.이처럼 자바스크립트의 모든 변수를 객체의 프로퍼티로 간주하면 쉽게 이해할 수 있다.
끌어올림(hoisting) 현상의 실체
- 최상위 레벨에 선언된 변수와 함수는 프로그램을 평가하는 시점에서 환경 레코드에 추가됨. - 최상위 레벨에 선언된 함수와 변수는 이미 환경 레코드에 추가된 상태이기 때문에 코드 어느 위치에서도 참조가 가능한 것. - 그래서 함수안의 함수에서 상위 함수의 변수, 전역 변수에 접근할 수 있는 것.
var
문과 함수 선언문으로 선언한 전역 변수는 [[Configurable]]
프로퍼티가 false
로 설정되어 있어 delete
연산자로 삭제할 수 없다.[[configurable]]
프로퍼티의 값이 true
가 되어 delete
연산자로 삭제가 가능하다.렉시컬 환경
을 생성한다,렉시컬 환경
은 환경레코드
를 가지고 있고, 환경 레코드 안에는 현재의 함수 안에서 선언된 변수, 중첩함수를 기록한다. (함수의 인자, 함수 안에서 중첩된 하위 함수의 참조, 함수 내부의 지역변수(var
로 선언된))선언적 환경 레코드
에는 인수 값이 설정되고, 없다면 undefined
로 설정된다.this 바인딩 컴포넌트
에 함수를 호출한 객체의 참조(전역 함수인경우 전역 객체) 의 참조가 저장된다.this 값은 함수가 호출되었을 때 그 함수가 속해 있던 객체의 참조 이며 실행 문맥의 디스 바인딩 컴포넌트가 참조하는 객체이다.
var tom = {
name: "Tom",
sayHello: function() {
console.log("Hello " + this.name);
}
};
tom.sayHello(); // -> Hello Tom
sayHello()
메소드는 tom 객체에 속한 메소드이므로, 해당 실행 문맥의 경우 this
바인딩 컴포넌트가 가리키는 객체가 tom
이다. 따라서 this
값이 "Tom"
이 된다.tom.sayHello()
의 값은 함수의 참조이다. 따라서 다음이 가능하다
// huck 객체를 생성
var huck = {name: "huck"};
// 함수의 참조를 대입
huck.sayHello = tom.sayHello;
huck.sayHello(); // -> Hello huck
sayHello
함수가 속한 객체가 huck
이므로 this
바인딩 컴포넌트가 가리키는 객체가 tom
에서 huck
으로 바뀌어 this
값이 huck 객체를 가리킨다.window
자바스크립트의 식별자 결정은 좀 더 안쪽 코드에 선언된 변수를 사용한다는 규칙을 가진다.
var a = "a";
function f(){
var b = "b";
function g(){
var c = "c";
console.log(a + b + c);
}
g();
}
f();
속박 변수
라고 한다.자유 변수
라고 한다.닫힌 함수
, 자유 변수를 가지고 있는 함수를 열린 함수
라고 한다.자바스크립트의 경우 식별자를 찾을 때, 외부 렉시컬 환경의 참조를 따라가는 방식을 취한다.
// 콜스택 상 가장 위의 함수 g 의 렉시컬 환경
함수 g 의 렉시컬 환경: {
선언적 환경 레코드 : {
c: "c";
},
외부 렉시컬 환경 참조 : 함수 f 의 렉시컬 환경
}
// 함수 g가 가리키고 있는 f 의 렉시컬 환경
함수 f 의 렉시컬 환경: {
선언적 환경 레코드 : {
b: "b";
},
외부 렉시컬 환경 참조 : 전역 의 렉시컬 환경
}
// 함수 f가 가리키고 있는 전역 의 렉시컬 환경
전역 의 렉시컬 환경: {
Object 환경 레코드 : {
bindObejct:{
a: "a";
}
},
외부 렉시컬 환경 참조 : null
}
scope chain
(ECMAScript 3 기준의 명명) 이라고 한다.사용하지 않는 객체란 다른 객체의 프로퍼티와 변수가 참조하지 않는 객체를 말한다.
var p = {x:1, y:2};
console.log(p); // -> Object {x:1, y:2}
// p 가 {x:1, y:2} 객체의 참조를 가지고 있음
// p 에 null 로 참조를 해제
p = null; // {x:1, y:2} 를 참조하지 않음. -> 메모리 해제
자기 자신이 정의된 환경에서 함수 안에 있는 자유 변수의 식별자 결정을 실행
하는 기능과 그 기능을 구현한 자료 구조의 모음이라고 할 수 있다.말로 보면 어렵고, 앞서의 예를 통해 살펴보겠다.
var a = "a";
function f(){
var b = "b";
function g(){
var c = "c";
console.log(a + b + c);
}
g();
}
f();
g
가 정의된 렉시컬 환경은 함수 g
를 둘러싸고 있는 바깥 영역 전체.g()
함수가 실행 되는 경우, 이 렉시컬 환경 안에서 자유 변수 a
와 b
의 식별자 결정을 한다.f
를 호출하면, 함수 f
의 렉시컬 환경이 생성된다.g
의 선언문을 평가해 함수객체를 생성한다. 이 렉시컬 환경에는 함수g
의 코드, 함수f
의 선언적 환경 레코드(변수 b
가 들어 있다.), 전역 객체의 참조(변수 a
가 들어 있다.)g
를 호출하면, 함수 g
의 렉시컬 환경이 생성되고, console.log(a + b + c)
가 실행되자 마자 함수 g
의 외부 렉시컬 환경 참조(scope
)를 따라 체인처럼 거슬러 올라가 변수 a와 b의 값을 참조한다.a
와 b
의 식별자 결정을 위한 자료구조이다f
가 호출되어 함수 g
가 평가되는 시점에 생성된다.이 예에서, 함수 g
의 함수 객체가 있는 동안에는 클로저는 가비지 컬렉션의 대상이 되지 않는다. 따라서, 함수 g
의 함수객체가 있는 한 클로저는 메모리에서 사라지지 않는다.
전형적인 클로저 사용의 예시
function makeCounter() {
var count = 0;
return f;
function f() {
return count++;
}
}
var counter = makeCounter();
console.log(counter); // -> 0
console.log(counter); // -> 1
console.log(counter); // -> 2
// 함수가 끝이났는데도 지속적으로 메모리에 남아있는 지역변수.
// makeCounter의 참조인 counter 가 남아있는 한, 클로저는 사라지지 않는다.
// 외부로부터 은폐되어 있고, 중첩함수의 안에서만 읽거나 쓸 수 있다.
// makeCounter가 새로이 호출되면 클로저가 새롭게 생성되어 초기화된다.
// f 의 참조인 counter 를 통해 함수를 사용할 시, 클로저가 새롭게 생성되는 것이 아닌 내부함수가 사용된다.
makeCounter
는 중첩 함수 f
의 참조를 반환한다.f
는 외부 함수 makeCounter
의 지역 변수 count
를 참조한다.counter
가 참조한다.makeCounter
의 렉시컬 환경 컴포넌트를 전역 변수 counter
가 f
의 함수 객체로 간접적으로 참조하게 되므로 가비지 컬렉션의 대상이 되지 않는다. (함수를 반환하는 함수 - makeCounter
, 그것의 참조값을 가진 변수 - 전역변수counter
)makeCounter
실행이 끝나도 makeCounter
의 렉시컬 환경 컴포넌트가 메모리에서 지워지지 않는다. 그렇기 때문에 지역 변수이자 makeCounter
렉시컬 환경 컴포넌트의 환경 레코드 프로퍼티중 하나인 counter
역시 메모리에서 지워지지 않는다.counter
는 클로저의 내부 상태로 저장된다. 또한 지역 변수이기 때문에 외부에서 읽거나 쓸 수 없다.counter
는 객체의 프로퍼티, f
함수는 객체의 메써드에 해당한다고 볼 수 있다. 객체의 경우 외부에서 프로퍼티를 읽고, 쓰기가 가능하지만 클로저의 경우 외부로부터 숨겨진 상태이다.캡슐화된 객체
라고 할 수 있다.여러 개의 내부 상태와 메서드를 가진 클로저
function makePerson(name, age) {
var _name = name;
var _age = age;
return { // 메서드를 여러 개 가진 객체를 반환
getName: function() {return _name;},
getAge: function() {return _age;},
setAge: function(x) {return _age = x;},
};
}
var person = makePerson("Tom", 18);
console.log(person.getName()); // -> "Tom"
console.log(person.getAge()); // -> 18
person.setAge(26);
console.log(person.getAge()); // -> 26
함수 팩토리 : 다양한 매개변수를 받는 함수를 여러 개 생성할 수 있다.
function makeMultiplier(x) {
return function(y) {
return x * y;
};
}
// 클로저를
var multi2 = makeMultiplier(2);
var multi10 = makeMultiplier(10);
console.log(multi2(3)); // -> 6
console.log(multi10(3)); // -> 30
잘못된 예 : 반복문 안에서 클로저 만들기
<script>
window.onload = function () {
var elm = document.getElementsByTagName("input");
for(var i=0; i<elm.length; i++) {
elm[i].onclick = function() {
console.log(i);
}
}
}
</script>
<body>
<input type="button" value="0">
<input type="button" value="1">
<input type="button" value="2">
</body>
onclick
이벤트 처리기로 등록한 함수 세 개가 바깥에 있는 변수 i
를 참조하는 클로저가 되었기 때문이다.onclick
이벤트 처리기가 실행되는 시점에는 for
문의 실행이 끝나있기 때문에, 클로저가 공유하는 변수 i
의 값이 3 인 상태이다.let
을 사용하여 변수를 선언하면 올바르게 이용 가능하다.전역 유효 범위의 오염을 방지하기 위한 수단.
전역 스코프를 오염시킨다
고 말한다.이름 공간
이다.예시
var myApp = || {};
// myApp이 이미 정의되어 있을 때는 그것을 사용,
// 정의되지 않은 경우는 빈 객체 생성
// 사용하고자 하는 변수와 함수를 프로퍼티로 추가
myApp.name = "Tom";
myApp.showName = function() {};
// 부분 이름 공간으로 사용할 수 있다.
myApp.view = {};
myApp.control = {};
라이브러리를 읽어들여 라이브러리 안의 함수 및 변수를 사용할 때에 충돌을 피하기 위해 전체 프로그램을 즉시 함수 안에 넣어 실행한다.
var x = "global"
(function() {
// 프로그램을 작성한다
var x = "local"
console.log(x);
})(); // -> "local"
console.log(x); // -> "global"
전형적인 모듈 정의의 예이다.
var Module = Module || {}; // 객체 이름공간 생성
(function (_Module) {
var name = "NoName"; // private 변수
function getName() {
return name;
} // private 함수
_Module.showName = function() {
console.log(getName()); // 프라이빗 함수를 사용하는 퍼블릭 함수
}; // public 함수
_Module.setName = function() {
name = x;
}; // public 함수
})(Module);
Module.setName("Tom");
Module.showName(); // -> Tom
showName
메서드는 getName
을 참조하고 있으며, setName
메서드는 name
을 참조하고 있다.name
과 지역함수 getName
은 클로저의 내부 상태로 저장된다.자바스크립트에서 함수도 객체의 일종이다. 따라서 함수는 값을 처리할 수 있고, 프로퍼티와 메서드를 가지고 있다.
일급 객체
라고 한다. 일급 객체인 함수는 일급 함수
라고 한다.프로퍼티 이름 | 설명 |
---|---|
caller | 현재 실행 중인 함수를 호출한 함수 |
length | 함수의 인자 개수 |
name | 함수를 표시할 때 사용되는 이름 |
prototype | 프로토타입 객체의 참조 |
프로퍼티 이름 | 설명 |
---|---|
apply() | 선택한 this와 인수를 사용하여 함수를 호출한다. 인수는 배열 객체 |
bind() | 선택한 this와 인수를 적용한 새로운 함수를 반환한다. |
call() | 선택한 this와 인수를 사용하여 함수를 호출한다. 인수는 쉼표로 구분한 값이다. |
constructor | Function 생성자의 기초 |
toString() | 함수의 소스 코드를 문자열로 만들어 반환 |
apply()
, call()
예제
function say(greetings, honorifics) {
console.log(greetings + " " + honorifics + this.name);
}
var tom = {name: "톰 하디"};
var jackson = {name: "마이클 잭슨"};
say.apply(tom, ["hello", "Mr."]) // hello Mr.톰 하디
say.apply(jackson, ["hi!", "Mr."]) // hi! Mr.마이클 잭슨
say.call(tom, "hello", "Mr.") // hello Mr.톰 하디
say.call(jackson, "hi!", "Mr.") // hi! Mr.마이클 잭슨
bind()
function say(greetings, honorifics) {
console.log(greetings + " " + honorifics + this.name);
}
var tom = {name: "톰 하디"};
var sayToTom = say.bind(tom);
sayToTom("Hello", "Mr."); // hello Mr.톰 하디
bind() 메서드를 통해 반환된 함수는 항상 인수로 받은 객체를 this 로 한다.
Memoization
: 함수를 호출했을 때의 인수값과 반환값을 한쌍으로 만들어 저장해두는 기법예시
function fib(n) {
if (n>2) return n;
// fib 함수객체에 n프로퍼티가 없는 경우
if(!(n in fib)) {
// 함수의 프로퍼티로 값을 저장
fib[n] = fib(n-1) + fib(n-2);
}
return fib[n];
}
for(var i=0;i<=20;i++) {
console.log((" "+i).slice(-2) + ":" fib(i));
}
// 0:0
// 1:1
// 2:1
// ...(중략)
// 19:4181
// 20:6765
고차 함수란 함수를 인수로 받는 함수 또는 함수를 반환하는 함수
apply()
, map()
, 파이썬의 map
과 같은 예시를 볼 수 있다.예시
// 수열을 표시하는 프로그램
var digits = "";
for(var i=0; i<10; i++) {
digits += i;
}
console.log(digits); // -> 0123456789
// 무작위 알파벳을 표시하는 프로그램
var randomChars = "";
for(var i=0; i<8; i++) {
randomChars += String.fromCharCode(Math.floor(Math.random() * 26) + "a".charCodeAt(0));
}
console.log(randomChars); // dizohqsf : 무작위 알파벳 뭉치
위의 예제들은 하는 일은 다르지만 사용하는 로직이 같다. 따라서 공통 부분을 고차 함수로 만들어 사용할 수 있다.
function joinStrings(n, f) {
var s = "";
for (var i=0; i<n; i++) {
s += f(i);
}
return s;
}
만들어진 고차함수 joinStrings
를 사용해서 똑같은 두 작업을 하는 함수를 생성하면 다음과 같다.
// 수열표시함수
var digits = joinString(10, function(i) {return i;});
// 무작위 알파벳 표시 함수
var randomChars = joinString(8, function(i) {return String.fromCharCode(Math.floor(Math.random() * 26) + "a".charCodeAt(0));});
커링
이란 인수 두 개 이상 받는 함수를 분해하여 인수가 하나인 함수의 중첩 함수로 변환하는 작업.말로 보면 어려우니 코드로 보자
// 커링된 add()
// 부분적인 인자의 목록을 받는다.
function add(x, y){
var old_x = x, old_y = y;
if (typeof old_y === 'undefined') {
// 부분적인 적용
return function (new_y) { return old_x + new_y; }
}
// 전체 인자를 적용
return x + y;
}
console.log(add(3, 5));
console.log(add(10)(10));
var add10 = add(10);
console.log(add10(20));
자바스크립트의 경우 콜백 함수를 매우 자주 사용한다.
자바스크립트의 함수는 일급 객체
이며 다른 함수에 인수로 넘겨질 수 있다. 다음과 같이 다른 함수에 인수로 넘겨지는 함수를 콜백 함수
라고 부른다
f(g, ...);
function f(callback, ...) {
...
callback();
...
}
f
의 인수로 넘겨진 함수 g
가 콜백함수이다. 이를 통해 함수 f
는 함수g
에 대한 제어권을 가질 수 있다.이벤트 처리기는 특정 이벤트가 발생 했을 때 실행하도록 등록하는 함수인데, 여기에서 사용되는 것이 콜백함수이다.
button.onclick = function() { ... };
// 또는 addEventListener 메서드를 사용
button.addEventListener("click", function () {...});
// 타이머 메서드의 경우에도 콜백함수를 인수로 받는다.
setInterval(function() { ... }, 2000);
함수 리터럴과 100% 같은 것은 아니므로 주의가 필요하다.
var square = function(x) { return x*x; };
// 화살표 함수 표현식으로 작성하면
var square = (x) => {return x*x;};
// 인수가 여러개라면
var f = (x, y, z) => { ... };
// 인수가 없다면
var f = () => { ... };
// 인수가 하나라면 인수묶는 괄호는 생략가능
var f = x => { ... };
// 즉시 실행 함수(IIFE)로 사용할 수 있다.
((x, y) => {return x*y})(1, 2); // -> 2
this
의 값이 함수를 정의할 때 결정된다. (함수 리터럴의 경우 호출시 정의됨.)arguments
변수가 없다.new
연산자를 붙여서 호출할 수 없다.)yield
키워드를 사용할 수 없다....args
의 추가arguments
프로퍼티를 통해 접근했다. 하지만 이는 list가 아니고 유사 배열 객체라서 배열로 바꾼 후 작업을 하는 경우가 많았다. 또한 ECMAScript6 에서 추가된 화살표함수에는 arguments
프로퍼티가 존재하지 않는다....args
는 배열이며, 화살표함수에서도 사용할 수 있다.
var sum = (...args) => {
for(var i=0, s=0; i<args.length; i++) {
s += args[i]
}
return s;
}
sum(1, 2, 3, 4, 5); // -> 15
함수의 인자에 대입연산자(=) 를 통해 기본값을 설정할 수 있다. (파이썬의 그것과 같음)
function multiply(a, b=1) {
return a*b
}
multiply(3,2); // -> 6
multiply(3); // -> 3
// 다른 인자의 값도 기본값으로 사용할 수 있다.
function add(a, b=a+1) {
return a+b;
}
add(2, 1); // -> 3
add(2); // -> 5
for/of
문은 이터레이터를 제어하는 구문이다.
예로, 배열의 forEach()
메서드가 있다. 배열의 요소를 순차적으로 검색하여 그 값을 함수의 인수로 넘기기를 반복한다.
var a = [5, 4, 3];
a.forEach(function(val) {console.log(val); });
// 5
// 4
// 3
이터레이터 iterator란 반복처리(iteration)가 가능한 객체를 말한다. 앞의 forEach()
메서드는 배열의 요소를 꺼내 그 값을 함수의 인수로 넘기고, 그 작업이 끝나면 배열의 다음 요소를 꺼내 인수로 넘기기를 반복한다. 함수 내의 작업은 내부적으로 처리되기 때문에 개발자는 각 처리단계를 제어할 수 없다. 하지만 이터레이터의 경우 반복처리(iteration)을 단계별로 제어할 수 있다.
이터레이터가 무엇인지 보자.
배열은 Symbol.iterator()
메서드를 가지고 있다. 이는 이터레이터를 반환하는 함수이다.
var a = [5, 4, 3];
var iter = a[Symbol.iterator]();
console.log(iter.next()); // Object {value:5, done: false}
console.log(iter.next()); // Object {value:4, done: false}
console.log(iter.next()); // Object {value:3, done: false}
console.log(iter.next()); // Object {value:undefined, done: true}
iter
의 next()
메서드를 호출할 때마다 value
와 done
프로퍼티를 갖는 iterator result
객체가 반환된다iterator result
객체에는 next()
메서드가 호출 될 때마다 value
프로퍼티에는 차례대로 꺼내진 배열 요소의 값이 들어가고, done
프로퍼티에는 요소의 열거가 끝났는지를 뜻하는 논리값이 들어간다.
function makeIterator(array) {
var index = 0;
return {
next: function() {
if(index < array.length) {
return {value: array[index++], done: false};
} else {
return {value: undefined, done: true};
}
}
}
}
이터레이터를 사용해서 이터레이션(반복처리) 를 하려면 개발자가 직접 처리를 작성해야 한다.
var a = [5, 4, 3];
var iter = a[Symbol.iterator]();
while(true) {
var iteratorResult = iter.next();
if (iteratorResult.done == true) break;
var v = iteratorResult.value;
console.log(v);
}
// 5
// 4
// 3
이러한 코드로서 실행할 수 있는데 이는 for/of 문을 사용하면 더욱 쉽게 표현할 수 있다.
var a = [5, 4, 3];
for(var v of a) {
console.log(v);
}
v
에 대입한다.Symbol.iterator()
메서드를 갖고 있다.Symbol.iterator()
메서드는 반환값으로 이터레이터를 반환한다.Symbol.iterator()
메서드를 가진 객체를 반복가능(iterable)한 객체라고 한다. 이에는 Array
, String
, TypedArray
, Map
, Set
이 있다.제네레이터는 function*
문으로 정의한 함수이며, 하나 이상의 yield
표현식을 포함한다.
function* gen() {
yield 1;
yield 2;
yield 3;
}
var iter = gen();
console.log(iter.next()); // Object {value: 1, done: false}
console.log(iter.next()); // Object {value: 2, done: false}
console.log(iter.next()); // Object {value: 3, done: false}
console.log(iter.next()); // Object {value: undefined, done: true}
gen()
은 호출해도 바로 실행되지 않고, 대신 이터레이터를 반환한다.next()
메서드가 호출되면 함수의 첫 번째 yield
연산자의 위치까지 실행하고, 결괏값으로 iterator result
를 반환한다. 이터레이터 리절트의 value
값으로 yield
표현식에 지정한 값을 저장하고, done
프로퍼티 값으로 제네레이터 함수를 끝까지 실행했는지 저장한다. 이때, 여기서 함수의 실행이 일시정지된다.next()
메서드가 호출되면 일시 정지한 위치에 있는 처리를 재개한 이후, 이터레이터 리절트의 value
값으로 yield
표현식에 지정한 값을 저장하고, done
프로퍼티 값으로 제네레이터 함수를 끝까지 실행했는지 저장한다. 역시 이때, 함수의 실행이 일시정지된다.next()
메서드가 호출된 후 또 다시 재개한다.yield
에 도착하면, value
가 undefined
고 done
프로퍼티가 true
인 이터레이터 리절트를 반환한다.for/of
문으로 반복 처리 할 수 있다.제네레이터 함수는 return 을 yield 로 쓰고, 일시정지가 되는 것으로 이해하면 쉽다.
제네레이터 함수 안에서 yield*
표현식을 사용하면 반복 가능한 객체를 지정할 수 있다.
function* f() {
yield "x";
yield "y";
}
function g() {
yield 0;
yield* [2, 4];
yield* "AB";
yield* f();
}
var iter = g();
for(var v of iter) console.log(v); // 0, 2, 4, A, B, x, y 순.