2022. 11. 18. 16:48ㆍ항해99/2주차 - 프로그래밍 기초
JavaScript의 자료형과 JavaScript만의 특성은 무엇일까 ?
- 느슨한 타입(loosely typed)의 동적(dynamic) 언어
느슨한 타입이란? (혹은 동적타입 언어)
타입없이 변수를 선언하는 것
자바스크립트가 변수를 선언할 때 타입없이 var,let,const 로만 선언함
느슨한타입과 반대되는 말은 강력한타입, 강력한타입은 반대로 변수를 선언할 때 타입을 선언해야함 ex) c언어 int a = 10;
- 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.동치 비교
아래의 예제는 엄격하지 않은 동치(==) 비교이며, 아래의 결과값은 좌우항 변환 할 경우
모두 ‘0 == 0 이기때문에’ `true` 이다.
null == undefined
'0' == 0
0 == false
'0' == false
명시적변환
명시적변환이란 개발자가 의도를 가지고 데이터타입을 변환시키는 것
타입을 변경하는 기본적인 방법은 `Object(), Number(), String(), Boolean()` 와 같은 함수를 이용하는데
new 연산자가 없다면 사용한 함수는 타입을 변환하는 함수로서 사용된다.
1. 숫자형으로 변환하기
Number()
정수형과 실수형의 숫자로 변환한다.
Number(“12345”); //12345
Number(“2”*2); //4
parseInt()
정수형의 숫자로 변환한다. 만약 문자열이 `숫자 0` 으로 시작하면 8진수로 인식하고(구형브라우저 O, 신형브라우저X), `0x, 0X` 로 시작한다면 해당 문자열을 16진수 숫자로 인식한다. 또한 앞부분 빈 공백을 두고 나오는 문자는 모두 무시되어 NaN을 반환한다.
parseInt(“27”) //27
parseInt(0033); //27
parseInt(0x1b); //27
parseInt(“ 2”); //2
parseInt(“ 2ㅎ”); //2
parseInt(“ ㅎ2”); //NaN
parseFloat()
부동 소수점의 숫자로 변환한다.
parseInt()와는 달리 parseFloat()는 항상 10진수를 사용하며
parseFloat() 또한 앞부분 빈 공백을 두고 나오는 문자는 모두 무시되어 NaN을 반환한다.
parseFloat(“!123”); //NaN
parseFloat(“123.123456”); //123.123456
parseInt(“123.123456”); //123
parseFloat(“ 123.123456”); //123.123456
parseFloat(“ a123.123456”); //NaN
2. 문자형으로 변환하기
String()
String(123); //”123"
String(123.456); //”123.456"
toString()
인자로 기수를 선택할 수 있다. 인자를 전달하지 않으면 10진수로 변환한다.
var trans = 100;
trans.toString(); //”100"
trans.toString(2); //”1100100"
trans.toString(8); //”144"
var boolT = true;
var boolF = false;
boolT.toString(); //”true”
boolF.toString(); //”false”
toFixed()
인자를 넣으면 인자값만큼 반올림하여 소수점을 표현하며 소수점을 넘치는 값이 인자로 들어오면 `0`으로 길이를 맞춘 문자열을 반환한다.
var trans = 123.456789;
var roundOff = 99.987654;
trans.toFixed(); //”123"
trans.toFixed(0); //”123"
trans.toFixed(2); //”123.46"
trans.toFixed(8); //”123.45678900"
roundOff.toFixed(2); //”99.99"
roundOff.toFixed(0); //”100"
3. 불린형으로 변환하기
Boolean()
Boolean(100); //true
Boolean(“1”); //true
Boolean(true); //true
Boolean(Object); //true
Boolean([]); //true
Boolean(0); //false
Boolean(NaN); //false
Boolean(null); //false
Boolean(undefined); //false
Boolean( ); //false
- ==, === 차이
자바스크립트는 엄격한 비교와 유형변환 비교를 모두 지원하므로, 어떤 연산자가 어떤 비교조건에 사용되는지가 중요하다.
===는 변수 유형을 고려하는 반면, ==는 변수 값을 기반으로 유형을 수정한다.
"0" == 0 // true
"0" === 0 // false
- 느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점은 무엇이고 보완할 수 있는 방법에는 무엇이 있을까
문제점
많은 기능 명세서와 API가 오고 가는 대형프로젝트(혹은 협업 시)에서 타입이 올바른지 체크하는 것이 굉장히 까다롭기 때문에
배포 시 예상치 못한 문제와 직면할 수 있다.
보완방법
JavaScript의 단점을 보완하여 정적 타입 체크와 강력한 문법을 추가한 TypeScript 혹은 Flow 사용하여 보완 가능
- undefined와 null의 미세한 차이들을 비교
undefined은 변수를 선언하고 값을 할당하지 않은 상태 ex) 상자도 없는것
null은 변수를 선언하고 빈 값을 할당한 상태(빈 객체)이다. ex) 여기에 사과 상자가 온다는 것은 알지만 현재는 없음
즉, undefined는 자료형이 없는 상태이다.
JavaScript 객체와 불변성이란 ?
- 기본형 데이터와 참조형 데이터
자바스크립트 데이터 타입은 크게 두가지인 기본(원시)형(Primitive Type)과 참조형(Reference Type)으로 분리됨
기본(원시)형에는 Number, String, Boolean, null, undefined가 있으며 ES6 에서는 Symbol 도 추가되었습니다.
참조형은 대표적으로 객체(Object)가 있고 그 하위에 배열(Array), 함수(Function), 정규표현식(RegExp) 등이 있으며,
ES6에서는 Map, Set, WeakMap, WeakSet 등도 추가되었습니다.
- 불변 객체를 만드는 방법
불변 객체
'변하지 않는 객체' 즉 이미 할당된 객체가 변하지 않는다는 뜻
자바스크립트에서 불변 객체를 만들 수 있는 방법은 기본적으로 2가지 인데 const와 Object.freeze()를 사용함
const
변수를 상수로 선언할 때 사용
const test = {};
test.name = "ludin";
console.log(test); // {"ludin"}
ES6에서의 const는 할당된 값이 상수가 되는 것이 아닌 바인딩된 값이 상수가 됨
즉 test변수가 상수가 되기 때문에 const 키워드로 선언된 test변수에는 객체 재할당은 불가능하지만 객체의 속성은 변경 가능
Object.freeze()
자바스크립트에서 기본적으로 제공하는 메소드임
공식 문서에서는 "객체를 동결하기 위한 메소드" 라고 적혀있다.
let test = {
name : 'ludin'
}
Object.freeze(test);
test.name = 'ludin_lee';
console.log(test) // {name: 'ludin'} << 바뀌지않음
두 가지 조합하기
(const의 재할당불가 + Object.freeze()의 객체속성 변경불가)
const test = {
'name' : 'ludin'
};
Object.freeze(test);
- 얕은 복사와 깊은 복사
얕은 복사
객체를 복사할 때, 해당 객체만 복사하여 새 객체를 생성
복사된 객체의 인스턴스 변수는 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조
따라서, 해당 메모리 주소의 값이 변경되면 원본 객체 및 복사 객체의 인스턴스 변수 값은 같이 변경
const obj1 = { a:1, b:2 };
const obj2 = obj1;
obj2.a = 100;
console.log( obj1.a ); // 100
깊은 복사
객체를 복사 할 때, 해당 객체와 인스턴스 변수까지 복사하는 방식.
전부를 복사하여 새 주소에 담기 때문에 참조를 공유하지 않음
1. (spread) 연산자 사용하기
...(spread) 연산자를 통해 { }안에 obj1의 속성을 복사하여 obj2에 할당하였다.
이제 obj1과 obj2는 다른 주소를 갖게되었다. (그러나 딱, 1 depth 까지만)
const obj1 = { a:1, b:2 };
const obj2 = { ...obj };
obj2.a = 100;
console.log( obj1 === obj2 ) // false
console.log( obj1.a ) // 1
2. Object.assign() 메소드 사용하기
Object.assign() 메소드를 통해 첫 번째 인자로 빈 { } 객체를, 두 번째 인자로 obj1 넣고 obj2 에 할당하였다.
이제 obj1과 obj2는 다른 주소를 갖게되었다. (그러나 딱, 1 depth 까지만)
const obj1 = { a:1, b:2 };
const obj2 = Object.assign({}, obj1);
obj2.a = 100;
console.log( obj1 === obj2 ) // false
console.log( obj1.a ) // 1
호이스팅과 TDZ는 무엇일까 ?
- 스코프, 호이스팅, TDZ
스코프
자바스크립트의 스코프는 함수 레벨 스코프를 따른다.
같은 함수 레벨에 존재하면 값을 참조할 수 있다는 건데
ES6에서 let 키워드가 도입되면서 블록 레벨 스코프를 사용할 수 있음
전역 스코프
어디서든 참조 가능
전역 변수
전역 스코프를 갖는 전역 변수
어디서든 참조 가능
지역 스코프
함수 자신과 하위 함수에서만 참조 가능
지역 변수
지역 스코프를 갖는 지역 변수
함수 내에서 선언된 변수로 해당 함수와 해당 함수의 하위 함수에서 참조 가능
암묵적 전역 변수
선언하지 않은 변수에 값을 할당하면 전역 객체의 프로퍼티가 되어 전역 변수처럼 동작한다.
하지만 변수 선언이 없었기 때문에 호이스팅은 발생하지 않는다.
console.log('test', test);
function temp () {
test = 10;
};
temp()
호이스팅
함수의 코드를 실행하기 전에 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것.
초기화를 제외한 선언만 호이스팅.
그렇기 때문에 선언, 정의된 코드보다 호출하는 코드를 먼저 배치할 수 있음.
변수의 선언과 초기화를 분리.
변수의 선언을 코드의 최상단으로 끌어올림
myName("Ludin");
function myName(name) {
console.log("제이름은 " + name + "입니다");
}
// "제 이름은 Ludin 입니다"
변수 선언 형식에 따른 초기화
var : 호이스팅 시 undefined로 변수를 초기화
function : 선언된 위치와 상관없이 동일하게 호출
let, const : 호이스팅 시 변수를 초기화하지 않음. (호이스팅 대상은 맞음)
함수 선언문과 함수 표현식에서 호이스팅 방식의 차이
console.log(num); // 호이스팅한 var 선언으로 인해 undefined 출력
var num; // 선언
num = 6; // 초기화
console.log(num2); // ReferenceError: num2 is not defined
let num2 = 2;
//-----------------------------------------------
myName("Ludin");
function myName(name) {
console.log("제이름은 " + name + "입니다");
}
// "제 이름은 Ludin 입니다"
TDZ(Temporal Dead Zone, 일시적 사각지대)
TDZ의 영항을 받는 구문 const, let, class
변수 스코프의 맨 위에서부터 ~ 변수의 초기화 완료 시점까지의 변수는 TDZ에 들어간 변수
코드의 작성 순서(위치)가 아니라 코드의 실행 순서(시간)에 의해 형성
let, const
let 변수 선언 코드가 그 변수에 접근하는 함수보다 아래에 위치하지만 함수의 호출 시점이 사각지대 밖이므로 정상 동작.
{
// <----- TDZ가 스코프 맨 위에서부터 시작
const func = () => console.log(letVar); // OK
// TDZ 안에서 letVar에 접근하면 ReferenceError
let letVar = 3; // letVar의 TDZ 종료 ------->
func(); // TDZ 밖에서 호출함
}
class
부모 클래스를 상속받을 경우, 생성자 안에서 super()호출을 하기 전까지 this바인딩은 TDZ안에 있다.
// (X)
class User extends Member {
constructor(phone) {
this.phone = phone;
super(phone);
}
}
// (O)
class User extends Member {
constructor(phone) {
super(phone);
this.phone = phone;
}
}
결론
- TDZ는 선언 전에 변수를 사용하는 것을 허용하지 않는다.
- var의 사용은 의도치 않은 중복선언과 재할당으로 문제가 생길 수 있기 때문에 사용하지 않는편이 좋다.
- 여러분이 많이 작성해온 let, const, var, function 이 어떤 원리로 실행되는지 알 수 있어요.
var,let,const의 차이
1.값 변경 가능 유무
var와 let을 값이 선언된 이후에도 값을 변경할 수 있지만, const는 생성할 때 선언된 초기값을 변경할 수 없음
2.함수스코프 vs 블록스코프
var은 함수스코프를 가지지만,
let과 const 변수는 블록 스코프를 가짐
3.호이스팅 가능 유무
var는 호이스팅이 가능하지만,
let과 const는 호이스팅이 불가능
실행 컨텍스트와 콜 스택
스코프 체인, 변수 은닉화
function()의 원리
엔진이 함수를 실행하는 방법
함수를 실행하기 위해서는 이름(식별자)이 필요합니다. 이름이 있어야 스코프에서 값을 참조할 수 있기 때문입니다.
예를 들어 function foo(){}를 정의하면 foo(); 구문을 이용해 함수를 실행할 수 있습니다.
순서)
엔진이 함수 선언문을 만나면 식별자를 관리하는 EnviromentRecord에 함수의 이름을 식별자로 넣고 함수 객체를 생성
함수 실행 구문 중 foo를 만나면 값을 스코프를 통해 가져옵니다. 그 다음 구문이 () 이므로 실행 가능하다면 실행
만약 스코프에서 식별자를 찾지 못했다면 참조 에러(ReferenceError)를 출력
식별자는 찾았지만 실행할 수 없는 타입이라면 타입 에러(TypeError)를 출력
not(); // ReferenceError: not is not defined
var foo = 'some';
foo(); // TypeError: foo is not a function
익명함수를 선언하는 방법
function(){} 구문은 이름 없는 “익명함수” 이므로 엔진이 스코프를 통해 값을 가져올 수 있는 방법이 없음
따라서 이 문법을 실행하면 함수의 이름이 필요하다고 문법 오류를 출력합니다.
function(){} // SyntaxError: function statement requires a name
이름 없는 함수를 선언할 수 있는 유일한 경우는 함수를 값으로 사용할 때임 ex)전달, 대입, 반환, 연산
var func = function(){console.log('ok');}() // ok
some(function(){console.log('ok');})() // ok
return function(){console.log('ok');}() // ok
(function(){console.log('ok');})(); // ok
+function(){console.log('ok');}() // ok
!function(){console.log('ok');}() // ok
자바스크립트 엔진은 단항연산자(-, +, ~, !)를 만나게 되면 function(){}을 값으로 평가합니다.
쉽게 말해 연산을 위해 함수 객체를 생성하게 되고 최종적으로 () 구문을 이용해 실행할 수 있는 것입니다.
이 원리를 이용한 즉시 실행 함수 중 가장 대중적인 방식은 (function(){})() 임
- 실행 컨텍스트와 콜 스택
실행 컨텍스트
자바스크립트 코드가 실행되는 환경을 의미로 두 가지 종류가 있음
1. Global Execution context
자바스크립트 엔진이 처음 코드를 실행할 때 Global Execution Context가 생성된다. 생성 과정에서 전역 객체인 Window Object (Node는 Global) 를 생성하고 this가 Window 객체를 가리키도록 한다.
2. Function Execution context
자바스크립트 엔진은 함수가 호출 될 때마다 호출 된 함수를 위한 Execution Context를 생성한다.
모든 함수는 호출되는 시점에 자신만의 Execution Context를 가진다.
자바스크립트는 실행 컨텍스트가 활성화되는 시점에 다음과 같은 현상이 발생
1. 호이스팅이 발생한다(선언된 변수를 위로 끌어올린다)
2. 외부 환경 정보를 구성한다
3. this 값을 설정한다.
call stack
코드가 실행되면서 생성되는 Execution Context를 저장하는 자료구조
1. 엔진이 처음 script를 실행할 때, Global Execution Context를 생성하고 이를 Call Stack에 push
2. 엔진이 함수를 호출할 때 마다 함수를 위한 Execution Context를 생성하고 이를 Call Stack에 push
프로그램이 함수 호출을 추적할때 사용
자바스크립트 엔진은
Call Stack의 Top에 위치한 함수를 실행하며 함수가 종료되면 stack에서 제거(pop)하고 제어를 다음 Top에 위치한 함수로 이동한다.
- 스코프 체인, 변수 은닉화
스코프 체인(Scope Chain)
스코프 체인(Scope Chain)은 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고,
의미 그대로 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것을 말한다.
자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들
참고) https://ljtaek2.tistory.com/140
변수 은닉화
외부 객체로부터 '속성 값(데이터, 멤버 변수값)'을 감추는 특성으로
여러 스코프에서 동일한 식별자를 선언한 경우, 무조건 스코프 체인 상에서 가장 먼저 검색된 식별자에만 접근이 가능
(function s(){
let a = 'hi'
})()
console.log(a)
//a is not defined
즉, 직접적으로 변경되면 안되는 변수에 대한 접근을 막는것이다.
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라는 클로저를 생성하면 함수 내부적으로 접근이 가능하다.
실습 과제
1.콘솔에 찍힐 b 값을 예상해보고, 어디에서 선언된 “b”가 몇번째 라인에서 호출한 console.log에 찍혔는지, 왜 그런지 설명해보세요. 주석을 풀어보고 오류가 난다면 왜 오류가 나는 지 설명하고 오류를 수정해보세요.
let b = 1;
function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b);
//const,let는 블럭 스코프에서 유효하기 때문에 함수 내부에 있는 변수들에 접근하여
//각 각 1, 101 (b++되어서) 출력됨
}
console.log(a);
// 여기서 오류가 나는 이유는 전역적으로 a라는 변수가 존재하지 않아서 입니다.
// 이것을 해결하기 위해서는 전역적으로 a 초기값 변수를 할당하면 됩니다. ex) let a = 10;
console.log(b); //전역 b변수에 접근해서 출력
hi(); //함수를 호출하여 a와 b에 값을 주었지만 전역변수가 아니고 블록 스코프에서 유효하기에 전역적으로는 반영되지 않음
console.log(b); //전역 b변수에 접근해서 출력
2. 두 값이 다른 이유를 설명해보세요.
1 == "1"; // 변수 값 자체를 고려함 1=1로 보기에 true 출력
1 === "1"; // 데이터 타입도 고려함 number형 1 = string형 1로 보기에 false 출력
오늘 공부를 끝내며
생각보다 어려웠고 이것들을 모른 체로 코딩테스트를 본다면 100% 풀지도 못했을 것 같다.
오늘 공부한 내용을 계속해서 Rewind 하여
앞으로 개발자 취업에 가장 첫 번째 관문이 될 코딩테스트를 보는데에 있어서
자신감 넘치는 모습만 보일 수 있도록 하자!!
'항해99 > 2주차 - 프로그래밍 기초' 카테고리의 다른 글
프로그래밍 기초 5일차 - 알고리즘 문제 풀기 (0) | 2022.11.29 |
---|---|
프로그래밍 기초 4일차 - 알고리즘 문제 풀기 (1) | 2022.11.29 |
프로그래밍 기초 3일차 - 알고리즘 모의고사 풀이 (0) | 2022.11.22 |
프로그래밍 기초 2일차 - 알고리즘 문제 풀기 (1) | 2022.11.20 |
프로그래밍 기초 0일차 - 시작하기 전 (0) | 2022.11.18 |