펼치다 라는 spread의 의미처럼, 여러개의 값을 하나로 묶은 배열을 다시 각각의 개별값으로 펼치는 문법이다.

//spread 구문(spread syntax)
const number = [1, 2, 3];
console.log(number); // (3) [1, 2, 3]  배열이 출력된다.
console.log(...number); //1, 2, 3

//rest parameter
const sumAll = (...args) => {
  let sum = 0;
  for (arg of args) {
    sum += arg;
  }
  return sum;
};

console.log(sumAll(1, 2, 3, 4)); //10

출력되는 창

배열로 묶어있던 값들이 괄호가 벗겨지고 각각의 개별값으로 펼쳐졌다. 그런데 잘 생각해보면 Rest parameter를 만들때도 '...'을 사용했다. 얼핏 비슷한것 같지만 Rest parameter는 여러 개의 아규먼트를 하나의 파라미터로 묶는방식이고, Spread 구문은 하나로 묶여 있는 값을 각각의 개별 값으로 펼치는 방식이다. 그래서 하나의 값으로 평가되는게 아니라 여러 값의 목록으로 평가된다.

const webPublishing = ['HTML', 'CSS'];
const interactiveWeb = [...webPublishing, 'JavaScript'];

console.log(webPublishing); //(2) ['HTML', 'CSS']
console.log(interactiveWeb); //(3) ['HTML', 'CSS', 'JavaScript']
//spread 구문을 활용하면 조금 더 간단하고 직관적이게 배열을 다룰 수 있다.

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

const arr3 = [...arr1, ...arr2];
console.log(arr3); //(6) [1, 2, 3, 4, 5, 6]

const latte = {
  esspresso: '30ml',
  milk: '150ml'
};

const cafeMocha = {
  ...latte,
  chocolate: '20ml',
}

console.log(latte); // {esspresso: "30ml", milk: "150ml"}
console.log(cafeMocha); // {esspresso: "30ml", milk: "150ml", chocolate: "20ml"}


//여러개의 파라미터가 있는 함수를 호출할 때 배열을 펼쳐서 각각의 아규먼트로 사용할 수 있다.
const introduce - (name, birth, job) => {
  console.log(`안녕하세요! 반갑습니다!`);
  console.log(`저는 ${name}입니다.`);
  console.log(`${birth}년 생이고,`);
  console.log(`직업은 ${job}입니다.`);
};

const myArr = ['지니', 1992, '고등학교 교사'];
introduce(...myArr); //함수를 호출할 때 아규먼트로 활용도 가능
//spread 구문 자체를 값으로 오해하면 안된다! 여러개의 값이 각각 매칭되는 상황이기에 가능

Spread 구문은 배열이나 객체를 복사하거나 혹은 복사해서 새로운 요소들을 추가할 때 유용하게 활용할 수 있다. 참고로 배열은 객체로 펼칠 수 있지만 객체는 배열로 펼칠 수 없다.

//하나의 값을 가진 배열을 펼쳤다고 하더라도 괄호가 벗겨지고 하나의 값으로 평가되는게 아니라 오류가 발생한다.
const numbers =  [1];
const number = ...number; // TypeError!

//배열을 펼쳐서 객체에 담으면 
const members = ['태호', '종훈', '우재'];
const newObject = { ...members };

console.log(newObject); // {0: "태호", 1: "종훈", 2: "우재"}
//0번부터 시작하는 배열의 인덱스가 프로퍼티 네임이 되서 객체가 만들어진다.

//객체는 배열로 펼칠 수 없다.
const topic = {
  name: '모던 자바스크립트',
  language: 'JavaScript', 
}
const newArray = [...topic]; // TypeError!

객체를 spread할 때는 반드시 객체를 표현하는 중괄호 안에서 활용해야 한다.

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

구조 분해 Destructuring  (0) 2024.12.17
모던한 프로퍼티 표기법  (1) 2024.12.17
조건부 연산자(삼항 연산자)  (0) 2024.12.16
함수  (0) 2024.12.15
null 병합 연산자 (??)  (0) 2024.12.15

조건 연산자 (Conditional operator)이면서 삼항 연산자 (Ternary operator)라고도 불리는 이 연산자는 자바스크립트에서 세 개의 피연산자를 가지는 유일한 연산자이다.

if문과 같은 원리로 조건에 따라 값을 결정할 때 활용되는데

//조건 ? truthy 할 때 표현식 : falsy 할 때 표현식
const cutOff = 80;

function passChecker(score) {
  return score > cutOff ? '합격입니다!' : '불합격입니다!';
};

//score > cutOff 가 truthy하다면 합격입니다를, falsy하면 불합격입니다를 return

console.log(passChecker(75)); //함수를 호출하는 점수가 75점이니까 불합격이 return된다.

//화살표 함수로 표현해보면
const passChecker = (score) => score > cutOff ? '합격입니다!' : '불합격입니다!';

console.log(passChecker(90)); //합격이 출력된다.

간단한 조건식의 경우에는 if문 보다 훨씬 더 간결하게 표현할 수 있는 장점이 있지만 내부에 변수나 함수를 선언한다거나 반복문 같은 표현식이 아닌 문장은 작성할 수 없다는 한계가 있기 때문에 if문을 완벽하게 대체할 수는 없다.

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

모던한 프로퍼티 표기법  (1) 2024.12.17
Spread 구문  (0) 2024.12.16
함수  (0) 2024.12.15
null 병합 연산자 (??)  (0) 2024.12.15
AND와 OR 연산자 심화  (0) 2024.12.15

 함수 선언(function declaration)

자바스크립트에서 함수는 다양한 방식으로 선언할 수 있는데, 가장 일반적인 방법은 function 키워드를 통해 함수를 선언하는 방식이고, 

// 함수 선언
function 함수이름(파라미터) {
  동작
  return; 리턴값
};

function sayHi() {
  console.log('Hi!');
};

다른 하나는 함수 선언을 변수에 할당하는 방식(함수 표현식 (function expression))이다. 

//함수 표현식
const printCode = function () {
  console.log('Code World!');
};

printCode();

 

자바스크립트에서 함수는 값으로 취급될 수도 있기 때문에 변수에 할당해서 함수를 선언할 수도 있다. 함수선언을 변수에 할당하는 모습만 함수 표현식이라고 오해할 수 있는데, 

// 변수에 할당해서 활용
const printJS = function () {
  console.log('JavaScript');
};

// 객체의 메소드로 활용
const codeLove = {
  printTitle: function () {
    console.log('Code');
  }
}

// 콜백 함수(callback function, 다른 함수의 파라미터에 전달된 함수)로 활용
myBtn.addEventListener('click', function () {
  console.log('button is clicked!');
});

// 고차 함수(higher order function, 변수에 호출된 값 할당해서 활용)로 활용
function myFunction() {
  return function () {
    console.log('Hi!?');
  };
};
//이중 괄호를 사용해서 고차함수를 바로 호출할수도 있다.
myFunction()();

