본 시리즈는 모던 자바스크립트 Deep Dive 책을 참고하여 작성하고 있습니다.
내용을 시작하기 전에, 몇 가지 용어에 대한 정의를 내리려고 한다.
값은 어떤 식이 평가되어 생성된 결과이다. 예를 들어, 10 + 20
이라는 식은 평가되어 숫자 값 30을 생성한다. 모든 값은 데이터 타입을 가지고, 메모리에 2진수의 나열로 저장된다.
값을 생성하는 방법에는 여러가지가 있다.
10 + 20
)sum !== 10
)'hello'
)리터럴은 사람이 이해 가능한 문자나 약속된 기호를 사용해 값을 생성하는 표기법이다. 사람이 이해할 수 있는 문자나 기호에는 아라비아 숫자, 알파벳, 한글, ‘’
, “”
, .
, []
, {}
등이 있다. 자바스크립트 엔진은 코드가 실행되는 시점인 런타임에 리터럴을 평가해 값을 생성한다.
정수 리터럴 | 100 |
---|---|
부동소수점 리터럴 | 10.5 |
2진수 리터럴 | 0b010001 |
문자열 리터럴 | "hello” |
불리언 리터럴 | true |
null 리터럴 | null |
객체 리터럴 | { name: ‘Lee’, address: ‘Seoul’ } |
배열 리터럴 | [ 1, 2, 3 ] |
함수 리터럴 | function() {} |
표현식은 값으로 평가될 수 있는 문이다. 다시 말해, 값으로 평가될 수 있는 문이면 모두 표현식이다. 표현식이 평가되면 새로운 값이 생성되거나 기존의 값을 참조한다. 리터럴도 값으로 평가되는 문이므로, 표현식이다. 이때, 표현식과 표현식이 평가된 값은 동치이다. 따라서 값이 위치할 수 있는 자리에는 표현식도 위치할 수 있고, 표현식이 다른 표현식의 일부가 되어 새로운 값을 만들 수도 있다.
100; // 리터럴 표현식
50 + 50; // 연산자 표현식
var score; // *-- 변수 선언문은 값으로 평가될 수 없으므로 표현식이 아니다
score = 50 + 50; // 할당문 (표현식)
score // 식별자 표현식
result = score * 500;
// 식별자 표현식 score는 100으로 평가됨
// result -> 50000
세미콜론(;)은 문의 종료를 의미하고, 자바스크립트 엔진은 세미콜론을 기준으로 문이 종료되는 위치를 파악한다. 예외로 코드 블록 { ... }
은 그 자체로 종결성을 가지기 때문에 세미콜론을 붙이지 않는다. 그런데 자바스크립트에서 세미콜론을 붙이는 것은 옵션이고, 생략 가능하다. 자바스크립트 엔진은 소스코드를 해석하면서 문의 끝이라고 예측되는 곳에 세미콜론을 자동으로 붙여준다. 이 기능을 세미콜론 자동 삽입 기능(Automatic Semicolon Insertion)이라고 한다. 그러나 ASI가 개발자가 의도한 대로 동작하는 경우가 있다.
var bar = function () {}
(function() {})();
// ASI 동작 결과 => var bar = function () {}(function() {})();
// 예측 => var bar = function() {}; (function() {})();
이러한 문제를 방지하기 위해 세미콜론을 붙여야 한다는 주장이 다수이다.
표현식은 문 그 자체가 될 수도 있고, 문의 일부가 될 수도 있다.
// var x; 처럼 변수 선언문은 표현식이 아니다
// x = 2; 처럼 할당문은 값으로 평가될 수 있으므로 표현식이다
// 따라서 아래 문의 일부만 표현식이다
var x = 2;
// 할당문은 표현식이다. 아래 문은 문이면서 표현식이다
x = 3 + 5;
표현식인 문과 표현식이 아닌 문을 구분하는 방법은 변수에 할당해 보는 것이다. 표현식은 값으로 평가될 수 있는 문이고, 변수는 하나의 값을 저장하기 위해 확보한 메모리 공간이기 때문에, 어떤 문을 변수에 할당할 수 있다는 것은 그 문의 평가 결과가 값이라는 것이고, 즉 그 문은 표현식이다.
var foo = var x; // Uncaught SyntaxError: Unexpected token 'var'
var bar = (x = 3 + 5);
console.log(var) // 8
크롬 개발자 도구에서는 표현식이 아닌 문을 실행하면 언제나 undefined
를 출력하고, 이를 완료 값이라고 한다. 완료 값은 평가 결과가 아니므로 이 값을 변수에 할당하거나 참조할 수 없다. 표현식인 문을 실행하면 그 식이 평가된 결과를 반환한다.
NaN
값을 반환한다.// 이항 산술 연산자 - 피연산자가 2개 이상
5 + 2
5 - 2
5 * 2
5 / 2
5 % 2
// 단항 산술 연산자 - 피연산자가 1개
var x = 1;
// -- 부수 효과가 있는 단항 산술 연산자
x++; // 선할당 후증가
x--; // 선할당 후감소
++x; // 선증가 후할당
--x; // 선감소 후할당
// -- 부수 효과가 없는 단항 산술 연산자
+x; // 피연산자를 숫자 타입으로 변환
-x; // 양수를 음수로, 음수를 양수로 반전하여 반환
문자열 연결 연산자: 피연산자 중 하나 이상이 문자열인 경우, 문자열로 연결되어 반환된다.
'1' + 2; // '12'
1 + '2'; // '12'
// 암묵적 타입 변환 - 타입 강제 변환
1 + true // 2
1 + false // 1
1 + null // 1
+undefined // NaN
1 + undefined // NaN
var x;
x = 10;
x += 5;
x -= 5;
x *= 5;
x /= 5;
x %= 5;
x += 'hi'; // '0hi'
var x = 2;
var y = 3;
x == y; // x와 y의 값이 같은가?
x === y; // x와 y의 값과 타입이 같은가?
x != y; // x와 y의 값이 다른가?
x !== y; // x와 y의 값과 타입이 다른가?
값만 비교할 때는 암묵적으로 타입이 자동 변환으로 타입을 일치시킨 후에 같은 값인지 비교한다. 이를 동등 비교(==)라고 한다. 따라서 5 == '5'
는 참이고, 5 === '5'
는 거짓이다. 그런데 동등 비교는 결과를 예측하기 어렵기 때문에 사용하지 않는 편이 좋다.
'0' == '' // false
0 == '' // true
0 == '0' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == null // false
false == undefined // false
일치 비교(===) 연산자는 좌항과 우항이 타입도 같고 값도 같아야 true를 반환한다. 그리고 암묵적 타입 변환을 하지 않는다.
5 === 5; // true
5 === '5'; // false
동등 비교 연산자 주의
NaN === NaN; // false
// --- NaN 검사를 위해서는 isNaN()을 사용해야 한다
****0 === -0 // true
0 == -0 // true
-0 === +0; // true
Object.is(-0, +0); // false
NaN === NaN;
Object.is(NaN, NaN); // true
5 > 0 // true
5 > 5 // false
5 >= 5 // true
5 <= 5 // true
조건식 ? 조건식이 true일 때 반환할 값 : 조건식이 false일 때 반환할 값
var x = 2;
console.log(x % 2 ? '홀수' : '짝수')
우항과 좌항의 피연산자를 논리 연산
||
: 논리합 (OR)&&:
논리곱 (AND)!
: 부정 (NOT) ⇒ 우항의 피연산자를 논리 연산true || true // true
true || false // true
false || true // true
false || false // false
true && true // true
true && false // false
false && true // false
false && false // false
'cat' && 'dog' // 'dog' -- 모두 참일 때, 마지막 피연산자를 반환
'cat' || 'dog' // 'cat' -- 좌항부터 확인해서 처음으로 참인 피연산자를 반환
!
연산자는 언제나 불리언 값을 반환한다. 피연산자는 불리언 값일 필요는 없다. 암묵적 타입 변환을 수행한다.||
, &&
연산자의 평가 결과는 불리언이 아닐 수 있다.var x, y, z;
x = 1, y = 2, z = 3; // 3
10 * (2 + 3) // 50
10 * 2 + 3 // 23
string, number, boolean, undefined, symbol, object, function
중 하나를 반환typeof null // => object
(ES7 도입)
2 ** 3 // 8
2 ** 2.5 // 2^(2.5) => 5.6568...
2 ** 0 // 1
2 ** -2 // 2^(-2) => 1/4 => 0.25
Math.pow(2, 3); // 지수 연산자 도입되기 전에 사용, 8 반환
var object = { name: "yj", number: 33, dog: { name: "jj" } }
var object_2 = { number: 34 }
// 옵셔널 체이닝 연산자
console.log(object_2.dog?.name) // "yj"
console.log(object_2.dog.name) // Uncaught TypeError: Cannot read properties of undefined (reading 'name')
// null 병합 연산자
console.log(object_2.dog); // undefined
console.log(object_2.dog ?? { name: "kk" }); // { name: "kk" }
// 프로퍼티 삭제 연산자
console.log(object); // { name: "yj", number: 33, dog: { name: "jj" } }\
delete object.number;
console.log(object); // {name: 'yj', dog: {…}}
// new 연산자
var fruits = new Array('사과', '바나나')
console.log(fruits); // ['사과', '바나나']
// 좌변 객체가 우변 생성자 함수와 연결된 인스턴스인지 판별
console.log(fruits instanceof Array) // true
// 프로퍼티 존재 확인
console.log('name' in object) // true
console.log('name' in object_2) // false
{
var foo = 1;
foo++;
}
if (true) {
console.log("hi");
}
if ... else
if (조건식1) {
// 조건식 1이 true
} else if (조건식2) {
// 조건식 2가 true
} else {
// 모두 false
}
var num = 2;
if (num > 0) {
console.log("양수");
} else {
console.log("양수가 아님");
}
num > 0 ? console.log("양수") : console.log("양수가 아님");
switch
switch (표현식) {
case 표현식1:
// 표현식 == 표현식1
break;
case 표현식2:
// 표현식 == 표현식2
break;
default:
// 해당되는 case 없을 경우 실행
}
var year = 2022;
var month = 2;
var days;
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31;
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = getFebDays(year);
break;
default:
console.log('Invalid month');
}
console.log("days");
for (변수 선언문 혹은 할당문; 조건식; 증감식) {
...
}
for (var i = 1; i >= 0; i--) {
console.log(i);
}
for (;;) {
// 무한루프
}
var count = 0;
while (count < 3) {
console.log(count);
count++;
}
while (true) {
// 무한루프
}
var count = 0;
// 코드 블록 한 번 실행 후, count가 3보다 작을 때까지 코드 블록 계속 반복 실행
do {
console.log(count);
count++;
} while (count < 3);
break
문을 사용하면 SyntaxErrorfoo: {
console.log(1);
break foo;
console.log(2);
}
console.log('hi');
// 실행 결과: 1, hi
for (var i = 0; i < 10; i++) {
if (i < 5) continue;
console.log(i);
}
// 5 6 7 8 9