본문 바로가기
TIL

2024.03.29 28일차 Javascript sort(), destructuring, closure, DOM

by Song.dev 2024. 3. 29.

배열 고차함수 sort()

  • 원본을 손상시키므로 복사 후 정렬
  • 정렬 기준이 없으면 아스키코드를 기준으로 정렬
  • 정렬 기준으로 콜백 함수 사용하므로 고차함수
// 배열 데이터 정렬하기
const nums = [6, 11, 3, 7, 9, 10, 27, 2, 100, 4, 1];
console.log(nums);

nums.sort();
console.log(nums); // [ 1, 10, 100, 11, 2, 27, 3, 4, 6, 7, 9]

   **   위의 코드는 10 을 문자로 보고 1 10 2 순서로 본 것

 

숫자 오름차순 정렬

  • 두 숫자를 비교해서 음수가 나오면 a < b 이므로 자리 유지
  • 두 숫자를 비교해서 양수가 나오면 a > b 이므로 자리 변경
// 숫자 오름차순 정렬
nums.sort((a, b) => a - b);

// 숫자 내림차순 정렬
// nums.reverse();     --> sort로 정렬 후 reverse()도 내림차 정렬되긴 함

nums.sort((a, b) => b - a);

 

객체 정렬

  • 객체를 매개변수로 받고 객체의 키를 지정해 기준으로 정렬
// 객체의 나이 기준 오름차순 정렬
userList.sort((a, b) => a.age - b.age );
console.log(userList);

// 급여 내림차
userList.sort((a, b) => b.salary - a.salary);

 


Javascript 구조 분해 할당(destructuring)

  • 객체나 배열을 다시 기본 데이터로 해체하는 것을 의미
  • 할당하고자 하는 변수의 개수가 분해하고자 하는 배열의 길이보다 크더라도 에러 발생X
    할당할 값이 없으면 undefined로 취급하기 때문
  • 분해 대상은 수정 또는 파괴되지 않음

 

 const [가져올 데이터] = [원본 배열, 객체];
const userNames = ['김철수', '강감찬', '박영희'];

// userNames에서 각각의 요소들을 다시 변수에 집어넣고 싶다.
// const kim = userNames[0];
// const kang = userNames[1];
// const park = userNames[2];

const [kim, kang, park] = userNames; // 0번인덱스 꺼내서 kim, 1번 kang...

console.log(`a: ${kim}, b: ${kang}, c: ${park}`)

 

기본값 default value

  • =을 이용하면 할당할 값이 없을 때 기본으로 할당해 줄 값인 '기본값(default value)'을 설정 가능
  • 값이 제공되지 않았을 때만 함수가 호출되므로, prompt는 한 번만 호출
// name의 prompt만 실행됨
let [surname = prompt('성을 입력하세요.'), name = prompt('이름을 입력하세요.')] = ["김"];

alert(surname); // 김 (배열에서 받아온 값)
alert(name);    // prompt에서 받아온 값

 

 

디스트럭쳐링을 통한 변수 교환

  • 기존에 임시 변수(ex.tmp)를 활용해서 swap 했는데 디스트럭쳐링을 사용하면 더욱 간단하게 swap 가능
let first = 10, second = 20;
[first, second] = [second, first];
// [first, second] = [20, 10];

console.log((`first : ${first} second: ${second}`));

 

 

'…'로 나머지 요소 가져오기

  • 배열에서 몇 개 요소만 기본 타입 변수에 저장하고 나머지들은 따로 하나의 배열에 담아둘 때 스프레드를 사용
  • rest는 나머지 배열 요소들이 저장된 새로운 배열
  • rest 대신에 다른 이름을 사용해도 되는데, 변수 앞의 점 세 개(...)와 변수가 가장 마지막에 위치해야 함
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert(name1); // Julius
alert(name2); // Caesar

// `rest`는 배열입니다.
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2

 

스프레드를 통한 배열 간편 복사

const 복사본 = [...원본배열];
// 스프레드를 통한 배열 간편 복사
const foods = ['감튀', '햄버거', '콜라'];
// const copyFoods = foods.slice();
// const copyFoods = foods.map(f => f);

const copyFoods = [...foods];

foods[0] = '치킨너겟';
copyFoods.push('밀크쉐이크');