파라미터로 함수를 선언해서 전달한 콜백함수로 활용하는 모습도 결과적으로는 함수선언을 값처럼 활용한다는 점에서 함수 표현식이라고 할 수 있다.

✅함수 표현식은 변수에 할당하는것이 포인트가 아니라 함수 선언을 값처럼 사용하는 방식이 포인트다.

함수 선언과 함수 표현식의 차이

//함수 선언
printCode(); //Welcome Coding World

function printCode() {
  console.log('Welcome Coding World');
};

함수 선언보다 먼저 함수를 호출했는데도 문제 없이 코드가 잘 동작한다. 선언문이 위쪽으로 끌어올려지는 현상을 호이스팅(Hoisting)이라고 한다.

//함수 표현식
printCode(); //ReferenceError

const printCode = function() {
  console.log('Welcome Coding World');
};

반면 함수 표현식은 변수 특성상 선언 이전에 접근할 수가 없다.

//함수 선언
function printCode() {
  function printJS() {
    console.log('JavaScript');
  }
  console.log('Hello Code World');
  printJS();
};

printCode(); //Hello Code World, JavaScript
printJS(); //ReferenceError

함수 선언은 변수의 var 키워드 처럼 함수 스코프를 가진다. 함수 안에 선언된 함수는 함수 밖에서 호출할 수 없지만 함수가 아닌 다른 코드블록에서 함수 선언을 하게 되면 모두 전역적으로 호출이 가능해진다.

//함수가 아닌 다른 코드블록에서 함수 선언을 해서 전역적으로 호출이 가능해진 모습
const x = 4;

if (x < 5) {
  function printJS() {
    console.log('JavaScript');
  }
};

{
  function printCode() {
    console.log('I love Coding');
  };
}

printCode(); //I love Coding
printJS(); //JavaScript

반면에 함수 표현식의 경우에는 할당된 변수에 따라 스코프(scope)가 결정된다.

var printJS = function () {
  console.log('JavaScript');
};

let printHi = function () {
  console.log('Hi');
};

const printCode = function () {
  conosole.log('Coding');
};
함수 선언 함수 표현식
-코드의 가독성
-변수 선언과 함수선언 명확하게 구분 가능
-함수 호출 자유로움
-반드시 선언 이후에 호출 가능
-가독성 good
-코드의 흐름 더 쉽게 파악 가능
-변수의 스코프도 활용 가능

일반적으로 개념을 설명할 때는 표현식보다 선언 부분이라는 의미가 좀 더 명확하게 구분되는 함수 선언이 보편적으로 사용된다.


파라미터(Parameter)의 기본 값

함수를 호출할때 함수의 밖에서 안쪽으로 다양한 값들을 전달받고자 한다면 파라미터를 활용해야한다.

자바스립트에서 함수의 파라미터는 할당 연산자로 기본값을 가질 수가 있다. 기본값이 있는 파라미터는 함수를 호출할 때 아규먼트를 전달하지 않으면, 함수 내부의 동작은 이 파라미터의 기본값을 가지고 동작하게 된다.

//  (name)이 파라미터이다.
function greeting(name) {
  console.log(`Hi! My name is ${name}!`);
}

//파라미터를 활용하면 전달하는 값에 따라서 다양한 결과로 동작하는 모습을 확인 할 수 있다.
greeting('Anna'); //Hi My name is Anna!
greeting('Tom'); //Hi My name is Tom!
greeting(); //Hi My name is Undefined!

// 파라미터 기본값
function sayHi(name = 'kelly') {
  console.log(`Hi! ${name}`);
}

sayHi('JavaScript'); // Hi! JavaScript
sayHi(); // Hi! kelly


//아규먼트가 파라미터로 전달될 때는 파라미터의 기본값과는 관계없이 함수를 호출할 때 작성한 순서 그대로 전달한다.
//때문에 기본값이 필요한 파라미터는 가급적 오른쪽에 작성하는것이 권장된다.
function greeting(name = 'Jenny', interest = 'Javascript') {
  console.log(`Hi! My name is ${name}!`);
  console.log(`I Like ${interest}!`);
};

greeting(undefined, 'Python'); //아규먼트가 undefined값으로 전달될 때도 동작한다.

//출력되는 값
Hi! My nmae is Jenny!
I Like Python!

arguments 객체

자바스크립트 함수 안에는 arguments라는 독특한 객체가 존재한다. arguments 객체는 함수를 호출할 때 전달한 아규먼트들을 배열의 형태로 모아둔 유사 배열 객체인데 특히, 함수를 호출할 때 전달되는 아규먼트의 개수가 불규칙적일 때 유용하게 활용될 수 있다.

function printArguments() {
  // arguments 객체의 요소들을 하나씩 출력
  for (const arg of arguments) {
    console.log(arg); 
  }
}

printArguments('Young', 'Mark', 'Koby');

‼️파라미터와 아규먼트를 헷갈릴 수도 있다. 하지만 꼭 구분지어서 알아두는 것이 좋다.

function greeting(name) { //함수 선언 부분에서 소괄호 안에 작성되는것이 파라미터
  console.log(`Hi My naame is ${name}!`);
};

greeting('Anna'); //함수 호출부분에서 파라미터로 전달하는 값에 해당하는 부분이 아규먼트
greeting('Jenny');
greeting('Tom');

Rest Parameter

arguments 객체를 이용하는 것 말고도 불규칙적으로 전달되는 아규먼트를 다루는 방법이 있다. 파라미터 앞에 마침표 세 개를 붙여주면, 여러 개로 전달되는 아규먼트들을 배열로 다룰 수 있다.
그리고 arguments객체는 유사 배열이기 때문에 배열의 메소드를 활용할 수 없는 반면, rest parameter는 배열이기 때문에 배열의 메소드를 자유롭게 사용할 수 있다는 장점이 있다.

function printArguments(...args) {
  // args 객체의 요소들을 하나씩 출력
  for (const arg of args) {
    console.log(arg); 
  }
}

printArguments('Young', 'Mark', 'Koby');

rest parameter는 다른 일반 파라미터들과 함께 사용될 수도 있는데

//앞에 정의된 parameter에 아규먼트를 먼저 할당하고 (first, second)
//나머지 아규먼트(...other)를 배열로 묶는 역할을 하기 때문에 
//일반 파라미터와 사용될때는 가장 마지막에 작성되어야 한다.
function printRankingList(first, second, ...others) {
  console.log('코드잇 레이스 최종 결과');
  console.log(`우승: ${first}`);
  console.log(`준우승: ${second}`);
  for (const arg of others) {
    console.log(`참가자: ${arg}`);
  }
}

printRankingList('Tommy', 'Jerry', 'Suri', 'Sunny', 'Jack');

이름 그대로 앞에 정의된 이름 그대로 앞에 정의된 파라미터에 argument를 먼저 할당하고 나머지 argument를 배열로 묶는 역할을 하기 때문에 일반 파라미터와 함께 사용할 때는 반드시 가장 마지막에 작성해야 한다.

