Scope/Closure/Hoisting

박재윤·2020년 12월 23일
0

자바스크립트

목록 보기
2/11

스코프 체인

꼬리를 물면서 계속 범위를 밖으로 넓혀나가며 변수를 찾는 관계

lexical scoping

범위를 지정하는 과정에서 변수가 소스코드 내에 어디에서 선언되었는지를 고려한다는 의미

var name = 'zero'; // (1)변수 선언 (6)변수 대입
function wow(word) { // (2)변수 선언 (3)변수 대입
  console.log(word + ' ' + name); // (11)
}
function say () { // (4)변수 선언 (5)변수 대입
  var name = 'nero'; // (8)
  console.log(name); // (9)
  wow('hello'); // (10)
}
say(); // (7)

// 결과: nero hello zero

Context

  • 처음 코드를 실행할 때 모든 것을 포함하는 전역 컨텍스트가 생긴다.
  • 함수를 호출할 때마다 함수 컨텍스트가 생긴다.
  • context 생성시 변수 객체, scope chain, this가 생성된다
  • 컽넥스트 생성 후 함수가 실행되는데, 사용되는 변수들은 변수 객체 안에서 값을 찾고 없다면 스코프 체인을 따라 올라가면서 찾는다.
  • 함수 실행이 마무리되면 해당 context는 사라진다.

https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0

Context 변화 예

var name = 'zero'; // (1)변수 선언 (6)변수 대입
function wow(word) { // (2)변수 선언 (3)변수 대입
  console.log(word + ' ' + name); // (11)
}
function say () { // (4)변수 선언 (5)변수 대입
  var name = 'nero'; // (8)
  console.log(name); // (9)
  wow('hello'); // (10)
}
say(); // (7)

// 결과: nero hello zero

이 코드가 실행이 될 때 context 변화

  • 코드가 실행될 때
'전역 컨텍스트': {
  변수객체: {
    arguments: null,
    variable: ['name', 'wow', 'say'],
  },
  scopeChain: ['전역 변수객체'],
  this: window,
}
  • say() 함수 호출될 때
'say 컨텍스트': {
  변수객체: {
    arguments: null,
    variable: ['name'], // 초기화 후 [{ name: 'nero' }]가 됨
  },
  scopeChain: ['say 변수객체', '전역 변수객체'],
  this: window,
}
  • wow() 함수 호출될 때
'wow 컨텍스트': {
  변수객체: {
    arguments: [{ word : 'hello' }],
    variable: null,
  },
  scopeChain: ['wow 변수객체', '전역 변수객체'],
  this: window,
}

이 때 lexical scoping에 따라서 scope chain이 함수 선언시에 정해져있다.

클로저

클로저를 설명할 때는 scope, context 개념을 함께 설명해야한다.

다음과 같은 코드가 있다.

var makeClosure = function() {
  var name = 'zero';
  return function () {
    console.log(name);
  }
};
var closure = makeClosure(); // function () { console.log(name); }
closure(); // 'zero';

이 때 전역 context, makeClosure의 context를 보면 다음과 같다.

"전역 컨텍스트": {
  변수객체: {
    arguments: null,
    variable: [{ makeClosure: Function }, 'closure'],
  },
  scopeChain: ['전역 변수객체'],
  this: window,
}
"makeClosure 컨텍스트": {
  변수객체: {
    arguments: null,
    variable: [{ name: 'zero' }],
  },
  scopeChain: ['makeClosure 변수객체', '전역 변수객체'],
  this: window,
}

그런데 closure를 만드는 상황, 즉, return function() ~ 하는 부분에서 context는 다음과 같다.

"closure 컨텍스트":  {
  변수객체: {
    arguments: null,
    variable: null,
  scopeChain: ['closure 변수객체', 'makeClosure 변수객체', '전역 변수객체'],
  this: window,
}

따라서 closure 함수에서 scope chain을 통해서 makeClosure의 name 변수에 접근할 수 있다.

이렇게 비공개로 변수를 만들어서 남들이 조작할 걱정을 덜 수 있다는 장점이 있다.

단점은 자바스크립트가 메모리 관리를 언제 해야할 지 모르기 때문에 메모리 낭비가 생길 수 있다는 단점이 있다.

문제

다음 코드에서 의도한대로 실행 결과가 나오지 않는 이유는?

for (var i = 0; i < 5; i++) {
  $('#target' + i).on('click', function() {
    alert(i);
  });
}

Hoisting

  • 선언이 최상위로 끌어올려진 것처럼 동작하는 특성
  • 변수 호이스팅
    • 선언된 변수들은 실행 context 안에서 만들어진다.
    • 선언되지 않은 변수들은 항상 전역변수이다.
    • 선언된 변수들은 코드가 실행되기 전에 만들어진다.
    • 선언되지 않은 변수들은 변수들을 할당하는 코드가 실행되기 전까지는 존재하지 않는다.
  • var 호이스팅
    • 선언과 값 대입이 전부 해당 스코프의 선두로 옮긴 것처럼 동작한다.

var, let 차이

  • var는 함수 블록 안에서 유효하다.
  • var는 전역 객체의 속성값을 생성한다.
  • var는 초기화까지 hoisting 된다.
  • let은 블록 스코프 안에서 유효하다.
  • let은 전역 객체 속성값을 생성하지 않는다.
  • let은 초기화는 hoisting 되지 않는다.

0개의 댓글