이번 3번째 시간은 Javascript의 연산 operator에 대해 알아볼 것이다.
이 연산은 한번 배워두면 가성비가 너무 좋다. 이유는 다른 프로그래밍언어에서도 공통적으로 사용이 가능하다.
이 연산이 쉽다고 조금 소홀이 공부하고 넘어가는 경우가 많은데, 실제로 현업에서 많은 주니어 분들이
코드 지적을 받는곳이 바로 연산이다.
그래서 여러분이 실수할 수 있는 코드 부분 한가지를 콕 찝어서 정리하고 넘어갈 테니 열심히 따라가 보자.
연산으로 넘어가기전 지난시간에 Variable과 const의 차이점 Mutable Data Type과 Immutable DataType
에 대해 설명해주었는데 조금 헷갈리게 설명해서 한번더 깔끔하게 정리해보자.
// 2.Variable == 변수 (변경될 수 있는 값), rw(read/write);
// JS == let (added in ES6)
이렇게 Variable 옆에 rw(read/write) 라고 지어보자
이 Variable은 메모리에 값을 읽고 쓰는게 가능하다.
우리가 Variable에 ellie라고 값을 할당하고
hello로 변경이 가능한것이 바로 Variable은 메모리에 읽고 쓰기가 가능하기 때문이다.
// 3.Constants == 한번 할당하면 값이 절대 바뀌지 X , r(read only)
// favor immutable data type always for a fw reasons;
// - security (보안) - 한번 작성한 값을 다른 해커들이 코드를 이상한 것을 삽입해 값을 계속 변경하는 것을 immutable data type으로 방지할 수 있다.
// - thread safety (스레드 안정성) - 애플리케이션이 실행될때 한가지 프로세서가 할당 -> 그 프로세서 안에 다양한 thread가 존재하는데 동시에 돌아가면서 어플리케이션을
// 동시에 조금 더 효율적이게 동작하도록 도와준다. 다양한 thread가 변수에 접근에 값을 변경할 수 있는데 동시에 값을 변경하는 것은 조금 위험할 수 있다.
// 그래서 가능한 값이 변하지 않는 것을 사용하는 것을 추천
// - reduce human mistakes -
// 그래서 앞으로 변경될 좋은 이유가 없다면 const를 이용해 프로그램을 작성하는게 좋은데
// 이렇게 해야 내가 나중에 코드를 변경하거나 다른 개발자가 코드를 바꿀 때 실수를 방지해줄 수 있다.
// 정리하자면 Javascript에서 변수를 선언할때
/*
Mutable Type(변경 가능한 유형)에 let
ImMutable Type(변경 불가한 유형)에 const 두가지가 있다.
*/
const daysInWeek = 7;
const maxNumber = 5;
반대로 constans는 read(읽기)만 가능하다.
그래서 Constans를 선언함과 값을 할당한 뒤로는 좌물쇠가 생겨버려 읽기만 가능하고 다시 다른 값을 쓰는게 불가
그래서 Javascript에서는 앞으로 변수에 값이 계속 바뀌어야 될 좋은 이유가 없다면 웬만해서는 Constant키워드를
이용해 작성하는 것이 바람직하고 좋은 습관이다.
// 4.Variable types
// primitive = single item: number, string, boolean, null, undefined | 더이상 나눠지지않는 타입
// object, box container | object = 위 single item을 여러개 묶어 한 단위로 한 박스로 관리하게 해주는 기능이 object이다.
// function, first-class function | function도 javscript에 데이터 타입 중 하나인데,
// first-class function = function도 데이터 타입처럼 변수에 할당이 가능하고 그렇기에 함수의 파라미터를 인자로 도 전달이 가능하고
// 함수의 return type으로도 function을 return할 수 있다. 라는 뜻
const count = 17; // integer = 정수
const size = 17.1; // decimal number = 소수점의 숫자
// 값은 다르나 type은 똑같은 number로 할당.
console.log(`value: ${count}, type: ${typeof count}`);
console.log(`value: ${size}, type: ${typeof size}`);
그리고 이 메모리에 저장되는 방법은 2가지가 있는데, primitiveType인지 object Type인지 따라 메모리에 값이 다른
방식으로 저장된다.
이 Primitve 타입의 경우에는 Value값 자체가 메모리에 저장된다.
반대로 object는 너무 커서 메모리에 한번에 다 올라갈 수 없다. 그래서 const ellie라고 선언하고 오브젝트를 할당하게 되면 ellie 가 가리키고 있는 곳에는 ref(레퍼런스)가 있다 그래서 이 ref는 실제로 오브젝트를 가리키고 있는 곳이다.
그래서 이 ref(레퍼런스)를 통해서 실제로 object가 담겨있는 메로리를 가리키게 되는 것
그래서 const ellie 라고 선언하면 이 ellie가 가리키는 포인터만 잠겨서 우리가 다른 오브젝트로 변경하는 것은
불가능 하지만 ellie에 이름과 나이는 변경이 가능한 것도 이 이유중 하나.
그래서 Primitive Type은 value로 값이 저장되고 object는 object를 가리키는 레퍼런스가 메모리에 저장된다라고 이해
// Note!
// Imutable data types: primitive types, frozen objects (i.e. object.freeze());
// Mutable data types: all objects by default are mutable in JS
// favor immutable data type always for a few reasons:
// - security
// - thread safety
// - reduce human mistakes
그래서 이 아이를 복사해서 정리해보면 좋은데 Data Types에는 Imutable data types & Mutable data types가 있다.
Imutable data types은 데이터 자체를 절대 변경 X, primitive types는 우리가 한번 ellie 라는 string을 정리하게 되면
ellie를 통째로 메모리에 올려다가 다른 string으로 변경 가능하지
ellie라는 string자체를 i를 빼고 다른것으로 바꾼다던지 데이터 자체 변경은 불가하다. 그리고 object중에서도
frozen object 꽁꽁 얼어있는 object가 있는데 이런 object 도 변경이 불가하기 떄문에 이런 이를 Imutable data types
이라고 한다.
반대로 변경이 가능한 데이터 타입은 바로 object이다 우리가 ellie라는 object를 선언하고
나서 ellie안에 있는 이름이나 나이를 변경했는데 그래서 object는 계속 변경이 가능하기 떄문에 바로 Mutable Data Types 이다.그리고 Javascript에서는 기본적으로 모든 오브젝트는 변경이 가능하다.
예를 들어 Javascript에 Array배열은 mutable 데이터 타입인데 간혹 다른 언어에서는
mutable Array & Imutable Array두가지를 분리해서 데이터 타입이 따로 존재하는 경우도 있는데 JS에서는 대부분은
object는 모두다 mutaion이 가능한 아이다.
여기까지 배웠다면 이제 operator를 알아보자.
첫번째로 살펴볼 operator는 contents nation인데 이럭게 + 기호를 사용해서 문자열과 문자열을 합해서 새로운 문자열 도 만들 수 있다. 그리고 문자열에 숫자를 넣어 숫자와 더하면 합쳐질 수 있고
`` 기호를 사용해 string literals도 만들 수 있다. ${}을 이용하면 변수값을 계산해 string으로 포함에 문자열로 만들 수 있다.
single Quate를 이용해도 그대로 문자열로 변환된 것을 볼 수 있다.
// 1.String concatenation
console.log('my' + 'cat');
console.log('1' + 2);
console.log(`string literals:
'''''
1 + 2 = ${1 + 2}`);
console.log('ellie\' s book');
우리가 이렇게 Single Quate를 이용하면 중간에 Single Quete나 특수 기호가 인식이 안되는데 그럴때는 \(백슬러쉬)를
이용해 넣어줘야 제대로 표시 된다.
// 1.String concatenation
console.log('my' + 'cat');
console.log('1' + 2);
console.log(`string literals:
'''''
1 + 2 = ${1 + 2}`);
console.log('ellie\' s book');
두번째는 너무 쉬운 Numeric operators이다.
// 2. Numeric operators
console.log(1 + 1); // add
console.log(1 - 1); // substract
console.log(1 / 1); // divide
console.log(1 * 1); // multiply
console.log(5 % 2); // remainder
console.log(2 * 3); // exponentiation
이렇게 숫자들을 +(더하고) 연산할 수 있다.
세번째는 우리가 흔하게 쓰는 Increment and decrement operators이다.
// 3. Increment and decrement operators
let counter = 2;
const preIncrement = ++counter;
counter라는 변수가 있으면 이 변수 앞에 ++기호를 앞에 붙여주면 바로 preIncrement라고 하는데.
// 3. Increment and decrement operators
let counter = 2;
const preIncrement = ++counter;
// counter = counter + 1;
// preIncrement = counter;
counter에 1을 +해서 다시 값을 할당 한 다음 카운터의 값이 1 증가하닌 3이 되서 하나를 증가한 다음
preIncrement에 counter값을 할당하는 것 그래서 이 preIncrement에는 숫자 3이 할당.
const postIncrement = counter++;
// postIncrement = counter;
/// counter = counter + 1;\
console.log(`preIncrement: ${postIncrement}, counter: ${counter}`);
반대로 postIncrement는 변수 다음에 ++기호를 붙이면 먼저 변수의 값을 할당한 다음 counter값을 1 증가 하는 것.
const preDecrement = --counter;
// counter = counter + 1;
// preIncrement = counter;
console.log(`preDecrement: ${preDecrement}, counter: ${counter}`);
const postDecrement = counter--
// postIncrement = counter;
/// counter = counter + 1;\
console.log(`postDecrement: ${postDecrement}, counter: ${counter}`);
--도 동일하다.
이것을 전위증감 && 후위증감 이라고도 불린다.
// 4. Assignment operators
let x = 3;
let y = 6;
x += y; // x = x + y;
x -= y;
x *= y;
x /= y;
네번제는 할당하는 operators인데 할당이 가능하고 다만 x + y다 하면 반복되는 x를 생략해서 계산
이런 식으로 많이 쓰게 된다.
// 5. Comparison operators
console.log(10 < 6); // less then
console.log(10 <= 6); // less then or equal
console.log(10 > 6); // greater then
console.log(10 >= 6); // greater then or equal
다섯번째는 비교하는 operators이다. 작거나 크거나 작거나 같거나를 비교하는 것
// 6. Logical operators: || (or), && (and), ! (not)
const value1 = false;
const value2 = 4 < 2;
이제 제일 중요한 Logical operators이다. or && and && not 이 세가지가 있다.
// || (or)
console.log(`or: ${value1 || value2 || check()}`);
function check() {
for (let i = 0; i < 10; i++) {
// wasting time
console.log('☺️');
}
return true;
}
or연산자를 보면 value난 expretion중에서 세가지중에 하나라도 true가 있다면 true로 계산되는 것.
const value1 = false;
const value2 = 4 < 2;
// || (or)
console.log(`or: ${value1 || value2 || check()}`);
function check() {
for (let i = 0; i < 10; i++) {
// wasting time
console.log('☺️');
}
return true;
}
value1은 이미 false로 할당, value2는 4가 2보다 작지 않으니 false, 세번째는 check()라는 함수가 있는데
이 check라는 함수는 쓸데없이 시간을 낭비하다 결국 true를 return하는 것이다. value1 & value2를 false가 되다가
결국 check()함수를 만나서 true로 return하는 것이다.
여기서 진짜 중요한 포인트는 바로 or연산자는 처음으로 true가 나오면 거기서 멈춤
const value1 = true;
const value2 = 4 < 2;
// || (or)
console.log(`or: ${value1 || value2 || check()}`);
function check() {
for (let i = 0; i < 10; i++) {
// wasting time
console.log('☺️');
}
return true;
}
이유는 or 중에 어떤것이 하나라도 true면 그냥 true이니
value1이 true면 뒤에가 true & false건 이미 true이니 더이상 check()가 출력이 안된다.
그말은 value1이 true이 끝나버리는것이다.
그래서 여기에서 많은 주니어 분들이 코드지적을 당하는데 이렇게 computaion이 heavy한 연산을 많이하는 함수를
호출하거나 또는 expression한 아이를 제일 앞에 두개 되면 안된다. 당연히 simple한 value를 앞에둬서
value들이 false일때만 마지막에 check()함수를 호출하는게 좋다. 그래서 expression이나 함수를 호출하는 에들을
제일 뒤에 호출하는게 좋은 방법
// && (and) =
console.log(`or: ${value1 || value2 || check()}`);
&&(and)연산자도 비슷한데, 대신 세가지항목이 다 true가 되야만 true로 return하는 아이,
그래서 value1이 false가 나오면 무조건 false이니 뒤에는 실행이 안된다.그래서 and도 heavy(헤비)한 operation일 수록
제일 뒤에서 check()하는게 좋다.
// often used to compress long if-statement
// nullableObject && nullableObject.something
if (nullableObject != null) {
nullableObject.something;
}
그리고 &(and)는 간편하게 null체크 같은걸 할때도 많이 쓰는데 이 Object가 null이면 false가 되 뒤에가 실행이 안되는데 그래서 즉 nullableObject가 null이 아닐때 object에 somthing이라는 value를 받아오게 된다.
그래서 이것을 코드로 풀어보면 위와같이 작성할 수 있다. 나중에 간편하게 코드를 작성할때 유용하게 사용이 가능하다.
// ! (not)
console.log(!value1);
마지막 not연산자는 값을 반대로 바꿔준다.
value1이 true기 때문에 false로 바꿔 변경한다.
// 7. Equality
const stringFive = '5';
const numberFive = 5;
// == loose equality, with type conversion
console.log(stringFive == numberFive);
console.log(stringFive != numberFive);
// == strict equality, no type conversion
console.log(stringFive === numberFive);
console.log(stringFive !== numberFive);
7번째로 Equality를 알아보자. 이렇게 == 두개의 Equal을 사용하면 바로 loose equality라고 불린다.
그 말은 type을 변경해서 검사를 하기 때문이다. 즉
// == loose equality, with type conversion
console.log(stringFive == numberFive);
console.log(stringFive != numberFive);
stringFive와 numberFive는 똑같다 true & 똑같지 않다 해서 false로 나온다.
그래서 Javascript 엔진은 문자열이긴 한데 안에 들어있는건 숫자 5니까 둘다 똑같다고 판단한다.
// === strict equality, no type conversion
console.log(stringFive === numberFive);
console.log(stringFive !== numberFive);
이 3개의 equal은 strict equality이다. 그 말은 type을 신경써서 type이 다르면 넌 다르다라고 하는 것.
그래서 ===를 사용하게되면 다른 같은 Five지만 다른 타입이니 false를 return하는 것이다.
그래서 웬만하면 코드를 검사할때 strict equal을 사용해 검사하는 것이 좋다.
// object equality by reference
const ellie1 = { name: 'ellie' };
const ellie2 = { name: 'ellie' };
const ellie3 = ellie1;
console.log(ellie1 == ellie2);
console.log(ellie2 === ellie2);
console.log(ellie1 === ellie3);
그래서 이 equelity를 공부할때는 object를 조금 신경써서 공부할 필요가 있는데, 우리는 object는 메모리에 탑재될 때
래퍼런스 형태로 저장되는데 ellie1과 ellie2는 똑같은 데이터가 들어있는 오브젝트 지만 실제로 메모리에는
ellie1 과 2에 각각 다른 레퍼런스가 들어있고
그 다른 레퍼런스는 서로다른 object를 가리키고 있다.그리고 ellie3은
이렇게 ellie1에 레퍼런스가 할당되 똑같은 ref를 가지고 있다. 그럼 콘솔에는 어떤 값이 출력될까?
// object equality by reference
const ellie1 = { name: 'ellie' };
const ellie2 = { name: 'ellie' };
const ellie3 = ellie1;
console.log(ellie1 == ellie2); // false | ellie1과 ellie2에는 각각 다른 ref가 저장되어있어 false로 출력
console.log(ellie1 === ellie2); // false | ellie1과 ellie2는 똑같은 type일 지라도 ref값이 달라 false
console.log(ellie1 === ellie3); // true | ellie1과 ellie3는 똑같나? ellie1이 가진 ref value를 3으로 할당해 1과 3은 똑같은 ref 라 true
ellie1과 ellie2에는 각각 다른 ref가 저장되어 있어 레퍼런스가 다르다. 그래서 false로 출력이 되고
그리고 ellie1과 ellie2는 똑같은 type이든 아니든 ref값이 달라 false
하지만 ellie1과 ellie3는 똑같나? true 이유는 ellie1이 가지고 있는 ref value를 3으로 할당헤 1과 3은 똑같은 ref가짐
console.log('=============================================');
console.log(0 == false); // true | 0과 null empty string은 다 false로 간주될 수 있으니 true
console.log(0 === false); // false | 0은 boolean이 아니기에 typescrit equal을 이용하면 false
console.log('' == false); // true | empty 문자열은 true
console.log('' === false); // false | empty 문자열은 boolean 문자열이 아니라 false
console.log(null == undefined); // true | null은 undefined은 같은 것으로 간주
console.log(null === undefined); // false | null과 undefined는 다른 타입 false
이 다음은 우리가 프로그래밍의 하이라이트인 condition operator와 hooks에 대해 알아보자.
이것을 써야 비로써 프로그래밍을 했다고 알 수 있다.
// 8. Conditional operators: if
// if, else if, else
const name = 'ellie';
if (name === 'ellie') {
console.log("Welcome, Ellie!");
} else if (name === 'coder') {
console.log('You are amazing coder');
} else {
console.log('unkwon');
}
8번째로 알아볼것은 if & else if & else이다. 그말은 if statement는 statement가 true면 그 안에 블록 실행한다.
그래서 이안에 console.log에 'Welcome, Ellie!'라는 것을 실행 그런데 name이 coder라고 하면
else if로 넘어가서 coder일때 coder가 실행된다.
const name = 'aaa';
if (name === 'ellie') {
console.log("Welcome, Ellie!");
} else if (name === 'coder') {
console.log('You are amazing coder');
} else {
console.log('unkwon');
}
둘다 해당되지 않는다면 else로 넘어간다.
// 9. Ternary operator: ?
// condition ? value1 : value2;
console.log(name === 'ellie' ? 'yes' : 'no');
그다음 if를 조금더 간단하게 사용할 수 있는
지금은 ellie가 아니기 떄문에 no가 출력된다. 간혹 Ternary operator를 계속 묶고 묶어서 쓰는 경우가 있는데
그렇게 하면 코드의 가독성이 떨어져 그런 경우 if나 switch를 쓰는게 좋다.
const browser = 'IE';
switch (browser) {
case 'IE':
console.log('go away!');
break;
case 'Chrome':
console.log('love you');
break;
case 'FireFox':
console.log('love you!');
break;
default:
console.log('same you!');
break;
}
그 다음에 배우는것은 Switch인데 쉽다. 값이 browser의 값이 'IE'면 case 'IE'가 실행된다.
case 'IE' log가 실행되는 것을 볼 수 있다.
const browser = 'IE';
switch (browser) {
case 'IE':
console.log('go away!');
break;
case 'Chrome':
case 'FireFox':
console.log('love you!');
break;
default:
console.log('same you!');
break;
}
똑같은 문자이 있다면 case를 연달아서 적용이 가능하다. if에서 else if를 반복한다면 switch를 사용하면 좋고 나중에
타입스크립트에서 정해져 있는 type을 검사하거나 enum비슷한 아이를 검사할때 swtich를 붙여쓰는 것이 좋다.
let i = 3;
while (i > 0) {
console.log(`while: ${i}`);
i--;
}
드디어 반복문을 배워볼 차례인데. white같은 경우는 statement가 false로 나오기 전까지는 무한대로 반복해서
도는 것을 말한다.
i는 3인데 white문으로 i가 > 0 보다 크니까 출력이 한번되고 i를 -1 감소시키고 그래서 2가되고 다시 물어서 -1 감소
해서 1 이런식으로 0이되니까 i가 0보다 크지않으니 1까지 멈추게 된다.
반대로 do while이라는 아이가 있는데 do&while문은 먼저 블럭을 실행한다음 조건이 맞는지 아닌지 검사한다.
do {
console.log(`do while: ${i}`);
i--;
} while (i > 0);
그래서 이것을 실행한뒤 i가 0이 되었는데도 불구하고 do안에 블럭이 실행되서 출력이 먼저되고나서 while문으로 i가 0인지 아닌지 검사할때 멈춘다.
그래서 블럭을 먼저 실행하고 싶다면 do&while문 조건문이 맞을때만 실행하고 싶다면 while을 사용하자.
// for loop, for (begin: condition: step)
for (i = 3; i > 0; i--) {
console.log(`for: ${i}`);
}
for (let i = 3; i > 0; i = i - 2) {
// inline variable declaration
console.log(`inline variable for: ${i}`);
}
이번에는 for loop에 대해 알아보자 이 for같은 경우
// for loop, for (begin: condition: step)
for (i = 3; i > 0; i--) {
console.log(`for: ${i}`);
}
시작하는 문장 condtion이 중간에 오고 그리고 마지막으로 어떤 스텝을 밟아 나갈 건지 명시하게 되어있다.
그래서 여기에 for문은 딱 한번만 실행하게 되는데 i의 값이 3으로 준비하고 i가 0보다 클때 console.log를 실행하고
출력하고 감소하는 방식이다.
그래서 for&loop는 begin은 처음에 딱한번만 호출하고 begin이 condition다 맞는지 안맞는지 검사한다음
블럭이 다 실행되면 step을 실행하게된다. 그래서 condition이 안맞을때까지 실행하는 것이다.
이렇게 기존에 변수에 값을 할당할 경우도 있고
// 반면 for안에 inline variable declartion이라고도 하는데
// 블록안에 let이라는 지역변수를 선언해 작성하는 것도 좋다.
for (let i = 3; i > 0; i = i - 2) {
// inline variable declaration
console.log(`inline variable for: ${i}`);
}
이렇게 let이라는 지역변수를 선언해 작성하는 방법도 있다.
// nested loops
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
console.log(`i: ${i}, j: ${j}`);
}
}
이런 while이나 for같은 것은 서로 nasted해서 작성할 수 있는데 위처럼 for문 안에 다시 for을 작성하면
i가 0일때 j를 0부터 9까지 돌리고 i 가 1이 될때 다시 0 부터 9까지 돌리개 된다. 이렇게 nasted하게 작성하면
O(n의 2승) 이다와 같이 cpu에 좋지가 않다. 그래서 대도록 피하는게 좋다.
// break, continue
// Q1. iterate from 0 to 10 and print only even numbers
// (use continue)
// Q2. iterate from 0 to 10 and print only numbers until
// reaching 8 (use break)
이런 loop안에서는 break나 countnue를 써서 loop를 끝낼 수 있다.
break는 loop를 완전히 끝내는 것이고, countinue는 loop를 끝내고 다시 다음 스텝으로 넘어갈 수 있다.
for (let i = 0; i < 11; i++) {
if(i % 2 === 0) {
// continue를 궅이 쓸필요 없다.
console.log(`q1, ${i}`);
continue;
}
}
1번째 문제는 0부터 10까지 짝수애들만 print해보는 문제다.
continue를 이용하면 되는데 for문으로 i가 11밑으로만 출력하도록 뺑뺑 돌려서
숫자가 홀수일때 continue를 써서 홀수일때 넘어가게 작성하면 된다.
// Q2. 주어진 범위 0부터 10까지 looping 하는 애들을 만들어보자. 8을 만날때
for(let i = 0; i < 11; i++) {
if (i > 8) {
break;
}
console.log(`q2, ${i}`)
}
// 끝
두번째 문제도 똑같이 for문으로 i가 10까지 출력하게 한뒤 if문을 사용해 i가 8보다 작을때 break를 넣어서 멈추게 하면 된다.