1.JavaScript의 타입과 자료의 특성
1) 동적 타입
JavaScript는 느슨한 타입(loosely typed)의 동적(dynamic) 언어입니다. JavaScript의 변수는 어떤 특정 타입과 연결되지 않으며, 모든 타입의 값으로 할당 (및 재할당) 가능합니다.
let foo = 42 // foo가 숫자
foo = 'bar' // foo가 이제 문자열
foo = true // foo가 이제 불리언
2) JavaScript 형변환
자바스크립트는 타입이 매우 유연한 언어이다. 때문에 때로는 자바스크립트 엔진이 필요에 따라 ‘암시적변환’ 을 혹은 개발자의 의도에 따라 ‘명시적변환’ 을 실행한다. 그렇다면 형변환을 통해 자바스크립트가 왜 유연한 언어가 되었는지 함께 살펴보자 !
(1)암시적변환
암시적 변환이란 자바스크립트 엔진이 필요에 따라 자동으로 데이터타입을 변환시키는 것이다.
산술 연산자
더하기(+) 연산자는 숫자보다 문자열이 우선시 되기때문에, 숫자형이 문자형을 만나면 문자형으로 변환하여 연산된다. (문자 > 숫자)
number + number // number
number + string // string
string + string // string
string + boolean // string
number + boolean // number
다른 연산자(-,*,/,%)
숫자형이 문자형보다 우선시되기 때문에 더하기와 같은 문자형으로의 변환이 일어나지 않는다. (문자 < 숫자)
string number // number
string string // number
number number // number
string boolean //number
number boolean //number
"2" flase ; // 0
2 * turu ; // 0
(2)명시적변환
명시적변환이란 개발자가 의도를 가지고 데이터타입을 변환시키는 것이다.
타입을 변경하는 기본적인 방법은 Object(), Number(), String(), Boolean() 와 같은 함수를 이용하는데 new 연산자가 없다면 사용한 함수는 타입을 변환하는 함수로써 사용된다.
var trans = 100; //Number
Object(trans); //100
console.log(typeof trans); //Number
String(trans); //”100"
console.log(typeof trans); //String
Boolean(trans); //true
console.log(typeof trans); //Bolean
2.== , === ??
동치 비교
아래의 예제는 엄격하지 않은 동치(==) 비교이며, 아래의 결과값은 좌우항 변환 할 경우 모두 ‘0 == 0 이기때문에’ true 이다.
null == undefined
“0” == 0
0 == false
“0” == false
JavaScript의 단점을 보완하여 정적 타입 체크와 강력한 문법을 추가한 TypeScript를 사용하여 보완 가능
4.undefined와 null의 미세한 차이
undefined은 변수를 선언하고 값을 할당하지 않은 상태, null은 변수를 선언하고 빈 값을 할당한 상태(빈 객체)이다. 즉, undefined는 자료형이 없는 상태이다.
따라서 typeof를 통해 자료형을 확인해보면 null은 object로, undefined는 undefined가 출력되는 것을 확인할 수 있다.
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
1) undefined
undefined는 원시값(Primitive Type)으로, 선언한 후에 값을 할당하지 않은 변수나 값이 주어지지 않은 인수에 자동으로 할당된다. 이 값은 전역 객체의 속성 중 하나로, 전역 스코프에서의 변수이기도 하다. 따라서 undefined 변수의 초기 값은 undefined 원시 값이다.
cf) undefined는 예약어가 아니기 때문에, 전역 범위 외에서 변수 이름으로 사용할 수 있다. 그러나 유지보수와 디버깅에 어려움을 겪을 수 있으므로 피하는 것이 좋다.
아래의 경우에 변수가 undefined를 반환한다.
.값을 할당하지 않은 변수
.메서드와 선언에서 변수가 할당받지 않은 경우
.함수가 값을 return 하지 않았을 때
2) null
null은 원시값(Primitive Type) 중 하나로, 어떤 값이 의도적으로 비어있음을 표현한다. undefined는 값이 지정되지 않은 경우를 의미하지만, null의 경우에는 해당 변수가 어떤 객체도 가리키고 있지 않다는 것을 의미한다.
2.객체와의 불변성이란?
1) javascript 객체
기본형(primitive Type) : number,String,Boolean,null,undifiend,symbol
참조형(Reference Type):
Object,Array,Function,Date,RegExp,Map,WeakMap,Set,WeakSet
불면성
데이터 영역의 메모리에 관한것으로 데이터 영역의 데이터는 한번 생성되었을 경우 수정이 안되는것을 의미하며 효율적으로 데이터를 저장하기 위해 발생했다.
2) 형변환
//숫자 -> 문자
let tt = 2;
tt+=""; // String(tt),tt.toString()
//문자 -> 숫자
let text = "hello"
tt*=1; // Number(text).parseInt(text)
3) 불변 객체를 만드는 방법
const text = {naem : "kim"}
Object.freeze(text);
let으로 선언하면 freeze()를 써도 재할당이 가능하므로 재할당이 불가한
const로 선언해야한다.
4) 얕은 복사 와 깊은 복사
(1) 얕은 복사 shllaw Copy
원래값 과 복사된 값이 같은 참조를 가리키고 있는 것을 말한다.
객체안에 객체가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면
이를 얕은 복사라고 한다.
(2) 깊은 복사 Deep Copy
깊은 복사된 객체는 객체안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.
ex)JSON.stringify()/loadsh 라이브 러리 사용
3.호이스팅과 TDZ
1) 스코프?호이스팅?TDZ?
(1)호이스팅? 인터프리터가 변수와 함께 메모리 공간을 선언전 미리 할당
var 선언) undifined로 초기화
let,const 선언) 초기화 X
-함수 선언문과 함수 표현식에서 호이스팅 방식의 차이예제
//ex)초기화 사용 예제
//함수호출이 함수자체 보다 엎서 존재하지만 동작이 잘됨
catName("호랑이");
console.log("제 개구리 이름은"+name+"입니다.);
}
console.log(num) // undefined 출력
var num; // 선언
num = 6; // 초기화
-------------
console.log(num); //ReferenceError
num =6;
let, const, var, function 이 어떤 원리
var : 변수 재선언 가능const, let : 변수 재선언 불가능
const : 변수 재할당 불가능 (상수)let : 변수 재할당 가능
var : functional-scope 로 호이스팅됨
const, let : block-scope 로 호이스팅됨
(2)TDZ
let,const,class 구문의 유효성을 관리하며 자바스크립에서 변수가 동작하는 중요한 방식이다.
(3)스코프
변수에 접근할 수 있는 범위
type: global(전역)과 local(지역)
전역에 선언되어있는 변수는 어느 곳에서든지 해당변수에 접근 할 수 잇다.
지역에 선언되어있는 변수는 해당지역에서만 접근할 수 있다.
>var a =1; //전역 스코프
function print(){
var a= 111;
console.log(a); //111
}
print();//111
console.log(a);//1
4) 실행 컨텍스트와 콜스텍
(1) 실행 컨텍스트
. 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
. 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념
<실행 컨텍스트가 활성화 되는 시점>
1.호이스팅이 발생
2.외부환경 정보를 구성한다.
3.this 값을 설정한다
<구성>
1.전역공간은 자동으로 컨텍스트로 구성된다.
2.함수를 실행한다.
3.eavl()함수를 실행한다.
4.block을 만든다.
호출 스택(call Stack)
호출 스택은 여러 함수들(functions)을 호출하는 스크립트에서 해당 위치를 추적하는 인터프리터 (웹 브라우저의 자바스크립트 인터프리터같은)를 위한 메커니즘입니다. 현재 어떤 함수가 동작하고있는 지, 그 함수 내에서 어떤 함수가 동작하는 지, 다음에 어떤 함수가 호출되어야하는 지 등을 제어합니다.
스크립트가 함수를 호출하면 인터프리터는 이를 호출 스택에 추가한 다음 함수를 수행하기 시작합니다.
해당 함수에 의해 호출되는 모든 함수는 호출 스택에 추가되고 호출이 도달하는 위치에서 실행합니다.
메인 함수가 끝나면 인터프리터는 스택을 제거하고 메인 코드 목록에서 중단된 실행을 다시 시작합니다.
스택이 할당된 공간보다 많은 공간을 차지하면 "stack overflow" 에러가 발생합니다.
5) 스코프 체인, 변수 은닉화
(1) 스코프 체인
자바스크립트에서 전역 변수는 전역 객체의 프로퍼티이다. (ECMAScript 명세에 정의되어 있음)
지역 변수는 그런 규정이 없지만, 변수를 각 함수 호출과 연관된 객체(call object)의 프로퍼티로 생각할 수 있다.
지역 변수를 어떤 객체의 프로퍼티로 생각한다면, 자바스크립트의 모든 코드는 스코프 체인을 갖고 있다. 스코프 체인은 해당 코드의 유효 범위(in scope) 안에 있는 변수를 정의하는 객체의 체인, 리스트다.
(2) 변수 은닉화
자바스크립트에서 일반적인 객체지향 프로그래밍 방식으로 prototype를 사용하는데 객체 안에서 사용할 속성을 생성할 때 this 키워드를 사용하게 됩니다.
하지만 이러한 Prototype을 통한 객체를 만들 때의 주요한 문제가 하나 있습니다. 바로 Private variables에 대한 접근 권한 문제입니다.
//아래 코드를 예시로 보도록 하겠습니다.
>function Hello(name) {
this._name = name;
}
Hello.prototype.say = function() {
console.log('Hello, ' + this._name);
}
let a = new Hello('영서');
let b = new Hello('아름');
a.say() //Hello, 영서
b.say() //Hello, 아름
a._name = 'anonymous'
a.say() // Hello, anonymous코드를 입력하세요
현재 Hello() 함수를 통해 생성된 객체들은 모두 name이라는 변수를 가지게 됩니다. 변수명 앞에 underscore()를 포함했기 때문에 일반적인 JavaScript 네이밍 컨벤션을 생각해 봤을때 이 변수는 Private variable으로 쓰고싶다는 의도를 알 수 있습니다.
하지만 실제로는 여전히 외부에서도 쉽게 접근가능한 것을 확인할 수 있습니다.
이 경우 클로저를 사용하여 외부에서 직접적으로 변수에 접근할 수 있도록 캡슐화(은닉화)할 수 있습니다.
function hello(name) {
let _name = name;
return function () {
console.log('Hello, ' + _name);
};
}
let a = new hello('영서');
let b = new hello('아름');
a() //Hello, 영서
b() //Hello, 아름
이렇게 a와 b라는 클로저를 생성함으로써 함수 내부적으로 접근이 가능하도록 만들 수 있습니다
실습 과제
콘솔에 찍힐 b 값을 예상해보고, 어디에서 선언된 “b”가 몇번째 라인에서 호출한 console.log에 찍혔는지, 왜 그런지 설명해보세요. 주석을 풀어보고 오류가 난다면 왜 오류가 나는 지 설명하고 오류를 수정해보세요.
let b = 1;
function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b); //const,let는 블럭 스코프에서 유효하기 때문에 함수 내부에 있는 변수들에 접근해서 출력됨
}
console.log(a); // 여기서 오류가 나는 이유는 전역적으로 a라는 변수가 존재하지 않아서 입니다.(이것을 해결하기 위해서는 전역적으로 a 초기값 변수를 할당하면 됩니다.)
console.log(b); //전역 b변수에 접근해서 출력
hi();
console.log(b); //전역 b변수에 접근해서 출력