Arrow Function

arrow function은 익명 함수를 좀 더 간결하게 표현할 수 있도록 ES2015에서 새롭게 등장한 함수 선언 방식이다. arrow function은 이름이 없는 익명함수이기 때문에 일반적으로 어떤 이름을 가진 변수에 할당하거나, 다른 함수의 아규먼트로 선언할 때 주로 활용된다.
아래 코드와 같이 표현식으로 함수를 정의할 때 활용될 수도 있고 콜백 함수로 전달할 때 활용할 수도 있다.

// 화살표 함수 정의
const getTwice = (number) => {
  return number * 2;
};

// 콜백 함수로 활용
myBtn.addEventListener('click', () => {
  console.log('button is clicked!');
});

화살표 함수는 기존의 function 함수를 조금 더 간결하게 표현하고자 등장했기 때문에 상황에 따라서는 훨씬 더 짧게 표현될 수도 있다.

// 1. 함수의 파라미터가 하나 뿐일 때
const getTwice = (number) => {return number * 2;};

// 파라미터를 감싸는 소괄호 생략 가능 (파라미터가 두개 이상이거나 없을 때는 반드시 소괄호를 작성)
const getTwice = number => {return number * 2;};

// 2. 함수 동작 부분이 return문만 있을 때
const sum = (a, b) => {return a + b;};

// return문과 중괄호 생략 가능 
// (하지만 함수 내부에서 조건문이나 반복문, 변수의 값을 할당하는 것과 같이 
// return문 외에 다른 표현들이 더 필요하다면 중괄호와 return 키워드는 생략할 수 없다.)
const sum = (a, b) => a + b;

// 3. 함수 내부 동작이 return문 하나뿐이어도 값이 객체인 경우는 주의해서 사용해야한다.
const getCode = () => {
  return {name: 'Ann', };
};

//중괄호와 return 키워드를 생략하게 되면 에러가 발생한다.
const getCode = () => {name: 'Ann', }; //Error

//객체라고 작성한 중괄호를 함수의 동작 부분을 구분하는 중괄호로 해석하기 때문이다. 이 땐 소괄호로 한번 더 감싸주면 해결된다.
const getCode = () => ({name: 'Ann', }); //{name: Ann}

그리고 Arrow function이 일반 함수와 몇 가지 차이점이 있는데 가장 대표적인 차이점은 arguments 객체가 없고, this가 가리키는 값이 일반 함수와 다르다는 것이다.

this

자바스크립트에는 this라는 조금 특별한 키워드가 있다. 웹 브라우저에서 this가 사용될 때는 전역 객체, Window 객체를 가지게 된다. 하지만 객체의 메소드를 정의하기 위한 함수 안에선 메소드를 호출한 객체를 가리킨다.

//이렇게 사용해도 잘 동작하지만 (물론 여기서 user을 this로 바꿔 사용해도 충분히 사용가능하다.)
const user = {
  firstName: 'Tess',
  lastName: 'Jang',
  getFullName: function () {
    return `${user.firstName} ${user.lastName}`;
  },
};

console.log(user.getFullName()); // Tess Jang

//이 메소드를 다른 객체에도 적용하고 싶어서 험수 외부로 분리하면 동작에 어려움이 있다.
function getFullName() {
  return `${user.firstName} ${user.lastName}`
};

const user = {
  firstName: 'Tess',
  lastName: 'Jang',
  getFullName: getFullName
};

const admin = {
  firstName: 'Alex',
  lastName: 'Kim',
  getFullName: getFullName
};

console.log(user.getFullName()); // Tess Jang
console.log(admin.getFullName()); // Tess Jang

//getFullName의 동작부분에서 user 객체만 바라보기 때문에 admin 객체에서 메소드를 호출하더라도 user 프로퍼티를 사용한다.

function getFullName() {
  return `${this.firstName} ${this.lastName}`
};

const user = {
  firstName: 'Tess',
  lastName: 'Jang',
  getFullName: getFullName
};

const admin = {
  firstName: 'Alex',
  lastName: 'Kim',
  getFullName: getFullName
};

console.log(user.getFullName()); // Tess Jang
console.log(admin.getFullName()); // Alex Kim

위 상황에선 this 키워드가 유용하게 사용될 수 있다. 자바스크립트에서 this는 함수를 호출한 객체를 가리키는 키워드인것이다. 그래서 코드를 작성할 때 값이 미리 결정되는게 아니라 함수가 호출될 때 어떤 객체가 그 함수를 호출했는지에 따라 상대적으로 값이 변하게 된다.

//일반 함수와 Arrow function은 this를 다루는 방식이 서로 다르다.

//화살표 함수에서 this 값은 일반 함수처럼 호출한 대상에 따라 상대적으로 변하는게 아니라
//화살표 함수가 실행되기 직전에 유효한 this 값과 똑같은 값을 가지고 동작한다.

console.log(this); //window객체가 호출된다.

const printThis = () => { //여기 화살표 함수가 선언되기 직전의 this는 윗줄에 window 객체이다.
  console.log(this);
};

const myObj = {
  cotent: 'myObj',
  printThis: printThis
};

const otherObj = {
  cotent: 'otherObj',
  printThis: printThis
};

myObj.printThis(); //window 객체
otherObj.printThis(); //window 객체, 그래서 코드를 실행했을 때 이런결과가 출력되는 것이다.

//이런 객체 특징때문에 객체에 메소드를 만들 때는 화살표 함수보다 일반 함수가 조금 더 권장된다.

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

Spread 구문  (0) 2024.12.16
조건부 연산자(삼항 연산자)  (0) 2024.12.16
null 병합 연산자 (??)  (0) 2024.12.15
AND와 OR 연산자 심화  (0) 2024.12.15
Falsy & Truthy  (1) 2024.12.15

물음표 두개(??)를 사용해서 null 혹은 undefined 값을 가려내는 연산자이다.

const example1 = null ?? 'I';
const example2 = undefined ?? 'love';
const example3 = 'Coding' ?? 'JavaScript';

console.log(example1, example2, example3); // ?

예시 1과 2를 보면 nill 병합 연산자 왼편에 각각 null과 undefined가 있다. 연산자 왼편의 값이 null이나 undefined라면 연산자 오른편의 값이 리턴되고, 예시 3처럼 연산자 왼편 값이 null이나 undefined 값이 아니라면 연산자 왼편 값이 리턴되는 동작 방식이다.

결과적으로 출력되는 값은 "I love Coding"

const example1 = null ?? 'I'; // I
const example2 = undefined ?? 'love'; // love
const example3 = 'Coding' ?? 'JavaScript'; //Coding

console.log(example1, example2, example3); // I love Coding

OR 연산자와 비슷해보이는데 실제로도 상황에 따라 똑같이 동작할 수 있다.

