JavaScript 문법

지영·2022년 9월 23일
0

JavaScript

목록 보기
37/37
post-thumbnail

https://media.vlpt.us/images/jen_jyseo/profile/11552755-7ca2-4f22-8f2c-6fbd037a1ca8/IMG_4377.GIF?w=240

❓JavaScript의 자료형과 JavaScript만의 특성은 무엇일까 ?

JavaScript는 느슨한 타입의 동적 언어이다.

JavaScript는 느슨한 타입(loosely typed)의 동적(dynamic) 언어이다.

JavaScript의 변수는 어떤 특정 타입과 연결되지 않으며, 모든 타입의 값으로 할당 (및 재할당) 가능하다.

let foo = 42 // foo가 숫자
foo = 'bar' // foo가 이제 문자열
foo = true // foo가 이제 불리언

JavaScript는 느슨한 타입의 동적 언어이기 때문에 변수 선언시 변수의 타입을 미리 선언하지 않아도 된다는 특징을 가지고 있다.


동적 언어의 문제점과 보완 방법

이러한 특징으로 인해 변수의 타입이 변하지 않아야 하거나 변수 타입을 체크해야 하는 경우 문제에 직면할 수 있다.

이러한 문제는 정적 타입 체크와 강력한 문법을 추가한 TypeScript를 사용하여 보완할 수 있다.

정적 타입 언어는 런타임 이전에 타입이 올바른지에 대한 검사를 시행하며, 동적 타입 언어는 런타임에 프로그램의 타입이 올바른지에 대한 검사를 실행한다.

만약 래퍼런스 오류를 유발하는 코드가 존재한다면 정적 언어는 컴파일하는 과정에서 오류를 출력하는 반면 동적 언어는 해당 구문이 실행되는 시점에서 오류를 출력한다.

TypeScript는 정적 타입 언어이기 때문에 런타임 이전(컴파일 과정)에 오류를 출력한다.

참조


JavaScript 형변환

자바스크립트는 변수 선언 시 타입을 지정하지 않기에 용도에 따라 개발자가 명시적으로 타입을 지정해주기도 하고, 자바스크립트 엔진 내에서 암시적으로 타입을 변환하기도 한다.

JavaScript 형변환에는 명시적 형변환과 암시적 형변환이 있다.

명시적 형변환

String()Number()\Boolean() 등의 함수를 사용하여 명시적으로 타입을 변환하는 형변환이이다.

//String
String(22) // "22"

암시적 형변환

비교, 산술, 논리 연산자 등을 이용시 자바스크립트 엔진이 필요에 따라 자동으로 타입을 변환하는 형변환이다.

  • + 연산자 사용 시 피연산자 중 문자열이 하나라도만 있으면 String Type으로 변환된다.undefined 나 null도 문자열로 변환된다.
'hi' + null; // 'hinull'
'hi' + 'world'; // 'hiworld'
  • + 연산자를 제외한 산술 연산자(-, /, *, >, < 등) 사용시 Number Type 으로 변환된다.
/* + 연산자를 사용할 경우 */
"1" + "1" // '11'

/* (-, /, *, >, <) 등의 산술 연산자를 사용한 경우 */
"1" - "1" //0
"1" * "1" //1
"1" / "1" //0
[0] - 1 //-1

이 때 숫자 이외의 글자가 들어있는 문자열, 배열 및 undefined는 Number Type으로 변환되지 않고 NaN(Not a Number)을 반환한다.

"hi" - 1 //NaN
["hi"] -1 // NaN
undefined - 1 // NaN
  • 동등 비교 연산자(==)를 사용할 경우 좌항의 형이 변환되어 평가된다.
true == 1;    // true  (true를 1로 변환) 
false == 0;   // true  (false를 0으로 변환)
'1' == true;  // true  ('1'를 true로 변환)
'1' == 1;     // true  ('1'를 1로 변환)
  • NOT 논리 연산자(!)를 두 번 사용할 경우 Boolean Type으로 변환한다.
let string = "";

!!string; // false

동등 비교 연산자와 일치 비교 연산자의 차이

