자바스크립트 공부 일지 6

코어자바스크립트 변수

3주차 시작


3주차 부터는 코어 자바스크립트를 읽어보면서 정리해보는 시간을 가지기로 했다. 사실 내용면에서는 모던자바스크립트 딥다이브가 더 상세하긴하다.
아무튼 정리를 시작해보자.

자료형


변수의 데이터는 어떤 순간엔 문자열일 수 있고 다른 순간엔 다른 데이터 타입이 되기도 한다.
이러한 특징을 동적 타입이라고 한다.

js
// no error
let message = "hello";
message = 123456;

숫자형


숫자형은 정수 및 부동소수점 숫자를 나타낸다.

NaN

NaN은 계산 중 에러가 발생했다는 것을 나타내주는 값이다. 부정확하거나 정의되지 않은 수학 연산 사용 시 계산 중 에러가 발생하는데, 이때 NaN이 반환된다.

js
alert( "숫자가 아님" / 2 ); // NaN, 문자열을 숫자로 나누면 오류가 발생합니다.

BigInt

암호 관련 작업같이 아주 큰 숫자가 필요한 경우, 아주 높은 정밀도가 필요한 경우 큰 숫자가 필요하다.

BigInt는 길이 상관없이 정수를 나타낼 수 있다.

js
// 끝에 'n'이 붙으면 BigInt형 자료입니다.
const bigInt = 1234567890123456789012345678901234567890n;

문자형


자바스크립트에선 문자열(string)을 따옴표로 묶습니다.

js
let str = "Hello";
let str2 = 'Single quotes are ok too';
let phrase = `can embed another ${str}`;

따옴표는 세 종류가 있습니다.

  1. 큰따옴표 : "Hello"
  2. 작은따옴표 : 'Hello'
  3. 역 따옴표(백틱, backtick) : 'Hello'

자바스크립트에선 이 둘 차이를 두지 않습니다. 아래와 같이 변수나 표현식을 문자열 중간에 손 쉽게 넣을 수 있습니다.

js
let name = "John";

// 변수를 문자열 중간에 삽입
alert( `Hello, ${name}!` ); // Hello, John!

// 표현식을 문자열 중간에 삽입
alert( `the result is ${1 + 2}` ); // the result is 3

${...}안에는 name같은 변수나 1 + 2 같은 수학 관련 표현식도 넣을 수 있습니다. 물론 더 복잡한 표현식도 넣을 수 있습니다. 이렇게 문자열 사이에 들어간 변수, 표현식은 평가가 끝난 후 문자열의 일부가 됩니다.

이 경우는 백틱 사용시에만 해당된다.

불린형


불린형은 truefalse 두 가지 밖에 없는 자료형입니다.

js
let nameFieldChecked = true; // 네, name field가 확인되었습니다(checked).
let ageFieldChecked = false; // 아니요, age field를 확인하지 않았습니다(not checked)

불린값은 비교 결과를 저장할 때도 사용됩니다.

js
let isGreater = 4 > 1;

alert( isGreater ); // true (비교 결과: "yes")

null


null 값은 오로지 비어있다라는 자료형을 만듭니다.

js
let age = null;

다른 언어에서는 이 null을 '존재하지 않는 객체에 대한 참조`나 널 포인터(null pointer)를 나타낼 때 사용합니다.

하지만 자바스크립트에선 null존재하지 않는(nothing)값, 비어 있는(empty)값, 알 수 없는(unknown)값을 나타내는 데 사용합니다.

undefined


undefined값도 null값처럼 자신만의 자료형을 형성합니다. undefined는 '값이 할당되지 않은 상태'를 나타낼 때 사용합니다.
변수는 선언했지만, 값을 할당하지 않았다면 해당 변수에 undefined가 자동으로 할당됩니다.

js
let age;

alert(age); // 'undefined'가 출력됩니다.

객체와 심볼


객체(Object)형은 특수한 자료형입니다. 객체형을 제외한 다른 자료형은 문자열이든 숫자든 한 가지만 표현할 수 있어 원시 자료형이라고 합니다.
심볼(Symbol)형은 객체의 고유한 식별자(unique identifier)를 만들 때 사용됩니다.

typeof 연산자


typeof연산자는 인수의 자료형을 반환합니다. 자료형에 따라 처리 방식을 다르게 하고 싶거나 변수의 자료형을 빠르게 알아내고자 할 때 유용합니다.

typeof연산자는 두 가지 형태의 문법을 지원합니다.

  1. 연산자 : typeof x
  2. 함수 : typeof(x)

괄호가 있든 없는 결과가 동일하게 작동하죠.

js
typeof undefined // "undefined"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof "foo" // "string"
typeof Symbol("id") // "symbol"
typeof Math // "object" (1)
typeof null // "object" (2)
typeof alert // "function" (3)

마지막 세 줄은 약간의 설명이 필요해 보이네요.

  1. Math는 수학 연산을 제공하는 내장 객체이므로 "Object"가 출력됩니다. 내장 객체는 객체형이죠.
  2. typeof null의 결과는 "Object"입니다. null은 별도의 고유한 자료형을 가지는 특수 값으로 객체는 아니지만, 하위 호환성을 유지하기 위해 오류를 수정하지 않고 남겨둔 상황입니다. 언어 자체의 오류이므로 null이 객체가 아님의 유의해야합니다.
  3. typeof는 피연산자가 함수면 "function"을 반환합니다. 그러므로 typeof alert"function"을 출력해줍니다.

