text/JavaScript

javascript 변수 관련 간략정리 (변수, 스코프, 호이스팅, 클로져, 함수, 즉시 실행 함수)

hoonzii 2023. 6. 14. 20:40
반응형

 

이력서에 " js 써봤다" 혹은 "쓸 줄 안다."라고 적으면 물어보는 것들이 있는데 여러 번 질문받았던 변수 관련 개념들에 대해 정리해 봤다.

(js에 대해 잘 모른다고 답변했음에도 불구하고! 매번 물어보는!!)

 

변수

변수는 값을 저장하는 메모리 공간!

크게 3가지의 상태로 나타남

 

1. 선언

  • 변수를 정의 (정보를 담을 공간을 정의)
let variable;

2. 초기화

  • 선언된 후, 값을 넣어 정의 (공간을 만들고, 공간에 정보를 집어넣기)
let variable = "hello world";

3. 할당

  • 코드가 실행될 때 (run) 메모리 위에 변수 공간이 지정된 상태

크롬 개발자 도구에서 예시

스코프(scope, 범위 혹은 그냥 스코프)

변수의 유효한 범위

해당 변수를 사용할 때 접근이 가능한 범위라고 해석하면 문제없을듯하다.

변수의 위치에 따라 접근이 가능하고, 불가능하고 가 나뉘게 되는데 이때 개념이 scope

 

전역 스코프

  • 코드 상 어디서든 접근이 가능한 경우를 일컫는다.

함수 스코프

  • javascript는 기본적으로 함수스코프
  • 함수 몸체에 선언한 변수는 해당 함수 안에서만 접근할 수 있음

블록 스코프

  • 중괄호 ({})로 둘러싸인 범위 내에서 변수 사용 가능
  • ES6부터 등장?

 

javascript는 변수를 정의 시 3가지의 키워드를 이용

let, const, var

const

  • 블록 스코프 (block scope)
  • 값이 변하지 않는 상수를 선언할 때 사용
  • 초기화 후 재 할당 불가능

let

  • 블록 스코프 (block scope)
  • 재선 언 불가능 : 같은 이름으로 다시 선언할 수 없음
  • 호이스팅(hoisting) : 변수가 선언되기 전에 사용 가능하며, 선언문은 최상위에 위치한 것으로 취급됨

var

  • 함수 스코프(function scope) 이거나 전역 스코프(global scope)
  • 재선 언 가능 : 같은 이름으로 다시 선언 가능
  • 호이스팅(hoisting) : 변수가 선언되기 전에 사용 가능하며, 선언문은 최상위에 위치한 것으로 취급됨
  • let과 const가 등장하기 전에는 자바스크립트에서 변수를 선언할 때 대부분 var를 사용했음

*const(es6부터), var의 경우 각각 상수를 의미하는 constant, 변수를 의미하는 variable이라는 단어에서 유래된 건 알겠는데, es6부터 let이라는 키워드로 변수 선언할 수 있게 추가되었다. 근데 왜 let일까?

Why was the name 'let' chosen for block-scoped variable declarations in JavaScript?

 

Why was the name 'let' chosen for block-scoped variable declarations in JavaScript?

I understand why var takes that name - it is variable, const - it is a constant, but what is the meaning behind the name for let, which scopes to the current block? Let it be?

stackoverflow.com

영어 논문에서 변수를 정의할 때 주로

‘New Idea { Let x be something else... prove something } Conclude New Idea'

요렇게 들 많이 사용한다고 한다. 이때 x를 ~라고 정의하자 에서 따왔다고 한다.

 

호이스팅

위에 생소한 단어가 등장했다.

var의 호이스팅이라는 개념인데, 호이스팅이란 javascript의 인터프리터가 실행 전

변수와 함수의 메모리 공간을 선언 전 미리 할당해 놓는 것을 말한다.

 

var의 경우, 호이스팅으로 위치에 상관없이 코드 최상단에 선언된 것 같이 동작하게 된다.

예를 들어, 아래와 같은 코드의 경우

console.log(num);
var num;
num = 9;

위 코드는 다른 언어(예를 들어 java) 같은 경우, 선언 및 할당 이전에 변수를 사용하고자 하면 에러가 나지만 var의 호이스팅으로

var num; // 최상단 이동
console.log(num);
num = 9;

과 같은 결과를 나타낸다. 이유는 var의 경우, 선언 후 undefined로 변수가 “초기화” 되기 때문에 사용이 가능한 것이다.

그러나 선언 없이 초기화할 경우, ReferenceError(참조에러)가 나타난다.

console.log(num2);
num2 = 6;