console.log('foods: ', foods);
console.log('copyFoods: ', copyFoods);

 

 

함수 매개변수 디스트럭쳐링

const { empName, hireDate } = employee;
console.log(`name: ${empName}, hire: ${hireDate}`);

function foo({empName, age}) {
  // const {empName, age} = employee;
  console.log(`내 이름은 ${empName}입니다.`);
  console.log(`나이는 ${age}입니다.`);
}

 

 

스프레드로 나머지 요소 가져오기

  • 나머지 요소는 새로운 배열로 묶임
// 배열 안에서 맨 앞에 2개만 각각의 변수에 저장하고
// 나머지는 다시 배열로 묶고 싶다.

const numbers = [1, 3, 5, 7, 9, 11, 13];
// const numsCopy = numbers.slice();

// const one =  numbers.shift();
// const three = numbers.shift();

const [one, three, ...numsCopy] = numbers;

console.log(one);
console.log(`three: ${three}`);
console.log(`numbers: ${numsCopy}`);

 

 

프로퍼티 키와 다른 이름을 가진 변수

분해하려는 객체의 프로퍼티: 목표 변수

 

const { age:empAge, birthDate } = employee;
console.log(empAge);

 

  • 변수명으로 사용할 수 없는 프로퍼티 키를 변수에 저장할 때도 사용
// 변수명에 '-' 사용 불가
// const {"font-size", background-color} = divStyle;
const {"font-size": fontSize, 'background-color': bgColor} = divStyle;
console.log(fontSize);
console.log(bgColor);

 

객체 디스트럭쳐링 ***

const {var1, var2} = {var1:..., var2:...}
{ 객체 프로퍼티의 패턴 } = { 분해하려는 객체 } 
// age만 수정, favorite 추가하면서 복사
const copyDog2 = {
  ...dog,
  age: 20,
  favorite: ['산책']
};

// 출력
{
  kind: '말티즈',
  name: '해피',
  age: 20,
  injection: false,
  favorite: [ '산책' ]
}

 

나머지 패턴 ‘…’

  • 분해하려는 객체의 프로퍼티 개수가 할당하려는 변수의 개수보다 많다면 나머지 패턴(rest pattern)을 사용
const{ kind, age: petAge, ... rest } = dog;

 

스프레드로 객체 복사

// 객체 안전 복사
const copyDog = { ... dog };
copyDog.age = 10;
console.log(dog);
console.log(copyDog);

 


Javascript 클로저

  • 클로저(closure)란 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수
  • 클로저를 통해 지역변수의 스코프를 늘려줄 수 있음

 

클로저가 필요한 이유

  • 아래 코드를 보면 num을 전역변수로 두고 num의 값을 1씩 증가시키는 함수 increase를 선언했을 때 중간에 어떤 코드가 num의 값을 수정하면 결과가 변함
  • 이런 이유 때문에 전역변수 사용을 자제해야 함 
    → 어떻게 num을 지역변수처럼 함수 내부에서만 사용할까?
// 카운팅 변수
let num = 0; // 전역 변수

// 카운트 숫자 상태 변경 함수
const increase = () => ++num;

console.log(increase());

num = 999;

console.log(increase()); // 2? => 1000

 

**  클로저 적용한 코드

  • 어떤 함수가 내부에 있는 헬퍼 함수를 리턴하게 하는 방법으로 클로저를 구현
  • num변수는 함수 외부에서 접근할 수는 없지만 지속적으로 값이 increase에 의해 제어되는 형태로 상태값이 유지되는 효과를 얻을 수 있음
const increaseClosure = () => {

    let num = 0; // 상태변수 (지역변수)

    function increase() {
        return ++num;
    }
    return increase;
};


const increase = increaseClosure();

console.log(increase());
console.log(increase());
console.log(increase()); // 3? OK!!

 

즉시 실행 함수

  • 함수를 1회성으로 사용할 목적으로 만드는 함수로 익명함수
  • 이름이 없어서 다른 곳에서 호출 불가
(function() {
    console.log('hello');
 })();
const increase = (() => {
    let num = 0;
    return () => ++num;
})();

console.log(increase());
console.log(increase());
console.log(increase());

 