const title1 = null || 'love cat';
const title2 = null ?? 'love dog';

console.log(title1); // love cat
console.log(title2); // love dog

하지만 null 병합 연산자는 왼쪽의 값이 nill이나 undefined인지 확인하고 OR 연산자는 왼쪽의 값이 falsy인지 확인하기 때문에 null이나 undefined가 아닌 falsy값을 활용할 때 결과가 서로 다르다.

const title1 = false || 'Merry Christmas!';
const title2 = false ?? 'Santa';

console.log(title1); // Merry Christmas!
console.log(title2); // false

const width1 = 0 || 150;
const width2 = 0 ?? 150;

console.log(width1); // 150
console.log(width2); // 0

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

조건부 연산자(삼항 연산자)  (0) 2024.12.16
함수  (0) 2024.12.15
AND와 OR 연산자 심화  (0) 2024.12.15
Falsy & Truthy  (1) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
console.log('I love u' && 'React!');

위 코드를 실행하면 어떤 결과 값이 출력될까? 

I live u 와 React! 모두 문자열이기 때문에 Truthy한 값이라 true and true가 되서 콘솔에 true가 출력될것 같다.

//출력된 값
React!

하지만 실행해보면 "React!"가 출력된걸 볼 수 있다. 자바스크립트에서 논리연산자는 매번 true 혹은 false를 리턴하는게 아니라 상황에 따라 양쪽 값 중 어느 한쪽을 선택하는 방식으로 동작한다.

//익숙하게 본 boolean 형태로 AND 연산자를 생각해보자
console.log(true && true); //ture
console.log(true && false); //false
console.log(false && true); //flase
console.log(false && false); //flase

AND 연산자는 양쪽 값이 모두 true인 경우에만 true를 리턴하고 어느 한쪽이라도 false일 경우 false를 리턴한다.

하지만 사실 왼쪽 값이 truthy하면 오른쪽 값을 리턴하고, 왼쪽 값이 falsy 하면 그대로 왼쪽 값을 리턴하는 방식으로 동작한다. 그래서 위에서 작성했던 문자열로 작성한 연산자에서 "React!"를 출력한 것이다. 문자열 "I love u"가 truthy 하기 때문에 오른쪽에 있는 "React!"가 리턴 된 것이다.

그렇다면 OR 연산자는?

//OR 연산자 boolean 타입
console.log(true || true); //ture
console.log(true || false); //true
console.log(false || true); //true
console.log(false || false); //false

둘 중 하나라도 true일 경우 true를 리턴하고 양쪽 다 false일 경우에 false를 리턴했다. 그런데 OR 연산자도 둘 중 하나를 선택하는 관점에서 바라보면 AND연산자와 정반대로

왼쪽 값이 truthhy 하면 그대로 왼쪽 값을 리턴하고, 왼쪽 값이 falsy하면 오른쪽 값을 리턴하는 방식으로 동작한다.

예시를 가지고 연산을 더 공부해보자.

console.log(null && undefined);
console.log(0 || true);
console.log('0' && NaN);
console.log({} || 123);
  1. null과 undefined 둘 다 falsy 값으로 flase AND false. 왼쪽 값이 falsy 하면 그대로 왼쪽 값을 리턴하기 때문에 null이 출력될것이다.
  2. 0은 falsy값, true는 말 그대로 true. false OR true. 왼쪽값이 falsy하면 오른쪽 값을 리턴하니 true가 출력될 것이다.
  3. 0이라는 문자열 true, NaN은 falsy 값. true AND false. 왼쪽 값이 true기 때문에 오른쪽 값을 리턴해 NaN이 출력될 것이다.
  4. 빈 배열과 객체 또한 truthy. 그리고 숫자 123은 true. true OR true. 왼쪽값이 truthy하면 그대로 왼쪽값을 리턴하기 때문에 {빈 객체}가 출력될 것이다.
function print(value) {
  const message = value || "Code World!";
  
  console.log(message);
};

print(); //Code World!
print('JavaScript'); //JavaScript

함수를 호출할 때 아무런 값도 전달하지 않으면 파라미터에 undefined, flasy한 값이 전달된다는 특징을 이용해서 함수를 호출할 때 전달한 값이 없다면 "Code World!"를,  전달한 값이 있을 땐 그 값을 사용하게끔 조건식처럼 활용할 수 있다.


AND 연산자와 OR 연산자 우선순위

function checkAnswer(value) {
  if (value < 10 && value > 0 && value !== 3) {
    return '정답입니다!';
  }
  return '틀렸습니다!';
};

console.log(checkAnswer(4)); // 정답입니다!

10보다는 작고, 0보다는 크지만 3은 아닐때 정답입니다를 리턴하고, 아니라면 틀렸습니다를 리턴하는 함수이다.

코드를 작성하다보면 다양한 상황을 고려하기 위해 이렇게 AND나 OR 연산자를 여러번 사용해야 할 수도 있다.

AND 연산자나 OR 연산자 중 하나만 계속해서 사용할 때는 문제 없지만, 만약 AND 연산자와 OR 연산자를 섞어서 사용할 때는 연산의 우선순위가 존재한다.

쉽게 설명해서 1 + 2 + 3 처럼 계속해서 더하기 연산자만 사용한다면 왼쪽부터 차례대로 더하면 되지만, 1 + 2 * 3 처럼 더하기와 곱하기 연산자가 섞여 있다면 연산자 우선순위를 고려해야 한다. AND 와 OR 연산자 사이에서는 AND 연산자의 우선순위가 더 높다.

console.log(true || false && false); // true
console.log((true || false) && false); // false

console.log('Coding' || NaN && false); // Coding
console.log(('Coding' || NaN) && false); // false

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

함수  (0) 2024.12.15
null 병합 연산자 (??)  (0) 2024.12.15
Falsy & Truthy  (1) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15

자바스크립트에서 다양한 값들은 맥락에 따라서 boolean 값으로 평가 될 수 있다.

빈문자열은 falsy값이지만, 빈 배열과 빈 객체는 truthy 값이다.
직접 boolean을 통해서 확인 해 볼 수도 있다.

 

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

null 병합 연산자 (??)  (0) 2024.12.15
AND와 OR 연산자 심화  (0) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14

많이는 사용되지 않지만 ES2015 이후로 6개의 데이터 타입 외에 2개의 데이터 타입이 추가되었다. 많이 활용되진 않으니 가볍게 정리를 해보자.

Symbol 심볼은 기본형 데이터 타입 중 하나로 코드 내에서 유일한 값을 가진 변수 이름을 만들 때 사용한다.

//이렇게 symbol이라는 함수를 통해 심볼 값을 만들어 낼 수도 있다.
const user = Symbol('this is a user');

//그리고 괄호 안에 심볼에 대한 설명을 붙일 수도 있다.
//이렇게 Symbol 값을 담게 된 user라는 이름의 변수는 다른 어떤 값과 비교해도 true가 될 수 없는 고유한 변수가 된다.
const user = Symbol('this is a user');

