스코프(Scope)
- 가시성(visibility)이라고도 불리며, 시야 또는 범위로 생각하면 편함.
- 프로그램의 현재 실행 중인 부분(실행 컨텍스트; execution context)에서 보이고 접근할 수 있는 식별자.
- 변수, 상수, 매개변수, 함수 등을 선언할때 유효범위를 의미.
- 변수의 스코프가 어떤 함수라고 말한다면, 함수를 호출할 때까지 함수 바디의 정해진 매개변수(formal argument)가 존재하지 않음.
- 함수를 호출할 때마다 매개변수가 나타나고, 함수가 반환하면 사라짐.
function f(x) {
return x + 3;
}
console.log(f(5));
console.log(x);
스코프와 존재
- 아직 선언하지 않은 변수나 함수가 종료되면서 존재하지 않게 된 변수는 스코프에 있지 않음.
- 변수가 스코프에 있지 않다는 말과 존재하지 않다는 말은 같지 않음.
- 존재한다는 말은 그 식별자가 메모리에 할당된 무언가를 가리킨다는 것을 의미.
- 존재하지만 스코프에 없는 변수들이 존재 할 수 있음.
- 또한 존재하지 않는다고 해도 JS는 메모리를 바로 회수하지 않음.
- 계속 유지할 필요가 없다는 표시가 있으면 주기적으로 가비지 콜렉션(garbage collection) 프로세스에서 메모리를 회수. (자동으로 일어남.)
정적 스코프
- 프로그램의 소스 코드를 보는 것은 프로그램의 정적(어휘적; lexical) 구조를 살피는 것. (단순하게 위에서 아래로)
- 하지만 프로그램을 실제 실행하면 이곳 저곳에서 움직임.
function f1() {
console.log('one');
}
function f2() {
console.log('two');
}
f2();
f1();
f2();
- JS의 스코프는 정적이며, 소스 코드만 봐도 변수가 스코프에 있는지 판단가능.
const x = 3;
function f() {
console.log(x);
console.log(y);
}
{
const y = 5;
f();
}
- JS의 정적 스코프는 Global Scope(전역), Function Scope, Block Scope.
전역 스코프(Global Scope)
- 스코프는 계층적이며 트리의 맨 아래에 바탕이 되는 스코프.
- 프로그램을 시작할 때 암시적으로 주어지는 스코프.
- 전역 스코프에서 선언한 것은 모든 범위에서 접근 가능.
- 전역 스코프에서 선언된 것들은 모두(변수, 함수, 객체 등) 전역속성.
- 전역 변수는 충분히 생각해서 사용해야 하고, 전역 스코프에 의존하지 않는 것이 정말 중요함.
let name = 'Irena';
let age = 25;
function greet() {
console.log(`hello, ${name}`);
}
function getBirthYear() {
return new Date().getFullYear() - age;
}
- 전역 변수보다 단일 객체에 보관하는 방법이 옳음.
const user ={
name ='Irena',
age =25
}
function greet() {
console.log(`hello, ${user.name}`);
}
function getBirthYear() {
return new Date().getFullYear() - user.age;
}
- 더욱 개선한다면, 함수가 전역 속성(변수, 객체)에 의존하지 않게 만듬.
function greet(user) {
console.log(`hello, ${user.name}`);
}
function getBirthYear(user) {
return new Date().getFullYear() - user.age;
}
블록 스코프(Block Scope)
console.log('before block');
{
console.log('inside block');
const x = 3;
console.log(x);
}
console.log(`outside block; x=${x}`);
변수 숨김(variable masking)
- 같은 이름의 변수나 상수가 다른 계층의 스코프에 동시에 존재할 때, 발생하는 효과.
{
let x = 'blue';
console.log(x);
{
let x = 3;
console.log(x);
}
console.log(x);
}
console.log(typeof x);
- 스코프는 계층적이고, 어떤 변수가 스코프에 존재하는지 확인하는 스코프 체인(scope chain)을 이용하면 유용.
- 또한 스코프 숨김을 하면 절대 접근할 수 없음.
{
let x = { color: 'blue' };
let y = x;
let z = 3;
{
let x = 5;
console.log(x);
console.log(y.color);
y.color = 'red';
console.log(z);
}
console.log(x.color);
console.log(y.color);
console.log(z);
}
함수 스코프(Function Scope)
- 특정 함수 내에서 정의된 변수 및 매개변수.
- 함수 내부에서만 접근 가능. (함수 내에서만 유효하며, 외부에서는 접근할 수 없음.)
function sayHello() {
let message = "Hello, World!";
console.log(message);
}
sayHello();
console.log(message);
클로저(closure)
- 함수 스코프는 클로저(Closure)를 구현하는 데 중요한 역할함.
- 클로저는 함수가 다른 함수 내에서 정의되고 반환될 때 발생하며, 외부 스코프의 변수에 접근할 수 있는 함수를 의미함.
- 즉, 함수가 생성될 당시의 외부 변수를 기억함. (생성 이후에도 계속 접근 가능)
function outerFunction() {
let outerVar = "I am from outerFunction!";
return function innerFunction() {
console.log(outerVar);
};
}
let innerFunc = outerFunction();
innerFunc();
function makeAdd(i) {
return function (w) {
return i + w;
};
}
let x = makeAdd(1);
console.log(x(1));
console.log(x(2));
let y = makeAdd(2);
console.log(y(1));
console.log(y(2));
let globalFunc;
{
let blockVar = 'a';
globalFunc = function () {
console.log(blockVar);
};
}
globalFunc();
- 일반적으로 자신의 스코프에 없는 것들을 접근 가능.
let f;
{
let o = { note: 'safe' };
f = function () {
return o;
};
}
let oRef = f();
console.log(oRef.note);
oRef.note = ' Not so safe after all!';
console.log(oRef.note);
- IIFE는 함수를 선언하고 즉시 실행하여 변수의 스코프를 제한하는 패턴.
(function(){// IIFE body})();
- 익명 함수를 만들고 그 함수에
()
괄호를 붙여 즉시 호출.
- 불필요한 전역변수가 필요없고, 또한 내부의 변수 접근 방지.
const f = (function () {
let count = 0;
return function () {
return console.log(`I have been called ${++count} time(s).`);
};
})();
f();
f();
호이스팅(Hoisting)
- JS는 전역 스코프 전체를 보고
function
과 var
로 선언한 변수를 맨 위로 끌어 올리는 호이스팅 매커니즘을 따름.
- 선언만 끌어 올라오며, 할당은 끌어올려지지 않음.
- 함수 선언문의 경우는 모든것이 호이스팅 됨.
- 호이스팅은 피해야 함.
- 순서가 얽히면 헷갈리고 유지보수가 어려움.
- 함수는 표현식으로 상수와 변수는
const
와 let
키워드로 이용하면, ReferenceError
를 반환.
함수의 Hoisting
myFunction();
function myFunction() {
console.log('hello world');
}
함수 표현식
- 함수 표현식을 이용하면 Hoisting을 방지.
myFunction();
const myFunction = function myFunction() {
console.log('hello world');
};
변수의 Hoisting
var
키워드에서만 Hoisting.
var
는 또한 함수 스코프만을 따름.
console.log(number);
var number = 2;
- 선언하지 않은 변수가
undefined
가 나오는 이유는,
- JS가 아래와 같이 작동하기 때문.
var number;
console.log(number);
number = 2;
var
는 여러 번 정의하더라도 무시함.
- 또한 새 변수를 만들 수 없고 하나만 존재.
var x = 3;
if (x === 3) {
var x = 2;
console.log(x);
}
console.log(x);
let
과 const
를 이용하면 Hoisting을 방지.
const
키워드
console.log(number);
const number = 2;
스트릭트 모드(strict mode)
var
로 변수 선언하는 것을 잊고 이용하면, JS는 전역 변수를 참조하려 한다고 간주하고 존재하지 않으면 스스로 만들었음. (암시적 전역 변수)
- 스트릭트 모드로 암시적 전역 변수를 허용하지 않을 수 있음.
'use strict'
코드를 코드 맨 앞에 사용.
(function () {
'use strict';
})();