[js] JavaScript definition

이성규·2022년 11월 18일
1

✅JavaScript의 자료형과 JavaScript만의 특성


🔷 느슨한 타입(loosely typed)의 동적(dynamic) 언어

  • JavaScript는 느슨한 타입(loosely typed)의 동적(dynamic) 언어이다.
    JavaScript의 변수는 어떤 특정 타입과 연결되지 않으며, 모든 타입의 값으로 할당 및 재할당 가능하다.
let foo = 42 // foo가 number
foo = 'bar' // foo가 이제 string
foo = true // foo가 이제 boolean
  • 자료의 타입은 있지만 변수에 저장되는 값의 타입은 언제든지 바꿀 수 있는 언어를 ‘동적 타입(dynamically typed)’ 언어라고 부른다.

🔷 JavaScript 형변환

  • 함수와 연산자에 전달되는 값은 대부분 적절한 자료형(타입)으로 자동 변환되는데 이런 과정을 '형 변환(type conversion)'이라고 한다.

  • 문자, 숫자, 논리형으로의 형 변환은 자주 일어나는 형 변환이다.

  • 문자형으로 변환
    무언가를 출력할 때 주로 일어난다.
    String(value)을 사용하면 문자형으로 명시적 변환이 가능하다. 원시 자료형을 문자형으로 변환할 땐, 대부분 그 결과를 예상할 수 있을 정도로 명시적인 방식으로 일어난다.

  • 숫자형으로 변환
    수학 관련 연산시 주로 일어난다.
    Number(value)로도 형 변환을 할 수 있다.
    숫자형으로의 변환은 다음 규칙을 따른다.

    Number(value) * 이때 변환할 수 없는 문자열 등을 넣으면 NaN이 뜸
    undefined : NaN
    null : 0
    boolean(true/false) : 1/0
    string : 처음 끝 공백 제거후, 문자열이 없으면 0, 있다면 숫자를 읽어서 변환 (실패하면 NaN)

  • 불린(boolean)

    숫자0, 빈문자열, null, undefined, NaN과 같이 직관적으로도 "비어있다고" 느껴지는 값들은 false, 그외의 값은 true
    "0", " "은 비어있지 않은 문자열이므로 true


🔷 동등연산자(==)와 일치연산자(===)

🔶 동등연산자 (==)

※ 동등 연산자로 좌항과 우항을 비교해서 서로 값이 같다면 'true' 다르면 'false'가 된다. '='의 개수에 따라 의미가 완전히 달라지니 주의하자.