user === 'this is user'; // false
user === 'user'; // false
user === 'Symbol'; // false
user === true; // false
user === false; // false
user === 123; // false
user === 0; // false
user === null; // false
user === undefined; // false
...

//심지어 똑같은 설명을 붙인 심볼을 만들더라도 두 값을 비교하면 false가 반환된다.
const symbolA = Symbol('this is Symbol');
const symbolB = Symbol('this is Symbol');

console.log(symbolA === symbolB); // false

BigInt 는 자바스크립트에서 아주 큰 정수(integer)를 표현하기 위해 등장한 데이터 타입이다. 자바스크립트의 숫자에는 안전한 정수 표현에 한계가 있는데, 안전한 정수 표현이라는게 조금 이상하긴 하지만 약 9,000조 정도의 숫자를 넘어가면 연산에 미세한 오류가 발생한다.

//Biglnt 타입의 값은 일반 정수 마지막에 알파벳 n을 붙이거나 Biglnt라는 함수를 사용하면 된다.
console.log(9007199254740993n); // 9007199254740993n
console.log(BigInt('9007199254740993')); // 9007199254740993n

//하지만 말 그대로 큰 정수를 표현하기 위한 데이터 타입이기 때문에 소수에 대한 표현은 지원하지 않는다.
1.5n; // SyntaxError

//소수로 결과가 리턴되는 연산은 소숫점 아랫부분을 버리고 정수 형태로 리턴한다.
10n / 6n; // 1n
5n / 2n; // 2n

//그리고 Biglnt 타입 끼리만 연산이 가능하고 서로 다른 타입은 명시적으로 타입변환을 해야한다.
3n * 2; // TypeError
3n * 2n; // 6n
Number(3n) * 2; // 6

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

AND와 OR 연산자 심화  (0) 2024.12.15
Falsy & Truthy  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14
자바스크립트 이벤트  (0) 2024.12.14

1. MouseEvent.button

마우스 이벤트 객체의 버튼 프로퍼티를 활용하면, 마우스 버튼을 눌렀을 때 일어난 이벤트에 대해서 어떤 버튼을 눌러서 일어난 이벤트인지를 정확하게 알아낼 수 있다.

내용 MouseEvent.type
0 마우스 왼쪽 버튼 click 마우스 왼쪽 버튼을 눌렀을 때
1 마우스 휠 contextmenu 마우스 오른쪽 버튼 눌렀을 때
2 마우스 오른쪽 버튼 dbclick 더블클릭 (동일 버튼 두번 누르기)
3 X1 (일반적으로 브라우저 뒤로 가기 버튼) mousedown 마우스 버튼을 누른 순간
4 X2 (일반적으로 브라우저 앞으로 가기 버튼) mouseup 마우스 버튼을 눌렀다 뗀 순간

동작을 하나 하더라도 여러 이벤트가 동시에 일어날 수 있다. 예를 들어 클릭을 한번 하더라도, mousedown, mouseup, click까지 3개의 이벤트가 발생하게 된다. 이렇게 동시에 일어날땐 각각의 순서에 대해서 잘 이해하는게 중요하다. 더블클릭의 경우엔 mousedown, mouseup, click, mousedown, mouseup, click, dbclick 순으로 이벤트가 발생되기 때문에 잘 생각해보자.

+ 참고로 mouseenter, mouseleave, mouseover, mouseout, mousemove 처럼 마우스 이동과 관련된 이벤트에서는 이 값이 null이나 undefined가 아니라 0이다.


2. MouseEvent.type

이벤트 타입 설명
mousedown 마우스 버튼을 누르는 순간
mouseup 마우스 버튼을 눌렀다 떼는 순간
click 왼쪽 버튼을 클릭한 순간
dblclick 왼쪽 버튼을 빠르게 두 번 클릭한 순간
contextmenu 오른쪽 버튼을 클릭한 순간
mousemove 마우스를 움직이는 순간
mouseover 마우스 포인터가 요소 위로 올라온 순간
mouseout 마우스 포인터가 요소에서 벗어나는 순간
mouseenter 마우스 포인터가 요소 위로 올라온 순간 (버블링이 일어나지 않음)
mouseleave 마우스 포인터가 요소에서 벗어나는 순간 (버블링이 일어나지 않음)

mouseover, mouseout VS mouseenter, mouseleave

1. 버블링이 일어나지 않는다.

 

마우스를 올렸을 때의 차이점을 보자.

over에서는 해당 요소 바깥에서 안쪽으로 이동할 때도, 자식 요소 부분으로 이동할 때도 이벤트가 발생하지만, enter에서는 이벤트핸들러가 등록된 바깥 요소에서 안쪽으로 마우스가 이동할 때만 이벤트 핸들러가 동작한다.

2. 자식요소의 영역을 계산하지 않는다. (mouseenter와 mouseleave는 자식 요소의 영역을 계산하지 않는다.)

위 영상에서 mouseover는 자식요소의 영역을 구분하기 때문에 자식요소로 이동했을 때도 마우스 움직임을 카운트 했다. 반면 mouseenter는 자식요소를 구분하지 못하기 때문에 마우스를 자식요소로 움직였다 부모요소로 움직여도 움직임이 카운트 되지 않았다.


3. MouseEvent.위치프로퍼티

프로퍼티 설명

clientX, clientY 마우스 포인터의 브라우저 표시 영역(이벤트가 발생한 순간에 브라우저가 콘텐츠를 표시할 수 있는 영역)에서의 위치 client 값은 그 순간 보여지는 화면을 기준으로 계산하기 때문에 스크롤 위치와는 무관하게 항상 보여지는 화면의 좌측 상단의 모서리 위치를 (0, 0)으로 계산한다.
pageX, pageY 마우스 커서의 전체 문서 영역에서의 위치 스크롤로 인해서 보이지 않게된 화면의 영역까지 포함해서 측정한다
offsetX, offsetY 마우스 포인터의 이벤트 발생한 요소(이벤트가 발생한 target이 기준)에서의 위치 offset 값은 이벤트가 발생한 대상을 기준으로 계산하기 때문에 스크롤 위치와는 무관하게 항상 대상의 좌측 상단의 모서리 위치를 (0, 0)으로 계산한다.
screenX, screenY 마우스 포인터의 모니터 화면 영역에서의 위치  
const box1 = document.querySelector('#box1');

function onMouseMove(e) {
  console.log(`client: (${e.clientX}, ${e.clientY})`);
  console.log(`page: (${e.pageX}, ${e.pageY})`);
  console.log(`offset: (${e.offsetX}, ${e.offsetY})`);
  console.log('------------------------------------');
}

box1.addEventListener('mousemove', onMouseMove);

하늘색 요소 안에서 마우스가 움직이면 console 창에서 위치를 알려준다.

어느 일정한 위치에 도달했을 때 이벤트를 발생시키거나, 마우스가 이동한 방향을 파악하는데 활용하거나 할 수 있다.