반대로 초기화 후 사용할 경우, referenceError 없이 동작한다.

num3 = 6;
console.log(num3);

*함수 역시 호이스팅 대상이다.

sayHello(); // 선언보다 사용이 앞서지만 동작 가능!
function sayHello(){ console.log("hello"); } 

let, const 역시 호이스팅 대상이지만 var랑 다르게 undefined로 초기화되지 않기 때문에 초기화 이전에 사용 시 에러가 나오게 된다.

 

클로저라는 개념

클로저는 정의에 따르자면

함수가 선언되었을 때 함수와 함수의 환경과의 조합이다.

위에 정의한 스코프는 함수를 호출할 때 정해지는 게 아니라 함수가 어디에 선언되어 있는지에 따라 정해진다.

이걸 렉시컬 스코핑(Lexical scoping)이라고 한다.

function sayName() {
	var name = "hoonzi";
	function call() {
		console.log(name);
	}
	call();
}
sayName();

위와 같은 코드가 존재할 때 sayName 함수를 실행시킨다면 어떤 결과가 나올까?

sayName 실행 → call 실행 → console.log 실행 → sayName 내 name 참조

함수가 중첩됐을 때 함수가 참조할 자기 자신 내의 변수가 없다면 외부에서 참조가 가능하고 렉시컬이라는 말은 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다.

(함수와 함수가 선언된 위치(환경)를 고려!!)

var name = "hoonzi"

function sayName() {
	var name = "hoonzinope"
	function call() {
		console.log(name); // "hoonzinope"
	}
	call();
}
sayName();
console.log(name); // "hoonzi"

위 코드의 경우 call이 호출될 때, name과 console.log(name)에서의 name은 다른 값을 갖게 된다. sayName 함수 내에서만 접근 가능한 변수를 생성할 수 있게 되는 것이다.

 

그전에 우선

함수 ——

javascript의 함수는 함수 선언식, 함수 표현식 두 가지로 선언이 가능하다.

함수 선언식의 경우,

function sayHello() {
	console.log("hello");
}
sayHello();

함수 표현식의 경우,

var func = function () { console.log("hello"); }
func();

함수 선언식의 경우, 호이스팅의 대상 / 함수 표현식의 경우 호이스팅 대상이 아니다.

고로, 아래와 같은 경우

func();
var func = function() { console.log("hello"); }

 

함수 표현 이전에 선언된 func1 변수는 var 호이스팅에 의해 undefined로 초기화되어 있고,

함수가 아니기 때문에 호출 시 함수가 아니라는 에러가 뜨게 된다.

 

함수 표현식의 경우

  • 콜백 인자로 넘겨질 수 있고,
  • 클로저로 이용될 수 있다.

함수 표현식의 경우, 클로저를 이용해 java의 클래스처럼 이용이 가능하다.

function makeAddFunc(x) {
	y = 10;
	return function(z){
		return x+y+z;
	}
}

add5 = makeAddFunc(5); // add5 = function(z) { return 5+10+z; }
add10 = makeAddFunc(10); // add10 = function(z) { return 10+10+z; }

console.log(add5(1));
console.log(add10(1));

 

또한 함수를 클래스처럼 이용가능하게 하고

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() { return this.name; }
    this.getAge = function() { return this.age; }
    return this;
}

var person = Person("hoonzi",14);
console.log(person.getName());
// "hoonzi"
console.log(person.getAge());
// 14

 

변수처럼 함수의 파라미터로 넘길 수 있는데

이런 특징과 클로저의 특징을 합치면

function makeCounter(countOrder){ // 함수를 인자로 받음
    var count = 0;
    return function() {
        count = countOrder(count); // 넘겨받은 함수를 이용
        return count;
    }
}

function increase(num) {
    return ++num;
}

function decrease(num) {
    return --num;
}

adder = makeCounter(increase);
console.log(adder(1));
console.log(adder(1));

miner = makeCounter(decrease);
console.log(miner(1));
console.log(miner(1));

함수 자체를 변수로 취급해 넘길 수 있고, 넘겨진 함수는 선언된 함수 내 변수(count)에 접근해 값을 변경시킨다.

대충 이 정도로 정리하면 다음엔 대답할 수 있겠지?

 

2023-06-15 추가 *********

위에 자료들 찾다가 발견한 키워드인 “즉시실행함수”에 대해 추가되면 좋을 거 같아서 여러 글들을 보고 정리

 

즉시 실행 함수란?

함수 선언식이 아닌 표현식의 일종으로 함수가 생성되고 바로 실행되는 함수를 일컫는다

