변수(Variable)와 식별자(Identifier)

 

들어가기 전에...

사람들은 변수(Variable)식별자(Identifier)를 혼용하는 경우가 많습니다.

"내가 혼용했었나?"라고 의문이 들면서 이해가가지 않을 수도 있습니다.

 

혼용을 하면서도 인지하지 못하는 경우가 많기 때문인데,

첫번째로는, 대화의 문맥에 따라 혼용하더라도 상대방이 어떠한 말을 하는지 유추할 수 있기 때문이고

두번째로는, 애초에 혼용해서 사용해왔기때문에 그 차이를 이해하지 못하고 같은 의미로 사용하기때문입니다.

 

"변수 name을 선언 안 한 것 같은데"

가 아닌,

"식별자 name을 선언 안 한 것 같은데"

가 정확한 문장이라면

 

읭? 이러실 수도 있습니다.

제가 읭? 이랬거든요.

 

 

변수(Variable)

변수라는 이름은 '변할 수 있는 수'라는 뜻으로 수학용어를 차용했다고합니다.

즉, 변경 가능한 데이터를 뜻하며 '데이터가 담길 수 있는 공간 또는 그릇'을 말합니다.

그리고 변수라는 이름을 수학용어에서 차용했다고해서 변수에 담기는 데이터가 '숫자'여야만 하는 것은 아닙니다.

 

 

식별자(Identifier)

변수가 되는 어떤 데이터를 식별하는 데 사용하는 이름을 말합니다.

변수명입니다.

 

 

변수식별자 알아보기

자바스크립트에서는 var, let, const 키워드를 통해 변수를 선언할 수 있습니다.

예시에서는 var를 통해 선언하겠습니다.

var name = '홍길동';

문자열 '홍길동' 데이터 값이 변수에 할당됩니다.

이 변수의 식별자(변수의 명)는 name이라고 할당합니다.

 

메모리 할당 개략적 표현1
메모리 할당 개략적 표현2

 

 

두 개념을 정확히 이해하고 분리해서 사용한다고 해서 뭐 달라지는 것? 없습니다.

하지만 그렇다고 해서 변수와 식별자의 차이를 이해하지않고 사용하는것은 바람직하지 않습니다.

(알고보면 정말 간단하니 더더욱 말이죠)

 

모르고 혼용하던 날들을 반성하며,

앞으로는 알고 혼용하던지~ 말던지, 다짐하며 변수와 식별자 개념, 차이에 대해 공부해보았습니다.

 


지식 출처

코어 자바스크립트:핵심 개념과 동작 원리로 이해하는 자바스크립트 프로그래밍,(저자 정재남)

콜 스택(Call stack)과 힙(Heap)

자바스크립트 엔진이 자바스크립트를 실행할 때 원시 타입 및 참조 타입을 저장하는 메모리 구조

  • 콜 스택 : 원시타입 값함수 호출의 실행 컨텍스트(Execution Context) 저장
  • : 객체, 배열, 함수와 같이 크기가 동적으로 변할 수 있는 참조타입 값 저장

 

동작 원리

이미지 및 코드 출처 : https://github.com/baeharam/Must-Know-About-Frontend/blob/master/Notes/javascript/stack-heap.md

let a = 10;
let b = 35;
let arr = [];

function func() {
  const c = a + b;
  const obj = { d: c };

  return obj;
}

let o = func();

 

1. 전역 실행 컨텍스트(GEC, Global Execution Context) 생성 후 원시값은 콜 스택에 참조값은 힙에 저장

  • 주소 값과 배열, 함수 값을 제외하고는 모두 원시 값(그리고 함수의 호출 값)이므로 콜 스택에만 저장된다.

  • 이때 함수의 호출은 콜 스택에 주소 값은 저장되나 값이 없다.

  • 주소 값과 배열, 함수의 값은 참조 값이므로 콜스택과 힙에 저장된다.

 

2. 함수 func()가 실행되면 새로운 함수 실행 컨텍스트(FEC, Function Execution Context)가 생성되며 원시 값이나 참조 값에 따라 각각 콜 스택, 힙에 동일하게 저장된다.

 

함수 컨텍스트의 원칙 4가지

  • 전역 컨텍스트 생성 후 함수 호출 시 함수 컨텍스트가 생성된다.

  • 컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope, chain, this가 생성된다.

  • 컨텍스트 생성 후 함수가 실행되는데, 사용되는 변수들은 변수 객체 안에서 찾고 없으면 스코프 체이닝으로 찾는다.

  • 험수 실행이 마무리되면 해당 컨텍스트는 사라진다.

    • 다만 클로저일 경우에는 제외되며 전역 컨텍스트는 페이지 종료 시 사라진다.

 

 

 