4. MouseEvent.relatedTarget

mouseenter, mouseleave, mouseover, mouseout 이벤트에는 relatedTarget이라는 프로퍼티가 존재하는데 target 프로퍼티가 이벤트가 발생한 요소를 담고 있다면, relatedTarget 프로퍼티는 이벤트가 발생하기 직전(또는 직후)에 마우스가 위치해 있던 요소를 담고 있다.

const box2 = document.querySelector('#box2');

function printEventData(e) {
  console.log('event:', e.type);
  console.log('target:', e.target);
  console.log('relatedTarget:', e.relatedTarget);
  console.log('------------------------------------');
  if (e.target.classList.contains('cell')) {
    e.target.classList.toggle('on');
  }
}

box2.addEventListener('mouseover', printEventData);
box2.addEventListener('mouseout', printEventData);

console창을 보면 relatedTarget은 cell-4 target은 box2이다. event는 mouseover이니, 종합해보면 cell-4에서 box2로 마우스가 이동했다는걸 알 수 있다.

그리고 이 창을 보면, relatedTarget은 body, target은 box2이고, event는 mouseout이다. 종합해보면 box에서 body로 마우스가 이동했다는걸 알 수 있다.

event가 mouseover일때는 마우스가 이동하기 직전의 요소, mousout 일때는 마우스가 이동한 직후의 요소를 담고있는걸 확인할 수 있다.


5. KeyboardEvent.type

이벤트 타입 설명
keydown 키보드의 버튼을 누르는 순간
keypress 키보드의 버튼을 누르는 순간 ('a', '5' 등 출력이 가능한 키에서만 동작하며, Shift, Esc 등의 키에는 반응하지 않음)
keyup 키보드의 버튼을 눌렀다 떼는 순간

keypress는 웹 표준에서 권장하는 이벤트는 아니기 때문에 keypress 보다 keydown 사용을 추천한다.


6. KeyboardEvent.key vs KeyboardEvent.code

키보드 이벤트 객체에는 key와 code 프로퍼티가 자주 사용되는데 key는 사용자가 누른 키가 가지고 있는 값을 나타내고 code는 누른 키의 물리적인 위치를 나타낸다.


7. input 태그 다루기

이벤트 타입 설명
focusin 요소에 포커스가 되는 순간
focusout 요소에 포커스가 빠져나가는 순간
focus 요소에 포커스가 되는 순간 (버블링이 일어나지 않음)
blur 요소에 포커스가 빠져나가는 순간 (버블링이 일어나지 않음)
change 입력된 값이 바뀌는 순간 (입력이 시작되기 전 값과 완료된 후 값 사이에 차이가 있을 때 발생, focusout될 때 입력이 완료되었다고 생각해 focusout 직전에 change 이벤트가 발생한다.)
input 값이 입력되는 순간 (esc나 shift 같이 값이 입력되는것이랑 상관 없는 key에는 반응하지 않는다)
select 입력 양식의 하나가 선택되는 순간
submit 폼을 전송하는 순간

8. 스크롤 이벤트

scroll 이벤트는 보통 window 객체에 이벤트 핸들러를 등록하고 window 객체의 프로퍼티와 함께 자주 활용된다.

function printEvent(e) {
  console.log(window.scrollY);
};

window.addEventListener('scroll', printEvent);


특히 scrollY 프로퍼티를 활용하면 스크롤된 특정한 위치를 기준으로 이벤트 핸들러가 동작하게 하거나 혹은 스크롤 방향(위로 스크롤 중인지/아래로 스크롤 중인지)을 기준으로 이벤트 핸들러가 동작하게끔 활용할 수도 있다.


'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

Falsy & Truthy  (1) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14
자바스크립트 이벤트  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13

마우스 이벤트

이벤트 타입 설명
mousedown 마우스 버튼을 누르는 순간
mouseup 마우스 버튼을 눌렀다 떼는 순간
click 왼쪽 버튼을 클릭한 순간
dblclick 왼쪽 버튼을 빠르게 두 번 클릭한 순간
contextmenu 오른쪽 버튼을 클릭한 순간
mousemove 마우스를 움직이는 순간
mouseover 마우스 포인터가 요소 위로 올라온 순간
mouseout 마우스 포인터가 요소에서 벗어나는 순간
mouseenter 마우스 포인터가 요소 위로 올라온 순간 (버블링이 일어나지 않음)
mouseleave 마우스 포인터가 요소에서 벗어나는 순간 (버블링이 일어나지 않음)

키보드 이벤트

이벤트 타입 설명
keydown 키보드의 버튼을 누르는 순간
keypress 키보드의 버튼을 누르는 순간 ('a', '5' 등 출력이 가능한 키에서만 동작하며, Shift, Esc 등의 키에는 반응하지 않음)
keyup 키보드의 버튼을 눌렀다 떼는 순간

포커스 이벤트

이벤트 타입 설명
focusin 요소에 포커스가 되는 순간
focusout 요소로부터 포커스가 빠져나가는 순간
focus 요소에 포커스가 되는 순간 (버블링이 일어나지 않음)
blur 요소로부터 포커스가 빠져나가는 순간 (버블링이 일어나지 않음)

입력 이벤트

이벤트 타입 설명
change 입력된 값이 바뀌는 순간
input 값이 입력되는 순간
select 입력 양식의 하나가 선택되는 순간
submit 폼을 전송하는 순간

스크롤 이벤트

이벤트 타입 설명
scroll 스크롤 바가 움직일 때

윈도우 창 이벤트

이벤트 타입 설명
resize 윈도우 사이즈를 움직일 때 발생

 

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

새로운 데이터 타입과 특징  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15
자바스크립트 이벤트  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13
객체, 요소, 노드  (1) 2024.12.13

이벤트 종류 알아보기

1. 이벤트 핸들러 등록하기

HTML의 속성이나 DOM 프로퍼티를 활용해 이벤트를 등록하는 방법 외에 Element.addEventListener('type', 'handler')를 통해서 이벤트 핸들러를 등록할 수 있다. 이 방법이 이벤트 핸들러를 등록할 때 가장 권장되는 방법이다. 이렇게 하면 하나의 요소에 여러개의 독립적인 이벤트 핸들러를 등록할 수가 있다.

<body>
  <div id="content" class="btns">
    <button id="myBtn">JS Click!</button>
    <button onclick="console.log('Hello Codeit!')">HTML Click!</button>
  </div>
  <script src="index.js"></script>
</body>
// 이벤트 등록하기
let btn = document.querySelector('#myBtn');

// btn.onclick = function () {
// 	console.log('Hi Code World!');
// };
// 이렇게 dom요소에 접근에 onclick 프로퍼티를 활용하는 방법 같은 이벤트 핸들러는 한가지 문제를 가지고 있다.
// innerHTML이나 className같은 프로퍼티를 활용할 때도 프로퍼티에 어떤 값을 할당하는 방식은 
// 기존에 있던 값을 덮어쓰는 형태로 동작하기 때문에 기존에 값을 유지하면서 일부만 수정하기는 어려운 단점이 있었다.
// onclick 프로퍼티도 새로 쓰면 기존의 값을 덮어쓴다.

