본문 바로가기
back-front end

[항해 99] Javascript의 특성

by lucian 2022. 7. 18.

1. JavaScript 자료형과 Javascript만의 특성은 무엇일까?

  •  자바스크립트는 느슨한 타입(loosely typed)의 동적(dynamic) 언어이다 : 특정 타입의 변수와 연결되지 않고, 모든 타입의 값들로 할당하고 재할당이 가능하다.
    이처럼 변수에 타입은 있지만 저장되는 값에 따라 값이 바뀌는 언어를 동적 타입 언어라고 한다.
let num = 1234 num='string' 
// 이렇게 num에 1234라는 숫자형 변수를 넣어줬지만, 다음 줄에 string타입의 문자형 변수로 바꿔줄 수 있다.

 

  • Javascript 형변환 : 자바스크립트의 형변환은 암시적 변환와 명시적 변환로 바뀐다.
    • 암시적 변환 : 자바스크립트 엔진이 필요에 따라 자동으로 데이터타입을 바꿔줌
      • +연산자가 진행될 땐, 숫자형보다 문자형이 우선시 된다.
        숫자형이 문자형을 만나면 문자형으로 변환되서 연산된다. > 100+"점" => "100점"
      • 다른 연산자(-, *, /, %) 가 진행될 땐, 숫자형이 우선시 된다.
        즉 문자형 숫자("100", "1" ....) 같은 경우 숫자로 변환되서 연산된다. > '100'/2 => 50
        아예 문자형인 경우 ("asdf", "123asdf" )  NaN을 반환한다. > 'asdf'/2=>NaN
    • 명시적 변환 : 우리가 직접 함수를 사용해서 데이터타입을 변환시키는 것이다.
      • 먼저 타입을 확인할 때 쓰는 typeof()함수이다. >  typeof('asdf')    // 'string'
      • 숫자형으로 바꿔주는 함수들
        • Number()  >  Number('12345') // 12345
        • parseInt()  >  parseInt('12345') // 12345
          - parseInt는 2진법, 10진법 을 명시해주면 그에 맞게 숫자로 바꿔준다.
             > parseInt('0021', 3) // 7  3진법의 '0021'을 숫자로 변환해줌
        • parseFloat() : 소수점의 숫자로 변환해줌.  > parseFloat('1.234') // 1.234
      • 문자형으로 바꿔주는 함수들
        • String() 문자형으로 변환 > String(123) // '123'
        • toString() 매개변수에 N진수 숫자를 넣어주면 N진법으로 변환해주고 없으면 문자형으로 바꿔준다.
          > 100.toString()  // '123'
          > 100.toString(8) // 매개변수 8이 들어와서 8진수의 문자형으로 바꿔준다. "144"
          > 100.toString(2) // 매개변수 2가 들어와서 2진수 문자로 변환 "1100100"
        • toFixed() 소숫점 반올림까지 표현한 문자열 반환. 매개변수로 소수점 자리수를 표현
          > 123.456789.toFixed()  // "123"
          > 123.456789.toFixed(2)  // "123.46"
          > 123.456789(8)  // '123.45678900; 
      •   불린타입으로 변환해주는 함수
        • Boolean() 
  • == vs ===
    • 느슨한 비교? 와 엄격한 비교라고 할 수 있다.
    • ==는 타입을 안본다. '0'==0일 때도 true , '0'===0은 false를 반환한다.
      > null == undefined  // true
      > '0' == 0 // true
      > 0 == false // true  => 보통 코딩에서 false는 0,  true는 1이다. 그래서 무한 while문도 while(1)  = while(true)
      > '0' == false // true 

https://medium.com/gdana/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%ED%98%95%EB%B3%80%ED%99%98%EC%9D%80-%EB%91%90%EA%B0%80%EC%A7%80%EB%8B%A4-b46875be4a88

 

자바스크립트의 형변환은 두가지다

자바스크립트는 타입이 매우 유연한 언어이다. 때문에 때로는 자바스크립트 엔진이 필요에 따라 ‘암시적변환’ 을 혹은 개발자의 의도에 따라 ‘명시적변환’ 을 실행한다. 그렇다면 형변환