3. 함수가 종료되면 함수 실행 컨텍스트는 사라진다.

 

 

4. 전체 코드 종료 후 페이지가 종료되면 전역 실행 컨텍스트가 사라진다.

이때 전역 실행 컨텍스트 제거에 따라 스택의 값이 없기 때문에 힙에서 참조하고 있던 값이 없어지므로 가비지 컬렉터에 의해 제거된다.

자바스크립트는 객체 생성 시 자동으로 메모리 할당, 참조된 값이 없으면 자동으로 가비지 컬렉션에 의해 메모리 해제가 된다.

++ 오해하면 안되는 것은 가비지 컬렉션이 자동으로 해제해준다고 해도 메모리 관리는 해줘야한다.

 

 


출처 및 참조

콜 스택(Call stack)과 힙(Heap)

https://github.com/baeharam/Must-Know-About-Frontend/blob/master/Notes/javascript/stack-heap.md

실행 컨텍스트

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

 

모듈 시스템: CommonJS, AMD, UMD, ES6

여러 기능에 관한 코드가 모여있는 하나의 파일

 

 

장점

  • 의존성을 줄여주기때문에 유지보수 용이
  • 모듈만의 네임스페이스 화 
  • 필요할때마다 재사용

위와 같은 이유로 모듈의 개념이 필요했고, Javascript에서는 여러가지를 시도하였다.

 

CommonJS

  • 서버사이드 및 데스크탑 어플리케이션에서 지원하기 위해 만든 방식으로 Node.js에서 사용 가능하다.
  • require, module.exports를 사용하는 방식이다. 
  • 여기서 module.exports의 module은 예약어이며 현재 모듈에 대한 정보를 가지고 있는 객체이다.
  •  
// search.js
const getWord = () => {};

module.exports = {
    getWord
};

// index.js
const searchModule = require('./search.js');
searchModule.getWord();

module.export를 exports로도 사용이 가능하며 관계를 명확히 이해해야한다.

  • module.exports빈 객체를 참조
  • exportsmodule.exports 참조
  • requiremodule.exports를 리턴받음

 

module.exports = { searchModule }; 
// module.exports는 빈객체를 잠조하므로 -> 빈객체 = { searchModule }

exports.searchModule = searchModule; 
// exports가 module.exports를 참조하므로 -> module.exports.searchModule라는 객체 = searchModule 

exports는 항상 module.eports를 참조하기 때문에 exports를 사용하면 직접 module.exports를 수정하지 않고 객체의 멤버를 만들거나 수정이 가능하다. 잠재적인 버그를 피할수 있다는데???? 추후 자세히 알아보자.

 

 

AMD(Asynchronous Module Definition)

  • 비동기 모듈에 대한 표준안.
  • 모듈 로딩이 다 될때까지 동기로 기다릴 수 없기 때문에 비동기 모듈방식이 브라우저쪽에서 더 큰 효과를 발휘한다.
  • define(), require()를 사용하는 방식이다.
  • 모듈로더로는 RequireJS를 추천한다.

HTML

<!DOCTYPE html>
<html lang="ko">
<head>
  <title>Document</title>
</head>
<body>
  <!-- data-main="require가 로드된 후 실행될 파일" src="requireJS에서 비동기로 사용할 파일" -->
  <script data-main="index.js" src="require.js"></script>
</body>
</html>

JS

// index.js
require.config({
  // 기본 경로
  baseUrl: '/',
  // 모듈에 해당하는 경로
  paths: {
    searchModule: 'searchModule',
    loaderModule: 'loaderModule',
}});

// 첫번째 인자에 해당하는 모듈이 로드되었을 경우 a로 받아서 getWord() 함수를 호출하는 콜백함수 실행.
// 의존성 모듈을 지정해주는 것
require(['searchModule'], (searchModule) => {
	searchModule.getWord();
});

// searchModule.js
// define을 통해 정의, require에서 의존성 모듈을 설정한 것처럼.. 
// 여기서도 콜백함수가 실행되기전 모듈을 지정할 수 있다.
define(() => {
  return {
  	getWord: () => 'word'
  }
});

 

 

UMD(Universal Module Definition)

  • CommonJS, AMD를 통합하기 위한 하나의 패턴.