function event1() {
	console.log('Hi Codeit!');
}

function event2() {
	console.log('Hi again!');
}
//위에 코드를 실행시킨다면 Hi Codeit!은 나오지 않고 Hi again!만 출력된다.
//지금은 간단한 코드이지만 각 이벤트 별로 중요한 리턴 값이 있을 경우에 그 리턴 값도 다뤄야 하는 문제가 생긴다.

// elem.addEventListener(event, handler)
btn.addEventListener('click', event1);
btn.addEventListener('click', event2);

Element.addEventListener('event', 'handler')는 이벤트 타입을 문자열로 첫번째 파라미터에 입력하고, 두번째 파라미터에는 이벤트 핸들러를 전달하면 된다. 메소드를 여러번 호출하게 되도 하나의 요소에 여러개의 독립적인 이벤트 핸들러를 등록할 수 있다.

실행해보면 이벤트1과 이벤트2가 동시에 실행되는것을 확인 할 수 있다.


2. 이벤트 핸들러 삭제하기

addEventListener 메소드를 활용해서 이벤트 핸들러를 등록했다면, Element.removeEventListner('type', 'handler')를 통해서 이벤트 핸들러를 삭제할 수 있다. 등록할 때와 똑같이 파라미터를 전달하면 해당되는 이벤트 핸들러가 삭제되는 방식이다.

단, 이벤트를 삭제할 땐 반드시 이벤트를 등록할 때 외부에 함수를 만들어서 해당 함수의 이름으로 핸들러를 전달해줘야 한다.

// elem.removeEventListener(event, handler)
btn.removeEventListener('click', event2);


btn.addEventListener('click', function() {
  console.log('event3!');
});

btn.removeEventListener('click', function() {
  console.log('event3!');
});

//이렇게는 생성도, 삭제도 되지 않는다.

function event3() {
  console.log('Hi event3!');
};
//이렇게 변수에 담아서

btn.addEventListener('click', event3);
//사용을 해주어야 한다

btn.removeEventListener('click', event3);

3. 이벤트 객체 (Event Object)

웹페이지에서 이벤트가 발생하면 관련된 정보를 담은 이벤트 객체가 만들어지고 이벤트 핸들러의 첫번째 파라미터로 전달된다. 이벤트 객체는 이벤트 종류마다 가지고 있는 프로퍼티가 다르고, 이벤트에 대한 유용한 정보들을 프로퍼티로 가지고 있다.

<body>
  <div id="content" class="btns">
    <input type="text" id="myInput" placeholder="type anything">
    <button id="myBtn">click me!</button>
  </div>
  <script src="index.js"></script>
</body>
// 이벤트 객체 (Event Object)
const myInput = document.querySelector('#myInput');
const myBtn = document.querySelector('#myBtn');

function printEvent(event) { //(event) 이벤트 핸들러의 첫번째 파라미터
  console.log(event);
    event.target.style.color = 'red';
}

myInput.addEventListener('keydown', printEvent);
myBtn.addEventListener('click', printEvent);

키보드를 누를땐 키보드 이벤트가, 클릭할 땐 클릭이벤트가 출력된다.
type은 이벤트 타입이, target은 이벤트가 발생한 요소를 담고 있다.

특히나 target은 DOM 요소가 담겨 있기 때문에 event.target.style.color = 'red'; 이런식으로 이벤트가 발생했을 때 해당 요소를 수정한다거나 혹은 그 요소가 가지고 있는 속성 값들을 참조해야 되는 상황에서 유용하게 활용된다.

프로퍼티 설명
type 이벤트 이름 (click, mouseup, keydown 등)
target 이벤트가 발생한 요소
currentTarget 이벤트 핸들러가 등록된 요소
timeStamp 이벤트 발생 시각(페이지가 로드된 이후부터 경과한 밀리초)
bubbles 버블링 단계인지를 판단하는 값

4. 이벤트 버블링 (Event Bubbling)

이벤트는 전파가 된다. 어떤 요소에서 이벤트가 발생하면 해당 요소에 등록된 이벤트 핸들러가 동작하는 것뿐만 아니라 (같은 타입의 이벤트에 한해서) 부모 요소로 이벤트가 계속해서 전파되면서 각 요소에도 등록된 이벤트 핸들러가 있다면 차례로 이벤트 핸들러가 동작한다.(최상단의 윈도우 객체를 만날때까지 반복)  자식 요소에서 부모 요소로 이벤트가 전파되는 것을 이벤트 버블링(Event Bubbling)이라고 한다.

이벤트 버블링이 일어나도 이벤트 객체의 target 프로퍼티는 변하지 않고 처음 이벤트가 발생한 시작점을 담고 있다.

참고로 이벤트 버블링은 이벤트 객체의 stopPropagation 메소드로 전파를 막을 수 있다.

for (let item of items) {
  item.addEventListener('click', function(e) {
    console.log('item Event');
    console.log(e.currentTarget);
    e.stopPropagation();
  });
};

//이 코드를 쭉 살펴보자면 items에 있는 item을 파라미터로 담고, 반복문을 실행한다.
//item에 클릭했을 때 이벤트 리스너 프로퍼티를 부여한다. 어떤 함수가 실행될거냐면
//console.log에 item Event라고 나오고, console.log에 이벤트 핸들러가 등록된 요소를 출력해주고
//버블링을 멈춰줘

위 코드를 위에서부터 해석해보자면,

for of 문으로 반복문을 실행할건데 items 함수에 담긴 item, 각 li 요소를 말한다. item이 클릭되는 이벤트가 발생하면 함수를 실행해주세요, 어떤 함수? console에 'item Event'라는 문자열 출력해주시고, console에 e.currentTarget 이벤트 리스너가 연결된 (핸들러가 등록된) 요소(즉, 클릭된 li 요소)를 출력해주세요. 그리고 나서 이벤트가 부모요소로 전파되는 버블링을 멈춰주세요.

메소드 하나로 간단하게 버블링을 막을 수는 있지만 정말 필요한 경우가 아니라면 가급적 버블링을 막는 일은 피하는것이 좋다. 지금처럼 아이템 영역에서 이벤트 버블링을 막아버리면 바로 위에 있는 리스트 뿐만 아니라 모드 부모요소 입장에서 아이템 영역만큼의 이벤트를 발생시킬 범위가 사라져버리게 된다.

예를들자면 페이지 전체에 어떤 이벤트를 만들고 싶을 경우 document나 body 같은 문서 전체를 다룰 수 있는 상위 요소의이벤트 핸들러를 만들어 줄텐데 버블링이 막혀있는 구간이 존재하게 되면 당연히 클릭 했을 때 버블링이 막혀버리기 때문에 그 부분만 원하는 이벤트 결과를 얻지 못한다. 버블링을 잘 이해하고 이벤트를 설계한다면 버블링을 막을 일이 많지 않다.

