Javascript 언어의 특성

김상선·2022년 5월 18일
0
post-thumbnail

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


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

동적 타입 언어(dynamically typed)
자료의 타입은 있지만 변수에 저장되는 값의 타입은 언제든지 바꿀 수 있는 언어

느슨한 타입(loosely typed) : 타입의 선언 없이 변수를 선언
강력한 타입(strong typed) : 타입과 함께 변수를 선언


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

  • 아래는 느슨한 타입(loosely typed)을 사용하는 Javascript 예시이다.
    foo 라는 변수(variable)의 값을 숫자 -> 문자 -> 불리언으로 타입 재할당할 수 있음을 보여준다.
let foo = 42 // 변수 foo의 타입은 숫자
foo = 'fighters' // foo를 문자열로 재할당
foo = true // foo를 불리언으로 재할당
  • 이와 달리 강력한 타입(strong typed)을 사용하는 JAVA 언어에서는 타입과 함께 변수를 선언해야한다.
int a = 13; // int 숫자형 타입 선언
String b = "thirteen"; // String 문자형 타입 선언




JavaScript 형변환

Javascript에서 함수와 연산자에 전달되는 값은 대부분 적절한 자료형으로 자동 변환된다. 이러한 과정을 "형 변환(type conversion)"이라고 한다.

명시적 타입 변환 explicit coercion : 개발자가 의도적으로 타입을 변환하는 것.
암묵적 타입 변환 implicit coertion : 개발자의 의도와는 상관없이 자바스크립트 엔진에 의해 자동으로 타입이 변환 되는 것.

아래는 명시적 타입의 변환의 방법이다.

문자열 타입으로 변환

var 변수 = String(2); //숫자를 문자열로 변환해줌
var 변수 = 숫자.toString(진법); //숫자를 문자열로 변환해줌 - 변환하면서 진법을 바꿀 수 있음
var 변수 = 숫자.toFixed(소수자리수); //숫자를 문자열로 변환해줌 - 실수형의 소수점 자리 지정가능
var 변수 = 숫자 + "문자열"; //숫자와 문자열을 하나의 문자열로 더해줌

숫자 타입으로 변환

var 변수 = parseInt(문자열);    	//문자열을 정수형 숫자로 변환해줌
var 변수 = parseFloat(문자열);  	//문자열를 실수형 숫자로 변환해줌
var 변수 = Number(문자열);    	//문자열를 정수&실수형 숫자로 변환해줌

숫자형으로 변환의 규칙

전달받은 값형 변환 후
undefinedNaN
null0
true / false1 / 0
string전달받은 문자열을 “그대로” 읽되, 처음과 끝의 공백을 무시.
문자열이 비어있다면 0이 되고, 오류 발생 시 NaN.


==, ===

== 동등 비교 연산자 : 0과 false를 구별하지 못한다.
=== 일치 연산자(strict equality operator) : 엄격한(strict) 동등 비교 연산자.
자료형의 동등 여부까지 검사하기 때문에 피연산자 a와 b의 형이 다를 경우 a === b는 즉시 false를 반환한다.

비교 연산자

a > b : a가 b 보다 큼
a < b : a가 b 보다 작음
a >= b : a가 b보다 크거나 같음
a <= b : a가 b보다 작거나 같음
a == b : 같음 (동등) a = b와 같이 등호가 하나일 때는 할당을 의미
a != b : 같지 않음 (부등 ≠ ), ‘불일치’ 연산자 !==는 부등 연산자 !=의 엄격한 버전

a == b 동등 연산자 (equality operator) 형이 다른 피연산자를 비교할 때 피연산자를 숫자형으로 바꿈.
그렇기 때문에 ==은 0과 false를 구별하지 못한다.
*a === b : 일치 연산자 (strict equality operator) 형 변환 없이 값을 비교하여, 자료형의 동등 여부까지 검사하는 연산자

alert( '2' > 1 ); // true, 문자열 '2'가 숫자 2로 변환된 후 비교가 진행됩니다.
alert( '01' == 1 ); // true, 문자열 '01'이 숫자 1로 변환된 후 비교가 진행됩니다.
alert( true == 1 ); // true

