6일차 - Javascript (원시 자료형과 참조 자료형, 얕은 복사와 깊은 복사, 스코프, ES6 신규 문법)

Yohan·2024년 2월 27일
0

코딩기록

목록 보기
6/156
post-custom-banner

원시 자료형과 참조 자료형

원시 자료형

  • 6개의 자료형(number, string, boolean, undefined, null, symbol)
  • 원시 자료형을 변수에 할당 -> 메모리 공간에 자체가 저장

참조 자료형

  • 원시 자료형이 아닌 모든 자료형
  • 배열, 객체가 대표적인 참조 자료형, 함수도 참조 자료형 !
  • 참조 자료형을 변수에 할당 -> 메모리 공간에 주소값이 저장
  • 참조 자료형 저장 되는 과정
    1. 저장 공간에 참조 자료형을 저장
    2. 그 저장공간을 참조할 수 있는 주소값을 변수에 저장
      (참조 자료형을 저장하는 특별한 저장 공간을 힙(heap)이라고 함)
    3. 변수 arr에 해당하는 저장공간에는 주소값이 저장되어 있고, 그 주소값을 통해서 참조 자료형에 접근가능 이를 참조한다(refer)고 합니다.
// 원시 자료형이 참조된 변수를 다른 변수에 할당
let num = 20;
let copiedNum = num;

// 참조 자료형이 할당된 변수를 다른 변수에 할당하기
let arr = [0, 1, 2, 3];
let copiedArr = arr;

// 두 변수가 같은지 확인하기 - 1
console.log(num === copiedNum); // true
console.log(arr === copiedArr); // true

// 원본을 변경하기
num = 30;
arr.push(4);

// 두 변수가 같은지 확인하기 - 2
console.log(num === copiedNum); // false
console.log(arr === copiedArr); // true

// 이 두 변수의 현재 상태는?
console.log(copiedNum); // 20
console.log(copiedArr); // [0, 1, 2, 3, 4]

얕은 복사

  • 주소값만 복사 (얕은복사를 하게되면 변경시 원본과 복사본 둘 다 바뀜 - 참조하는 주소값이 똑같기 때문에)
let y = x;

깊은 복사 (배열, 객체)

  • 깊은 복사를 통해 다른 주소값으로 저장 가능(원본과 복사본 중에 하나만 수정 가능)

slice( )

  • slice()를 통해 배열을 복사하여 heap에 다른 주소값으로 저장 가능
let arr = [0, 1, 2, 3];
let copiedArr = arr.slice();

copiedArr.push(4);

console.log(arr); // [0, 1, 2, 3]
console.log(copiedArr); // [0, 1, 2, 3, 4] 
						// 주소가 다르기 때문에 원본배열에는 4가 추가되지 않음

Object.assign()

let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = Object.assign({}, obj);

console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false (주소값이 다르기 때문)

JSON.stringify()와 JSON.parse()

  • JSON.stringify()는 참조 자료형을 문자열 형태로 변환하여 반환JSON.parse()는 문자열의 형태를 객체로 변환하여 반환
    먼저 중첩된 참조 자료형을 JSON.stringify()를 사용하여 문자열의 형태로 변환하고, 반환된 값에 다시 JSON.parse()를 사용하면, 깊은 복사와 같은 결과물을 반환합니다
const arr = [1, 2, [3, 4]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr); // false
console.log(arr[2] === copiedArr[2]); // false
  • 실습
// console.log("code" === "code");
// console.log(3.14 === 3.14);
// console.log([1, 2, 3] === [1, 2, 3]);
// console.log({ foo: "bar" } === { foo: "bar" });

// let x = { foo: 3 };
// let y = x;
// y = 2; // 위 주소값을 없애고 원시 자료형이 새로 할당
// console.log(y);

// let myArr = [2, 3, 4, 5];
// let ourArr = myArr;
// ourArr[2] = 25;
// ourArr = undefined; // 주소값에 undefined, 배열엔 영향 x

let player = { score: 3 };

function doStuff(obj) {
  obj.score = 2;
}
doStuff(player);
console.log(player.score);

스코프 (가장 중요)

  • 바깥쪽 스코프에서 선언한 변수는 안쪽 스코프에서 사용 가능 <->
  • 안쪽 스코프에서 선언한 변수는 바깥쪽 스코프에서 사용 불가
  • 본인포함 본인보다 바깥쪽에 있는 스코프로만 접근 가능
// 밖에서 `let message;`해줄 경우에는 `'Hello kimcoding'` 출력
let username = "kimcoding";
// let message;
if (true) {
  message = `Hello, ${username}!`;
  console.log(message); // 'Hello kimcoding'
}
console.log(message); // ReferenceError
  • 지역변수는 전역변수보다 더 우선순위
// 지역 변수 name을 선언하고 초기화하기 때문에 함수 밖에서는 효력x
let name = "김코딩";

function showName() {
  let name = "박해커"; // 지역변수 
  console.log(name);
}

console.log(name); // ?
showName(); // ?
console.log(name); // ?

----------------------------------------
//  외부에 있는 전역 변수 name을 직접 변경하기때문에 함수 이후에 출력되는 name은 모두 "박해커"로 변경
let name = "김코딩";

function showName() {
  let name = "박해커";
  console.log(name);
}

console.log(name); // ?
showName(); // ?
console.log(name); // ?
  • var
    • 호이스팅

클로저

  • 외부 함수의 변수에 접근할 수 있는 내부 함수
    1. 함수 outerFn에서는 변수 globalVar에 접근할 수 있습니다.
  1. 함수 innerFn에서는 변수 globalVar와 함수 outerFn 내부의 outerFnVar에 접근할 수 있습니다.