동등 연산자 (==)

두 피연산자의 자료형을 일치시킨 후, 엄격하게 비교를 수행

일치 연산자 (===)

자료형 변환 없이 두 피연산자가 엄격히 같은지 판별

동등 비교 연산자(==, !=)와 일치 비교 연산자(===, !==)의 가장 큰 차이점은 형변환의 유무이다.

일치 비교 연산자(===, !==)는 형변환을 하지 않은 상태에서 두 피연산자가 서로 같은지 비교를 진행한다.

일치 비교 연산자(===, !==)는 두 피연산자의 자료형까지 모두 일치/불일치해야하는 경우에 사용이 적절하다.


undefined와 null

undefined

undefined는 ‘아무 값도 할당받지 않은 상태’를 의미한다.

변수 선언에 의해 확보된 메모리 공간을 처음 할당이 이뤄질 때까지 빈 상태로 내버려두지 않고 자바스크립트 엔진이 undefined로 초기화한다.

undefined는 개발자가 의도적으로 할당하기 위한 값이 아니라 자바스크립트 엔진이 변수를 초기화 할 때 사용하는 값이다.

null

null은 원시 자료형 null로 분류된다.

프로그래밍 언어에서 null은 변수에 값이 없다는 것을 의도적으로 명시할 때 사용한다.

변수에 null을 할당하는 것은 변수가 이전에 참조하던 값을 더 이상 참조하지 않겠다는 의미이다.

이는 이전에 할당되어 있던 값에 대한 참조를 명시적으로 제거하는 것을 의미하며, 자바스크립트 엔진은 누구도 참조하지 않은 메모리 공간에 대해 가비지 콜렉션을 수행할 것이다.

참조


❓JavaScript 객체와 불변성이란 ?

원시값과 참조값

JavaScript 데이터 타입은 크게 원시값과 참조값으로 나뉜다.

원시값

원시값에는 Numver, String, Boolean, null, undefined 이 있다.

참조값

참조값에는 Object이 있고, 하위로 Array, Function, RegExp이 있고

ES6에사 Map, Set, WeakMap, WeakSet등도 추가되었다.

원시값과 참조값의 차이

원시값은 값을 그대로 변수에 할당하고,
참조값은 값이 저장된 주소값을 변수에 할당(참조)한다는 차이가 있다.

원시값과 참조값이 각각 어떻게 메모리에 저장되는지 알고싶다면 클릭

원시값과 참조값이 어떤 위치에 저장되는지 알고싶다면 클릭


불변객체를 만드는 방법

불변객체(**immutable object)**란?

불변객체(immutable object)는 생성 후 그 상태를 바꿀 수 없는 객체를 말한다.

생성 후 상태를 바꿀 수 없다는 것은 무슨 의미일까?

이는 메모리 힙에서 그 객체가 가리키고 있는 데이터 자체의 변화가 불가능하다는 것을 의미한다!

(객체는 실제 데이터는 메모리에 저장하고 그 힙 영역을 가리키는 주소 값을 콜스텍에서 가지고 있다.)

불변객체는 언제 사용할까?

자바스크립트에서는 참조값인 객체의 내부 프로퍼티를 수정했을 때는 가변성이라는 성질로 인하여 기존의 객체도 변하게 된다.

객체를 복사해서, 프로퍼티를 변경하고 싶지만 원본객체는 유지하고 싶을 때 불변 객체를 만들어 사용할 수 있다.

불변객체를 만드는 법

불변 객체를 만드는 방법은 다양한데, 대표적으로 얕은 복사와 깊은 복사를 하는 방법이 있다.

불변객체로 만들어야 할 객체의 프로퍼티 값으로 또 다른 참조값이 들어가있다면 얕은 복사만으로는 불변 객체를 만들 수 없다.

깊은 복사를 할 경우 객체의 프로퍼티 값으로 들어가 있는 또 다른 참조값까지 복사할 수 있도록 재귀적으로 복사를 수행해야 완벽한 불변 객체를 만들 수 있다.

let example = {
    name : "sjy",
    gender : 'female',
    friend : { 
        name : "ldy"
    }
}