alert( false == 0 ); // true , 0과 false를 구분하지 못하고 true.
alert( false === 0 ); // false, 숫자형과 불린형으로 피연산자의 형이 달라서 false.




느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점은 무엇이고 보완할 수 있는 방법에는 무엇이 있을지 생각해보세요.

느슨한 타입의 동적 언어는 변수를 선언할 때 타입 체크를 하지 않기 때문에 편리하지만,
규모가 커질 수록 변수의 타입이 올바른지 체크하는 것이 까다롭기 때문에 오류를 찾기 어렵다.
동적언어의 이러한 문제는 런타임 에러를 발생시킨다.

  • Javascript 의 변수 선언 오류 예시
alert(1 + true); // 1 + 1 = 2

자바스크립트에서는 숫자형과 불린형을 더해도 오류가 나지 않는다.
예시에서는 불린형 True를 1로 바꾸기 때문에 1 + 1 = 2를 반환한다.

JavaScript의 단점을 보완하여 정적 타입 체크강력한 문법을 추가한 TypeScript를 사용하여 보완 가능

Typescript는 정적 타입을 명시할 수 있다는 것이 javascript 와의 가장 큰 차이점이다. IDE같은 개발 도구에 개발자가 의도한 변수나 함수 등의 목적을 명확하게 전달할 수 있다. 그렇기 때문에 코드 자동 완성이나 잘못된 변수 & 함수 사용에 대한 에러 알림 같은 피드백을 받을 수 있게 된다. 즉, '자바스크립트를 실제로 사용하기 전에 있을만한 타입 에러들을 미리 잡는 것' 이 타입스크립트의 사용 목적이다.
Typescript는 컴파일의 결과물로 JavaScript 코드를 출력한다. 최종적으로 런타임에서는 Typescript에서 컴파일로 출력된 JavaScript 코드를 구동시키게 된다.

  • Javascript의 변수 선언 예시
const a = 3;
const b = '5';
console.log(a*b)
  • Typescript의 변수 선언 예시
const a:number = 3;
const b:string = '5';
console.log(a*b)

undefined와 null의 미세한 차이들을 비교해보세요.

undefined: 변수를 선언하고 값을 할당하지 않은 상태, 설정 불가, 쓰기 불가 속성, 자료형이 없음
null : 변수가 의도적으로 비어 있음. 자료형은 object(객체). bulean 값은 False.

typeof null // 'object'
typeof undefined // 'undefined'
null === undefined // false
null == undefined // true
null === null // true
null == null // true
!null // true
isNaN(1 + null) // false
isNaN(1 + undefined) // true

참고자료 :
https://hanamon.kr/javascript-undefined-null-%EC%B0%A8%EC%9D%B4%EC%A0%90/


JavaScript 객체와 불변성이란 ?

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

데이터의 두 가지 타입

  • 원시 자료형(primitive type)과 참조 자료형(reference type)이 있다.
  • 원시 자료형이 할당될 때에는 변수에 값(value) 자체가 담긴다.
  • 참조 자료형이 할당될 때는 보관함의 주소(reference)가 담긴다.

기본형 데이터 Primative Type = 원시 타입
일반적인 값을 저장하는 데이터 타입. 객체가 아닌 데이터 유형을 말한다.
불변성을 띈다. = 변경 불가능한 값 (immutable value)

  • Number : 숫자형
  • String : 문자형
  • Boolean : true & false
  • null: 의도적으로 비어있음을 표현
  • undefined : 변수를 선언하고 값을 할당하지 않은 상태
  • Symbol : ES6에 추가, 객체 속성을 만드는 데이터 타입)

참조형 데이터 Reference Type = 객체 (object)
변수에 할당 할 때 값이 아닌 그 값의 위치 정보를 가지는 데이터 타입.
컴퓨터 과학에서의 객체란 식별자로 참조할 수 있는 메모리 상의 값을 말한다.
가변성을 띈다. = 변경 가능한 값 (mutalbe value)

  • Object 객체 {}
    - Array 배열 []
    - function 함수 ( function(){} )
    - RegExp 정규표현식

객체와 프로퍼티

객체 : 원시 값을 제외한 나머지값은 모두 객체이며, 객체는 0개 이상의 프로퍼티로 구성된 집합이다.
프로퍼티 : (key)와 (value)로 구성된다. 프로퍼티 값이 함수일 경우 메서드(method)라 부른다.


