본문 바로가기
Archive

Javascript 언어의 특성

by livemehere 2022. 7. 1.

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

자바스크립트는 변수를 선언할 때 var, let ,const 를 사용한다.

다른언어에서 볼 수 있는 "타입"을 함께 선언하지 않는다.

이유는 자바스크립트에서 변수는 특정 타입에 결속되지 않고, 어떤 타입이든 할당할 수 있기 때문이다.

 

이점은 장점이자 동시에 단점으로 작용할 수 있다.

프로젝트의 규모가 커지게 된다면 각 변수들이 여기저기서 import, export 될 수 있다.

그렇게된다면 협업을 할때도, 혼자 개발을 할때도 항상 선언부를 찾아가서 의도를 파악하거나, 전체 코드를 읽어야 할 수있다.

 

또 만약 의도치 않은 타입을 잘못입력했다면, 그 에러는 런타임에서 발생하고, 개발자의 실수를 막아주지 못한다.

 

그래서 정적언어로서 이단점을 개선하기 위해서 Typescript가 나왔고, typescript는 javascript 로 컴파일되어 사용된다.

 

Javascript의 형 변환

형 변환이라는 것은 자료형이 변환되는 것을 말합니다.

javascript에서는 세가지의 형변환이 있는데

1. String 으로

2. Number 으로

3. Boolean 으로

가 있습니다.

 

원시타입과 객체(Object)는 형변환이 다르게 작용합니다.

 

String 변환

명시적으로 변환

String(1234) // '1234'
(1234).toString() // '1234' tip. Number형태는 ()의 활용으로 숫자의 범위를 명시해야 합니다.
String(true) // 'true'

Boolean 변환

명시적으로 변환

Boolean('adsf'); // true
Boolean(0); // false

암시적으로 변환

||, &&, !

해당 연산자는 조건에 맞으면 실제 피연산자를 반환하지만 내부적으로 형변환이 일어납니다.

 

또한 Boolean의 경우 truthy, falsy 한 값이 있습니다. 이 값들은 형변환시에 적용됩니다.

Falsy : '', 0, NaN, null, undefined, fasle

Truthy : 나머지 전부

Number 변환

명시적으로 변환

Number('1234'); // 1234

암시적으로 변환

'1' + 1; //'11'
'1' - 1; // 0

'4' / 2 // 2
'5' % 2 // 1

Number의 암시적 변환은 조금 복잡합니다.

위 크드와 같이 - , / , % 는 숫자로 계산되는 반면, 덧셈은 string 이 됩니다.

 

Object 변환

[1] + [2,3] // '12,3'

Object의 경우에 .toString() 으로 각각 변환한 다음 string끼리 합치는 여산을 수행합니다.

 

== & ===

== : 값 자체만 비교

=== : 값 & 타입까지 비교

console.log(true == 1); // true 
console.log(true === 1); // false

예시를 보면 바로 이해가됩니다.

1는 truthy 하지만 boolean 타입은 아닙니다!

또한 '1' == 1 은 true 지만, '1' === 1 은 false 입니다

 

null & undefined

null == undefined // true
nul === undefined // false

두개는 엄밀히 다르다.

undefined는 자료형 자체가 undefined이고, null 은 object이다.

 

할당은 되었지만, 값이 없음을 의미하고,

null 은 어느것도 참조하지 않는 상태를 의미한다.

 

그래서 null 값이 들어간다면 가비지컬렉션이 동작한다.

불변성(Immutability)

React를 하다보면 state의 불변성을 유지해야한다고 한다. 그래서 값을 직접바꾸는 것이아니라, 새로운 객체를 만들어 필요한 부분을 수정해서 재할당 한다. 불변성의 개념은 React에서 state와 props에 녹아낸것이지 React에 국한된 개념이 아니다.

 

그러렇다면 불변성은 왜 켜야하는 것일까?

 

예를들어서 내가 사용하고있는 변수가, 의도치않은 값으로 변경된다면 어떻게 해야할까?

모든 코드를 찾압보면서 sideEffect가 발생한 곳을 찾아내야한다.

 

이것이 이유이다. 개발을 할때 객체를 직접 변경하지 않는다는 규칙을 지키면,

예상 가능하고 신뢰할 수 있는 코드가 된다. class를 사용할 때 내부 맴버를 private으로 사용하는 이유와 일맥상통한다.

 

자바스크립트에서 Immutable 한 자료형은 원시타입이다.(primitive type)

  • Boolean,
  • String
  • Number
  • Null
  • undefined
  • Symbol

원시타입들은 변수에서 재할당을 하게되면 새로운 메모리주소에 생성해 할당한다.즉, 불변성을 고려할 필요없이, 불변성을 유지하고있다.

let name = 'foo';
name = 'bar';

Mutable type

Javascript에서 모든 객체(Object)는 변할 수 있는 값이다.

const person ={
	name:'kong'
}

person.name = 'ha';

위와 같이 객체의 property를 조작한 경우에는 새로운 객체를 만든 것이 아니라 같은 객체를 수정한 것이 된다.

불변성을 지키려면 아래와 같이 새로운 객체를 만들어 참조값을 변경해야한다.

let person ={
	name:'kong'
}

person = {
	name:'ha'
}

 

이 불변성의 동작을 이해하면 객체를 복사할 때 알아야 하는 얕은 복사(Shallow copy) 와 깊은 복사(Deep copy)는 자연스레 이해할 수 있다.

객체를 복사하는 방법은 여러가지가 있지만 간단히 spread 문법으로 예를 들어보자면