alert (1 == 2) // false
alert (1 == 1) // true
alert (1 == '1') // true
alert ("JS" == "JAVA") // false
alert ("JS" == "JS) // true

  • typeof 1 -> Number
  • typeof '1' -> String
    인데, true가 나온다. 데이터 타입에 관계없이 Value만 같으면 된다.

🔶 일치연산자 (===)

※ 일치 연산자로 좌항과 우항이 "정확"하게 같을 때 true, 다르면 false가 된다.

alert (1 == '1') // true
alert (1 === '1') // false

  • '==='는 숫자 1과 문자 1을 다르게 인식한다. 반면에 '=='는 양쪽의 값을 같다고 판단한다. 바로 이것이 "정확"의 의미이다. 즉, ===는 서로 같은 수를 표현하고 있더라도 데이터 형이 같은 경우에만 같다고 판단하기 때문이다. 결론부터 말하자면 == 연산자 대신 === 연산자를 쓰는 것을 강력히 권고한다. 아래의 예시를 더 보자.

alert (null == undefined) // true
alert (null === undefined) // false
alert (true == 1); // true
alert (true === 1) // false
alert (true == '1') // true
alert (true === '1') // false
alert (0 === -0) // true
alert (NaN === NaN) // false

  • null과 undefined는 값이 없다는 의미의 데이터 형이다. null은 값이 없음을 명시적으로 표시한 것이고, undefined는 그냥 값이 없는 상태라고 생각한다.

    의도함 -> null
    의도하지 않음 -> undefined

  • NaN(Not a Number)은 0/0과 같은 연산의 결과로 만들어지는 특수한 데이터 형인데 숫자가 아니라는 뜻이다. (또는 계산할 수 없음 이라는 뜻)


🔷 느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점은 무엇이고 보완할 수 있는 방법

  • 행 도중에 변수에 예상치 못한 타입이 들어와 타입에러가 발생할 수 있음
    동적타입 언어는 런타임 시 확인할 수 밖에 없기 때문에, 코드가 길고 복잡해질 경우 타입 에러를 찾기가 어려워 진다.
    이러한 불편함을 해소하기 위해 TypeScipt나 Flow 등을 사용할 수 있다.

🔷 undefined와 null의 차이점

  • 위에서 설명한 것과 같이 null과 undefined는 값이 없다는 의미의 데이터 형이다. null은 값이 없음을 명시적으로 표시한 것이고, undefined는 그냥 값이 없는 상태라고 생각한다.

    alert( null === undefined ); // false
    alert( null == undefined ); // true

  • 일치 연산자 ===를 사용하여 null과 undefined를 비교하면 두 값의 자료형이 다르기 때문에 일치 비교 시 false

  • 동등 연산자 ==는 피연산자가 undefined나 null일 때 형 변환을 하지 않는다. 즉, undefined와 null을 비교하는 경우에만 true를 반환하고, 그 이외의 경우(null이나 undefined를 다른 값과 비교할 때)는 무조건 false를 반환


✅ JavaScript 객체와 불변성이란 ?

  • Immutability은 함수형 프로그래밍의 핵심 원리이다.
    불변 객체를 사용하면 복제나 비교를 위한 조작을 단순화 할 수 있고 성능 개선에도 도움이 된다.
    하지만 객체가 변경 가능한 데이터를 많이 가지고 있는 경우 오히려 부적절한 경우가 있다.
    ES6에서는 불변 데이터 패턴(immutable data pattern)을 쉽게 구현할 수 있는 새로운 기능이 추가되었다.

🔷 기본형 데이터와 참조형 데이터

🔶 기본형 데이터

string
number
boolean
undefined
null
ES6 부터 추가된 symbol

  • 기본형 데이터는 불변성인데 불변성이라는것은 한번 만든 데이터의 값은 다른 값으로 변경할 수 없는 것을 말한다.
    만약 기본형 데이터가 변경된다면 그건 데이터를 아에 새로 만드는 동작이고,
    한 번 만들어진 값은 가비지 컬렉팅(GC)을 당하지 않는 한 영원히 변하지 않는다.
    기본형 데이터는 재할당시 기존 값이 달라지는 것처럼 보이지만,
    기존의 값이 달라지는 것이 아닌 새로운 데이터 영역에 재할당한 값을 저장하고,
    재할당한 주소값을 변수영역에 연결해주는 것이다.
    결국 기존의 데이터 영역이 변하는 것이 아니고 새롭게 재할당한 데이터 영역이 생기는 것이다.

🔶 참조형 데이터

array
object
function

  • 배열과 객체, 그리고 함수가 대표적이며, 참조형 데이터는 객체의 변수 영역이 별도로 존재한다.
    이 변수 영역에는 얼마든지 다른 값을 대입할 수 있다. 이 부분 때문에 흔히 참조형 데이터는 가변적이라고 말한다. 하지만 여기서 ‘가변’은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립한다는 것을 알아야한다.

🔶 기본형 데이터와 참조형 데이터 차이

  • 대부분의 책에서는 ‘기본형은 값을 복사하고 참조형은 주솟값을 복사한다’고 설명하지만 사실은 모든 데이터타입은 변수에 할당하기 위해서 주솟값을 복사해야 하기 때문에 엄밀히 따지면 자바스크립트의 모든 데이터 타입은 참조형 데이터일 수밖에 없다.
    다만 기본형은 주솟값을 복사하는 과정이 한 번만 일어나고, 참조형은 한 단계를 더 거치게 된다는 차이가 있다.

📌 기본형데이터는 불변성으로 한번 만든데이터는 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않는다.
참조형데이터는 객체의 내부 프로퍼티를 변경할때는 가변성으로 객체의 변수 영역이 별도로 존재한다.


🔷 JavaScript 형변환

🔶 숫자를 문자열로 변환(number to string)

1) (숫자 or 불리언).toString()
2) String(숫자 or 불리언)
3) "" + (숫자 or 불리언)

🔶 문자열를 숫자로 변환(string to number)

1) parseInt(정수 문자열) || parseFloat(실수 문자열)
2) Number(문자열 or 불리언)
3) + (문자열 or 불리언)
4) (문자열 or 불리언) * 1