불변 객체를 만드는 방법

불변 객체 (immutable object)
어떤 객체의 내부에 프로퍼티들을 변경할 수 없도록 되어있는 객체

불변 객체를 만드는 법

  • 새 객체를 하드코딩

  • 얕은 복사 : 바로 아래 단계의 값들만 복사하는 방법

  • 깊은 복사 : 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법

  • 객체의 방어적 복사(defensive copy)
    Object.assign

  • 불변객체화를 통한 객체 변경 방지
    Object.freeze

출처:
https://overcome-the-limits.tistory.com/271
https://poiemaweb.com/js-immutability

불변 객체가 필요한 경우

만약 객체를 복사해서, 내부 프로퍼티를 변경하고 싶을 때, 복사한 객체를 변경하더라도, 원본 객체가 변하지 않아야 하는 경우가 생긴다.
즉, 객체에 변화를 가해도 원본이 그대로 남아있어야 하는 경우이다. 이런 경우에 '불변 객체'가 필요하다.

  • ex) 정보가 바뀌었으면 알림 전송하는 경우, 바뀌기 전의 정보와 바뀐 후의 정보를 보여줘야하는 경우 등

얕은 복사와 깊은 복사

얕은 복사 : 직속 프로퍼티만 변경이 방지되고 중첩 객체까지는 영향을 주지 못함.
깊은 복사 : 객체의 중첩 객체까지 동결하여 변경이 불가능한 읽기 전용의 불변객체를 구현하는 것.


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

스코프, 호이스팅, TDZ

스코프(scope) : 식별자가 유효한 범위.
모든 식별자는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다. 이를 스코프라 한다.

  • 식별자(identifier) : 변수 이름, 함수 이름, 클래스 이름 (var, let, const, function, class, etc...)

스코프란 식별자를 검색할 때 사용하는 규칙이라고도 할 수 있다. 함수의 매개 변수는 함수 몸체 내부에서만 참조할 수 있고, 외부에서는 참조할 수 없다. 이것은 함수의 매개변수가 참조할 수 있는 유효범위, 즉 스코프가 함수 몸체 내부로 한정되기 때문이다.

호이스팅(hoisting) : 식별자 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징. 호이스팅은 스코프(전역/지역) 단위로 동작한다.

자바스크립트 엔진은 식별자 선언이 소스코드의 어디에 있든 상관없이 어디서든지 변수를 참조할 수 있다.

TDZ(Temporal Dead Zone) : 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 일시적 사각지대라고 부른다.

// 런타임 이전에 선언 단계가 실행된다. 아직 변수가 초기화되지 않았다.
// 초기화 이전의 일시적 사각지대에서는 변수를 참조할 수 없다.
console.log(foo); // ReferenceError: foo is not defined

let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined

foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1


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

  • 함수를 정의하는 4가지 방식
    - 함수 선언문
    - 함수 표현식
    - function 생성자 함수
    - 화살표 함수(ES6)
//함수선언문
function add(x,y){
  return x+y;
}
//함수 표현식
var add = function(x,y){
  return x+y;
};
//function 생성자 함수
var add = new function('x','y','return x+y');
//화살표 함수
var add = (x,y)=>x+y;

함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수는 함수의 생성시점이 다르다. 힘수 선언문은 코드가 한줄씩 순차적으로 실행되는 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행된다.

  • 아래의 예시를 보면,
    함수 선언문으로 정의된 add 함수는 호이스팅 됐지만, 함수 표현식으로 정의된 sub 함수는 호출되지 않았다.
// 함수 참조
console.dir(add); // ƒ add(x, y)
console.dir(sub); // undefined

// 함수 호출
console.log(add(2, 5)); // 7
console.log(sub(2, 5)); // TypeError: sub is not a function

// 함수 선언문
function add(x, y) {
  return x + y;
}

// 함수 표현식
var sub = function (x, y) {
  return x - y;
};

let, const, var, function의 원리

키워드letconstvar
변수 중복 선언금지금지가능
변수 재할당가능금지가능
스코프블록레벨블록레벨함수레벨
호이스팅TDZTDZ발생