(function (root, factory) {

    // AMD
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);

    // CommonJS
    } else if (typeof module === 'object' && module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();

    // Browser
    } else {
        // Browser globals (root is window)
        root.returnExports = factory();

}}(typeof self !== 'undefined' ? self : this, function () {
    // Just return a value to define the module export.
    // This example returns an object, but the module
    // can return a function as the exported value.
    return {};
}));

공식코드 출처 : https://github.com/umdjs/umd/blob/master/templates/returnExports.js

 

 

 

ES6(ES2015) 방식

  • import, export 방식을 사용한다.
  • 모든 브라우저가 지원하지 않기 때문에 Babel의 @babel/plugin-transform-modules-commonjs을 통해 컴파일한다.
  • export하는 방식에 따라 import해서 사용하는 방식이 달라진다.
// 1. export default
const getWord = () => {};
export default getWord;

// 1. import
import getWord from 'searchJS';

// 2. export {}
const getWord = () => {};
export { getWord };

// 2. import
import {getWord} from 'searchJS';

default export는 모듈내에서 한번만 사용가능하며 named export는 여러번 사용가능하다.

namex dexport로 내보내면 { }를 통해 불러오고 as 키워드를 통해 다른이름으로 사용하거나 *를 통해 한번에 불러오거나 등의 작업을 할 수 있다.

참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/export

 

 

 


 

모듈 시스템: CommonJS, AMD, UMD, ES6

https://github.com/baeharam/Must-Know-About-Frontend/blob/master/Notes/javascript/module.md

 

 

즉시 실행 함수 (IIFE, Immediately-Invoked Function Expression)

  • 즉시 실행하여야하지만 전역 스코프(Global Scope)를 오염시키지 않으려고 할때 사용한다.
  • 함수 리터럴을 ( )로 감싼 뒤 바로 실행하는 형태가 일반적이며 기명도 가능하고 익명도 가능하다.
  • ( )로 감싸주는 이유는 자바스크립트는 function(){ } 키워드를 사용할 경우 파서가 선언문으로 인지한다.
  • 선언문은 해석기에서 실행 후 사라지기에 값으로 존재하지 않는다. 때문에 "함수 표현식"을 통해 명시적으로 나타내줘야한다.
  • ( )를 붙이는 것 외에도 연산자를 앞에 붙일 경우에도 즉시 실행된다.
-function(a, b){ return console.log(a + b) })(1,2) // 3
// 즉시 실행됨

하지만 보통 ( )로 묶는 표현 방법을 사용한다.

(function(a, b){ return console.log(a + b) })(1,2) // 3
// 즉시 실행됨

;(function(a, b){ return console.log(a + b) })(1,2) // 3
// 즉시 실행되며 앞에 값과 이어진 값으로 평가받는것을 방지할 때 세미콜론을 사용한다.

 

함수 선언문이 아닌 함수 표현식으로 작성해야한다.

const hello = function(){} // (X)

(function(){
    // ..
})(); // (O)

(function(){
    // ..
}()); // (O)

 

단순히 위의 예시는 코드 스타일의 차이이나 화살표함수일 경우 아래 예시처럼 첫번째 방법만 사용이 가능하다.

(() => {
    // ..
})(); // (O)

(() => {
    // ..
}()); // (X)

 

아래와 같이 호출하는 경우 함수 호출을 하기 위한 호출대상이 명세에서 말하는 Member Expression 이어야하나 화살표함수는 Assignment Expression 이기때문에 불가능하다고 한다. 

(화살표 함수의 => 화살표는 연산자가 아닌 것으로 취급되며 화살표 함수 자체의 파싱이 특별하게 취급되기 때문이라고 하는데,  Assignment Expression로 인식되나봄???)

 

참고 ↓↓↓↓

https://stackoverflow.com/questions/34589488/es6-immediately-invoked-arrow-function/34589765#34589765

 

ES6 immediately invoked arrow function

Why does this work in a Node.js console (tested in 4.1.1 and 5.3.0) but doesn't work in the browser (tested in Chrome)? This code block should create and invoke an anonymous function that logs Ok. ...

stackoverflow.com

 

위와 같은 이유로 ( ) 없이 사용하는 ! 연산자또한 즉시실행 함수 표현식에서 문법오류가 발생한다.

((a, b) => a + b)(1, 2); // 3
!(a, b) => a + b)(1, 2); // Uncaught SyntaxError

 


 

출처 및 참고

IIFE (Immediately-Invoked Function Expression)

https://github.com/baeharam/Must-Know-About-Frontend/blob/master/Notes/javascript/iife.md

 

자바스크립트의 IIFE

https://velog.io/@doondoony/javascript-iife

+ Recent posts