본문 바로가기

Javascript/Javascript(2022 version)

Javascript 기초(4) - 함수 및 객체

728x90
반응형

1) Javascript 함수

   1-1) 함수 복습

   1-2) 함수 표현식

   1-3) 화살표 함수(arrow function)

   1-4) 콜백 함수(callback function)

   1-5) 콜스택 / 콜백 함수 활용 예시

2) Javascript 객체

   2-1) 객체 생성 방법

   2-2) 점 표기법 / 응용 방법

   2-3) in 연산자 / for in 문

   2-4) Spread Operator(스프레드 연산자)

   2-5) 참조형 데이터 타입 활용 예시

 

 

 

 

 

1) Javascript 함수

1-1) 함수 복습

// 복습
// 함수 선언
/*
	함수 호이스팅 : 함수 호출 구문이 함수 선언문보다 위에 있어도 코드가 정상 실행됨
	(함수 선언문이 최상단으로 끌어올려저서 코드 형태 상에서는
	호출 구문이 상단에 있는 것처럼 보여도 내부에서는 함수 선언문이 먼저 최상단에서 처리된 뒤
	함수 호출을 처리함!!)
*/
function showMessage(name, age) {
  // code block
  return undefined;
}

// 함수 호출
showMessage();

// 인수(argument) - 함수 호출 구문에 명시
showMessage("sangbeom", 27);

// (참고) var 호이스팅
console.log(num); // undefined
var num = 10;

// 위의 코드는 var 호이스팅이 발생하여 아래와 같이 동작함!!
var num;
console.log(num); // undefined
num = 10;

// 콜스택 이해를 위한 예시
function ingoo() {
  console.log(3);
  return 4;
}

function goak() {
  console.log(1);
  return ingoo();
}

function hello() {
  goak();
  console.log(5);
  ingoo();
  return 10;
}

const result = hello();
const answer = goak();

console.log(result);

/*
	출력 결과
	1
	3
	5
	3
	1
	3
	10
*/

// 자바스크립트는 코드를 실행하기 전에 콜스택의 익명함수가 전체 코드를 한 줄 한 줄 훑어본 뒤에 코드를 한 줄 한 줄 실행함!!(코드 실행이 전부 완료되면 익명함수 또한 콜스택에서 사라짐)
// 함수는 return을 만나야 비로소 종료됨!! >> return : 함수를 종결시키는 역할

 

 

1-2) 함수 표현식

// 함수 선언문
function showMessage() {}

// 함수 표현식 >> 함수 호출 시 "변수명();" 형태로 호출하기에 함수명을 따로 지정할 필요가 없어 일반적으로 함수 표현식은 익명함수로 생성함!!
const showMessage2 = function() {};

// 함수 표현식은 함수 선언문과 달리 호이스팅이 발생하지 않음!!
// 단, var 예약어를 사용해 함수 표현식을 정의한 경우에는 var 호이스팅이 발생하여 함수 호출을 먼저 쓰는 경우에는 변수명만 선언되어 함수 선언부를 인식하지 못하여 코드 실행 시 함수를 찾을 수 없다는 에러가 발생함!!
// 대입 연산자를 사용한 함수(함수 표현식)도 무언가의 값이다!!
// 함수 표현식은 별도로 함수명을 기재할 필요 없는 익명함수 형태로 많이 사용됨!! >> 함수 호출 시 함수명이 아닌 "변수명();"으로 호출하기 때문에 익명함수로 선언해도 호출 시 문제되지 않음!!

// 즉시 실행 함수 : 함수를 선언하자마자 바로 호출하여 함수를 실행함!!
(function () {
  console.log("hello world");
})();

showMessage3(); // (중요) 함수 표현식은 호이스팅이 발생하지 않아 함수 호출을 선언보다 먼저 할 시 에러가 발생함
const showMessage3 = function () {
  console.log("good morning");
};


// 함수 선언문
function showMessage4() {
  console.log("hello func");
}

// 함수를 호출하지 않고 함수 이름만 console.log() 찍었을 시 함수 자체가 값으로 찍힘!!
console.log(showMessage4);

// 새로운 변수를 만들고 해당 변수에 함수명을 넣으면 함수가 정상 실행됨
const fun = showMessage4;
fun();

// 함수도 값이기에 변수에 할당이 가능함!!(like 함수 표현식)
const showMessage5 = function() {};

// (매우 중요)함수도 값이다!!

 

 

1-3) 화살표 함수(arrow function)

// 화살표 함수
// 함수 표현식을 좀 더 간결한 문법으로 만드는 방법
// ES6에서 추가된 문법으로 '화살표 함수(arrow function)'라고 함!!

// 문법
// 함수 표현식
const sum = function (a, b) {
  return a + b;
};