var 로 선언한 변수

  • 변수 중복 선언 허용 : 초기화문 유무에 따라 다르게 동작
  • 함수 레벨 스코프 : 오로지 함수의 코드블록만을 지역스코프로 인정
  • 변수 호이스팅 : var로 선언한 변수는 변수 선언문 이전에 참조 가능

let 으로 선언한 변수

  • 변수 중복 선언 금지 : 변수를 중복 선언하면 문법 에러(SyntaxError) 발생
  • 블록 레벨 스코프: 모든 코드 블록을 지역 스코프로 인정
  • 변수 호이스팅 : 선언단계와 초기화단계가 분리되어 진행. TDZ 발생. 호이스팅이 발생하지 않는 것처럼 동작함.

const 로 선언한 변수

  • 선언과 초기화 : 선언과 동시에 초기화 해야함
  • 변수 재할당 금지
  • 상수
  • let vs const
    변수 선언에는 기본적으로 const를 사용하고 let은 재할당이 필요한 경우에 한정해 사용하는 것이 좋다. 변수를 선언하는 시점에서는 변수의 재할당이 필요할지 잘 모르는 경우가 많으며, 객체는 재할당하는 경우가 드물다. 따라서 변수를 선언할 때는 일단 const 키워드를 사용하고, 반드시 재할당이 필요하면 그때 let으로 변경해도 된다.
  • 함수의 구성요소

실행 컨텍스트와 콜 스택


스코프 체인, 변수 은닉화

스코프의 종류

구분설명스코프변수
코드 전역 Global코드의 가장 바깥 영역전역 스코프전역 변수
코드 지역 Local함수 몸체 내부지역 스코프지역 변수

스코프 체인
스코프가 하나의 계층적 구조로 연결되는 것을 스코프 체인이라고 한다.
모든 지역 스코프의 최상위 스코프는 전역 스코프이다.

스코프 체인으로 연결된 계층적 구조는 부자관계로 이루어진 상속과 유사하다.
부모의 자산은 상속을 통해 자식이 자유롭게 사용할 수 있지만, 자식의 자산을 부모가 사용할 수는 없다.

캡슐화와 정보 은닉

캡슐화 : 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것.
정보 은닉 : 객체의 특정 프로퍼티나 메서드를 감출 목적으로 캡슐화를 사용하는 것. 정보 은닉은 객체간의 상호 의존성, 즉 결합도(coupling)를 낮추는 효과가 있다.
클로저 : 외부 함수보다 중첩함수가 더 오래 유지되는 경우, 중첩함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저(closure)라고 부른다.

함수 몸체 내부의 매개 변수는 외부에서 참조할 수 없다. 즉, 함수의 스코프 체인(함수 몸체 내부의 지역 변수가 함수의 생명주기와 함께 소멸되는 것)을 이용해 함수 몸체 내부의 매개 변수를 은닉화, 즉 숨길 수 있다. 이때 함수는 매개 변수를 참조할 수 있는 클로저(closure)다.

function A(){
  let temp = 'a' 
  return temp;
} 
console.log(temp) // error: temp is not defined
const result = A()
console.log(result); // a
// 함수 A의 매개 변수 temp는 은닉화 되어있다.
// result에 함수 A()를 선언했더니 매개변수 a가 참조되어 출력되었다.
// 함수 A는 매개 변수 temp를 참조할 수 있는 클로저이다.

실습 과제

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

function hi () {

const a = 1; // 지역 변수 은닉화

let b = 100; // 지역 변수 은닉화

b++; // 함수가 1바퀴 돌면 b값 증가

console.log(a,b); // TDZ. 함수가 실행되기 전까지 출력되지 않음.

}

// console.log(a); // ReferenceError: a is not defined
// 5번째 줄 a=1;은 지역변수로 hi()함수가 실행되지 않으면 참조되지 않는다.

console.log(b); // 1; 1번째줄 전역 변수가 전역 스코프로 출력되었다.

hi(); // 1 101; 6번째줄 지역변수가 참조되어 출력되었다.

console.log(b); // 1; hi() 함수가 종료되면 매개 변수의 생명주기도 종료된다. 다시 전역 변수 b가 출력됨.
profile
일요일을 좋아합니다.

0개의 댓글