+여담

버블링 외에도 캡쳐링(capturing)이라는 흐름이 존재한다. 실제 코드에서 자주 쓰이진 않는다. 캡처링 단계에서 이벤트를 발생시켜야 하는 일은 매우 드물다.

  1. 캡처링 단계 : 이벤트가 하위 요소로 전파되는 단계
  2. 타깃 단계 : 이벤트가 실제 타킷 요소에 전달되는 단계 = 이벤트 객체의 target 프로퍼티가 되는 요소에 등록되어있던 이벤트 핸들러가 동작하는 단계 (가장 처음 이벤트 핸들러가 동작하는 순간)
  3. 버블링 단계 : 이벤트가 상위 요소로 전파되는 단계

캡처링은 이벤트가 발생하면 가장 먼저, 그리고 버블링의 반대방향으로 진행되는 이벤트 전파 방식이다.

이벤트가 발생하면 가장먼저 window 객체에서부터 target까지 이벤트 전파가 일어나고, (캡쳐링) 타겟에 도달하면 등록된 이벤트 핸들러가 동작하고, (타깃) 이후에 다시 window 객체로 이벤트가 전파된다. (버블링)

자주 사용되진 않지만 그럼에도 불구하고 캡처링 단계에서 이벤트 핸들러를 동작해야 된다면? addEventListener에 세번째 프로퍼티에 true 또는 { capture:true }를 전달하면 된다.

for (let elem of document.querySelectorAll('*')) {
  elem.addEventListener("click", e => alert(`캡쳐링 단계: ${elem.tagName}`), true);
  elem.addEventListener("click", e => alert(`버블링 단계: ${elem.tagName}`));
}

5. 이벤트 위임 (Event Delegation)

버블링 개념을 활용하면 자식 요소 각각에 이벤트 핸들러를 하나씩 등록할 필요 없이 부모 요소에서 한 번에 자식 요소들에 발생한 이벤트를 관리할 수도 있다.

이렇게 이벤트를 다루는 방식을 자식 요소의 이벤트를 부모 요소에 위임한다고 해서 이벤트 위임(Event Delegation)이라고 한다.

예시 상황

만약 이미 있는 요소들에 이벤트 핸들러를 다 설정했는데 새로운 아이템을 추가 하는 상황이 발생하게 되면 추가된 아이템에는 이벤트 핸들러가 동작하지 않는다. 그래서 매번 추가할 때마다 이벤트 핸들러를 새로 등록해야 되는 문제가 있다. 이럴땐 이벤트 버블링을 활용하자.

자식요소에서 발생하는 이벤트를 부모요소에서 다루는 방식 = 이벤트 위임

리스트에 이벤트 핸들러를 하나밖에 등록하지 않았지만 아이템 각각의 이벤트 핸들러를 등록한것처럼 동작하고, 나중에 추가한 아이템도 이벤트 핸들러가 잘 동작한다. 하지만 그래도 문제가 한가지 있다. 자식요소를 제외한 온전한 부모요소를 클릭해도 이벤트 핸들러가 동작된다. item(li)영역을 벗어난 list(ul)영역을 선택해도 done class가 toggle되어 이벤트가 발생한다. 그래서 이벤트 위임을 할때는 명확하게 원하는 요소에서 의도한 동작이 일어나게끔 따로 처리를 해줘야한다.

// 이벤트 위임 (Event Delegation)
const list = document.querySelector('#list');

list.addEventListener('click', function(e) {
	// target프로퍼티의 tagName이 LI인지 확인하거나
	// if (e.target.tagName === 'LI')
  if (e.target.classList.contains('item')) {
    //classList의 contains 메소드를 이용해 item이라는 class를 가지고 있는지 확인하면 해결할 수 있다.
    e.target.classList.toggle('done');
  }
});

const li = document.createElement('li');
li.classList.add('item');
li.textContent = '일기 쓰기';
list.append(li);

tagName 프로퍼티는 해당 요소의 태그 이름 값을 대문자로 담고 있는 프로퍼티이고, classList의 contains 메소드는 파라미터로 전달하는 값이 해당 요소의 클래스 속성에 있는지 확인해 불린형태로 결과를 리턴해주는 메소드이다.


6. 브라우저의 기본 동작

브라우저에는 각 태그별 혹은 상황별로 기본적으로 약속된 동작들이 있다.

예를 들어 마우스 오른쪽 버튼을 클릭하면 상황에 맞는 메뉴 창이 뜬다거나, input 태그에 커서를 두고 키보드 키를 누르면 해당 값이 입력된다거나 하는. 만약 이런 동작들을 막고 싶다면 이벤트 객체의 preventDefault 메소드를 통해 막을 수가 있다.

<body>
  <a id="link" href="https://www.codeit.kr/">Link</a>
  <input type="checkbox" id="checkbox">
  <input type="text" id="input" placeholder="input">
  <p id="text">
    I Love Codeit!
  </p>
  <script src="index.js"></script>
</body>
// 브라우저의 기본 동작
const link = document.querySelector('#link');
const checkbox = document.querySelector('#checkbox');
const input = document.querySelector('#input');
const text = document.querySelector('#text');

//event.preventDefault 브라우저의 기본적인 동작을 막는 메소드
link.addEventListener('click', function(e) {
  e.preventDefault(); //링크가 클릭되면 기본동작(href속성 값으로 넘어가는것)이 되지 않고
  alert('지금은 이동할 수 없습니다.'); //경고창을 띄워주세요.
});

//키도브 이벤트로 활용해보기
input.addEventListener('keydown', function(e) {
  if (!checkbox.checked) { //체크박스가 체크되지 않으면
    e.preventDefault(); //input태그에서 키가 입력되지 않게 막아주시고
    alert('체크박스를 먼저 체크해 주세요.'); //경고창을 띄워주세요.
  }
});

//마우스 우클릭 방지해보기, 마우스 우클릭 = contextmenu 라는 타입으로 이벤트가 발생된다.
text.addEventListener('contextmenu', function(e) {
	e.preventDefault(); //다른곳에서는 우클릭이 되지만 텍스트 위에서 우클릭을 할 경우
	alert('마우스 오른쪽 클릭은 사용할 수 없습니다.'); //경고창이 띄워진다.
});

//문서 전체에 우클릭 방지를 하고 싶다면 text를 document로 바꿔주면 된다.

하지만 각 HTML 태그들이 가지고 있는 고유한 역할과 의미를 훼손하게 될 수도 있기 때문에 꼭 필요한 경우에만 주의해서 사용해야 한다.


'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

이벤트 자세히 다뤄보기  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13
객체, 요소, 노드  (1) 2024.12.13
이벤트와 클릭  (0) 2024.12.13

+ Recent posts