By 김훈민
NHN Technology Services Corp.
Front-End Development Team
http://huns.me
Lexical
Environment
in ECMA-262 Edition 5.1
ECMA-262 Edition 5, 2009
ECMA-262 Edition 3 탄생 이후 10년
JSON
Function
Object
Arrays
use strict
Date
New Features
in ECMA-262 Edition 5
1 more thing.
I will note that there are some real
improvements in ES5, in particular to
Chapter 10 which now uses declarative
binding environments. ES1-3's abuse of
objects for scopes (again I'm to blame
for doing so in JS in 1995, economizing on objects
needed to implement the language in a big hurry) was
a bug, not a feature.
https://mail.mozilla.org/pipermail/es-discuss/2010-April/010915.html
Before ECMA 262-5…?
Variable Object
Chapter 10.
Execution Context
ECMAScript 코드를 실행하는 환경
Execution Context
<script>
var sayHello = "Hello";
function person(){
var first = 'David';
function getFirstName(){
eval("first = 'Steven'");
return first;
}
alert(sayHello + getFirstName());
}
person();
</script>
Execution Context
Execution Context Stack
Execution Context
Execution Context
Execution Context
Execution Context
Executable Code
<script>
var sayHello = "Hello";
function person(){
var first = 'David';
function getFirstName(){
eval("first = 'Steven'");
return first;
}
alert(sayHello + getFirstName());
}
person();
</script>
Function
Code
Global
Code
Eval
Code
Evaluating
Initializing
execution context
Initializing
execution context
Initializing
execution context
Pseudo code
ExecutionContext = {
LexicalEnvironment : [Lexical Environment],
VariableEnvironment : [Lexical Environment],
ThisBinding : [object]
}
* […]은 Value Type
Lexical Environment
자원을 어디에서 찾을 것인가
Components of Lexical Environment
lexical environment = {
environment record : -,
outer environment reference : -
}
지역 식별자 덩어리
유효범위 내의 식별자를 바인딩
중첩 유효범위를 가질 수 있는 환경에
서 상위 lexical environment를 참조
2 type of environment record
Declarative
vs
Object
DeclarativeEnvironmentRecord : {
x : 10,
y : 20,
result : 30
}
ObjectEnvironmentRecord : {
bindObject : globalObject
}
Establishing
an Execution Context
실행 콘텍스트 초기화
thisBinding, Hoisting
Global Code
{
LexicalEnvironment : globalEnv,
VariableEnvironment : globalEnv,
ThisBinding : window
}
Global Execution Context
ObjectEnvironmentRecord : {
bindingObject : window
},
OuterEnvironmentReferece : null,
globalEnv
function sum(x, y){
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
Global
Execution Context
sum’s
Execution Context
Function Code
thisBinding
sunFunctionEC.thisBinding = null;
function sum(x, y){
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
{
ThisBinding : null,
}
sumFunctionEC
thisBinding
sunFunctionEC.thisBinding = null;
function sum(x, y){
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
{
ThisBinding : null,
}
sumFunctionEC
ThisBinding이 null, undefined인 경우
this = global
“use strict” 모드에서는
this = null
thisBinding on Function
sunFunctionEC.ThisBinding = foo;foo.sum(10, 20);
{
ThisBinding : foo,
}
sumFunctionEC
{
ThisBinding : null,
LexicalEnv : -
}
sumFunctionEC
EnvironmentRecord : {}
localEnv
localEnv
Creating local lexical environment
Declaration Binding
Instantiation
실행 콘텍스트 바인딩 초기화
function sum(x, y){
console.log(barr);
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
{
ThisBinding : null,
LexicalEnv : localEnv
}
sumFunctionEC
EnvironmentRecord : {
}
x : 10,
y : 20,
localEnv
Declaration Binding Instantiation
EnvironmentRecord : {
x : 10,
y : 20,
}
function sum(x, y){
console.log(barr);
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
printResult : Function Reference
Hoisting
{
ThisBinding : null,
LexicalEnv : localEnv
}
sumFunctionEC
localEnv
Declaration Binding Instantiation
function sum(x, y){
console.log(barr);
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
EnvironmentRecord : {
x : 10,
y : 20,
printResult : Function Reference,
}
arguments : Arguments Object
{
ThisBinding : null,
LexicalEnv : localEnv
}
sumFunctionEC
localEnv
Declaration Binding Instantiation
EnvironmentRecord : {
x : 10,
y : 20,
printResult : Function Reference
}
function sum(x, y){
console.log(barr);
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
result : undefined,
barr : undefined
Hoisting
{
ThisBinding : null,
LexicalEnv : localEnv
}
sumFunctionEC
localEnv
Declaration Binding Instantiation
EnvironmentRecord : {
x : 10,
y : 20,
printResult : Function Reference,
result : undefined,
barr : undefined
}
function sum(x, y){
console.log(barr);
var result = x + y;
var barr = function(){
console.log("barr");
}
function printResult(){
console.log(result);
}
return printResult;
}
sum(10, 20);
{
ThisBinding : null,
LexicalEnv : localEnv
}
sumFunctionEC
localEnv
Declaration Binding Instantiation
function sum(x, y){
console.log(barr);
var result = x + y;
var barr = function(){
console.log("barr");
}
function foo(){
console.log("foo");
}
return result;
}
sum(10, 20);
result : 30,
barr : Function Reference
{
ThisBinding : null,
LexicalEnv : localEnv
}
sumFunctionEC
EnvironmentRecord : {
x : 10,
y : 20,
foo : Function Reference
result : undefined,
barr : undefined
}
localEnv
Declaration Binding Instantiation
result : 30,
barr : Function Reference
Identifier Resolution
식별자와 묶여있는 값을 찾아가는 과정
스코프 체인의 원리
outer environment reference
The outer environment reference is used
to model the logical nesting of Lexical Environment values
var a = 10;
(function foo() {
var b = 20;
(function bar() {
var c = 30;
console.log( a + b + c );
})();
})();
Identifier Resolution #1
Global
Execution Context
foo’s
Execution Context
bar’s
Execution Context
var a = 10;
(function foo() {
var b = 20;
(function bar() {
var c = 30;
console.log( a + b + c );
})();
})();
LexicalEnvironment : {
EnvironmentRecord : {
c : 30
}
OuterEnvironmentReference :
fooEC.LexicalEnvironment
},
bar’s EC
LexicalEnvironment : {
EnvironmentRecord : {
b : 20
}
OuterEnvironmentReference :
globalEC.LexicalEnvironment
},
foo’s EC
LexicalEnvironment : {
EnvironmentRecord : {
a : 10
}
OuterEnvironmentReference : null
},
Global EC
Identifier Resolution #1
var a = 10;
(function foo() {
var b = 20;
(function bar() {
var c = 30;
console.log( a + b + c );
})();
})();
LexicalEnvironment : {
EnvironmentRecord : {
c : 30
}
OuterEnvironmentReference :
fooEC.LexicalEnvironment
},
barEC
LexicalEnvironment : {
EnvironmentRecord : {
b : 20
}
OuterEnvironmentReference :
globalEC.LexicalEnvironment
},
fooEC
LexicalEnvironment : {
EnvironmentRecord : {
a : 10
}
OuterEnvironmentReference : null
},
globalEC
Identifier Resolution #2
Closure
자유변수를 간직한 코드 블럭
A lexical environment that defines the environment
in which a Function object is executed.
Function Object’s
Internal property
[[Scope]]
function sum(x, y){
var result = x + y;
function printResult(){
console.log("foo:”+ result);
}
return printResult;
}
var print = sum(10, 20);
print();
LexicalEnvironment = {
EnvironmentRecord : {
x : 10,
y : 20,
result : undefined,
printResult : function
},
OuterEnvironmentReference :
globalEC.lexicalEnvironment
},
ThisBinding : null
sum’s EC
[[Scope]]:
printResult function
sumEC’s lexical environment
Creating Function Object
function sum(x, y){
var result = x + y;
function printResult(){
console.log("foo:”+ result);
}
return printResult;
}
var print = sum(10, 20);
print();
[[Scope]]:
printResult function
sumEC’s lexical environment
ThisBinding : null
sum’s EC
LexicalEnvironment = {
EnvironmentRecord : {
x : 10,
y : 20,
result : undefined,
printResult : function
},
OuterEnvironmentReference :
globalEC.lexicalEnvironment
},
Closure
function sum(x, y){
var result = x + y;
function printResult(){
console.log("foo:”+ result);
}
return printResult;
}
var print = sum(10, 20);
print();
[[Scope]]:
printResult function
sumEC’s lexical environment
LexicalEnvironment = {
EnvironmentRecord : {
x : 10,
y : 20,
result : undefined,
printResult : function
},
OuterEnvironmentReference :
globalEC.lexicalEnvironment
},
Closure
[[Scope]]: sumEC’s lexical environment
printResult function
Creating printResult’s EC
LexicalEnvironment = {
EnvironmentRecord : {
…
},
OuterEnvironmentReference :
sumEC’s lexical environment
},
ThisBinding : null
printResult’s ECfunction sum(x, y){
var result = x + y;
function printResult(){
console.log("foo:”+ result);
}
return printResult;
}
var print = sum(10, 20);
print();
VariableEnvironment
LexicalEnvironment의 쌍둥이
Execution Context Pseudo code
ExecutionContext = {
LexicalEnvironment : [lexical environment],
VariableEnvironment : [lexical environment],
ThisBinding : [object]
}
* […]은 type
LexicalEnvironment and VariableEnvironment
components of an execution context
are always Lexical Environments
- 10.3 Execution Contexts in ECMA 262-5 -
VariableEnvironment = LexicalEnvironment
{
ThisBinding : null,
LexicalEnv : globalEnv,
VariableEnv : null
}
Global EC
EnvironmentRecord : {
bindingObject : window
},
OuterEnviromentReferece : null
globalEnv
var foo = "abc";
with({ foo : "bar" }) {
function f() {
console.log(foo);
}
f();
}
GlobalEC.VariableEnv = GlobalEC.LexicalEnv;
globalEnv
The value of the VariableEnvironment component
never changes while the value of the
LexicalEnvironment component
may change
during execution of code within an execution context.
- 10.3 Execution Contexts in ECMA 262-5 -
With Statement #1
{
ThisBinding : null,
LexicalEnv : globalEnv,
VariableEnv : globalEnv
}
Global EC
EnvironmentRecord : {
bindingObject : window
},
OuterEnviromentReferece : null
globalEnv
var foo = "abc";
with({ foo : "bar" }) {
function f() {
console.log(foo);
}
f();
}
ObjectEnvironmentRecord : {
bindingObject : -
},
OuterEnviromentReferece :
newEnv
{foo:bar}
newEnv,
GlobalEC.LexicalEnv
With Statement #2
{
ThisBinding : null,
LexicalEnv : newEnv,
VariableEnv : globalEnv
}
Global EC
EnvironmentRecord : {
bindingObject : window
},
OuterEnviromentReferece : null
globalEnv
var foo = "abc";
with({ foo : "bar" }) {
function f() {
console.log(foo);
}
f();
}
ObjectEnvironmentRecord : {
bindingObject : { foo : bar }
},
OuterEnviromentReferece :
GlobalEC.LexicalEnv
newEnv
With Statement #3
{
ThisBinding : null,
LexicalEnv : newEnv,
VariableEnv : globalEnv
}
Global EC
EnvironmentRecord : {
bindingObject : window
},
OuterEnviromentReferece : null
globalEnv
ObjectEnvironmentRecord : {
bindingObject : { foo : bar }
},
OuterEnviromentReferece : null
newEnv
globalEnv
with문이 끝나면
GlobalEC.LexicalEnv = GlobalEC.VariableEnv;
Reference
1. ECMAScript Language Specification 5.1 Edition
2. ECMAScript 5 spec: LexicalEnvironment versus VariableEnvironment
3. ECMA-262-5 in detail. Chapter 3.2 Lexical environments
Thanks.
email : kim.hunmin@nhn.com
facebook : facebook.com/jeokrang
blog : http://huns.me

Lexical environment in ecma 262 5

  • 1.
    By 김훈민 NHN TechnologyServices Corp. Front-End Development Team http://huns.me Lexical Environment in ECMA-262 Edition 5.1
  • 2.
    ECMA-262 Edition 5,2009 ECMA-262 Edition 3 탄생 이후 10년
  • 3.
  • 4.
  • 5.
    I will notethat there are some real improvements in ES5, in particular to Chapter 10 which now uses declarative binding environments. ES1-3's abuse of objects for scopes (again I'm to blame for doing so in JS in 1995, economizing on objects needed to implement the language in a big hurry) was a bug, not a feature. https://mail.mozilla.org/pipermail/es-discuss/2010-April/010915.html
  • 6.
  • 7.
    Chapter 10. Execution Context ECMAScript코드를 실행하는 환경
  • 8.
    Execution Context <script> var sayHello= "Hello"; function person(){ var first = 'David'; function getFirstName(){ eval("first = 'Steven'"); return first; } alert(sayHello + getFirstName()); } person(); </script> Execution Context
  • 9.
    Execution Context Stack ExecutionContext Execution Context Execution Context Execution Context
  • 10.
    Executable Code <script> var sayHello= "Hello"; function person(){ var first = 'David'; function getFirstName(){ eval("first = 'Steven'"); return first; } alert(sayHello + getFirstName()); } person(); </script> Function Code Global Code Eval Code Evaluating Initializing execution context Initializing execution context Initializing execution context
  • 11.
    Pseudo code ExecutionContext ={ LexicalEnvironment : [Lexical Environment], VariableEnvironment : [Lexical Environment], ThisBinding : [object] } * […]은 Value Type
  • 12.
  • 13.
    Components of LexicalEnvironment lexical environment = { environment record : -, outer environment reference : - } 지역 식별자 덩어리 유효범위 내의 식별자를 바인딩 중첩 유효범위를 가질 수 있는 환경에 서 상위 lexical environment를 참조
  • 14.
    2 type ofenvironment record Declarative vs Object
  • 15.
    DeclarativeEnvironmentRecord : { x: 10, y : 20, result : 30 }
  • 16.
  • 17.
    Establishing an Execution Context 실행콘텍스트 초기화 thisBinding, Hoisting
  • 18.
    Global Code { LexicalEnvironment :globalEnv, VariableEnvironment : globalEnv, ThisBinding : window } Global Execution Context ObjectEnvironmentRecord : { bindingObject : window }, OuterEnvironmentReferece : null, globalEnv
  • 19.
    function sum(x, y){ varresult = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); Global Execution Context sum’s Execution Context Function Code
  • 20.
    thisBinding sunFunctionEC.thisBinding = null; functionsum(x, y){ var result = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); { ThisBinding : null, } sumFunctionEC
  • 21.
    thisBinding sunFunctionEC.thisBinding = null; functionsum(x, y){ var result = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); { ThisBinding : null, } sumFunctionEC ThisBinding이 null, undefined인 경우 this = global “use strict” 모드에서는 this = null
  • 22.
    thisBinding on Function sunFunctionEC.ThisBinding= foo;foo.sum(10, 20); { ThisBinding : foo, } sumFunctionEC
  • 23.
    { ThisBinding : null, LexicalEnv: - } sumFunctionEC EnvironmentRecord : {} localEnv localEnv Creating local lexical environment
  • 24.
  • 25.
    function sum(x, y){ console.log(barr); varresult = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); { ThisBinding : null, LexicalEnv : localEnv } sumFunctionEC EnvironmentRecord : { } x : 10, y : 20, localEnv Declaration Binding Instantiation
  • 26.
    EnvironmentRecord : { x: 10, y : 20, } function sum(x, y){ console.log(barr); var result = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); printResult : Function Reference Hoisting { ThisBinding : null, LexicalEnv : localEnv } sumFunctionEC localEnv Declaration Binding Instantiation
  • 27.
    function sum(x, y){ console.log(barr); varresult = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); EnvironmentRecord : { x : 10, y : 20, printResult : Function Reference, } arguments : Arguments Object { ThisBinding : null, LexicalEnv : localEnv } sumFunctionEC localEnv Declaration Binding Instantiation
  • 28.
    EnvironmentRecord : { x: 10, y : 20, printResult : Function Reference } function sum(x, y){ console.log(barr); var result = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); result : undefined, barr : undefined Hoisting { ThisBinding : null, LexicalEnv : localEnv } sumFunctionEC localEnv Declaration Binding Instantiation
  • 29.
    EnvironmentRecord : { x: 10, y : 20, printResult : Function Reference, result : undefined, barr : undefined } function sum(x, y){ console.log(barr); var result = x + y; var barr = function(){ console.log("barr"); } function printResult(){ console.log(result); } return printResult; } sum(10, 20); { ThisBinding : null, LexicalEnv : localEnv } sumFunctionEC localEnv Declaration Binding Instantiation
  • 30.
    function sum(x, y){ console.log(barr); varresult = x + y; var barr = function(){ console.log("barr"); } function foo(){ console.log("foo"); } return result; } sum(10, 20); result : 30, barr : Function Reference { ThisBinding : null, LexicalEnv : localEnv } sumFunctionEC EnvironmentRecord : { x : 10, y : 20, foo : Function Reference result : undefined, barr : undefined } localEnv Declaration Binding Instantiation result : 30, barr : Function Reference
  • 31.
    Identifier Resolution 식별자와 묶여있는값을 찾아가는 과정 스코프 체인의 원리
  • 32.
    outer environment reference Theouter environment reference is used to model the logical nesting of Lexical Environment values
  • 33.
    var a =10; (function foo() { var b = 20; (function bar() { var c = 30; console.log( a + b + c ); })(); })(); Identifier Resolution #1 Global Execution Context foo’s Execution Context bar’s Execution Context
  • 34.
    var a =10; (function foo() { var b = 20; (function bar() { var c = 30; console.log( a + b + c ); })(); })(); LexicalEnvironment : { EnvironmentRecord : { c : 30 } OuterEnvironmentReference : fooEC.LexicalEnvironment }, bar’s EC LexicalEnvironment : { EnvironmentRecord : { b : 20 } OuterEnvironmentReference : globalEC.LexicalEnvironment }, foo’s EC LexicalEnvironment : { EnvironmentRecord : { a : 10 } OuterEnvironmentReference : null }, Global EC Identifier Resolution #1
  • 35.
    var a =10; (function foo() { var b = 20; (function bar() { var c = 30; console.log( a + b + c ); })(); })(); LexicalEnvironment : { EnvironmentRecord : { c : 30 } OuterEnvironmentReference : fooEC.LexicalEnvironment }, barEC LexicalEnvironment : { EnvironmentRecord : { b : 20 } OuterEnvironmentReference : globalEC.LexicalEnvironment }, fooEC LexicalEnvironment : { EnvironmentRecord : { a : 10 } OuterEnvironmentReference : null }, globalEC Identifier Resolution #2
  • 36.
  • 37.
    A lexical environmentthat defines the environment in which a Function object is executed. Function Object’s Internal property [[Scope]]
  • 38.
    function sum(x, y){ varresult = x + y; function printResult(){ console.log("foo:”+ result); } return printResult; } var print = sum(10, 20); print(); LexicalEnvironment = { EnvironmentRecord : { x : 10, y : 20, result : undefined, printResult : function }, OuterEnvironmentReference : globalEC.lexicalEnvironment }, ThisBinding : null sum’s EC [[Scope]]: printResult function sumEC’s lexical environment Creating Function Object
  • 39.
    function sum(x, y){ varresult = x + y; function printResult(){ console.log("foo:”+ result); } return printResult; } var print = sum(10, 20); print(); [[Scope]]: printResult function sumEC’s lexical environment ThisBinding : null sum’s EC LexicalEnvironment = { EnvironmentRecord : { x : 10, y : 20, result : undefined, printResult : function }, OuterEnvironmentReference : globalEC.lexicalEnvironment }, Closure
  • 40.
    function sum(x, y){ varresult = x + y; function printResult(){ console.log("foo:”+ result); } return printResult; } var print = sum(10, 20); print(); [[Scope]]: printResult function sumEC’s lexical environment LexicalEnvironment = { EnvironmentRecord : { x : 10, y : 20, result : undefined, printResult : function }, OuterEnvironmentReference : globalEC.lexicalEnvironment }, Closure
  • 41.
    [[Scope]]: sumEC’s lexicalenvironment printResult function Creating printResult’s EC LexicalEnvironment = { EnvironmentRecord : { … }, OuterEnvironmentReference : sumEC’s lexical environment }, ThisBinding : null printResult’s ECfunction sum(x, y){ var result = x + y; function printResult(){ console.log("foo:”+ result); } return printResult; } var print = sum(10, 20); print();
  • 42.
  • 43.
    Execution Context Pseudocode ExecutionContext = { LexicalEnvironment : [lexical environment], VariableEnvironment : [lexical environment], ThisBinding : [object] } * […]은 type
  • 44.
    LexicalEnvironment and VariableEnvironment componentsof an execution context are always Lexical Environments - 10.3 Execution Contexts in ECMA 262-5 -
  • 45.
    VariableEnvironment = LexicalEnvironment { ThisBinding: null, LexicalEnv : globalEnv, VariableEnv : null } Global EC EnvironmentRecord : { bindingObject : window }, OuterEnviromentReferece : null globalEnv var foo = "abc"; with({ foo : "bar" }) { function f() { console.log(foo); } f(); } GlobalEC.VariableEnv = GlobalEC.LexicalEnv; globalEnv
  • 46.
    The value ofthe VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context. - 10.3 Execution Contexts in ECMA 262-5 -
  • 47.
    With Statement #1 { ThisBinding: null, LexicalEnv : globalEnv, VariableEnv : globalEnv } Global EC EnvironmentRecord : { bindingObject : window }, OuterEnviromentReferece : null globalEnv var foo = "abc"; with({ foo : "bar" }) { function f() { console.log(foo); } f(); } ObjectEnvironmentRecord : { bindingObject : - }, OuterEnviromentReferece : newEnv {foo:bar} newEnv, GlobalEC.LexicalEnv
  • 48.
    With Statement #2 { ThisBinding: null, LexicalEnv : newEnv, VariableEnv : globalEnv } Global EC EnvironmentRecord : { bindingObject : window }, OuterEnviromentReferece : null globalEnv var foo = "abc"; with({ foo : "bar" }) { function f() { console.log(foo); } f(); } ObjectEnvironmentRecord : { bindingObject : { foo : bar } }, OuterEnviromentReferece : GlobalEC.LexicalEnv newEnv
  • 49.
    With Statement #3 { ThisBinding: null, LexicalEnv : newEnv, VariableEnv : globalEnv } Global EC EnvironmentRecord : { bindingObject : window }, OuterEnviromentReferece : null globalEnv ObjectEnvironmentRecord : { bindingObject : { foo : bar } }, OuterEnviromentReferece : null newEnv globalEnv with문이 끝나면 GlobalEC.LexicalEnv = GlobalEC.VariableEnv;
  • 50.
    Reference 1. ECMAScript LanguageSpecification 5.1 Edition 2. ECMAScript 5 spec: LexicalEnvironment versus VariableEnvironment 3. ECMA-262-5 in detail. Chapter 3.2 Lexical environments
  • 51.
    Thanks. email : kim.hunmin@nhn.com facebook: facebook.com/jeokrang blog : http://huns.me