**  operate, operateSol, operateSol2

세 함수 모두 동일하게 작동하며 표현만 다름

const operate = () => (n1, n2) => n1 * n2;  // 실무에서 이렇게까지 줄이지는 않음
const operateSol = () => {
  return (n1, n2) => n1 * n2;
};
const operateSol2 = () => {
  return function(n1, n2) {
    return n1 * n2;
  };
};

 

클로저로 여러 함수를 가진 객체 리턴

  • 하나의 변수를 유지하면서 여러 연산 (더하기, 빼기, 결과 반환)을 적용해야 하는 경우
    하나의 객체 안에 여러 헬퍼 함수를 담아서 리턴 
function createCalculator() {
  let total = 0;                      --> 마치 전역변수처럼 여러 함수를 사용해도 유지
  return {
    add: num => total += num,
    subtract: num => total -= num,
    getTotal: () => total
  };
}

 

***  클로저 는 이외에도 더 많은 내용이 있으니 참고

 


Web API DOM

DOM (Document Object Model)

  • HTML문서를 파싱하여 브라우저가 이해할 수 있는 자료구조인 DOM을 생성
  • DOM이란 HTML문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API를 제공하는 트리 형태의 자료구조

=> HTML 태그들을 객체화

 
 // HTML 구조
 <div class="wrap clearfix" title="abc">
    <ul id='list'>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </div>
  
  // div 객체화
  div = {
  tagName: 'div',
  children: [ul],
  parentNode: body,
  nextSibling: null,
  prevSibling: null,
  className: 'wrap clearfix',
  classList: ['wrap', 'clearfix'],
  attributes: {
    'class': 'wrap clearfix',
    'title': 'abc'
  }
}

console.log(div.parentNode.parentNode);  
// { tagName: 'html', children: [ undefined, undefined ] }

 

HTML 요소와 노드 객체

  • 태그 요소는 요소 노드 객체로 변환
  • 요소의 속성은 어트리뷰트 노드로, 텍스트 콘텐츠는 텍스트 노드로 변환
  • 이런 노드 객체들을 트리화한 구조로 구성

1. 문서 노드(document node)

  • DOM트리의 최상위에 존재하는 루트 노드이며 document객체
  • 다른 노드들에게 접근하기 위한 진입점 역할

2. 요소 노드(element node)

  • 각 html요소들을 가리키는 객체
  • 중첩된 태그에 의해 부모 자식 상속관계를 형성합니다.

3. 어트리뷰트 노드(attribute node)

  • 속성에 대한 데이터를 모아놓은 객체이며 해당 요소 노드와 형제 관계\

4. 텍스트 노드(text node)

  • 텍스트 노드는 요소 노드의 자식 노드

 

요소 노드 접근 방법

1. id를 통한 접근

document.getElementById('아이디')

 

2. css 선택자를 통한 접근 (요소 1개)

document.querySelector('css선택자')
  • 인수로 전달한 CSS선택자를 만족하는 요소 노드가 여러 개인 경우 첫 번째 노드만 반환
  • 요소 노드가 존재하지 않으면 null을 반환
  • 선택자 문법이 맞지 않으면 DOMException 에러 발생

 

3. css 선택자를 통한 접근 (해당 요소 모두)

document.querySelectorAll('css선택자')
  • DOM 컬렉션 객체인 NodeList라는 유사 배열 객체를 반환

***  getElementById 가 querySelector 보다 빨라서 id가 있으면 getElementById 로 잡는 것이 좋음

 

유사배열

  • 배열전용 push, pop, shift, unshift, filter, map 사용 불가
  • 유사 배열 → 배열 변경 방법 : [...유사배열]
  const $fItemList = document.querySelectorAll(".f-item"); // NodeList
  const $fItemList = [...document.querySelectorAll(".f-item")]; // Array

 


++ 개인

 

자바스크립트 컴파일러 바벨

  • 바벨은 주로 ES6+ 코드를 이전 버전과 호환되는 코드로 변환하는 데 사용
  • 브라우저 호환성에 대한 걱정 없이 최신 자바스크립트 문법을 사용 가능
  • 다만 웹팩(Webpack)과 같은 모듈 번들러가 하는 일을 처리하지 못함

 

출처 https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/