JS 튜토리얼 기본 2부

밍글·2023년 4월 12일
0

JS튜토리얼정리

목록 보기
2/10
post-thumbnail

이번에는 연산자 일부와 반복문, 함수, 거기에 객체까지 다룰 예정이다. 계획으로는 10부까지 작성을 하는 걸 목표로 정해뒀는데 1부의 양을 보니 턱없이 부족하다는 생각이 들어 객체까지 다룰 수 있으면 일부 다룰 예정이다.😎 다음에는 객체 나머지 부분과 객체일부와 자료구조와 자료형에 대해서 다룰 예정이다.


nullsigh 병합 연산자

nullish 병합 연산자인 '??'를 사용하면 그 값이 확정되어있는 변수를 찾을 수 있다.

예를 들어 a ?? b는 다음과 같은 의미를 지닌다.

  • a가 null이나 undefined가 아니면 a를 반환한다.
  • 그 이외의 경우는 b를 반환한다.

만약 '??'연산자 없이 동일한 동작을 하는 코드를 작성하려면 다음과 같이 작성해야 한다.

(a !== null && a !== undefined) ? a : b;

'??'를 통해 코드 길이를 간결하게 만들 수 있다. 또한 삼항연산자처럼 여러 번 사용할 수 있다. 그 예제는 다음과 같다.

let firstName = null;
let lastName = null;
let nickName = "삐약이";

alert(firstName ?? lastName ?? nickName ?? "익명의 사용자"); // 삐약이
//만약 nickName에도 값이 없다면 "익명의 사용자"가 출력된다.

⚠️주의⚠️
??는 웬만하면 &&나 ||와 함께 사용할 수 없다. ➡️ 이 뜻은 무조건 사용할 수 없다는 것이 아닌 괄호를 꼭 써야한다는 의미이다.
let x = (1 && 2) ?? 3; 예를 들면 이런식으로 사용하는 건 괜찮지만 괄호없이 사용하면 에러가 일어난다.

반복문

반복문에는 다른 언어들과 마찬가지로 while, do while, for문이 있다. break랑 continue라는 지시자도 있는데 이런 거는 다른 언어에서도 많이 다루기 때문에 인라인 변수 선언만 다루고 넘어가도록 하겠다.

인라인 변수 선언

변수를 반복문 안에서만 선언하는 방식을 '인라인' 변수 선언이라고 부른다. 이렇게 선언한 변수는 반복문 안에서만 접근 가능하다. 예시 코드들은 다음과 같다.

for (let i = 0; i < 5; i++) {
  alert(i); // 0, 1, 2, 3, 4
}
alert(i); // 접근이 불가능하므로 에러가 발생하게 된다

let j = 0;

for (j = 0; j < 5; j++) { // 기존에 정의된 변수 사용
  alert(j); // 0, 1, 2, 3, 4
}

alert(j); // 5, 반복문 밖에서 선언한 변수이므로 사용할 수 있다

번외 : 조건문➡️switch- case문

이거는 원래는 조건문을 다룰 때 자주 나오지만 if문에 밀리기 때문에 자주 사용하지 않는다. 그러나 최근에 switch문을 사용하였는데 여러 개의 "case"문을 묶는거에서 많이 헤맸기 때문에 정리하고자 한다. 결과값을 값게 할 case문은 한데 묶을 수 있는데 다음과 같이 묶을 수 있다. 또한 switch문은 일치 비교로 조건을 확인한다.

let a = prompt("값을 입력해주세요.");
switch (a) {
  case '0':
  case '1':
    //이런식으로 case문을 한데 묶어줄 수 있다.
    alert( '0이나 1을 입력하셨습니다.' );
    break;

  case '2':
    alert( '2를 입력하셨습니다.' );
    break;

  case 3:
    alert( '3을 입력하셨습니다.' ); 
	/*만약 3을 입력할 경우 숫자형이 아닌 문자열 '3'을 변환하여 반환하기 때문에 
    이 부분이 아닌 default로 넘어가게 된다.*/
    break;
  default:
    alert( '알 수 없는 값을 입력하셨습니다.' );
}

함수

함수에 대해서는 다른 언어를 다뤄봤다면 잘 알 수도 있다. 그렇기 때문에 여기서는 정의나 선언 방식보다는 함수 요소에 집중하여 정리할 것이다.

지역 변수, 외부 변수, 매개변수, 반환값

  • 지역 변수 : 함수 내에서 선언한 변수로써 함수 안에서만 접근 가능하다.
  • 외부 변수 : 전역 변수라고도 하며, 함수 외부의 변수를 의미한다. 외부 변수는 해당 함수가 아니더라도 모든 함수에서 접근하여 사용할 수 있다.
  • 매개변수 : 임의의 데이터를 함수 안에 전달할 수 있는 변수이다. 매개변수는 다음과 같이 전달할 수 있다.
    함수 호출 시 매개변수에 인수를 전달하지 않으면 그 값은 undefined가 된다. 인수를 전달하지 않을 때 undefined가 아닌 다른 값을 호출하고 싶다면 기본값을 설정해주면 된다.