🔶 불리언으로 변환

  1. Boolean ( 숫자 || 문자열 || 객체 || undefined || null )
    : Boolean() 생성자 함수를 new 연산자 없이 호출하여 불리언 타입으로 변환하는 방법
  2. !! ( 숫자 || 문자열 || 객체 || undefined || null )
    : 부정 논리연산자(!)를 연달아 두번 사용해서 불리언 타입으로 변환하는 방법

🔷 불변 객체를 만드는 방법

  • '불변 객체'란 - '변하지 않는 객체' 즉 이미 할당된 객체가 변하지 않는다는 뜻을 가지고 있다.

🔶 JavaScript Immutability[참고]

// Object freeze 객체를 불변하게 만들기
let test = {
    name : 'Lee'
}

Object.freeze(test);
test.name = 'Jung';
console.log(test)  // { name: 'Lee' }

🔷 얕은 복사와 깊은 복사

🔶 얕은 복사(Shallow Copy)

  • 객체를 복사할 때, 해당 객체만 복사하여 새 객체를 생성한다.
    복사된 객체의 인스턴스 변수는 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조한다.
    따라서, 해당 메모리 주소의 값이 변경되면 원본 객체 및 복사 객체의 인스턴스 변수 값은 같이 변경된다.

🔶 깊은 복사(Deep Copy)

  • 객체를 복사 할 때, 해당 객체와 인스턴스 변수까지 복사하는 방식.
    전부를 복사하여 새 주소에 담기 때문에 참조를 공유하지 않는다.

✅ 호이스팅과 TDZ

🔷 스코프

  • 자바스크립트의 스코프는 함수 레벨 스코프를 따른다.
    같은 함수 레벨에 존재하면 값을 참조할 수 있다는 건데
    ES6에서 let 키워드가 도입되면서 블록 레벨 스코프를 사용할 수 있게 됐다.

🔶 전역 스코프

  • 어디서든 참조 가능

🔶 전역 변수

  • 전역 스코프를 갖는 전역 변수

  • 어디서든 참조 가능

🔶 지역 스코프

  • 함수 자신과 하위 함수에서만 참조 가능

🔶 지역 변수

  • 지역 스코프를 갖는 지역 변수

  • 함수 내에서 선언된 변수로 해당 함수와 해당 함수의 하위 함수에서 참조 가능

🔶 암묵적 전역 변수

  • 선언하지 않은 변수에 값을 할당하면 전역 객체의 프로퍼티가 되어 전역 변수처럼 동작한다.

  • 하지만 변수 선언이 없었기 때문에 호이스팅은 발생하지 않는다.

(variable = 1) === (window.variable = 1)

//////////////////////////////////////

console.log('test', test);

function temp () {
  test = 10;
};

temp(); // test is not defined

🔷 호이스팅

  • 함수의 코드를 실행하기 전에 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것.
  • 초기화를 제외한 선언만 호이스팅.
  • 그렇기 때문에 선언, 정의된 코드보다 호출하는 코드를 먼저 배치할 수 있음.
  • 변수의 선언과 초기화를 분리.
  • 변수의 선언을 코드의 최상단으로 끌어올림.
catName("달래");

function dogName(name) {
  console.log("제 반려견의 이름은 " + name + "입니다");
}

// "제 강아지의 이름은 달래 입니다"
  • 변수 선언 형식에 따른 초기화

var : 호이스팅 시 undefined로 변수를 초기화
function : 선언된 위치와 상관없이 동일하게 호출
let, const : 호이스팅 시 변수를 초기화하지 않음. (호이스팅 대상은 맞음)

console.log(num); // 호이스팅한 var 선언으로 인해 undefined 출력
var num; // 선언
num = 6; // 초기화

console.log(num2); // ReferenceError: num2 is not defined
let num2 = 2;

//-----------------------------------------------

catName("달래"); // "제 반려견의 이름은 달래 입니다"

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

catName("달래"); // "제 반려견의 이름은 달래 입니다"

🔷 TDZ (Temporal Dead Zone, 일시적 사각지대)

  • TDZ의 영항을 받는 구문 const, let, class
  • 변수 스코프의 맨 위에서부터 ~ 변수의 초기화 완료 시점까지의 변수는 TDZ에 들어간 변수
  • 코드의 작성 순서(위치)가 아니라 코드의 실행 순서(시간)에 의해 형성