그럼 함수표현식과는 뭐가 다르냐. 동어반복 같지만 생성되고 바로 실행된다는 점이 크게 다르다.

// 예시 1
let func1 = function() { console.log("hi!"); }
func1(); // -> 호출되어야 console.log 실행
// 예시 2
let func2 = (function() { console.log("hi!"); })();
// 선언 즉시 실행

예시 1번의 경우, 함수가 표현식으로 선언됐지만 func1() 호출 시에만 동작하는 반면

예시 2번의 경우, 선언 즉시 동작하며 func2를 함수처럼 호출 시 에러가 나는 걸 확인할 수 있다.

예시 1번의 경우
예시 2번의 경우

괄호를 양쪽에 써주는 이유는 함수를 함수 선언문이 아닌 함수 표현식이라는 걸 확실히 해주기 위해서 인데, 예시 1번처럼 명확하게 함수 표현식이 맞을 경우엔 생략이 가능하다.

let func1 = function() { console.log("hi"); }();
// 결과는 "hi" 

 

어떻게 쓰는지(작성하는지)?

즉시 실행 함수는 위 코드에 써진대로 함수 표현식과는 살짝 표현이 다르다.

함수 표현이면서 즉시 실행시키기 위해 함수 키워드(function) 앞에

unary expression(!,+,-,~,… ) 붙이거나 혹은 void 키워드를 붙이거나

그냥 ( )로 감싸준다.

그리고 함수 호출처럼 뒤에 ()를 붙인 뒤 선언해 준다.

 

특징?

즉시 실행 함수 내 변수 값들은 함수 바깥에서 참조가 불가능하다. (private 변수 생성 효과)

(function() {
	var variable;
	
	function init() { variable = 10; console.log(variable); }
	init();
})();
console.log(variable);

즉시 실행 함수 바깥에서 내부 변수인 variable 참조하려고 할 때 에러가 나는 걸 볼 수 있다.

 

한번 선언 이후로 사라지기 때문에 싱글톤 패턴처럼 사용가능

위 구현된 makeCounter를 즉시 실행 함수로 다시 구현

let counter = (function() {
	let count = 0;
	return {
		increase : function() { count += 1; console.log(count); },
		decrease : function() { count -= 1; console.log(count); }
	}
})();

 

언제 쓰는지? 왜 쓰는지?

자바스크립트 모듈을 만들 때 사용한다.

가령 a.js와 b.js를 포함하는 html이 존재한다고 했을 때

<script src="./a.js"></script>
<script src="./b.js"></script>

//a.js
let temp = 1;
console.log(temp);

//b.js
let temp = 2;
console.log(temp);

동일한 변수 이름을 참조해 동작할 경우, 같은 html 파일 내 동작하니 에러가 나게 된다.

 

즉시 실행 함수로 선언 (function() { …. } )(); 후 html에서 불러오게 되면 각 변수는 함수 내에서만 유효하고, 함수 외부에서 참조할 수 없기 때문에 동일한 이름으로 선언해도 동작이 가능하게 된다.

 

a.js 와 b.js를 각각 아래와 같이 코드를 고치고 동작시

//a.js
(function () {
  let temp = 2;
  console.log(temp);
})();

//b.js
(function () {
  let temp = 3;
  console.log(temp);
})();

잘 동작하는 걸 확인할 수 있다.

 

 

 

참고링크

- 변수

  • https://bbeeyaks-moment.tistory.com/entry/%EB%B3%80%EC%88%98%EB%9E%80-%EB%B3%80%EC%88%98%EC%9D%98-%EC%84%A0%EC%96%B8%EC%B4%88%EA%B8%B0%ED%99%94%ED%95%A0%EB%8B%B9

- let 유래

  • https://stackoverflow.com/questions/37916940/why-was-the-name-let-chosen-for-block-scoped-variable-declarations-in-javascri

- 호이스팅

  • https://developer.mozilla.org/ko/docs/Glossary/Hoisting

- 클로져

  • https://www.sitepoint.com/demystifying-javascript-closures-callbacks-iifes/
  • https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
  • https://poiemaweb.com/js-closure

- 함수선언

  • https://joshua1988.github.io/web-development/javascript/function-expressions-vs-declarations/

- 즉시실행함수

  • https://vvkchandra.medium.com/essential-javascript-mastering-immediately-invoked-function-expressions-67791338ddc6
  • https://beomy.tistory.com/9
  • https://velog.io/@jakeseo_me/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%9D%BC%EB%A9%B4-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-33%EA%B0%80%EC%A7%80-%EA%B0%9C%EB%85%90-8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%84%EC%88%98%EC%9A%94%EC%86%8C-IIFE-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0
반응형