JavaScript 알고리즘 주차 시작

gimsoari·2022년 11월 18일
0

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

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

  • JavaScript의 변수에 값을 할당하거나 재할당할 경우 무조건적으로 어떤 특정 타입으로 연결되지 않고, 해당 값의 자료형으로 연결된다. C, C#, C++, Java 등의 언어는 정적 언어로 변수 선언 시 할당할 값의 자료형을 함께 선언해주어야 하는 것과 다르다.
  • 예시)

    let foo = 10 foo의 자료형은 숫자
    foo = 'bar' 기존 foo의 자료형이 문자열로 바뀜
    foo = true 기존 foo의 자료형이 불린형으로 바뀜`

2) JavaScript 형변환

  • 원시형 자료의 형 변환
    1번 문항의 예시처럼 JS는 함수와 연산자에 전달되는 값을 적절한 자료형으로 자동 변환해준다. 이러한 과정을 "형 변환(type conversion)"이라고 한다. 전달받은 값을 "명시적 변환(의도를 갖고 원하는 타입으로 변환)"하는 경우도 형 변환이라고 할 수 있다.
  • 명시적 변환 예시)
    let value = true value의 자료형 자동적으로 Boolean으로 지정한다
    value = String(value) 자료형을 문자열로 명시적 변환한다
    console.log(value) //true
  • 객체의 형 변환
    : 객체끼리 더하는 연산을 하거나, 빼는 연산을 하면 객체는 자동 형 변환되어 원시값으로 변환된 후 연산이 수행된다. 객체는 논리 평가 시 true를 반환한다. 따라서 객체는 숫자형이나 문자형으로만 형 변환이 일어난다. 객체끼리 연산을 하거나 수학 관련 함수를 적용할땐 주로 숫자형으로 형 변환되고, alert와 같이 객체를 출력하려고 할 땐 문자형으로 형 변환된다.
    객체 형 변환은 'hint(목표로 하는 자료형)'라 불리는 값이 구분 기준이 되어 세 종류로 구분할 수 있다.

string: 문자열 변환
number: 숫자형 변환
default: 연산자가 기대하는 자료형이 확실치 않을 때 hint는 default가 된다. 예를 들어 이항 덧셈 연산자 +의 경우 피연산자의 자료형에 따라 문자열을 합칠 수도 있고, 숫자를 더할 수도 있다. 따라서 +의 인수가 객체일 때 hint는 default가 된다. Date 객체를 제외한 모든 내장 객체는 hint가 default인 경우와 number인 경우를 동일하게 처리하기 때문에 default로 지정되는 경우를 굳이 모두 외우고 있을 필요는 없다.
자바스크립트는 형 변환이 필요할 때, 다음과 같은 알고리즘에 따라 원하는 메서드를 찾고 호출한다.
1> 객체에 objSymbol.toPrimitive메서드가 있는지 찾고, 있다면 메서드를 호출한다. Symbol.toPrimitive는 시스템 심볼로, 심볼형 키로 사용됩니다.
2> 1에 해당하지 않고 hint가 "string"이라면,
obj.toString()이나 obj.valueOf()를 호출한다.(존재하는 메서드만 실행됨).
3> 1과 2에 해당하지 않고, hint가 "number"나 "default"라면 obj.valueOf()나 obj.toString()을 호출한다(존재하는 메서드만 실행됨).

  1. ==, ===
  • ==는 Equal Operator이로, ===는 Strict Equal Operator이다.
    a == b라고 할때, ==는 a와 b의 '값'이 같다면 true, 다르다면 false를 반환한다. (JavaScript는 자동 형 변환을 하기 때문이다)
    a === b라고 할때, ===는 a와 b의 '값'과 '값의 자료형'이 모두 같아야만 true를 반환하고, 이외엔 false를 반환한다.
    원시형 자료의 경우 값과 값의 자료형만을 비교하지만, 배열이나 객체의 경우 각 변수의 메모리 주소까지 비교해 값과 값의 자료형이 같아도 메모리 주소가 다르다면 ==과 ===은 모두 false를 반환한다.
  1. 느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점은 무엇이고 보완할 수 있는 방법에는 무엇이 있을지 생각해보세요.
  • JavaScript는 런타임 시 변수의 타입이 결정되기 때문에 타입 에러가 생기는 경우가 있다. 이를 해결할 방법으로 TypeScript를 활용할 수 있다.
    런타임 이전에 타입이 올바른지에 대한 검사를 시행한다. ReferenceError를 유발하는 코드가 존재한다면 정적 언어는 컴파일 과정에서 오류를 출력하는 반면, 동적 언어는 해당 구문이 실행되는 시점에서 오류를 출력한다. TypeScript를 트랜스 파일(컴파일)하면 브라우저에서 자바스크립트가 출력된다.
  1. undefined와 null의 미세한 차이들을 비교해보세요.
  • undefined는 변수의 값이 지정되지 않을 것이며, null은 의도적으로 값을 넣지 않았음을 의미한다.
    따라서 undefined의 자료형은 undefined이며, null의 자료형은 객체이다.

예시)
let testVar; alert(testVar); //undefined
alert(typeof testVar); //undefinedlet
testVar2 = null; alert(testVar2);//null
alert(typeof testVar);//object

🐤 JavaScript 객체와 불변성이란 ?

- 1) 기본형 데이터와 참조형 데이터

** 선언 / 할당

선언 과정: 공간을 확보하고 변수명과 주소를 매칭시키는 과정
할당 과정: 해당 변수를가 가르키는 주소의 공간에 데이터를 저장하는 과정

**

기본형 타입(Primitive Type)
숫자(Number)
문자열(String)
불리언(Boolean)
null
undefined
심볼(Symbol)

기본형 데이터는 값을 그대로 할당하는 것.
메모리 내에 고정된 크기로 저장되면서, 원시 데이터 값 자체를 보관, 불변적
기본적으로 데이터는 하나의 메모리를 사용한다.(재사용)

예를 들면
var a; var b; var c = b; a = 10; b = false;

  • 변수 a는 주소 414을 b는 주소 415를 c는 주소 416을 확보하고 해주당 주소를 변수명과 맵핑 시킨다.
  • 기존 변수명을 새롭게 할당하려고 하는 경우, 새로운 변수는 별도의 공간을 확보하고, 불리언 값을 통해 기존 변수에 대입된다.(기본 값은 직접적 비교 가능)

b와 c가 같다고 표현되어 있으니, false의 명제는 유일하기에 b === c가 된다.

하지만 여기에 c = 10을 재할당 하면 c = 20이기에 b와 c는 같은 값이 아니게 된다.

참조형 타입(Reference Type)
객체(Object)
배열(Array)
함수(Function)
날짜(Date)
정규표현식(RegExp)
Map
WeakMap
Set
WeakSet

참조형은 기본형 데이터의 집합임으로
참조형 데이터는 값이 지정된 주소의 값을 할당합니다.
객체로 예를 들면 var obj = { a:10, n:"sol" } var obj2 = obj;

기본형 타입과 마찬가지로 메모리 공간을 확보하고 주소를 변수명과 매칭시키는 과정은 동일합니다.

할당과정에서 그 값이 기본형이 아닌
참조형 데이터들은 property와 data, 즉 key:value로 묶인 쌍들로 이루어져 있어 propery명과 value가 담길 주소를 매칭하기 위해 공간을 임의로 1000주소에 확보합니다.
1000에 a를 넣고 1001에 a의 value(10)을 넣습니다.
또 b를 1000에 할당하고 1002에 b의 value("sol")을 넣습니다.
결과적으로 선언을 하며
ex)410의 주소에 할당된 상태에서 key:value를 매칭 시킨 값을 410에 매칭시킵니다.

그리하여 obj2는 411번에 공간을 확보하고 obj가 가리키고 있는 1000주소를 할당하게 됩니다.

이것이 바로 참조가 이루어지는 형식입니다. 객체는 어딘가에 따로 저장되어 있고 그 객체가 저장된 주소만을 복사해 온 것입니다.

2) JavaScript 형변환

3) 불변 객체를 만드는 방법
먼저 불변(immutability)객체는 말 그대로' 변하지 않는 개체' 즉 이미 할당된 객체가 변하지 않는다는 뜻이다.

  • JavaScript에서 불변객체를 만드는 방법은 기본적으로 constObject.freeze()를 사용하는 것이다.
    const키워드는 변수를 "상수"로 선언할 수 있다.
    하지만 상수로 선언한 객체는 완전한 불변 객체는 아니다.
    const에 할당된 값이 상수가 되는 것이 아니라 할당 된 값이 상수가 되는, 즉 const로 선언한 변수가 상수가 되기 때문에 객체 재할당은 불가능하지만 객체의 속성은 변경이 가능하다.

Object.freeze()
먼저 메소드 사용법부터 알아보면 사용법은 간단하다.
var test = { name:"kim" } Object.freeze(tset);

test변수에 key/value를 가진 객체를 할당 후 test변수를 동결 객체로 만들면 된다.

하지만 이 처럼 객체 재할당은 가능하다tset.name = 'sol'; 때문에 Object.freeze()도 불변 객체라고 할 수 없다.

결국 불변 객체는 constObject.freeze()를 조합하여 만들 수 있다(const의 재할당 불가 + Object.freeze의 객체 속성 불가).

const a = {age:18}; Object.freeze(a);
위와 같이 const할당 후 Object.freeze()로 해당 변수를 동결 객체를 만들면 "객체의 재할당과 속성 변경이 불가능한" 불변 객체가 된다.

- 4) 얕은 복사와 깊은 복사
얕은 복사는 객체의 참조값(주소 값)을 복사하고, 깊은 복사는 객체의 실제 값을 복사합니다.

먼저 JavaScript에는 원시값과 참조값 두 가지 데이터 타입의 값이 존재합니다.

  • 원시값은 기본 자료형(단순 데이터)를 의미합니다. 변수에 원시값을 저장하려면 변수의 메모리 공간에 실제 데이터 값이 저장되고 할당된 변수를 조작하려고 하면 저장된 실제 값이 조작됩니다.
  • 참조값은 여러 자료형으로 구성되는 메모리에 저장된 객체입니다. 변수에 객체를 저장하면 독립적인 메모리 공간에 값을 저장하고 변수에 저장되 메모리 공간의 참조(위치 값)을 저장해서 변수를 조작하는 것은 객체 자체를 조작하느 것이 아닌, 해당 객체의 참조를 조작하는 것입니다.

원시값을 복사할 때 값은 다른 독립적인 메모리 공간에 할당하기에 복사/수정을 해도 기존 원시값을 저장한 변수에는 영향을 끼치지 않습니다. 이처럼 실제 값을 복사하는 것을 깊은 복사라고 합니다. 하지만 이것은 자료형에 깊은 복사입니다.

참조값을 복사할 때는 변수가 객체의 '참조'를 가리키고 있기 때문에 복사된 변수 또한 객체가 저장된 메모리의 공간의 참조를 가르키고 있습니다. 복사를 하고 객체를 수정하면 두 변수는 똑같은 참조를 가리키고 있기 때문에 기존 객체를 저장한 변수에 영향을 끼칩니다. 이처럼 참조값(주소값)을 복사하는 것을 얇은 복사라고 합니다.

.../복사하는 방법/...

문제 1.

콘솔에 찍힐 b 값을 예상해보고, 어디에서 선언된 “b”가 몇번째 라인에서 호출한 console.log에 찍혔는지, 왜 그런지 설명해보세요.
주석을 풀어보고 오류가 난다면 왜 오류가 나는 지 설명하고 오류를 수정해보세요.

 let b = 1; 

function hi () {

const a = 1;

let b =100;

b++;

console.log(a,b);

}

//console.log(a);

console.log(b);

hi();

console.log(b);

그냥 출력)
1
1 101
1
주석 해제 출력)
Uncaught ReferenceError: a is not defined
//a는 function hi에 실행된 함수다. 즉 글로벌 변수가 아닌 로컬 변수다. 즉 함수가 호출되어 선언과 할당이 이루어지고 함수가 종료되면 소멸되서 함수 외부에서 const a를 찾을 수 없다.

호이스팅과 TDZ는 무엇일까

*Hoisting(호이스팅)**
// 호이스팅이란, 인터프리터가 변수와 함수의 메모리 공간을 선언전에 미리 할당하는 것을 말함.
변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는 것을 말함.

hi(1);
 function hi (a){
 a++;
 console.log(a)
 }

함수도 마찬가지로 선언이 실행 된 후 실행문이 동작한다.

  • JS는 초기화를 제외한 선언만 호이스팅한다.
console.log(catName)
var catName; //선언
catName = "sol"; // 초기화

위 코드는 호이스팅되어 아래와 같이 해석된다.

var catName;
console.log(catName); //undefined
catNmae = "sol"; // 초기화 

초기화는 호이스팅 되지 않고, 선언만 호이스팅된다.

JS는 const, let, var 등 모든 선언에서 호이스팅이 일어난다.
*var는 선언과 동시에 undefined로 초기화 저장되므로 에러가 안나지만, const, let는 초기화 되지 않은 상태로 저장되므로 에러가 발생함.
초기화되지 않은 변수는 참조할 수 없기에 에러가 발생함.

- **변수는 3단계에 걸쳐 생성된다.**
-**1단계: 선언 단계(Declaration phase)**

변수를 실행 컨텍스트의 변수 객체에 등록한다.이 변수 객체는 스코프가 참조하는 대상이 된다.

- **2단계: 초기화 단계(Initialization phase)**

변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.이 단계에서 변수는 undefined로 초기화 된다.

- **3단계: 할당 단계(Assignment phase)**

undefined로 초기화된 변수에 실제 값을 할당한다.

var 키워드로 선언한 변수는 선언 단계와 초기화 단계가 한번에 이뤄진다.         

즉, 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화한다. 

따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다. 

이후 변수 할당문에 도달하면 비로소 값이 할당된다.

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

즉, 스코프에 변수를 등록(선언 단계)하지만 초기화 단계는 변수 선언문에 도달했을 때(코드 실행 후) 이뤄진다. 

초기화 이전에 변수에 접근하려고 하면 참조 에러가 발생한다. 이는 아직 변수가 초기화되지 않았기 때문이다. 

즉, 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다. 

따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다.

TDZ(Temporal Dead Zone)

hoisting(호이스팅) 개념과 비슷하다고 할수 있는데

function a () {
	console.log(x);
    const x = 'x2'; //TDZ
}
  • const, let선언보다 변수에 접근하는 경우 발생함.
  • TDZ는 let, const, class구문의 유효성읠 관리한다.
  • function, import, var 구문은 TDZ에 영향을 받지 않고 현재 스코프에서 호이스팅된다.
  • TDZ는 선언문이 존재하는 스코프 범위 안에서 변수에 영향을 준다.
  • TDZ는 const, let, class 구문의 유효성에 영향을 미치는 중요한 개념이다. TDZ는 변수 선언 전에 변수를 사용하는 것을 허용하지 않는다.
if(something) { //Outerblock scope
	if(simethingElse) {//Inner block scope
    pi; 					//Temporal Dead Zone(TDZ)
    const pi = 3.14;
    	}
    }
  • 반대로 var는 변수 선언 전에도 사용할 수 있기에 땨문에 사용을 피하는 것이 좋음.

막간 개념
변수 선언 시 값을 할당하지 않으면 -> undefined
선언되지 않은 변수를 참조하면 // ReferenceError: foo is not defined
배열이 기본으로 제공해주는 함수들이 있습니다. 이 중 원하는 데이터를 필터링 해주는 함수는 Array.portopype.filter()

profile
[...김가네 솔이는 코딩을 합니다.]

0개의 댓글