즉, 위 코드에서 클로저는 두 조합을 찾을 수 있었습니다.

  1. 함수 outerFnouterFn에서 접근할 수 있는 globalVar
  2. 함수 innerFninnerFn에서 접근할 수 있는 globalVar, outerFnVar
    -> 스코프의 성질 생각!
  • innerFn()을 반환하는하는 대신 innerFn를 리턴
// 클로저 사용 패턴 1
function outerFn() {
  const outerFnVar = 'outer 함수 내의 변수';
  const innerFn = function() {
    return 'innerFn은 ' + outerFnVar + '에 접근할 수 있습니다.';
  }
	return innerFn;
}
// createFoodRecipe(foodName)에서 getFoodRecipe을 리턴해서 getFoodRecipe()에 접근 가능하게 함(기존 스코프에서 불가능한 것(외부에서 내부로 접근 가능하게 하는 것)을 사용 가능하게 만든 것이 클로저) 
function createFoodRecipe (foodName) {
  const getFoodRecipe = function (ingredient1, ingredient2) {
    return `${ingredient1} + ${ingredient2} = ${foodName}!`;
  }
  return getFoodRecipe;
}

const highballRecipe = createFoodRecipe('하이볼');
highballRecipe('콜라', '위스키'); // '콜라 + 위스키 = 하이볼!'
highballRecipe('탄산수', '위스키'); // '탄산수 + 위스키 = 하이볼!'
highballRecipe('토닉워터', '연태고량주'); // '토닉워터 + 연태고량주 = 하이볼!'
  • return function 구조는 클로저임을 암묵적으로 알려주며 익명함수이기도 하다. 익명함수는 보안적인 부분에서 유리
```jsx
function makePancake(powder) {
  return function (sugar) {
		return function (pan) {
			return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
		}
	}
}

const addSugar = makePancake('팬케이크가루');
const cookPancake = addSugar('백설탕');
const morningPancake = cookPancake('후라이팬');

// 잠깐 낮잠 자고 일어나서 ...
const lunchPancake = cookPancake('후라이팬');
function makePancakeAtOnce (powder, sugar, pan) {
  return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
}

const morningPancake = makePancakeAtOnce('팬케이크가루', '백설탕', '후라이팬')
// 잠깐 낮잠 자고 일어나서 만든 팬케이크를 표현할 방법이 없다.

ES6 신규 문법

spread

  • 주로 배열을 풀어서 인자로 전달하거나, 배열을 풀어서 각각의 요소로 넣을 때에 사용
  • ... 붙이면 배열을 풀어줌
function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

// 아래 두 코드는 같은 코드
// console.log(numbers[0] + numbers[1] + numbers[2]);
console.log(sum(...numbers));
  • spread쓰면 깊은 복사
let arr = [1, 2, 3];
let arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4); // 참고: spread 문법은 기존 배열을 변경하지 않으므로(immutable), arr2를 수정한다고, arr이 바뀌지 않습니다.

console.log(arr); 
console.log(arr2);
// 질문: arr와 arr2의 값은 각각 무엇인가요?
// [ 1, 2, 3 ]
// [ 1, 2, 3, 4 ]

rest

  • 파라미터를 배열의 형태로 받아서 사용할 수 있습니다. 파라미터 개수가 가변적일 때 유용ㄷ
  • 매개변수에 ... 붙이면 spread와 반대로 배열을 묶어줌
function sum(...theArgs) {
  let result = 0;
  for(let i = 0; i < theArgs.length; i++) {
  	result += theArgs[i];
  }
  return result;
}

sum(1,2,3) // 질문: 어떤 값을 리턴하나요?
sum(1,2,3,4) // 질문: 어떤 값을 리턴하나요?

구조 분해(destructing)

  • 구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식
    https://kindjjee.tistory.com/9

화살표 함수(arrow function)

  • 함수를 정의하는 방법은 함수선언문, 함수표현식이 있다.
// 함수선언문
function sum (x, y) {
	return x + y;
}

// 함수표현식
const subtract = function (x, y) {
	return x - y;
}
  • ES6가 등장하면서 함수를 정의하는 방법이 하나 더 생겼다.
    바로 화살표 함수(arrow function)다. 함수표현식으로 함수를 정의할 때 function 키워드 대신 화살표(=>)를 사용
// 화살표 함수
const multiply = (x, y) => {
	return x * y;
}
  • 화살표 함수의 규칙들이 몇 가지 있다.
  1. 매개변수가 한 개일 때, 소괄호( () )를 생략할 수 있다
// 매개변수가 한 개일 때, 소괄호를 생략할 수 있습니다.
const square = x => { return x * x }

// 위 코드와 동일하게 동작합니다.
const square = ( x ) => { return x * x }

// 단, 매개변수가 없는 경우엔 소괄호를 생략할 수 없습니다.
const greeting = () => { return 'hello world' }
  1. 함수 코드 블록 내부가 하나의 문으로 구성되어 있다면 중괄호( {} )를 생략할 수 있다. 이때 코드 블록 내부의 문이 값으로 평가될 수 있으면 return 키워드를 생략할 수 있다. (= 중괄호가 사라지면 return문도 사라짐)
const squre = x => x * x

// 위 코드와 동일하게 동작합니다.
const square = x => { return x * x }

// 위 코드와 동일하게 동작합니다.
const square = function (x) {
	return x * x
}
profile
백엔드 개발자
post-custom-banner

0개의 댓글