🔶 let, const

{
    // <----- TDZ가 스코프 맨 위에서부터 시작
    const func = () => console.log(letVar); // OK

    // TDZ 안에서 letVar에 접근하면 ReferenceError

    let letVar = 3; // letVar의 TDZ 종료 ------->

    func(); // TDZ 밖에서 호출함
}

: let 변수 선언 코드가 그 변수에 접근하는 함수보다 아래에 위치하지만 함수의 호출 시점이 사각지대 밖이므로정상 동작.

🔶 class

  • 부모 클래스를 상속받을 경우, 생성자 안에서 super()호출을 하기 전까지 this바인딩은 TDZ안에 있다.
// (X)
class User extends Member {
  constructor(phone) {
    this.phone = phone;
    super(phone);
  }
}

// (O)
class User extends Member {
  constructor(phone) {
    super(phone);
    this.phone = phone;
  }
}

📌 결론

  • TDZ는 선언 전에 변수를 사용하는 것을 허용하지 않는다.
  • var의 사용은 의도치 않은 중복선언과 재할당으로 문제가 생길 수 있기 때문에 사용하지 않는편이 좋다.

🔷 함수 선언문과 함수 표현식에서 호이스팅 방식의 차이

🔶 함수 선언식 (function declartion)

  • 함수명이 정의되어 있고, 별도의 할당 명령이 없는 것
    함수 전체를 호이스팅 합니다. 정의된 범위의 맨 위로 호이스팅되서 함수
function sum(a,b) {
    return a + b;
}

🔶 함수 표현식 (function Expression)

  • 정의한 function을 별도의 변수에 할당하는 것
    별도의 변수에 할당하게 되는데, 변수는 선언부와 할당부를 나누어 호이스팅 하게 됩니다.
const sum = function(a,b) {
    return a + b;
}

📌 함수 선언식으로 작성한 함수는, 함수 전체가 호이스팅 된다고 하였는데, 전역적으로 선언하게 되면, 중복적으로 동명의 함수를 쓰게 되었을때, 원치 않는 결과를 초래할 수 있습니다. 이를 방지하려면 함수 표현식으로 작성하면 됩니다.


🔷 let, const, var, function의 실행 원리

  • var : 변수 재선언 가능
    const, let : 변수 재선언 불가능
  • const : 변수 재할당 불가능 (상수)
    let : 변수 재할당 가능
  • var : functional-scope 로 호이스팅됨
    const, let : block-scope 로 호이스팅됨

🔷 실행 컨텍스트와 콜 스택

🔶 실행 컨텍스트

  • Execution Context 는 자바스크립트의 핵심 개념으로, 코드를 실행하기 위해 필요한 환경이다.
    더 자세히 말하자면, 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
    자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념.
    모든 코드는 특정한 실행 컨텍스트 안에서 실행된다.
    javascript는 어떤 execution context가 활성화되는 시점에 선언된 변수들을 위로 끌어올리고(hoisting), 외부 환경 정보를 구성하고, this값을 설정하는 등의 동작을 수행하는데, 이로 인해 다른 언어에서는 발생할 수 없는 특이한 현상들이 발생한다.
  • 자바스크립트의 주요한 실행 컨텍스트에는 두 가지가 있다.

    Global Execution Context
    Fuction Execution Context

🔶 콜 스택

  • call은 호출을 뜻한다.
    stack은 출입구가 하나뿐인 깊은 우물 같은 데이터 구조다.
    따라서 callstack은 자바스크립트가 함수 호출을 기록하기 위해 사용하는 우물 형태의 데이터 구조이다.
    항상 맨 위에 놓인 함수를 우선으로 실행된다. 이런 식으로 자바스크립트 엔진은 가장 위에 쌓여있는 context와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.

🔷 스코프 체인, 변수 은닉화

🔶 스코프 체인

  • 스코프에 식별자가 없으면 상위 스코프에서 다시 찾아 나간다.
    이 현상을 스코프 체인 이라고 하며 스코프가 중첩되어있는 모든 상황에서 발생한다.

🔶 변수 은닉화

  • 외부 객체로부터 '속성 값(데이터, 멤버 변수값)'을 감추는 특성
profile
개발자를 위한 발걸음

0개의 댓글