let copyObject = function(target){
    let result = {};
    for(let prop in target){
        result[prop] = target[prop];
    }
    return result;
}

let example2 = copyObject(example);

example2.name = "kim";
example2.friend.name = "ldy2";

console.log(example === example2); // false
console.log(example.name, example2.name);// sjy kim
console.log(example.friend.name, example2.friend.name); // ldy ldy2 

얕은 복사와 깊은 복사

얕은 복사 (Shllow Copy)

얕은 복사란 객체를 복사할 때 원본 객체의 프로퍼티 값과 복사된 객체의 프로퍼티 값이 같은 주소값을 가리키고있는 객체를 말한다.

객체의 프로퍼티 값으로 참조값이 있을 경우 이 프로퍼티가 원본 객체의 프로퍼티와 동일한 주소값을 참조하고 있다면 이를 얕은 복사라고 한다.

얕은 복사 방법

⭐️ Object.assign()

첫번째 인자로 전달한 객체에 다음인자로 들어온 객체의 프로퍼티를 복사한다.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = Object.assign({}, obj);

copiedObj.b.c = 3

obj === copiedObj // false
obj.b.c === copiedObj.b.c // true

⭐️ Spread 연산자

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = {...obj}

copiedObj.b.c = 3

obj === copiedObj // false
obj.b.c === copiedObj.b.c // true

깊은 복사 (Deep Copy)

깊은 복사란 객체를 복사할 때 원본 객체의 프로퍼티 값과 복사된 객체의 프로퍼티 값이 다른 주소값을 가리키고있는 객체를 말한다.

⭐️ 재귀함수를 이용한 복사

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

function copyObj(obj) {
  const result = {};

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      result[key] = copyObj(obj[key]);
    } else {
      result[key] = obj[key];
    }
  }

  return result;
}

const copiedObj = copyObj(obj);

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false

⭐️ JSON.stringify()

JSON.stringify()는 객체를 json 문자열로 변환하는데 이과정에서 원본 객체와의 참조가 모두 끊어진다. 객체를 json 문자열로 변환후 JSON.parse()를 이용해 다시 자바스크립트 객체로 만들어주면 깊은 복사가 된다.

하지만 이방법은 사용하기는 쉽지만 다른 방법에비해 아주 느리다고 알려져있다.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = JSON.parse(JSON.stringify(obj));

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false

⭐️ 라이브러리 사용

lodash 라이브러리를 사용하면 깊은 복사를 더 쉽게 할 수 있다.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = _.cloneDeep(obj);

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false

❓호이스팅과 TDZ는 무엇일까 ?

호이스팅, TDZ, 스코프

호이스팅(Hoisting)

호이스팅은 변수의 선언문을 유효 범위의 최상단으로 끌어올리는 행위이다!

var 키워드로 선언한 변수는 선언 단계 와 초기화 단계 가 동시에 진행된다.

var 키워드로 선언한 변수는 선언 단계 와 초기화 단계 가 동시에 진행된다.

코드 평가 시점에 환경 레코드에 식별자를 키로 등록한 다음, 암묵적으로 undefined를 바인딩한다.

이러한 이유로 var키워드로 선언한 변수는 코드 실행 단계에서 변수 선언문 이전에도 참조가 가능하다. 단, 변수 선언문 이전에 참조한 변수의 값은 언제나 undefined이다.

var 키워드로 선언한 변수에 할당한 함수 표현식도 이와 동일하게 동작한다.

함수 선언문으로 선언한 함수

함수 선언문으로 정의한 함수가 평가되면 함수 이름과 동일한 이름의 식별자를 환경 레코드에 바인딩된 객체에 키로 등록하고 생성된 함수 객체를 즉시 값으로 할당한다.

이것이 함수 호이스팅이 발생하는 원인이다.

함수 선언문으로 정의한 함수는 코드 실행 단계에서 함수 선언문 이전에도 호출가능하다.

let, const 로 선언한 변수

let,const 키워드로 선언한 전역 변수(let,const 키워드로 선언한 변수에 할당한 함수 표현식 포함)는 환경 레코드에 등록되고 관리된다.