function showMessage(name, text) { // 인자: name, text
  alert(name + ': ' + text);
}
showMessage('John', 'Hello!'); // John: Hello!
shoeMessage('Tony'); // Tony: undefined
//..............기본 값 전달 예시..............
function showMessage2(name, text = "nothing") {
  alert( name + ": " + text );
}
showMessage2("Ann"); // Ann: nothing
  • 반환값 : return을 통해서 반환할 수 있다. return문이 없거나 return 지시자만 있는 함수는 undefined를 반환하게 된다. return문을 여러 줄을 작성하고 싶다면 return()을 이용해서 하면 된다. 이 때 ()부분을 줄바꿈해서는 안된다!

함수 표현식 Part

콜백 함수

먼저 코드를 보고 이해하면 될 것이다. 매개변수가 3개 있는 함수, ask는 질문에 따라 yes나 no형태의 버튼을 눌렀을 때 실행되는 함수를 호출하는 구조를 가지고 있다.

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

function showOk() {
  alert( "동의하셨습니다." );
}

function showCancel() {
  alert( "취소 버튼을 누르셨습니다." );
}

// 사용법: 함수 showOk와 showCancel가 ask 함수의 인수로 전달됨
ask("동의하십니까?", showOk, showCancel);

함수 ask의 인수, showOk와 showCancel은 콜백 함수 또는 콜백이라고 불린다.

함수를 함수의 인수로 전달하고, 필요하다면 인수로 전달한 그 함수를 "나중에 호출(called back)"하는 것이 콜백 함수의 개념이다. 위 예시에선 사용자가 "yes"라고 대답한 경우 showOk가 콜백이 되고, "no"라고 대답한 경우 showCancel가 콜백이 된다.

식 vs 문

함수 표현'식'과 함수 선언'문'의 차이는 명확히 존재하기 때문에 정리하도록 하겠다.

1️⃣ 표현 형태가 다르다

문 : 함수가 독자적인 구문 형태로 존재한다. 여태까지 보여줬던 예시가 선언문이라고 보면 된다.
식 : 함수는 표현식이나 구문 구성 내부에 생성된다. 예를 들면 다음과 같다.

let sum = function(a, b) {
  return a + b;
};

2️⃣ 함수 선언문은 함수 선언문이 정의되기 전에도 호출할 수 있다.
var과 비슷하게 호이스팅 현상으로 함수선언을 위로 끌어올린다. 또한 이렇게 될 경우 나중에 sayHi에 대한 함수를 적었어도 스크립트 실행 준비 단계에서 생성되기 때문에, 스크립트 내 어디에서든 접근할 수 있게 된다.
그러나 함수 표현식으로 정의한 함수는 함수가 선언되기 전에 접근하는 게 불가능하다. 설령 불러냈어도 값을 반환하지는 않는다. 그 예시는 다음과 같다.

sayHi("John"); // Hello, John

function sayHi(name) {
 alert( `Hello, ${name}` );
}
//.......함수 선언식 방식..........
sayHello("John"); //Hello, John이 alert로 뜨지 않는다.

let sayHello = function(name) {  
 alert( `Hello, ${name}` );
};

3️⃣ 함수 선언문이 코드 블록 내에 위치하면 블록 밖에서는 함수에 접근하지 못한다.

let age = prompt("나이를 알려주세요.", 18);

// 조건에 따라 함수를 선언함
if (age < 18) {

  function welcome() {
    alert("안녕!");
  }

} else {

  function welcome() {
    alert("안녕하세요!");
  }

}

// 함수를 나중에 호출하면 접근할 수 없어서 에러를 일으킨다
welcome(); // Error: welcome is not defined

그러나 표현식을 이용하면 블록 밖에서도 함수에 접근할 수 있다.

let age = prompt("나이를 알려주세요.", 18);

let welcome;

if (age < 18) {

  welcome = function() {
    alert("안녕!");
  };

} else {

  welcome = function() {
    alert("안녕하세요!");
  };

}

welcome(); // 제대로 동작하게 된다

화살표 함수

함수 표현식을 좀 더 간결하게 만들 수 있는 방법이다. 기본 구조는 다음과 같다.

let func = (arg1, arg2, ...argN) => {expression; return result;}
중괄호를 없이 작성할 수 있지만 이럴 때는 expression부분이 한 줄일 때 가능하다. 이럴 경우에는 return값을 굳이 해주지 않고 expression만 가지고 할 수 있다.
인수가 없다면 let func = () => 형태로 할 수 있다.

객체

객체는 키(key)와 값(value)로 구성된 프로퍼티의 집합이다. 이런식으로 구성되어 있다고 생각하면 된다.

빈 객체를 만드는 방법은 다음과 같다.