medium.com

 

  • 느슨한 타입(loosely typed)의 동적 언어의 문제점은 무엇이고 보완할 수 있는 방법
    • 문제점
      • 동적언어는 말 그대로 변수의 타입이 들어가는 값에 따라 바뀌는 언어이다. 정적언어는 기본적으로 변수의 타입을 지정해주는 반면 동적언어는 지정을 안해주기 때문에 추후 오류가 발생할 수 있다. 
        변수를 할당하고 이 값은 숫자만 받아야지 생각하고 함수의 매개변수에 이 변수를 넣어 함수를 동작하게 만들었는데 문자형 값이 들어오면 오류가 발생한다. 당연히 오류가 발생하지만 동적언어는 코드가 길어지면 길어질수록 어디서 발생하는지 찾기 어려워진다. 반면에 정적변수는 이를 선언하기 애초에 타입을 지정해주기에 문제를 앞에서 바로 찾을 수 있다.
      • 또한 동적언어에 변수를 선언할 때의 유동적으로 변할 수 있을 만큼 메모리를 할당한다. 왜냐면 값을 할당을 할 때 이 값이 문자인지 숫자인지 모르기 때문에 이 두개가 들어갈 수 있을 만큼의 메모리를 받아와야지 문제가 생기지 않는다. 
        반면에 정적언어는 그 타입을 지정해주기 때문에 타입에 맞게 필요한 만큼만 메모리를 받아온다.
        숫자가 4가 필요하고 문자가 2가 필요하다면 동적언어는 모든 변수에 4만큼 가져와야 숫자, 문자가 커퍼되는 반면 정적언어는 타입을 지정했기 때문에 문자변수는 메모리에서 2만 가져오면 된다.
        이처럼 동적언어는 변수에 자원을 쓰는 것이 정적언어에 비해 비효율적이다. 
    • 문제점 보안
      • 이를 방지하기 위해 javascript는 TypeScript를 사용한다.
        • typescript란 MS가 2012년에 발표한 자바스크립트를 기반으로 정적 타입 문법을 추가한 프로그래밍 언어이다.  정적 타입의 컴파일 언어이기에 컴파일 과정 즉 코드를 작성하는 단계에서 오류를 확인 할 수 있다. ( 자바스크립트는 인터프립트 언어(비공식적으로)..)
        • typescript는 자바스크립트의 슈퍼셋이기에 자바스크립트의 기본 문법에 타입스크립트 문법을 추가한 것이다. 따라서 동작되는 js파일의 확장자를 ts로 변경해서 컴파일할 수도 있다.
        • 즉 typescript는 javascript와 100% 호환되게 만들었기에 js를 쓸 수 있는 곳이라면 어디서든 ts를 쓸 수 있다.

https://www.samsungsds.com/kr/insights/typescript.html

 

활용도가 높아지는 웹 프론트엔드 언어, 타입스크립트[TypeScript]

활용도가 높아지는 웹 프론트엔드 언어, 타입스크립트[TypeScript]

www.samsungsds.com

 

  • undefiend? null 이란 다른건가?
    • undefined  : 자바스크립트는 동적언어기에 변수에 담기는 값에 따라 자료형이 결정되게 된다. 이렇기에 변수는 선언했으나 값을 할당하지 않아 자료형이 정해지지(undefined) 않은 상태이다. typeof을 하면 undefined 가 나온다.
    • null : 변수를 선언하고 그 값에 null이란 값을 할당한 상태. null이란 자료형으로 정해진 상태이다. 
      typeof 함수로 값을 직어보면 object타입 즉 객체타입이라고 나온 것을 볼 수 있다.
    • null==defined 와 null ===defined : 우선 위에서 적은대로 ==은 느슨한 비교이다. 그렇기에 true가 나오고, ===는 엄격한 비교이기에 false가 나온다.

 

 