let, const 키워드로 선언한 변수는 전역 객체에서 선언되어도 전역 객체의 프로퍼티(Window의 프로퍼티)가 되지 않고 전역 환경 레코드의 선언적 환경 레코드에 등록되고 관리된다.

let, const 키워드로 선언한 변수는 전역 객체의 프로퍼티가 되지 않기 때문에 전역객체의 프로퍼티로서 참조할 수 없다.

const 키워드로 선언한 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.

let, const 키워드로 선언한 변수도 변수 호이스팅이 발생하는 것은 변함이 없다.

let, const 로 선언한 변수는 런타임에 초기화 단계, 즉 런타임에 실행 흐름이(컨트롤이) 변수 선언문에 도달하기 전까지 일시적 사각지대(TDZ[Temporal Dead Zone])에 빠지기 때문에 참조할 수 없고 참조 에러(ReferenceError)가 발생한다.

스코프

var 키워드로 선언한 변수는 오로지 함수의 코드 블록만 지역 스코프로 인정하는 함수 레벨 스코프를 따른다.

let,const 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try/catch 문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.

블록문이 실행되면 블록문의 코드 블록을 위한 블록 레벨 스코프를 생성해야 한다.
이를 위해 선언적 환경 레코드를 갖는 렉시컬 환경을 새롭게 생성하여 기존의 전역 렉시컬 환경을 교체한다.
이때 새롭게 생성된 블록을 위한 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 블록문이 실행되기 이전의 전역 렉시컬 환경을 가리킨다.
블록문의 실행이 종료되면 블록문이 실행되기 이전의 렉시컬 환경으로 되돌린다.


실행 컨텍스트와 콜 스택

실행 컨텍스트

실행 컨텍스트는 소스코드를 실행 하는 데 필요한 환경을 제공하고,
코드의 실행 결과를 실제로 관리하는 영역이다.

실행 컨텍스트는 식별자(변수, 함수, 클래스 등의 이름)를 등록하고 관리하는 스코프(렉시컬 환경)와 실행 순서 관리(실행 컨텍스트 스택)를 구현한 내부 메커니즘으로,

모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.

식별자를 찾을 때나 전역 객체의 프로퍼티를 찾을 때는 스코프 체인을 통해 스코프(실행 컨텍스트의 렉시컬 환경)를 검색하고,

객체의 프로퍼티를 찾을 때는 객체 생성시상속받은 프로토타입을 프로토타입 체인을 통해 검색한다.

생성된 실행 컨텍스트는 스택 자료구조로 관리된다. 이를 콜스텍이라 한다.

코드를 실행하면 코드가 실행되는 시간의 흐름에 따라 실행 컨텍스트 스택에는 실행 컨텍스트가 추가되고 제거된다.

콜스택은 코드의 실행 순서를 관리한다.

콜스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행중인 코드의 실행 컨텍스트이다.

실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트를 실행 중인 실행 컨텍스트라 부른다.


스코프 체인, 변수 은닉화

스코프 체인

자바스크립트 엔진은 코드 실행 중 식별자를 만나면 해당 함수 컴포넌트의 렉시컬 환경 컴포넌트의 환경 레코드에서 먼저 해당 식별자를 탐색하고,

없다면 해당 렉시컬 환경 컴포넌트의 외부 렉시컬 환경 참조 컴포넌트의 환경 레코드에서 탐색하고, 없다면 렉시컬 환경 컴포넌트의 외부 렉시컬 환경 참조 컴포넌트의 값이 null 이 될 때까지 해당 식별자를 찾는다.

스코프 체인의 종점인 전역 객체에서도 찾지 못해 외부 렉시컬 환경 참조 컴포넌트의 값이 null 이라면, 참조 오류를 반환한다.

변수 은닉화

직접적으로 변경되면 안 되는 변수에 대한 접근을 막는 것을 변수 은닉화라고 한다.

클로저를 통해 변수 은닉화를 할 수 있다.

클로저는 무었일까?

profile
천천히 운영되는 개발 블로그

0개의 댓글