Scope n Closures 본문
본 글은 Javascript Scope and Closure by Zell Liew 의 번역 글입니다.
스코프(Scope) 란 ?
Javascript에서 스코프(Scope) 란 어떤 변수들에 접근할 수 있는지를 정의합니다.
스코프엔 두가지 종류가 있는데요, 이는 전역 스코프(global scope) 와 지역 스코프(local scope) 로 나뉩니다.
전역 스코프(Global Scope) 란?
변수가 함수 바깥이나 중괄호 ( { } ) 바깥 에 선언되었다면, 전역 스코프 에 정의된다고 합니다.
const globalVariable = 'some value'
전역 변수를 선언하면 코드 모든 곳(함수 포함)에서 해당 변수를 사용할 수 있습니다.
const hello = 'Hello World!';
function sayHello () {
console.log(hello);
}
console.log(hello); // 'Hello World!'
sayHello(); // 'Hello Worldr!'
하지만 전역 스코프에 변수를 선언할 수는 있어도, 그러지 않는 것이 좋습니다.
왜냐하면, 두개 이상의 변수의 이름이 충돌하는 경우가 생길 수 있기 때문입니다.
만약 변수를 const 나 let 을 사용하여 선언하였다면, 이름에 충돌이 발생할때마다 에러가 발생합니다.
// Don’t do this!
let thing = 'something';
let thing = 'something else'; // thing has already been declared
만약 var 를 이용하여 변수를 선언햇다면, 마지막 변수가 변수를 덮어쓰게 됩니다.
이렇게되면 디버깅에 어려움이 생기기 때문에 이런 식으로 사용하면 안됩니다.
// Don’t do this!
var thing = 'something'
var thing = 'something else' // perhaps somewhere totally different in your code
console.log(thing) // ‘something else’
그래서 여러분은 언제나 전역 변수가 아닌, 지역 변수로써 변수를 선언해야 합니다.
지역 스코프(Local Scope)
코드의 특정 부분에서만 사용할 수 있는 변수는 지역 스코프에 있다고 할 수 있습니다.
이런 변수를 지역 변수라고 불립니다.
Javascript에서는 두가지의 지역 변수가 존재합니다.
이는 함수 스코프(Function Scope) 와 블록 스코프(Block Scope) 입니다.
먼저 함수 스코프(Function Scope) 부터 알아보면,
함수 스코프(Function Scope) 란?
함수 내부에서 변수를 선언하면, 그 변수는 선언한 함수 내부에서만 접근할 수 있습니다.
함수 바깥에서는 해당 변수에 접근할 수가 없습니다.
아래의 예제를 보시죠.
function sayHello () {
const hello = 'Hello World!';
console.log(hello);
}
sayHello(); // 'Hello World!'
console.log(hello); // hello is not defined
변수 hello 는 sayHello 의 스코프 내에 존재한다는 것을 알 수 있습니다.
블록 스코프(Block Scope) 란?
중괄호 ( { } ) 내부에서 const 또는 let 으로 변수를 선언하면, 그 변수들은 중괄호 블록 내부에서만
접근할 수 있습니다
예제를 보시면
{
const hello = 'Hello World!';
console.log(hello); // 'Hello World!'
}
console.log(hello); // 'hello is not defined'
예제에서 볼 수 있듯 변수 hello 는 중괄호 내부의 스코프에만 존재합니다.
함수를 선언할 때는 중괄호를 사용해야 하므로 블록 스코프는 함수 스코프의 서브셋(subset)입니다.
함수 호이스팅(Function hoisting)과 스코프(Scope)
함수가 함수 선언식(function declaration) 으로 선언되면, 현재 스코프(Scope) 의 최상단으로 호이스팅됩니다.
예제를 보시면
// This is the same as the one below
sayHello()
function sayHello () {
console.log('Hello World!');
}
// This is the same as the code above
function sayHello () {
console.log('Hello World!');
}
sayHello()
두가지 경우엔 같은 결과를 보입니다.
반면 함수가 함수표현식(Function Expression) 으로 선언이 된다면,
함수는 현재 스코프(Scope) 의 최상단으로 호이스팅되지 않습니다.
sayHello() // Error, sayHello is not defined
const sayHello = function () {
console.log(aFunction)
}
이렇게 두 방식은 행동이 다르기 때문에, 함수 호이스팅은 혼란을 줄 수 있으므로 사용하면 안됩니다.
항상 함수를 호출하기전에 선언해 놓아야 합니다.
함수는 서로의 스코프에 접근할 수 없다.
함수들이 각각 선언되었을 때, 서로의 스코프에는 접근할 수 없습니다.
어떤 함 수가 다른 함수에서 사용되더라도 접근할 수 없습니다.
예제를 보시면
function first () {
const firstFunctionVariable = 'I’m part of first'
}
function second () {
first()
console.log(firstFunctionVariable) // Error, firstFunctionVariable is not defined
}
함수 second 는 변수 firstFunctionVariable 에 접근할 수 없는 것이 확인됩니다.
네스팅된 스코프(Nested Scope)
함수가 다른 함수 내부에서 정의되었다면, 내부 함수는 외부 함수의 변수에 접근할 수 있습니다.
이런 행동을 렉시컬 스코핑(Lexical Scoping) 이라고 부릅니다.
하지만, 외부 함수는 내부함수의 변수에 접근할 수 없습니다.
function outerFunction () {
const outer = 'I’m the outer function!'
function innerFunction() {
const inner = 'I’m the inner function!'
console.log(outer) // I’m the outer function!
}
console.log(inner) // Error, inner is not defined
}
스코프가 어떻게 동작하는지 예를 들자면, 수사드라마에서 자주나오는 취조실 특수유리를 반대로 생각하시면 됩니다.
취조실 특수유리는 밖에선 내부가 보이지만, 내부에선 밖을 볼 수 없습니다. 그 반대로 생각하시면 이해하시는데 도움이 될 거라 생각이 듭니다.
클로저(Closures) 란 ?
함수 내부에 함수를 작성할때마다, 여러분은 클로저를 생성한 것입니다.
내부에 작성된 함수가 바로 클로저입니다.
클로저는 차후에 외부 함수의 변수를 사용할 수 있기 때문에 대게 반환하여 사용합니다.
function outerFunction () {
const outer = 'I see the outer variable!'
function innerFunction() {
console.log(outer)
}
return innerFunction
}
outerFunction() // I see the outer variable!
여기에서 내부 함수는 반환되기 때문에, 함수를 선언하자마자 반환되도록 코드를 수정하면,
function outerFunction () {
const outer = 'I see the outer variable!'
return function innerFunction() {
console.log(outer)
}
}
outerFunction() // I see the outer variable!
클로저는 외부 함수의 변수에 접근할 수 있기 때문에, 일반적으로 두가지 목적을 위해 사용합니다.
1. 클로저로 사이드 이펙트 제어하기
함수에서 값을 반환할 때를 제외하고 무언가를 행할 때 사이드 이펙트(side effects) 가 발생합니다.
여러 가지 것들이 사이드 이펙트가 될 수 있는데요, 예를 들어 Ajax 요청이나 timeout 을 생성할때,
또는 console.log 를 선언하는 것도 사이드 이펙트(side effects) 라고 볼 수 있습니다.
function (x) {
console.log('A console.log is a side effect!')
}
보통 Ajax나 timeout과 같이 코드 흐름을 방해하는 것들이 신경 쓰일때, 클로저를 활용하여 사이드 이펙트를 제어합니다.
예제를 보면
function playBall() {
setTimeout(function() {
console.log('Shooting');
}, 1000)
}
위의 playBall 함수는 사이드 이펙트가 존재합니다. 바로 timeout 이죠.
이제는 선수가 어떤 슛팅을 할 것인지 선택할 수 있도록 해봅니다.
function playBall(type) {
setTimeout(function() {
console.log(type + 'Shooting');
}, 1000)
}
playBall('heading'); //'headingShooting'
함수를 실행하면, 1초뒤 console이 찍히는걸 볼 수있습니다.
문제가 생겻습니다. 어떤 슈팅을 할지 알자마자 슈팅을 하고싶지 않습니다.
원하는 시점에서 슈팅을 하고 싶다면,
이 문제를 해결하기 위해서, 준비동작을 할 수있는 motion 함수를 작성 할 수 있습니다.
그리고 motion 함수 내부에서는 클로저인 playBall 을 반환합니다.
이제 여러분이 원하는 시점에서 언제든지 반환된 함수를 호출할 수 있고,
이 함수는 호출한 뒤 1초뒤에 슈팅을 하게 만들어집니다.
function motion (type) {
return function () {
setTimeout(function() {
console.log(type + 'Shooting');
}, 1000)
}
}
var motionAction = motion('heading');
motionAction(); //'headingShooting'
이와 같이 클로저를 활용하여 사이드 이펙트를 줄일 수 있습니다.
여러분이 원할때 내부 클로저를 호출할 수 있는 함수를 만드는 것이죠.
2. private 변수와 클로저
함수 내의 변수는 함수 바깥에서 접근할 수 없습니다.
그 변수들은 접근할 수 없기 때문에 Private 변수 라고 불립니다.
하지만 종종 해당 변수들에 접근해야할때가 존재합니다.
이것 또한 클로저를 활용하여 사용할 수 있습니다.
function secret (secretCode) {
return {
saySecretCode () {
console.log(secretCode)
}
}
}
var theSecret = secret('Hello World!')
theSecret.saySecretCode()// 'Hello World!'
해당 예제에서 saySecretCode 는 유일하게 secret 함수 바깥에서 secretCode 를 노출하는 함수(클로저) 입니다. 따라서, 이런 함수를 특권 함수(privileged function) 라고 부르기도 합니다.
출처(https://medium.com/@khwsc1/%EB%B2%88%EC%97%AD-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8A%A4%EC%BD%94%ED%94%84%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%80-javascript-scope-and-closures-8d402c976d19)
'STUDY > Javascript N Jquery' 카테고리의 다른 글
여러개 요소에 여러개 이벤트 바인딩 하기 (0) | 2019.03.05 |
---|---|
인수와 매개변수란? (0) | 2019.02.28 |
2 TYPE의 Prototype (0) | 2019.02.28 |
Jquery $(document).ready() 와 $(window).load()의 차이점 (0) | 2019.02.27 |
var vs let vs const 의 차이점 (0) | 2019.02.26 |