// 위의 함수 표현식 코드와 동일한 코드를 화살표 함수로 변형하여 나타냄
// 화살표 함수
const sum2 = (a, b) => {
  return a + b;
};

// 본문이 1줄인 경우, 중괄호와 return을 생략하고 한 줄에 이어서 쓸 수 있음!!
const sum3 = (a, b) => a + b;

// 매개변수가 1개일 경우, "()" 생략 가능함!!
const sum4 = (a) => a + 1;
// const sum5 = a => a + 1; // 이것도 오류 없이 정상 실행되지만 많은 개발자들이 소괄호와 중괄호를 사용하여 코드를 작성함(일종의 syntax에 해당)

 

 

1-4) 콜백 함수(callback function)

// (매우 중요)함수도 값이다!!

// callback 함수 : 함수도 값이기에 매개변수에도 함수를 넣을 수 있음!!
// callback 함수 예시
function hello(fn) {
  console.log(fn());
}

function print() {
  return 10;
}

hello(print); // 출력 결과 : 10

// 매개변수(parameter)의 값은 함수 자체의 값이고, 'hello' 함수 호출 시 '인수값(argument)'으로 함수값을 전달함!!
// 'hello' 함수 내부에서 'print' 함수를 호출함
// 이 내용을 이해하기 위해서는 "함수도 값이다"라는 사실을 반드시 유념해야 함!!

// callback 함수 내에서도 인수값(argument)을 넣을 수 있다!
function hello2(fn) {
  let sangbeom = "javascript";
  console.log(fn(sangbeom));
}

function print2(name) {
  return 10 + name;
}

hello2(print2); // 출력 결과 : 10javascript


/*
	함수 선언문과 함수 표현식의 차이점
	- 함수 표현식의 장점 : "호이스팅" 해결
	- 함수 표현식의 단점 : 가독성 이슈
*/


function ingoo(callback) {
  return callback;
}

function goak(callback) {
  const fn = function () {
    return 30;
  };
  const result = 1 + callback(fn);
  return result;
}

function getNumber(callback) {
  return 2 * callback();
}

console.log(goak(getNumber)); // 출력 결과 : 61

// 위의 함수들을 화살표 함수로 바꿔서 표현한 것
const ingoo = (callback) => callback
const goak = (callback) => 1 + callback(() => 30)
const getNumber = (callback) => 2 * callback()

 

 

1-5) 콜스택 / 콜백 함수 활용 예시

function goak(callback) {
  const fn = function () {
    console.log("1");
    return 30;
  };
  const result = 1 + callback(fn);
  console.log("2");
  return result;
}

function getNumber(callback) {
  console.log("3");
  return 2 * callback();
}

console.log(goak(getNumber));
console.log(getNumber(() => 60));

/*
	출력 결과
	3
	1
	2
	61
	3
	120
*/

 

 

 

 

2) Javascript 객체

2-1) 객체 생성 방법

// 객체는 자바스크립트를 잘 다루기 위해 꼭 알아야 할 '데이터'이다!
// '데이터 타입'은 크게 '원시형'과 '객체형'의 2가지로 분류됨!

/*
1) 원시형
    - 문자형
    - 숫자형
    - boolean
    - undefined
    - null
2) 객체형(참조형)
    - 배열
    - 객체
    ...
*/

// 객체를 만드는 방법(문법)
// 1. 객체 생성자 문법 : new 키워드를 사용하여 객체를 생성하는 방법
const user = new Object();
console.log(user); // 출력 결과: {}

// 2. 객체 리터럴 문법 : "{}"를 활용하여 객체를 생성하는 방법
const user2 = {};
console.log(user2); // 출력 결과: {}

// 변수 하나에 여러가지 데이터를 담고싶을 때 사용하는 것이 "배열"이다! >> 어떤 특정 리스트를 구현할 때 좋음
// 변수 하나에 여러가지 데이터를 담고싶을 때 사용하는 것이 "객체"이다! >> 어떤 특정 사람 혹은 사물의 상세정보를 입력할 때 좋음

const students = ["곽인구", "강찬수", "김건영", "김성희", "김주형"];
// 배열에 학생 리스트를 담는다면, 총 학생 숫자도 구하기 쉽고, 하나의 변수에 배열로 담다보니 관리가 용이하다!
// 하지만 배열의 경우, 학생 한 명의 정보를 저장하는 데에는 한계가 있다.

// 학생 한 명의 정보를 저장하기 위해서 사용하는 '데이터 타입'이 바로 '객체'이다!!
// 객체는 사람, 주문, 에어컨 등과 같이 실제 존재하는 개체를 표현할 때 적합하다!
const Person = {
  name: "곽인구",
  age: 32,
  "content-type": "text/javascript",
};

console.log(Person);

// 객체는 우리가 앞서 배웠던 CSS 문법과 굉장히 유사하다!!
// 변수 'Person' 안에서 'name' key에는 '곽인구'라는 value를 입력하고,
// 'age' key에는 32라는 value를 입력함!!