1️⃣let user = new Object(); // '객체 생성자' 문법
2️⃣let user = {}; // '객체 리터럴' 문법

🚨그 중 '객체 리터럴'문법 방식에서 설명하도록 하겠다.

객체를 만드는 방법

let user = {     // 객체
  name: "John",  // 키: "name",  값: "John"
  age: 30        // 키: "age", 값: 30
};
alert(user.name); //John
alert(user.age); //30
user.hobby = "hiking"; //hobby 프로퍼티 추가
delete user.hobby; //프로퍼티 삭제
alert( "age" in user ); // true ➡️ 해당 프로퍼티가 존재하기 때문

⭐️만약 여러 단어를 조합해 만든 프로퍼티 이름인 경우
let user = {
name: "John",
age: 30,
"likes birds": true // 복수의 단어는 따옴표로 묶어야 한다.
};
호출도 alert(user["likes birds"]);로 해주어야 한다. delete 또한 마찬가지이다.
⭐️prompt로 객체 프로퍼티에 접근하고 싶을 경우
let key = prompt("사용자의 어떤 정보를 얻고 싶으신가요?", "name");
alert( user[key] ); ➡️ 여러 단어 조합한 프로퍼티 방식처럼 하기
⭐️프로퍼티를 나열할 경우
for (let key in obj)

프로퍼티 값을 기존 변수에서 받아와 사용하는 경우

function makeUser(name, age) {
  return {
    name: name,
    age: age,
    // ...등등
  };
}

let user = makeUser("John", 30);
alert(user.name); // John

객체 정렬 방식

여기가 조금 복잡한데 정리를 잘해도록 하겠다. 프로퍼티를 for..in으로 호출을 할 때 정수 프로퍼티(interger property)는 자동으로 오름차순으로 정렬되고 그 외의 프로퍼티는 객체에 추가한 순서 그대로 정렬된다.

let codes = {
  "49": "독일",
  "41": "스위스",
  "44": "영국",
  "1": "미국"
};

for (let code in codes) {
  console.log(code); // 1, 41, 44, 49 순으로 콘솔에 나온다
}

'정수 프로퍼티’라는 용어는 변형 없이 정수에서 왔다 갔다 할 수 있는 문자열을 의미한다.

이를 해결하기 위해서는 정수 프로퍼티를 적용시키지 않도록 앞에 '+'를 붙여줘야 한다.

let codes = {
  "+49": "독일",
  "+41": "스위스",
  "+44": "영국",
  // ..,
  "+1": "미국"
};

for (let code in codes) {
  console.log( +code ); // 49, 41, 44, 1 순으로 콘솔에 나온다
}

참조에 의한 객체 복사

객체와 원시 타입의 근본적인 차이 중 하나는 객체는 ‘참조에 의해(by reference)’ 저장되고 복사된다는 것!

원시값은 서로 다른 메모리 공간에 저장되기 때문에 어느 한쪽에서
재할당을 해도 서로 간섭할 수 없다. 하지만 객체는
같은 메모리 공간을 가리키기 때문에 값에 대한 변경이 가능하다. 객체 비교 시 동등 연산자 ==와 일치 연산자 ===는 동일하게 동작하게 된다.

let user = { name: "John" };

let admin = user; // 참조값을 복사함

변수는 두 개이지만 각 변수엔 동일 객체에 대한 참조 값이 저장이 되는 형식이다. 아래는 값에 대한 변경을 보여주고 있다.

let user = { name: 'John' };

let admin = user;

admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨

alert(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인

객체를 복제하고 싶다면? Object.assign

구문 : Object.assign(target, ...sources)
target : 목표 객체. 출처 객체의 속성을 복사해 반영한 후 반환할 객체
sources : 출처 객체. 목표 객체에 반영하고자 하는 속성들을 갖고 있는 객체들
return 값은 target이 된다.

let user = { name: "John" };

let test1 = { canTest: true };
let test2 = { canOtherTest: true };

// test1과 test2의 프로퍼티를 user로 복사
Object.assign(user, test1, test2);

// user = { name: "John", canTest: true, canOtherTest: true }

//덮어씌워지기도 가능하다
let user2 = { name: "John" };

Object.assign(user2, { name: "Pete" });

alert(user2.name); // user2 = { name: "Pete" }

//이런식으로 간단하게 복사가 가능하다
let user3 = {
  name: "John",
  age: 30
};

let clone = Object.assign({}, user3);

‼️다만, 이 방식은 얕은 복사방식이다. 얕은 복사본은 중첩 객체를 처리하지 못한다는 점을 기억해 두시기 바란다.‼️
이 문제를 해결하려면 user[key]의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용해야 하는 깊은 복사를 해야 한다. 알고리즘을 구현하는 방법도 있지만 그것보다는 이 JS lodash 라이브러리에서 _.cloneDeep(value)을 이용하면 더 수월할 것이다.

profile
예비 초보 개발자의 프로젝트와 공부 기록일지

0개의 댓글