컴퓨터 공학에서 스코프는 변수의 유효범위를 뜻한다. 범위는 블록 또는 함수를 기준으로 나뉘어진다. 변수가 블록 안쪽에 선언 되었는가, 바깥쪽에 선언되었는가에 따라 변수의 유효 범위가 달라진다.
바깥쪽 스코프에서 선언한 변수는 안쪽 스코프에서 사용 가능하다. 하지만 안쪽에서 선언한 변수는 바깥쪽 스코프에서는 사용 불가능하다.
스코프는 중첩이 가능하다. 겹겹이 쳐진 울타리를 상상하면 된다. 가장 바깥쪽의 스코프를 전역 스코프(global scope)라고 부르고, 다른 스코프는 전부 지역 스코프(local scope)이다.
전역 변수, 지역 스코프에 선언한 변수는 지역 변수라고 한다.지역 변수는 전역 변수보다 우선순위가 높다.
쉐도잉(variable shadowing)이라고 부른다.let name = 'suri'; // 전역 변수
function showName() {
let name = 'masuri'; // let 키워드로 지역 변수 선언
console.log(name);
}
console.log(name); // suri
// 안쪽 스코프에 있는 지역 변수에 접근할 수 없다.
showName(); // masuri
// 안쪽 스코프에 새로 선언한 지역 변수 name은 전역 변수 name과 다르고, 변수명이 동일하지만 높은 우선순위를 가진다.
console.log(name); // suri
==========비교==========
let name = 'suri';
function showName() {
name = 'masuri'; // let 키워드를 사용하지 않았다. 전역변수 name을 그대로 사용한다. 같은 변수다.
console.log(name);
}
console.log(name); // suri
showName(); // masuri
// 함수가 실행되고 난 이후에는 전역변수 name의 값이 바뀌므로 바뀐 값이 출력된다.
console.log(name); // masuri
중괄호를 기준으로 범위를 구분한다.
function 키워드로 함수를 선언하면 함수 스코프가 만들어진다. 함수의 실행부터 종료까지다. 단, 화살표 함수를 사용하면 블록 스코프로 취급한다.
function showName(){
console.log('suri');
} // 표현식
let showName = function() {
console.log('suri')
} // 선언식
for (let i = 0; i < 3; i++) {
console.log('hello');
}
console.log(i); // ReferenceError
=======비교=======
for (var i = 0; i < 3; i++) {
console.log('hello');
}
console.log(i); // 3
블록 스코프 안에서 정의된 변수는 그 범위를 벗어나면 접근할 수 없다. ReferenceError를 던진다. e.g. for문 조건식에 들어가는 변수 i를 블록 스코프 밖에서 출력하려고 하는 경우
var 키워드로 선언된 변수 i는 블록 스코프를 벗어나도 같은 함수 스코프에서는 사용이 가능하다. 즉, 블록 스코프를 따르지 않고 함수 스코프를 따른다.
var는 블록 스코프를 무시하므로, 스코프에 대한 이해가 없으면 혼란스러울 수 있다. 따라서 var 보다는 let으로 변수 선언 하기를 권장한다.
let 키워드는 재선언을 방지한다. 코딩할 때 변수 재선언을 할 필요는 없다. 보통 이런 경우는 버그이기 때문이다. let 키워드로 실수를 방지할 수 있다.
브라우저에는 window 객체가 존재하다. (콘솔 창 확인) 브라우저 창을 의미하는 객체이기도 하고, 전역 항목을 담고 있기도 하다. var로 선언된 전역 변수와 전역 함수가 window 객체에 속한다.
var myAge = '22';
console.log(window.myAge); // 22
function foo() {
console.log('you are young');
} // 선언식으로 함수를 선언
var fooo = function() {
console.log('hello');
} // var 키워드로 표현식으로 함수를 선언
console.log(foo === window.foo); // true
console.log(fooo === window.fooo); // true
Sources - 파일 열기 - 라인 클릭으로 breakpoint 지정 - 새로고침
여러 참고서에서 클로저에 대한 다양한 정의들이 있다.
=> 함수 내에서 다른 함수(내부함수)가 리턴이 되면, 이 함수를 클로저 함수라고 부르고, 내부 함수가 외부 함수에 있는 변수에 접근이 가능하다. 1차로 이렇게 정리한다.
이거 읽어보기....
클로져는 "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말합니다. 이 환경은 클로저가 생성 된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다." 라고 합니다.
여기서의 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"입니다. 특이하게도 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 합니다. 유어클레스 영상에서 언급되는 "외부함수의 변수에 접근할 수 있는 내부함수"를 클로져 함수로 부르는 이유도 그렇습니다.
클로저 함수: 클로저는 외부함수의 컨텍스트에 접근할 수 있는 내부함수를 뜻합니다. 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있습니다.
클로저 사용 예시: 클로저를 통해 커링(currying, 함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어 각각 인자를 받게 하는 방법), 클로저 모듈(변수를 외부 함수 스코프 안쪽에 감추어, 변수가 함수 밖에서 노출되는 것을 막는 방법) 등의 패턴을 구현할 수 있습니다.
클로저의 단점: 일반 함수였다면 함수 실행 종료 후 가비지 컬렉션(참고 자료: MDN '자바스크립트의 메모리 관리') 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 됩니다. 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문입니다. 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있습니다.
자바스크립트는 가비지 컬렉션을 통해 메모리 관리를 합니다. 객체가 참조 대상이 아닐 때, 가비지 컬렉션에 의해 자동으로 메모리 할당이 해제됩니다.
const adder = x => y => x + y;
adder(5)(7);
typeof adder(5) // 함수
adder(5) // y => x + y; 인데 여기에서 x가 5이다.
======비교======
const adder = function (x) {
return function (y) { // 리턴값이 함수의 형태다.
return x + y;
}
}
======비교======
let add = function(x) {
let sum = function(y) {
return x + y;
}
return sum;
}
let foo = add(1); // foo는 x에 1이 할당된 상태를 기억하고 있는 내부 함수 sum을 의미하는 것이다.
foo(3);
let total = foo(6); // total은 7의 값을 갖는다.
함수를 리턴하는 함수다.
내부 함수는 외부 함수의 변수에 접근 가능하다.
외부 함수의 실행이 끝나도 외부 함수 내 변수가 메모리 상에 저장이 된다. (어휘적 환경을 저장하기 때문이다.)
const adder = function (x) {
return function (y) {
return x + y;
}
}
const add5 = adder(5);
add5(7) // 12
add5(10) // 15