// '데이터 타입'에는 어떠한 것이든 들어올 수 있음!!
// >> String, Number, Boolean, Function, Array, Object

 

 

2-2) 점 표기법 / 응용 방법

// 객체 또한 변수 하나에 여러가지 값이 있다보니, 특정 값만 뽑아올 수 있어야 함!

// 객체 안에 있는 'name'을 console.log();를 이용해 콘솔에 찍고 싶다면
// 아래와 같이 코드를 실행(점 표기법 활용)하면 됨!!
console.log(Person.name); // 점 표기법

// '점 표기법' 예시 >> 실행은 안 됨!!(console.log();가 이미 있기 때문에 syntax error가 발생함)
// const console = {
//     log: function() {
//         console.log('hello world')
//     }
// }
// console.log()

// key 부분에 특수문자(-, \, ...)가 들어간 경우가 있음
// >> 이 경우, 양쪽 끝에 "", ''을 넣음(대부분은 ""를 씀)

// 만약 key에 특수문자가 들어갈 경우, value를 뽑고 싶다면 '점 표기법'이 아닌 '대괄호 표기법'을 사용해야 함!!
console.log(Person["content-type"]);

// 응용 방법 >> 임의의 변수를 하나 만든 다음에 "content-type" 내용을 해당 변수에 넣어주어도 됨!
let contentType = "content-type";
console.log(Person[contentType]);


// 객체는 '점 표기법'을 이용하여 key, value를 추가할 수 있음!!
Person2.height = 180;
console.log(Person2);

// 객체는 '점 표기법'을 이용하여 value를 수정할 수 있음!!
Person2.height = 170;
console.log(Person2);

 

 

2-3) in 연산자 / for in 문

// 목적 : 객체 안에 있는 'key'를 알아내는 방법 >> 'in 연산자'
// 'in 연산자'를 사용해서 특정 객체에 해당 key가 있는지 없는지 검사한 후 그 결과를 boolean 값(true / false)으로 반환함

const Person2 = {
  name: "곽인구",
  age: 32,
  "content-type": "text/javascript",
};

console.log("name" in Person2); // true
console.log("subname" in Person2); // false


// for in 문 : 객체 내용을 반복문을 통해 안에 있는 정보를 다 보고 싶을 때 사용함
for (let key in Person2) {
  console.log(key); // 해당 객체의 key 값 출력
  console.log(Person2[key]); // 해당 객체의 value 출력
}


// 객체는 참조형 데이터 타입에 해당하기 때문에 value에 메모리 주소를 할당 시 실제 저장되어 있는 다른 공간의 메모리 주소를 입력하여 참조하도록 함!!
console.log({} === {}); // false(같지 않다!!) >> 서로 다른 주소값을 참조하고 있기 때문에 다르다!!

const obj = {};
const clone = obj;
console.log(clone === obj); // true(같다!!) >> 대입연산자를 통해 각각의 값에 대한 주소값을 그대로 할당했기에 같다!!

const clone2 = {};

const obj2 = {
  name: "sang",
  age: 27,
};

for (let key in obj2) {
  clone2[key] = obj2[key];
}

console.log(clone2);
console.log(clone2 === obj2); // false(같지 않음)
/*
	>> clone2는 앞서 이미 객체 리터럴 방식("{}"를 이용해 객체 생성)을 통해
	객체를 새로 생성한 상태에서 for in 문을 활용해
	각각의 key에 따른 value를 obj2에서 주소값을 가져와서 그대로 할당했기 때문에
	각 key와 value들이 동일한 주소값을 참조할지라도 clone2라는 객체가 참조하는 주소값은
	앞서 언급한대로 이미 객체 리터럴 방식을 이용해 객체를 새로 생성한 시점부터 이미 obj2와는
	다른 주소값을 참조하고 있기에 비교 결과는 'false'가 출력됨!!
*/

 

 

2-4) Spread Operator(스프레드 연산자)

// Spread Operator(스프레드 연산자)
// 객체 리터럴 문법에서 안에 있는 key, value 쌍들에 대해 각각의 주소값을 그대로 가져와 참조하도록 함!!

const obj2 = {
  name: "sang",
  age: 27,
};

const clone3 = { ...obj2 };
console.log(clone3);

console.log(clone3 === obj2); // false
// >> 이유: 객체는 참조형(객체형) 데이터 타입에 해당하므로 스프레드 연산자를 사용해 데이터를
// 가져올 시 key와 value 자체가 아닌 각각의 key와 value들에 대한 주소값을 가져오는 것이기에
// 위의 코드의 경우, 스프레드 연산자를 쓰기 전에 먼저 "{}"를 사용해 새로운 객체를 생성한 뒤
// 스프레드 연산자를 통해 name, age key들에 대해 각각의 주소값을 가져와서 앞서 새로 생성한
// 객체에 넣어주었기에 데이터를 가져오는 obj2 객체와 clone3 객체는 참조하는 주소값이 달라지기에
// 결과적으로 'false'를 결과값으로 반환함!!