2. JavaScript 객체와 불변성이란 ?

  • 기본형 데이터와 참조형 데이터
    • 기본형 데이터 : 자바스크립트에서 지정한 데이터 타입 이므로 불변적이다.
      • Number
      • String
      • Boolean
      • Symbol
      • null
      • defined
    • 참조형 데이터 : 변수에 값을 할당할 때 값이 아닌 데이터의 주소를 저장하는 데이터
      • Object
      • Array : const로 지정된 배열의 내부를 바꿀 수 있는 이유는 참조형 변수라서 그렇다. 데이터의 주소를 const해준 것뿐 데이터 주소 안을 const한 것은 아니기 때문에
      • Map
      • 등등 ( 우리가 만든 객체들도 다 참조형이다. 즉 참조형은 데이터의 집합이라 생각하면 된다. 그 데이터의 집합의 주소를 가지고 오는 것)

  • 불변 객체 만들기 (얕은 복사 : shallow copy, 깊은 복사 : deep copy)
     ex) 객체 A를 복사해 배열 B에 넣고 싶어서 B.push(A)를 사용해 배열안에 넣었다. 이 배열 안에 있는 객체를 C라 하겠다. 그 후 객체 A에 수정할 것이 생겨 고쳤더니 배열 B 안에 있는 복사한 객체A 즉 C가 변경됬다. 난 바꾸고 싶지 않은데...
    왜냐하면 C는 참조형변수인 객체A의 주소를 그대로 복사한 것이고 그 주소 안에 있는 내용을 바꿨기 때문에 객체A도 복사된 객체A 즉 C도 똑같을 수밖에 없다.
    이런 경우를 방지하고자 불변객체를 만든다. 객체 내부에 변수들을 변경할 수 없도록 되어있는 것이 불변객체이다.
    • 얕은 복사 : shallow copy
      - 얕은 복사는 위에서 말한 예제의 경우이다. 사본의 데이터 또는 원본의 데이터를 복사하더라도 동일한 참조형 데이터 주소를 가리키고 있기 때문에 두 개의 데이터다 변경되는 것이다.
    • 깊은 복사 : deep copy
      - 깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다.
      • Object.assign(target, sources) 
      • Spread Operator
        - 위의 두개의 방법은 객체안에 객체가 있을 경우 즉 중복 객체에서는 깊은 복사를 할 수가 없다.
        - 이럴 땐 중복객체도 직접 복제하면 된다. > 복사된_객체 = 복사된_객체.안에_중복객체.concat()

      • Object.freeze 

- Object assign()함수 (중복객체 포함)

let o1 = { name: "kim", score: [1, 2] };
let o2 = Object.assign({}, o1);

o2.score = o2.score.concat(); // 원본을 복제함 (인자는 추가할 값)

- Object assign()함수 다른 예제

// Copy
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
console.log(obj == copy); // false

// Merge
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };

const merge1 = Object.assign(o1, o2, o3);

console.log(merge1); // { a: 1, b: 2, c: 3 }
console.log(o1);     // { a: 1, b: 2, c: 3 }, 타겟 객체가 변경된다!

// Merge
const o4 = { a: 1 };
const o5 = { b: 2 };
const o6 = { c: 3 };

const merge2 = Object.assign({}, o4, o5, o6);

console.log(merge2); // { a: 1, b: 2, c: 3 }
console.log(o4);     // { a: 1 }

- Object freeze()함수 다른 예제

const user1 = {
  name: 'Lee',
  address: {
    city: 'Seoul'
  }
};

// Object.assign은 완전한 deep copy를 지원하지 않는다.
const user2 = Object.assign({}, user1, {name: 'Kim'});

console.log(user1.name); // Lee
console.log(user2.name); // Kim

Object.freeze(user1);

user1.name = 'Kim'; // 무시된다!

console.log(user1); // { name: 'Lee', address: { city: 'Seoul' } }

console.log(Object.isFrozen(user1)); // true

 

 