요약


자바스크립트에는 여덟 가지 기본 자로형이 존재합니다.

  • 숫자형 – 정수, 부동 소수점 숫자 등의 숫자를 나타낼 때 사용합니다. 정수의 한계는 ±253 입니다.
  • bigint – 길이 제약 없이 정수를 나타낼 수 있습니다.
  • 문자형 – 빈 문자열이나 글자들로 이뤄진 문자열을 나타낼 때 사용합니다. 단일 문자를 나타내는 별도의 자료형은 없습니다.
  • 불린형 – true, false를 나타낼 때 사용합니다.
  • null – null 값만을 위한 독립 자료형입니다. null은 알 수 없는 값을 나타냅니다.
  • undefined – undefined 값만을 위한 독립 자료형입니다. undefined는 할당되지 않은 값을 나타냅니다.
  • 객체형 – 복잡한 데이터 구조를 표현할 때 사용합니다.
  • 심볼형 – 객체의 고유 식별자를 만들 때 사용합니다.

실행 컨텍스트


호이스팅


Javascript 호이스팅이란, 인터프리터 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다. var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화합니다. 반면 letconst로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않습니다.

js
catName("클로이");

function catName(name) {
  console.log("제 고양이의 이름은 " + name + "입니다");
}

함수 호출이 함수 자체보다 앞서 존재하지만 해당 코드 역시 정상적으로 동작합니다.

호이스팅은 다른 자료형과 변수에도 잘 작동합니다. 변수를 선언하기 전 먼저 초기화 하고 사용할 수 있는 것이죠.

let과 const 호이스팅

letconst로 선언한 변수도 호이스팅 대상이긴하나, var와 달리 호이스팅 시 undefined로 변수를 초기화하지 않습니다. 따라서 변수 초기화를 수행하기 전 읽는 코드가 먼저 나타나면 예외가 발생하죠.

스코프


자바스크립트 스코프는 2가지로 나눌 수 있다. 전역 스코프
코드 어디서든 참조 가능 지역 스코프
함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조 가능
변수를 대상으로 한다면 아래와 같다. 전역 변수 전역에서 선언된 변수로 어디서든 참조 가능 지역 변수 지역내에서 선언된 변수로 그 지역과 지역의 하부 지역에서 참조

렉시컬 스코프

js
var x = 1;

function foo() {
  var x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // ?
bar(); // ?

두가지 패턴을 예측할 수 있는데 첫번째는 함수를 어디서 호출하였는지에 따라 상위 스코프를 결정하는 것이고 두번째는 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이다. 첫번째 방식으로 함수의 상위 스코프를 결정한다면 함수 bar의 상위 스코프는 함수 foo와 전역일 것이고, 두번째 방식으로 함수의 스코프를 결정한다면 함수 bar의 스코프는 전역일 것이다.

프로그래밍 언어는 이 두가지 방식 중 하나의 방식으로 함수의 상위 스코프를 결정한다. 첫번째 방식을 동적 스코프(Dynamic scope)라 하고, 두번째 방식을 렉시컬 스코프(Lexical scope) 또는 정적 스코프(Static scope)라 한다. 자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따른다.

렉시컬 스코프는 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다. 자바스크립트는 렉시컬 스코프를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다. 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다. 위 예제의 함수 bar는 전역에 선언되었다. 따라서 함수 bar의 상위 스코프는 전역 스코프이고 위 예제는 전역 변수 x의 값 1을 두번 출력한다.

암묵적 전역

아래 코드를 보자

js
var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 식별자
  y = 20;
  console.log(x + y);
}

foo(); // 30

이 예제에서 y는 선언하지 않은 식별자이다. 따라서 y = 20이 실행되면 참조 에러가 발생할 것처럼 보인다. 하지만 선언하지 않은 식별자 y는 마치 선언된 변수처럼 동작한다. 이는 선언하지 않는 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문이다. 즉, 브라우저의 전역 객체인 window의 프로퍼티로 저장한다. window.y = 20 하지만 변수 선언없이 단지 전역 객체의 프로퍼티로 추가될뿐 따라서 y는 변수는 아니다. 따라서 변수가 아닌 y는 변수 호이스팅이 발생하지 않는다.

js
// 전역 변수 x는 호이스팅이 발생한다.
console.log(x); // undefined
// 전역 변수가 아니라 단지 전역 프로퍼티인 y는 호이스팅이 발생하지 않는다.
console.log(y); // ReferenceError: y is not defined

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 변수
  y = 20;
  console.log(x + y);
}

foo(); // 30

최소한의 전역변수 사용


js
var MYAPP = {};

MYAPP.student = {
  name: 'Lee',
  gender: 'male'
};

console.log(MYAPP.student.name);

즉시 실행 함수를 이용한 전역변수 사용 억제


전역변수 사용을 억제하기 위해, 즉시 실행 함수(IIFE, Immediately-Invoked Function Expression)를 사용할 수 있다. 이 방법을 사용하면 전역변수를 만들지 않으므로 라이브러리 등에 자주 사용된다. 즉시 실행 함수는 즉시 실행되고 그 후 전역에서 바로 사라진다.

js
(function () {
  var MYAPP = {};

  MYAPP.student = {
    name: 'Lee',
    gender: 'male'
  };

  console.log(MYAPP.student.name);
}());

console.log(MYAPP.student.name);