클로저는 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 해준다.
const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
const divMaker = tagMaker('div');
divMaker('hello'); // '<div>hello</div>'
divMaker('suri');
const spanMaker = tagMaker('span');
spanMaker('hello'); // '<span>hello</span>'
spanMaker('som');
const makeCounter = () => {
let value = 0;
return {
increase: () => {
value = value + 1
},
decrease: () => {
value = value - 1
},
getValue: () => value
}
}
const counter1 = makeCounter();

정보의 접근 제한(캡슐화)counter1.increase();
counter1.increase();
counter1.increase();
counter1.getValue(); // 3
counter2.decrease();
counter2.decrease();
counter2.getValue(); // -2
모듈화라고 한다.===1번===
let username = 'kimcoding';
if (username) {
let message = `Hello, ${username}!`;
console.log(message); // 'hello, kimcoding'
}
console.log(message); // reference 에러
===2번===
let greeting = 'Hello';
function greetSomeone() {
let firstName = 'Josh';
return greeting + ' ' + firstName;
}
console.log(greetSomeone()); // Hello Josh
console.log(firstName); // ReferenceError
단, 화살표 함수를 사용하면 블록 스코프로 취급한다. 그래서 이게 뭐? var 키워드로 선언된 변수는 화살표 함수도 무시하지 않는다며..
const 키워드로 배열을 할당하고 pop push 등 메서드를 사용하는 거. 이건 재할당이라고 보진 않나보다.
궁금한 게 if 조건문 안에 단순히 변수만 들어가 있으면 그 변수가 선언되었나?로 해석하면 되나요?
스코프 8번 문제. inner(); 이게 무슨 의미일까. 함수를 호출해서 실행한다. 그냥 리턴값이 되는건가. 변수에 담지 않으면 활용은 하지 못하는건가. 함수 바디 안에 내용을 실행을 하고 (눈에 보이진 않지만) 리턴 값을 가지고 있다.