3. 호이스팅과 TDZ란?

  • 스코프, 호이스팅, TDZ
    • 스코프(Scope) :  변수에 접근할 수 있는 범위, 자바에서 public, private 처럼 변수에 접근할 수 있는 권한? 으로 보면 될 것 같다.
      - 스코프는 전역 스코프(Global Scope)와 지역 스코프(Local Scope)로 나뉜다.
      • 전역스코프는 어느 곳에서든 변수에 접근할 수 있다.
        - 전역스코프는 js파일에 함수들 밖에 있는 변수들이 전역스코프이다. 이는 this.란 키워드로 함수 내부에서도 접근이 가능하고 함수 밖에서도 접근할 수 있다.
      • 지역스코프는 해당 지역에서만 접근할 수 있다.
        - 함수 내부에서 변수를 선언하는데 이 변수는 함수 내부에서 밖에 쓸  수가 없다. 이를 함수스코프 이면서 동시에 지역스코프의 예이기도 하다.

    • 호이스팅(hoisting) : 변수들의 선언을 모두 상단으로 올려 함수 유효 범위 최상단에서 선언하는 것을 말한다.
      • var 변수의 경우 호이스팅은 defined 변수로 초기화
        - var 변수를 예를 들어, 
          console.log(a);     var a; //  선언의 의미
        - 원래 a가 선언과 초기화가 되어있지 않은채 console.log에 출력됬다. 이는 명백히 오류지만 javascript에선 호이스팅으로 출력이 가능하다. 또한 var은 할당을 하지 않을 시 자동적으로 undefined로 초기화 해준다.
        결국 호이스팅과 var의 초기화로 인해 오류 대신 undefined가 출력된다.
        - let과 const는 호이스팅이 가능하지만 초기화는 되지 않아 오류가 발생한다.
        - 또한 우린 호이스팅이 되지 않게 가급적이면 let과 const를 사용하고 코드 상단부에 선언과 초기화를 해야한다.
    • TDZ(Temporal Dead Zone) : let, const, class의 유효성을 관리
      • 간단한 원리이다. 선언 전에 변수 또는 클래스에 접근하는 것을 금지한다. 변수 선언 전에 어떤 것도 사용하지 않는다.
      • constructor() 내부에 super()를 호출하기 전까지 this는 TDZ에 있다. 즉 super()를 호출하여 부모 클래스의 생성자를 호출하고 this.로 자식 클래스의 변수를 호출 가능하단 의미이다. 
      • 기본 매개변수는 글로벌과 로컬 스코프 사이 즉 중간스코프에 위치한다. 기본 매개변수 또한 TDZ제한이 있다.
      • var, function, import는 TDZ제한이 없다. 먼저 이 3가지는 모두 호이스팅이 가능하기에 변수를 선언하기 전 호출을 해도 오류를 발생하지 않는다.
  • 함수 선언문과 함수 표현식에서 호이스팅 방식의 차이
    - 함수 선언문과 함수 표현식은 크게 차이가 없다. 그러나 호이스팅 방식의 차이가 난다.
    - 함수 선언문은 호이스팅이 허용되고 함수 호출문은 호이스팅이 허용되지 않는다.
    - 밑의 예제를 통해 확인하면 알기 쉽다. 
// 함수 선언문
function getName() {
    console.log('name');
}
// 함수 표현식
var name = function() {
   console.log('name');
};

 

- 함수 선언은 말그대로 function으로 함수를 선언했다. 앞서 호이스팅에서 함수가 선언되지 않은 상태에서 함수를 불렀는데 이는 함수선언문이 호이스팅 허용되기 때문에 에러가 발생하지 않은 것이다.

 

- 그러나 함수 표현식은 다르다. 함수표현식은 변수에 함수를 할당하는 것인데, 이는 var변수라도 호이스팅이 허용되지 않는다. 밑의 예제를 보면,

count;    // undefined

count();      // Uncaught TypeError: count is not a function at <anonymous>:1:1

var count = function() {
    console.log('count는 1이다.');
}

맨 윗줄 count는 var변수이기 때문에 출력에 오류가 발생하지 않고 undefined가 출력된다. 하지만 count() 변수는 함수표현식으로 적용된 변수이기에 호이스팅이 적용되지 않아 오류를 발생한다.

 

  • 실행 컨텍스트와 콜 스택
    • 실행 컨텍스트(Execution Context)
      - 코드를 실행하기 위해 필요한 환경 정보들을 모아놓은 객체? 자료구조?의 개념으로 보면 될 것 같다.
      - 실행 컨텍스트가 활성화 되는 시점부터 변수 선언들이 위로 끌어올려지고(호이스팅), 환경정보를 담고, this값을 설정한다. 이처럼 자바스크립트가 돌아가기 위해서 실행 컨텍스트가 필요한데, 이는 두가지로 나눠진다.
      • 전역 실행 컨텍스트(Global Execution Context)
        - 디폴트로 js파일이 엔진에 처음으로 로드될 때 실행된다.
      • 함수 실행 컨텍스트(Function Execution Context)
        - js파일 내의 함수를 실행했을 때 그안의 정보들을 담은 컨택스트를 가진다.
    • 콜 스택(callstack)
      - 호출해서 스택에 넣어줘!
      - 자료구조에서 stack은 FILO(First input Last output)이다. 즉 첫번째로 들어온 것이 마지막에 나간다.란 뜻이다.
        컵에 섞이지 않는 액체 A,B,C를 넣은다고 생각하자. A가 들어가고 B가 들어가고 C가 들어가면 나올때는 C가 나오고, B, A 순으로 진행된다. 즉 A가 먼저 들어가서 마지막에 나온다. 이것이 자료구조 stack의 동작원리이다.
      - js은 싱글 스레드이기 때문에(한 동작밖에 실행을 못함) 한번에 하나의 코드만 실행할 수 있다.

 

