x = (a !== null && a !== undefined) ? a : b;
firstName ?? lastName ?? nickName ?? "익명의 사용자”
둘 다 의미는 비슷하다 하지만 첫 번째는 과거에 사용했고 두 번째는 현재 사용하는 용법이다.
우선 첫 번째는 그 동안 배웠던 삼항식과 똑같다.
a가 null이 아니고 undefined가 아니라면 a를 반환하고 그렇지 않다면 b를 반환한다는 의미이다.
두 번째는 왼쪽부터 순서대로 null or undefined인지 확인하고 처음으로 나온 null or undefined가 아닌 값을 반환한다.
모든 값이 null or undefined라면 가장 오른쪽 값을 반환한다.
이는 전에 배웠던 || 연산과 비슷하게 보인다.
하지만 극명한 차이가 있다.
바로 || 연산은 Truthy한 값을 찾는다는 점이다.
const result1 = 0 || null || 3;
const result2 = 0 ?? null ?? 3;
이때 result1은 처음으로 truthy한 값인 3을, result2는 처음으로 null or undefined가 아닌 0을 반환한다.
참고로 ?? 는 && 혹은 || 과 같이 사용할 수 없다.
//기본 용법 while(condition) { ... }//3회 반복하는 while문 let i = 0; while(i<3) { ... i++; }
반복문의 본문이 한 번 실행되는 것을 반복(iteration)이라고 부른다.
만약 본문이 한 줄이라면 중괄호를 생략해도 큰 지장이 없다.
그리고 두 번째를 보면 i를 증가시켜서 3보다 크게 만들어 while문을 빠져나오는 구조이다.
이때 i에 대한 증감식이 없다면 while은 평생 실행되며 무한루프에 빠지게 된다.
지금 예시로 작성된 코드는 순방향으로 반복문이 실행된다.
하지만 과거엔 순방향보단 역방향이 효율적인 측면에서 우수해서 역방향을 선호했다.
최근엔 브라우저의 발전으로 인해 둘의 차이가 미미해졌다고 한다.
//기본 용법 do { ... } while(condition);
do...while문을 사용하게 되면condition은 맨 아래줄로 내릴 수 있다.
보통의 while문과 똑같지만특이한 점은 조건이 만족하지 않더라도무조건 1번은 실행된다는 점이다.
예를 들어
let i = 10000;
do {
console.log(i);
i++;
} while(i<0);
를 실행하게 되면 10000은 출력된다는 의미이다.
DOM(Document Object Model)
우선 DOM은 객체로 이루어져 있다.(전에 js는 모두 객체로 이루어져 있다고 언급했었다.)
하지만 이에 접근하기 위해선 querySelector()라는 함수를 이용해 접근할 수 있다.
만약 js가 HTML문서에서 import 되었다면 querySelector()를 사용하면 모든 곳에 접근이 가능할까?
우선 무조건 정답은 아니다.

