학습일지(23.03.02.목)

FAST FOX·2023년 3월 2일
0

학습일지

목록 보기
13/39
post-thumbnail

1. 원시 자료형 & 참조 자료형

1-1 원시 자료형 (primitive data type)

number,string,boolean,null,undefined처럼 고정된 저장 공간을 차지하는 자료형을 의미한다.

1-2 참조 자료형(reference data type)

대량의 데이터를 다루기에 적합한 array,object,function의 자료형을 의미한다.

1-3 차이점


위의 그림처럼 참조 자료형은 하나의 공간에 값을 넣는게 아닌 주소를 부여하고 heap이라는 공간에 그 주소에 맞는 데이터를 묶어둔다. 그리고 값에 변동을 준다면 주소를 보고 heap에서 해당 데이터 묶음을 찾은 뒤에 변경을 준다.

이와 같은 이유 때문에 원시 자료형은 데이터를 복사한 뒤 복사본에 변화를 주어도 원본에는 영향이 없지만, 참조 자료형의 데이터는 주소를 복사하기 때문에 복사본에 변화를 주게되면 원본에도 영향을 주게된다.

원시 자료형과 참조 자료형의 차이점이 한가지 더 있다. 원시 자료형은 병경이 불가능하다는 것이고 참조 자료형은 변경이 가능하다는 것이다.

물론 원시 자료형의 경우 let num = 20num = 30이라는 재할당을 이용하여 변경이 가능하다. 그렇다면 변경이 불가능하다는 말은 무엇인가?
원시 자료형의 경우 기존의 20이 들어있던 공간에 30을 넣는 것이 아니고 새로운 공간에 let num = 30을 넣는 것이다.

그리고 이렇게 변경 후에 남겨진 자료는 JS엔진이 메모리에서 자동을 삭제하고 이런 기능을 가비지 콜렉터라고 한다.

2. 얕은 복사와 깊은 복사

2-1 얕은 복사

• 배열의 복사

let new = arr.slice()

let new = [...arr]


console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false

• 객체 복사

let new = Object.assign({},obj)

let new = {...obj}

console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false

❗️이렇게 복사를 하더라도 참조 자료형의 경우 주소가 다르기 때문에console.log(original === copied)를 했을 때 각각의 값은 같을지라도 false가 나오게 된다.

❗️참조 자료형 내부에 또 참조 자료형이 중첩되어 있을 경우에는 위의 방법을사용해도 복사할 수 없다.

2-2 깊은 복사

참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것을 의미한다. JS 내부적으로는 깊은 복사를 수행할 수 없기 때문에 다른 문법을 응용한다.

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

❗️깊은 복사라고 할지라도 함수의 경우에 위의 방법을 사용하면 null을 반환하게 된다.

또 다른 깊은 복사의 방법으로는 외부 라이브러리를 사용하는 방법이 있고 이 방법이 완전한 깊은 복사 방법이다.
node.js환경에서 외부 라이브러리인 lodash 또는 ramda를 설치하면 된다.

<lodash의 cloneDeep을 이용>
  
const lodash = require('lodash');

const arr = [1, 2, [3, 4]];
const copiedArr = lodash.cloneDeep(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

3. 스코프

3-1 스코프의 규칙

  1. 안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능하다.

  2. 스코프는 중첩이 가능하다.

  3. 가장 바깥쪽의 스코프는 전역 스코프(Global Scope), 그 이외에는 지역 스코프(local scope)라고 부른다.

  4. 지역 변수가 글로벌 변수보다 더 높은 우선순위를 가지고 있다.

3-2 스코프의 종류

  1. 블록 스코프 : 중괄호로 둘러싼 범위
if(true) {
  console.log('good!')
}

const getAge = () => {
  return user.name;
}// 화살표 함수는 함수지만 블록 스코프에 포함된다.
  1. 함수 스코프 : 함수로 둘러싼 범위
function getName (user) {
  return user.name;
}

4. 클로저

클로저는 함수와 그 함수 주변의 상태의 주소 조합이다. 조금 더 이해하기 쉽게 풀어서 설명한다면 함수와 그 함수가 접근할 수 있는 변수의 조합이다

const globalVar = '전역 변수';

function outerFn() {
  const outerFnVar = 'outer 함수 내의 변수';
  const innerFn = function() { 
    return 'innerFn은 ' + outerFnVar + '와 ' + globalVar + '에 접근할 수 있습니다.'; 
    //함수 innerFn의 반환값
  }
	return innerFn; //함수 outerFn의 반환값은 innerFn이라는 함수.
}

const innerFnOnGlobal = outerFn(); // innerFnOnGlobal은 outerFn의 반환값인 함수 innerFn이 된다.
console.log(innerFnOnGlobal); // [Function: innerFn]
console.log(innerFnOnGlobal()); // innerFn은 outer 함수 내의 변수와 전역 변수에 접근할 수 있습니다.
const message = innerFnOnGlobal(); // innerFnOnGlobal()의 반환값이 message가 된다.
console.log(message); // innerFn은 outer 함수 내의 변수와 전역 변수에 접근할 수 있습니다.

위의 함수innerFnOnGlobal를 호출해도 변수outerFnVar는 스코프 밖에 있기 때문에 값을 받을 수 없어야 한다. 하지만 innerFnOnGlobalinnerFn의 주솟값을 가지고 있고, innerFn클로저로서 outerFnVar에 접근 할 수 있다.

이러한 클로저의 특성을 이용하면 다음과 같은 코드를 구현할 수 있다.

function createFoodRecipe (foodName) {
  const getFoodRecipe = function (ingredient1, ingredient2) {
    return `${ingredient1} + ${ingredient2} = ${foodName}!`;
  }
  return getFoodRecipe;
}

const highballRecipe = createFoodRecipe('하이볼');
highballRecipe('콜라', '위스키'); // '콜라 + 위스키 = 하이볼!'
highballRecipe('탄산수', '위스키'); // '탄산수 + 위스키 = 하이볼!'
highballRecipe('토닉워터', '연태고량주'); // '토닉워터 + 연태고량주 = 하이볼!'

5. 커링

여러 전달인자를 가진 함수를 함수를 연속적으로 리턴하는 함수로 변경하는 행위이다. 다음 예시에서 currySum과 같은 함수가 커링 함수이다.

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

function currySum(a) {
	return function(b) {
		return a + b;
	};
}

console.log(sum(10, 20) === currySum(10)(20)) // true

이러한 커링 함수는 전체 프로세스의 일정 부분까지만 실행하는 경우 유용하다.

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('후라이팬'); 

위의 예시처럼 필요에 따라서 함수 전체를 돌리는게 아닌 필요한 과정만을 실행할 수 있도록 해준다.

profile
준비하는 개발자

0개의 댓글