console.log("my" + " cat"); // my cat
console.log("1" + 2); // 12
console.log(`string literals: 1 + 2 = ${1 + 2}`); // string literals: 1 + 2 = 3
console.log(2 + 1); // 더하기
console.log(4 - 1); // 빼기
console.log(6 / 2); // 나누기 (몫)
console.log(3 * 5); // 곱하기
console.log(5 % 2); // 나누기 (나머지)
console.log(2 ** 3); // 2의 3승
let counter = 2;
const preIncrement = ++counter;
console.log(`preIncrement: ${preIncrement}, counter: ${counter}`); // preIncrement: 3, counter: 3
counter변수에 1이 증가된 3의 값이 counter에 다시 저장이 되고, 그 counter 변수가 preIncrement에 할당 된다.
let counter = 2;
const postIncrement = counter++;
console.log(`postIncrement: ${postIncrement}, counter: ${counter}`); // postIncrement: 2, counter: 3
연산자가 변수 뒤에 붙은 경우, 먼저 1을 증가하는 것이 아니라 postIncrement에 기존 값인 2가 담긴 counter 변수를 먼저 할당한 이후에야 counter 변수에 1이 증가되어 저장된다.
위에서는 증가 연산자로 예를 들었지만 감소 연산자(--)를 사용할 때도 동일한 과정을 따른다.
let x = 3;
let y = 6;
x += y; // x = x + y;
x -= y; // x = x - y;
x *= y; // x = x * y;
x /= y; // x = x / y;
console.log(10 < 6); // 미만 false
console.log(10 <= 6); // 이하 false
console.log(10 > 6); // 초과 true
console.log(10 >= 6); // 이상 true
|| (or) 연산자는 하나의 값이라도 true의 결과값을 가질 경우 true를 리턴한다. 아래에서 로그하는 예시를 보면 앞의 두 변수가 모두 false를 리턴하므로 맨 뒤의 true를 리턴하는 check 함수 내부 코드를 전부 출력한 뒤 결국 or: true가 콘솔창에 출력되는 것을 확인할 수 있다.
const value1 = false;
const value2 = 4 < 2;
console.log(`or: ${value1 || value2 || check()}`); // or: true
function check() {
for (let i = 0; i < 10; i++) {
console.log("wasting time...");
}
return true;
중요 point
이렇듯 || (or) 연산자는 제일 처음 true의 값을 받게되면 거기서 연산을 멈춘다. 이유는 어떤 것이든 하나라도 true면 되기 때문에 그 뒤의 연산이 의미가 없어지기 때문이다.
이럴 때 연산을 많이하는 함수를 호출하거나 expression같은 것들을 제일 앞에 두게되면 안됨. 가장 마지막에 두는 것이 효율적인 코드 작성법임.
&& (and) 연산자는 모든 조건이 true를 리턴해야 true를 출력하는 연산자이다. 아래 예시를 보면 결국 check함수만이 true를 리턴하므로 콘솔창에는 or: false가 출력되는 것을 알 수 있다.
const value1 = false;
const value2 = 4 < 2;
console.log(`or: ${value1 && value2 && check()}`); // or: false
function check() {
for (let i = 0; i < 10; i++) {
console.log("wasting time...");
}
return true;
}
중요 point
따라서 && (and) 연산자는 제일 처음 false의 값을 받게되면 어차피 true를 출력하지 못할 것을 알기 때문에 거기서 연산을 멈춘다. 이유는 어떤 것이든 하나라도 false라면 결국 false를 출력하기 때문에 그 뒤의 연산이 의미가 없어지기 때문이다.
이때도 연산을 많이하는 함수를 호출하거나 expression같은 것들을 제일 마지막에 두는 것이 효율적인 코드 작성법임.
! (not) 연산자는 값을 반대 값으로 바꾸어 출력해준다. 아래 예시에서 value1 변수는 false인데 콘솔창에서 !를 변수 앞에 붙여 출력하니 true 값이 출력되는 것을 확인할 수 있다.
const value1 = false;
console.log(!value1); // true
타입이 동등한지 확인하지 않음.
즉, 변수 stringFive가 문자열이긴 하지만 안의 숫자는 동일하게 5를 가지고 있으므로 동일한 값을 가지는 것으로 이해함.
const stringFive = "5";
const numberFive = 5;
console.log(stringFive == numberFive); // true
console.log(stringFive != numberFive); // false
타입까지 동등해야 동일한 값을 가지는 것으로 이해함. 둘 다 동일한 숫자 5를 가지고 있지만 타입이 다르므로 엄격한 동등의 측면해서 둘은 다른 값이라고 출력함.
const stringFive = "5";
const numberFive = 5;
console.log(stringFive === numberFive); // false
console.log(stringFive !== numberFive); // true
실제 코드 작성 시에는 엄격한 동등 비교를 할 수 있도록 하자.
const elena1 = {name: 'elena'};
const elena2 = {name: 'elena'};
const elena3 = elena1;
console.log(elena1 == elena2); // false
console.log(elena1 === elena2); // false
console.log(elena1 === elena3); // true
오브젝트는 메모리에 탑재될 때 reference 형태로 저장됨.
위의 예시에서 엘레나1과 2에는 똑같은 데이터가 들어있지만, 실제 메모리에서 엘레나1과 2에는 각기 다른 reference가 들어있고, 그 다른 reference는 서로 다른 object를 가리키고 있다!
엘레나3은 엘레나1의 ref가 그댈 할당되어있으니 둘은 똑같은 ref를 가지고 있다.
따라서,
엘레나1과 엘레나2는 ref가 다르므로 false,
엘레나1과 엘레나2는 타입에 상관없이 ref 값이 다르므로 false,
엘라나1과 엘레나3는 ref값이 동일하므로 true를 출력한다.
중요한 조건 연산에 대해 알아보자!
아래는 흔히 볼 수 있는 if 조건문의 예시이다.
if statement가 true이면, 그 안의 block을 실행한다.
따라서 아래 예시는 Welcome, Elena를 출력한다.
name 변수의 값이 달라지면 출력되는 결과 값이 달라진다.
const name = "elena";
if (name === "elena") {
console.log("Welcome, Elena!"); // Welcome, Elena!
} else if (name === "paulina") {
console.log("Oh, How are you, Paulina?");
} else {
console.log("Who are you...?😶");
}
삼항 연산자는 if 조건문을 조금 더 간략하게 사용할 때 요긴하게 쓰인다. 상황에 따라 if와 삼항연산 또는 추후 배울 switch문을 적재적소에 사용해야 한다.
condition ? (condition이 true이면) value1 : (false이면) value2
console.log(name === "elena" ? "yes" : "no"); // yes
Switch를 이용한 조건문을 사용하는 경우 :
const browser = "Internet Explorer";
switch (browser) {
case "Internet Explorer":
console.log(`I don't like you`);
break;
case "Chrome":
case "Firefox":
console.log("Love you guys");
break;
default:
console.log("same all!");
break;
}
while문의 조건이 false가 되기 전까지는 무한대로 내부 코드가 실행됨.
let i = 3;
while (i > 0) {
console.log(`while : ${i}`);
i--;
}
// while : 3
// while : 2
// while : 1
반대로 do while문은 조건을 확인하지 않고 무조건 block을 먼저 한번 실행 후, 그 다음 조건을 확인 후 다시 false가 될 때까지 내부 코드를 실행함.
do {
console.log(`do while : ${i}`); // do while: 0
i--;
} while (i > 0);
앞전의 while문 예시에서 이미 i는 0이 되었으므로 do while문에서는 그 i 값인 0을 먼저 출력한 후에 이후 조건에 따라 진행한다. 여기서는 0을 출력한 후 다음 조건이 false가 되므로 더 이상 출력되는 값이 없다.
따라서 조건문 상관없이 block을 먼저 실행하고자 한다면 do while문을, 조건문이 맞을 때만 block을 실행하고자 한다면 while문을 사용하자!
for(begin; condition; step)
for (let i = 3; i > 0; i--) {
console.log(`for: ${i}`);
}
// for: 3
// for: 2
// for: 1
while이나 for 반복문은 서로 nesting해서 작성할 수 있다.
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
console.log(`i: ${i}, j: ${j}`);
}
}
위의 예시를 보면,
i가 0일때 j를 0 - 9까지 돌리고
i가 1일때 j를 0 - 9까지 돌리고 .... 이런 과정을 반복한다.
이러한 반복문의 nesting은 웬만하면 피하는 것이 좋다!
반복문에서 특정 부분만 스킵 후 다시 원래의 과정대로 진행하는 방법
for (i = 0; i < 10; i++) {
if (i % 2 !== 0) {
continue;
}
console.log(i);
}
// 홀수를 제외한 짝수 0,2,4,6,8만 출력함
위 예시를 보면 조건문의 i % 2의 나머지 값이 0이 아닌 경우(짝수는 항상 0의 나머지 값을 가지므로, 즉, 홀수인 경우) continue를 주어 홀수 지점이 되면 skip하여 짝수만 출력되도록 작성되었다.
반복문을 완전히 끝내는 방법
for (i = 0; i < 11; i++) {
if (i > 8) {
break;
}
console.log(i);
}
// 0부터 8까지만 출력 후 반복문을 빠져나옴
위 예시에서는 i의 값이 8을 넘어서게 되면 아예 반복문을 빠져나와 8까지의 숫자만 출력되도록 작성되었다.