해당 그림을 살펴보면 일반적으로 script를 불러올때 script가 download됨과 동시에 execution이 일어난다
하지만 그 구간에선 HTML parsing puased, 즉, HTML문서가 parsing을 멈춘다는 것이다.
parsing이란? HTML에서는 해당 페이지를 객체화하여 구조화하고 추출하는 행위
그렇다면 script가 선언되고 나오는 body영역은 정상적으로 script가 인식을 하지 못한다는 것이다.
이를 해소하기 위해 script를 import할때 defer라는 속성을 부여할 수 있다.
그러면 HTML parsing이 멈추지 않고 일어나며 script execution은 parsing이 종료된 후 일어나게 된다.
이번엔 HTML문서에서 각각의 element들에게 접근하는 방법을 살펴보자
우선 class를 이용해 접근하려면 CSS와 비슷한 방법을 채택한다.
const first = document.querySelector('.first');
first에 first라는 class를 가진 요소가 할당된다.
이때 first라는 class를 가진 전 후의 요소도 출력할 수 있을까?
가능하다
우선 반복문을 통해 알아보도록 하자
function next(node) {
// validation
if (typeof node === 'string') {
node = document.querySelector(node);
}
do {
node = node.nextSibling;
} while (node.nodeType !== 1);
return node;
}
해당 node에 다음 요소를 찾는 함수이다.
이때 node에 들어갈 수 있는 값으로는 class 이름을 할당한 변수 혹은 class이름을 그대로 넣는 경우가 있다.(ex.fist or '.first')
그러면 nextSibling은 무슨 역할을 할까?
바로 다음 node를 찾는 역할을 한다.
그러면 반복문이 한 번만 실행되면 다음 요소를 찾는 것이 아닌가?
그렇지 않다. 이유는 이는 다음 'node'를 찾는 역할이기 때문이다.
이는 마지막에 nodeType에 대해 적어보겠다.
node란? DOM의 최소 단위
그러면 nodeType이 1일때 반복문을 탈출한다고 그러는데 왜 하필 1일까?
바로 element의 nodeType을 1로 취급하기 때문이다.
그러면 이번엔 nodeType을 몇가지를 살펴보자
nodeType
nodeType의 대표적인 예로는 다음이 있다.
괄호 안에 입력된 숫자들이 해당 nodeType을 대표하는 수이다.
이제 위의 코드를 이해할 수 있어졌다.
nodeType은 MDN 공식문서에서 자세히 살펴볼 수 있다.
그렇다면 굳이 반복문을 사용하는 이유가 무엇일까?
이유는 바로 개행과 주석때문이다.
우리는 코드를 작성할때 사람이 읽을 수 있게 작성한다.
따라서 자연스럽게 개행과 주석이 생길 수 밖에 없다.
이를 가려내고자 반복문을 사용하는 것이다.
이전 요소는 next 대신 previous를 사용하면 찾아낼 수 있다.
그렇다면 항상 이런 방식을 통해 다음 혹은 이전 요소를 구분해야하는가?
앞에서 했던 행동들이 무색하게 nextElementSibling이라는 함수가 존재한다.
이는 element node에 한해서만 계산을 한다.
하지만 알아놓으면 DOM구조에 대한 이해를 많이 도와줄 수 있다.
// 기본 용법
for (begin; condition; step) {
// ... 반복문 본문 ...
}
for문은 앞으로 매우 많이 사용할 것이다.
for문의 begin에 사용되는 변수는 for문이 종료됨과 동시에 사라진다.
이를 방지하려면 다음과 같이 사용이 가능하다.
//begin과 step 생략
let begin = 0;
for ( ; condition ;) {
// ... 반복문 본문 ...
begin++; // or begin--;
}
이때 주의해야 할 점은 ; 은 절대 생략해선 안된다.
하지만 이 방식은 begin이라는 변수를 지정해줌과 동시에 재활용이 불가능해지므로 지양해야 한다.
여기서 한 가지 궁금점이 생긴다
condition도 생략 가능한거 아니야?
가능은 하다. 하지만 생략하는 순간 무한 루프문이 탄생한다.
continue
반복문에서 특정 step을 건너뛰고 싶을때 사용한다
for (let i = 0; i < 10; i++) {
// 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
if (i % 2 == 0) continue;
console.log(i); // 1, 3, 5, 7, 9가 차례대로 출력됨
}
헷갈리지 말아야 할 점은 continue가 실행되면 continue아래에 있는 구문들은 실행하지 않는다는 점이다.
break;
반복문에서 특정 step에 도달하면 반복문을 탈출할때 사용한다
for (let i = 0; i < 10; i++) {
// 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
if (i % 2 == 0) continue;
alert(i); // 1만 출력됨
}
continue와의 차이점은 반복문을 완전히 탈출한다는 점이다.
참고로 삼항식에는 continue와 break를 사용할 수 없다.
break의 또 다른 활용법
알고리즘 문제를 풀다보면 이중 반복문을 자주 접할 수 있다.
이때 내부의 반복문을 break하고 외부의 반복문을 break해야만 이중 반복문에서 탈출할 수 있다.
하지만 label을 사용하여 전체 반복문을 break하는 방법이 존재한다.
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`(${i},${j})의 값`, '');
// 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
if (!input) break outer; // (*)
// 입력받은 값을 가지고 무언가를 함
}
}
console.log('완료!');
이때 외부의 반복문에게 outer라는 label을 부여한다.
그리고 내부의 반복문에서 특정 step에 도달하면 break 뒤에 label을 사용해 해당 label을 보유하고 있는 반복문이 break 되도록 작성한 것이다.
이는 외부의 반복문이 종료 되도록 유도한다
우선 shift와 pop을 이용하여 stack(LIFO)과 queue(FIFO)구조를 설계할 수 있다.
LIFO -> Last In First Out
FIFO -> First In First Out
shift는 배열의 맨 앞에 있는 element를 pop은 배열의 가장 마지막에 있는 element를 반환한다.
이때 주목해야 할 점은 단순히 제거가 아닌 반환에 있다
이번엔 함수를 작성할때 사용하는 방법에 대해 살펴보겠다.
만약 shift와 pop을 선언된 배열에 사용하게 되면 해당 배열이 오염된다.
따라서 앞서 배웠던 spread syntax를 사용해 해당 배열을 다른 배열로 복사하고 복사된 배열을 오염시키는 방법이 효율적이다
const arr1 = [...arr2];
in문은 해당 객체에서 원하는 것이 포함되어 있는지를 확인한다.
하지만 해당 객체에서 발견하지 못했다면 부모 요소에게까지 접근해 확인한다.
따라서 이는 해당 객체만 검사하기엔 위험하다
이를 해결하기 위해 hasOwnProperty()라는 함수가 존재한다.
우선 주의점으로는 이 함수는 js가 안전을 보장해주지 못한다.
이게 무슨 뜻이냐면 만약 대상 객체 안에 똑같은 이름의 함수 function hasOwnProperty()가 선언되었다면 그 함수를 실행해버린다.
const javaScript = {
creator: 'Brendan Eich',
createAt: '1995.05',
standardName: 'ECMAScript',
currentVersion: 2024,
hasOwnProperty() {
return '안녕!';
},
};
console.log(javaScript.hasOwnProperty('creator')); //'안녕!'을 출력한다
따라서 안전하게 사용하기 위해선 Object.prototype.call()을 이용하여 원하는 객체를 불러오는 방법이 있다.
Object.prototype.hasOwnProperty.call(’javaScript’ , ‘creator’);
최근에는 Object.prototype.hasOwnProperty()를 해소하기 위해 hasOwn()이라는 함수도 등장하였다.
hasOwn(${objectName} , 'key값');
이는 코드가 매우 간결하지만 위에서 hasOwnProperty()의 함수와 출력 결과가 일치한다.