const arr = [1,2,3,{name:'kong'},5];

const arr2 = [...arr];

arr2[3].name = 'ha';

console.log(arr, arr2);

위와 같이 변경하면 arr도 arr2 name:'ha' 이 된다.

 

호이스팅 과 TDZ

TDZ 란 "Temporal Dead Zone" 으로 사각지대를 의미한다.

당연하게도, 변수를 선언하지 않고 접근하려고 할 때 Reference Error 가 발생한다.

변수를 참조하려고 할때 그 환경에서 변수가 선언되지 않은 곳, 접근할 수 없는 곳을 TDZ 라고한다.

 

이 TDZ의 영향을 받는 것과 아닌것들이 있다.

이본적으로 선언 -> 할당 -> 참조 의 과정을 거치는데

 

javascript에서는 var 키워드와 function 은 호이스팅이 되면서 코드상으로 참조보다 선언이 아래에 위치해도 된다.

"호이스팅"이란 선언부를 최상단으로 끌어올리는 현상을 말한다.

아래의 예시처럼 add()함수는 선언되기 이전에 참조한 것 같지만, javascript가 function을 최상단으로 올리고 동작하고 있다.

이를 Hoisting 이라고한다.

add(1,2)

function add(x,y){
	return x+y;
}

스코프(Scope)

Javascript 뿐만 아니라 거의 모든 언어에서 변수의 유효범위가 존재한다.

지역변수 와 전역변수로 나누어 볼 수 있는데, 전역변수는 어떤 함수내에도, 브라켓 내에도 속해있지 않은 환경에서 선언한 변수를 의미하고

이는 하위 모든 스코프에서 접근이 가능하다.

 

하지만 지역변수 같은 경우에는 함수 스코프와 블록 스코프로 나뉜다.

 

자바스크립트에서는 함수를 생성하면 그 내부적으로 변수를 가지게된다.

함수 내부에서 선언한 변수들은 함수 바깥에서 접근할 수 없다.

 

조금 주의애햐 할 것은 블록스코프이다.

블록스코프는 함수가아니라 말그대로

{
	// 여기 ~~
}

를 말한다.

이 블록 스코프 내에서 선언한 변수들은 외부에서 참조가 불가능하다.

하지만 let, const 로 선언된 변수들에 해당하는 사항이다.

만약 var, 나 function을 선언하면 외부에서 접근이 가능하다

그래서 var, let, const 의 특징을 알고 있어야한다.

간단히 정리해 보자면

 

var: 재선언, 재할당 가능, 호이스팅 됨, function 스코프

let : 재할당 가능, block 스코프

const : 둘다 불가능 , block 스코프

 

실행 컨텍스트

실행컨텍스트라는 것은 함수가 실행될 때, 그 실행환경의 구성요소들을 말한다.

구성요소는 3가지로

1. Environment Records : Lexical 스코프내의 함수와 변수의 선언을 저장하는 곳

2. Reference to the outer environment : 외부 환경을 참조(실시간 변동)

3. this binding : 전역실행 context 에서는 window(global)이지만, 함수 실행컨텍스트에서는 this를 호출되는 시점에서 다시 바인딩합니다. 객체의 의해서 참조되지 않으면 undefined로 남아있음

 

어떤 환경(함수내, 전역환경) 에서 변수를 참조할 때는 이렇게 생성된 실행 컨텍스트 내에서 참조를 하게 되고, 만약 그 환경 내에서 찾지 못할 경우 2번을 통해서 실행된 컨텍스트로 가고, 거기서 없으면 또 가고.. 최종적으로 없으면 참조하지 못한다.

이 현상을 스코프체인이라한다. 체인처럼 연결되어있다고해서..!

 

클로저(Closure) 와 은닉화

위 개념들을 가지고 있으면 악명높은 클로저도 이해할 수 있습니다.

우선 클로저의 정의는 "어휘적(lexical) 환경의 조합"이라고한다.

쉽게 말하면 "내부함수가 외부함수에 접근할 수 있는 것" 을 말한다.

반대로 외부함수에서는 내부함수의 변수에 접근할 수 없다.

 

react에서 useState() hook이 클로저를 활용해서 만들어졌다.

 

예를들어서 아래와 같이 함수를 만들고 값을 반환해서 사용하는 useState()를 만들었다고 가정하면

count라는 변수에 직접 접근하여 변경할 수 있지만

function useState(initalValue = undefined){
	let value = initalValue;
    
    return value;
}

let count = useState(0);
console.log(count);
count++;
console.log(count);

아래와 같이 함수를 리턴하면 접근이 불가능하다. 은닉화 라고한다.

(()()는 setState처럼 구현하기 위해서 함수를 바로 실행한 형태입니다)

function useState(initalValue = undefined){
    let innerState = initalValue;
    
    const state = () => innerState;
    
    const setState = (value) => {
    	innerState = value;
    }
    
    return (()=> [state,setState])();
}

let [count,setCount] = useState(0);
console.log(count());
setCount(3);
console.log(count());

간단 실습

let b = 1; // (가)

function hi () {

    const a = 1;

    let b = 100; (나)

    b++; (다)

    console.log(a,b); (다)실행후 (나)출력

}

//console.log(a); // 이 주석을 푼다면 전역스코프에 a 변수는 존재하지 않기 때문에 Reference Error

console.log(b); // (가)출력

hi();

console.log(b); // (가)출력

// 1 
// 1 101
// 1

 

 

 

반응형