예를 들어, 

// (1)

const a = "hello world!";

const bar = () => { // (5)
    console.log(a)
}; // (6)

const foo = ()=>{ // (3)
	bar() // (4)
}; // (7)

foo(); // (2)

// (8)

Global Execution context가 1로 시작이 되고, 그 뒤 function excution context가 실행이 된다. 한마디로 js 파일이 동작할 때의 흐름이라 생각하면 편할 것 같다. 전역으로 1이 시작되고, 그 안에 최초 실행시 스냅샷, 식별자들에 대한 정보, 외부환경 정보 등을 conext에 담은 다음 callstack에 푸쉬해준다. 이제 2,3번인 foo 함수 실행 컨텍스트의 정보가 콜스택에 푸쉬되고 그 뒤 4,5번 bar 컨텍스트가 푸쉬 실행이 완료되면 넣었던 반대방향 6,7,8로 종료가 된다.

 

  • 스코프 체인과 변수 은닉화
    • 스코프 체인(Scope Chain)
      - 간단하다. 스코프는 범위를 나타내고 함수 안에 함수를 쓸 수 있기에 계층적인 범위를 가질 수 있다. 즉 변수를 참조할 때, 그 변수가 선언된 곳을 찾아야하는데 함수의 안부터 바깥으로 단방향으로 이동한다. 이 때 변수의 선언이 중복이 된다면 변수를 참조한 것에서부터 가장 먼저 찾은 식별자만 인식한다.
    • 변수 은닉화(Variable shadowing)
      - class처럼 변수를 사용하고 싶을 때, 정보를 은닉하고 싶을 때 변수 은닉화를 사용한다.
      - 함수 내부에 변수를 넣어 직접적으로 변수의 접근을 막는다.
function a(){
  let temp = 'a' 
  
  return temp;
} 

// console.log(temp)  error: temp is not defined
const result = a()
console.log(result); //a

변수 temp는 직접적으로 값을 사용하지 않고 함수 a를 통해서만 접근이 가능하다. 이것이 변수 은닉화이다.

 

 

https://velog.io/@solseye/JS-%EC%9E%98-%EB%B4%90-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8A%A4%EC%BD%94%ED%94%84Scope-%EC%8B%B8%EC%9B%80%EC%9D%B4%EB%8B%A4

 

JS | 잘 봐, 자바스크립트 스코프(Scope) 싸움이다 🔥

자바스크립트는 우리나라의 행정구역을 광역시/도 ➡️ 시/군/자치구 ➡️ 읍/면/동으로 나누는 것처럼 코드의 영역을 스코프로 나누어 관리한다. 스코프는 단방향으로 연결되는 체인을 형성하

velog.io

https://velog.io/@mincho/%EB%B3%80%EC%88%98%EC%9D%98-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%99%80-%EC%9D%80%EB%8B%89%ED%99%94

 

변수의 클로저와 은닉화

함수 + 함수를 둘러싼 환경(Lexical environment)함수를 둘러싼 환경이라는 것이 바로 앞에서 설명했던 렉시컬 스코프다.함수를 만들고 그 함수 내부의 코드가 탐색하는 스코프를 함수 생성 당시의 렉

velog.io

 

 

 

4. 실습 과제

let b = 1;

function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b);  // 1, 101
}

//console.log(a); // 전역 스코프에 변수가 정의된게 없으므로 에러 발생
console.log(b); // 1

hi();

console.log(b); // 1

 

댓글