console.log(clone3.name === obj2.name); // true
// >> 이유: 객체는 앞서 살펴본 바와 같이 참조형(객체형) 데이터 타입에 해당하므로
// 스프레드 연산자를 사용해 데이터를 가져올 시 key와 value 자체가 아닌 각각의 key와 value들에 대한
// 주소값을 가져오는 것이기에 위의 코드의 경우, 스프레드 연산자를 쓰기 전에
// 먼저 "{}"를 사용해 새로운 객체를 생성한 뒤
// 스프레드 연산자를 통해 name, age key들에 대해 각각의 주소값을 가져와서 앞서 새로 생성한
// 객체에 넣어주었기에 데이터를 가져오는 obj2 객체의 각 key, value들과
// clone3 객체의 각 key와 value들은 동일한 주소값을 참조하게 되기 때문에
// 결과적으로 'true'를 결과값으로 반환함!!

 

 

2-5) 참조형 데이터 타입 활용 예시

// 참조형 데이터 타입 이해를 위한 예시
const obj3 = {
  name: "sang",
  age: 27,
  sizes: {
    height: 180,
    weight: 80,
  },
};

// const clone4 = { ...obj3, sizes: { ...obj3.sizes } };
// console.log(obj3.sizes === clone4.sizes); // false
const clone4 = { ...obj3 };
console.log(obj3.sizes === clone4.sizes); // true
console.log(obj3 === clone4); // false

clone4.sizes.height = 190;
console.log(clone4.sizes.height);
console.log(obj3.sizes.height);


/*
	코드 설명
	우선 obj3의 경우, 데이터 타입이 객체이기에 참조형에 해당하므로
	const 예약어를 통해 clone4를 선언하고 값으로 "{ ...obj3, sizes: { ...obj3.sizes } }"을
	할당하면 먼저 스프레드 문법을 적용하여 앞서 정의한 obj3의 객체 리터럴 문법을 해제("{}"를 벗겨낸다는 의미)하고
	각 key, value 쌍들을 clone4 객체에 넣어준다.
	(여기까지 하고 두 객체를 비교하면 두 객체는 동일한 주소값을 참조하고 있기에 비교 시 같다고 확인 가능합니다.)
	이때 "sizes" key 같은 경우, 앞서 obj3 전체를 스프레드 문법을 적용하여 푼 다음 객체 안의 값들을
	그대로 clone4에 넣어준 뒤 해당 key에 대해 새로운 객체를 생성한다. 그리고 기존 obj3의 sizes key 안의 value들을
	스프레드 문법으로 해제하여 해당 값을 새로운 주소값을 가진 sizes key의 value를 key, value 쌍들로 채운다.
	이렇게 되면 "obj3.sizes === clone4.sizes"가 값 자체는 동일하지만 서로 다른 주소값을 참조하고 있기 때문에
	비교 결과를 boolean 값 중 false로 출력한다. 이것은 객체가 원시타입이 아닌 참조형 데이터 타입이기 때문이다.
	이어서 아래의 "clone4.sizes.height = 190;"를 실행시켜 clone4의 'sizes' key 내부의 'height' key를
	190으로 재할당하여 값을 수정하면,
	console.log(clone4.sizes.height);는 수정된 값인 190이 콘솔에 출력되고,
	console.log(obj3.sizes.height);는 애초에 obj3와 clone4의 참조값이 다르기에 obj3의
	height key의 value인 180이 그대로 유지되고 출력된다.
	만일 앞서 const 예약어를 사용해 clone4를 선언하고 값을 할당할 시 기존 값인
    "{ ...obj3, sizes: { ...obj3.sizes } }"에서 "sizes: { ...obj3.sizes }"를 빼고
    단순히 obj3 내부의 key, value 쌍들을 스프레드 문법을 통해 중괄호를 벗겨서
	해당 값들을 그대로 넣어준다면
	"obj3.sizes === clone4.sizes"는 clone4의 sizes key 값은
    obj3의 key 값을 그대로 참조하기에 결과값이
	true로 나온다.
	이어서 "clone4.sizes.height = 190;"은 clone4와 obj3 중
    어느 하나의 sizes key 내부의 height key 값을 재할당하여
	수정하면 양쪽에 수정사항이 반영되어 동일하게 190으로 출력된다.
*/


// 대부분 자바스크립트 코드 작성 시 참조형 데이터 타입(객체, 배열)은 const로 선언함!!
// (참고) 원시 데이터 기준으로는 한 메모리 안에 하나의 값이 할당됨!!