DevLog__[javascript: 부족한 개념정리]

Jaewon Lee·2020년 9월 22일
1

Javascript

목록 보기
6/12
post-thumbnail

# Intro

1차적으로 지금까지 배운 내용 중에 부족하거나, 기억하기 쉽지 않은 내용들을 위주로 정리할 것이다. 사실 내 기준에서 기억하기 쉽지 않았던 것들인데, 이 글을 읽는 사람들도 나와 같은 것을 모르길 바라면서 써본다. 하지만 내 블로그를 보는 사람은 없겠지...? 😂

그래도 계속 쓰면 언젠가 보겠지 하하. 같이 공부하는 분이 스코프, 클로저 글에 좋아요 하나 달아주셔서 기부니가 기부니하다. 나와 같은 선에서 시작하는 모든 분들에게 도움이 되길 바란다!

# javascript

뭐 엄청 많다..정리하니까 한 열 몇개 정도 되는 것 같다. 엄격한 비교 vs 느슨한 비교, 얕은 복사 vs 깊은 복사.....단어도 이상하고 어감이가 정감이 안간다. 오늘은 코드 위주로 이해를 해볼 겸 코드 설명이 많다! 바로 정리해보자!

레릿고~레릿고~🧚🏻‍♀️


1. Primitive vs Reference


Primitive Type

  • 원시(primitive) 타입의 데이터는 변수에 할당이 될 때 메모리 상에 고정된 크기로 저장이 되고 해당 변수가 원시 데이터 값을 보관한다. 원시 타입 자료형은 모두 변수 선언, 초기화, 할당 시 값이 저장된 메모리 영역에 직접적으로 접근한다. 즉, 변수에 새 값이 할당이 될 경우, 변수에 할당된 메모리 블럭에 저장된 값을 바로 변경한다.

  • 원시 자료형으로는 Boolean, number, String, null, undefined가 있다.

  • Primitive Type의 변수 복사
    • 변수 간에 원시 타입 데이터를 복사할 경우, 데이터의 값이 복사된다.
      var x = 100;
      var y = x;
      x = 99;
      console.log(y);	// 100;

Reference Type

  • 참조(reference) 타입의 데이터는 크기가 정해져 있지 않다. 변수에는 힙(heap) 메모리의 주소값을 저장한다.

  • 참조 타입은 변수의 값이 저장된 메모리 블럭의 주소를 가지고 있고 자바스크립트 엔진이 변수가 가지고 있는 메모리 주소를 이용해서 변수의 값에 접근한다.

  • 참조 자료형으로는 Array, Function, Object가 있다.

  • Reference Type의 변수 복사
    • 변수 간에 참조 타입 데이터를 복사할 경우, 데이터의 주소값이 복사된다.
var x = { count : 100 };
var y = x;
x.count = 99;
console.log(y);	// 99
  • 분리된 메모리 공간을 생각해 보자. 아래 코드에서 Object는 'arr' 뿐이다.
let num = 123;
const msg = "hello";
let arr = [1, 2, 3];
const isOdd = true;
/*
원시 자료형의 데이터가 저장되는 공간 (stack)
1 | num |   123
2 | msg | "hello"
3 | arr | heap의 12번부터 3개  // (실제 데이터가 저장되어 있는 주소)
4 |isOdd|   true
==============================
Object 자료형의 데이터가 저장되는 공간 (heap)
10 ||   
11 || 
12 || 1
13 || 2 
14 || 3
*/


2. const vs let vs var


const

  • 재선언 불가, 재할당 불가
  • const로 선언된 객체와 배열을 살펴보자!
// const는 오브젝트에도 잘 동작한다.
const MY_OBJECT = {'key': 'value'};
// 오브젝트를 덮어쓰면 오류가 발생한다.
MY_OBJECT = {'OTHER_KEY': 'value'};
// 하지만 오브젝트의 키는 보호되지 않는다.
// 그러므로 아래 문장은 문제없이 실행된다.
MY_OBJECT.key = 'otherValue'; 
// 오브젝트를 변경할 수 없게 하려면 Object.freeze() 를 사용해라!
// 배열에도 똑같이 적용된다.
const MY_ARRAY = [];
// 배열에 아이템을 삽입하는 건 가능하다.
MY_ARRAY.push('A'); // ["A"]
// 하지만 변수에 새로운 배열을 배정하면 에러가 발생한다.
MY_ARRAY = ['B']

let

  • 재선언 불가, 재할당 가능
let a = 200;
a = 300;     
console.log(a); // 300
let b = 100;
let b = 101; // Uncaught SyntaxError: Identifier 'b' has already been declared

var

  • 재선언 가능, 재할당 가능
var a = 200;
a = 300;     
console.log(a);  // 300
var b = 100;
var b = 101;
console.log(b);  //101


3. 느슨한 비교 vs 엄격한 비교


  • 느슨한 비교
    느슨한 비교(‘==‘)는 값 자체가 같으면 true를 반환한다.

  • 엄격한 비교
    엄격한 비교(‘===‘)는 값과 주소값 모두 같아야 true를 반환한다.


4. TestBuilder Matcher : toEqual(‘==’)과 toBe(‘===‘)


  • toEqual은 느슨한 비교이고, toBe는 엄격한 비교인 것을 알 수 있다.
// 먼저 toEqual을 살펴보자
const overTwenty = ['hongsik', 'minchul', 'hoyong’];
// allowedToDrink에 overTwenty의 주소값을 할당한다.
let allowedToDrink = overTwenty;
overTwenty.push('san’);
// 주소값이 같기 때문에 overTwenty에 'san'을 추가하면 allowedToDrink도 업데이트된다.
// True
expect(allowedToDrink).toEqual(['hongsik', 'minchul', 'hoyong', 'san’]);
overTwenty[1] = 'chanyoung';
// 주소값이 같기 때문에 overTwenty[1]을 'chanyoung'로 재할당하면 allowedToDrink도 업데이트된다.
//True
expect(allowedToDrink[1]).toEqual('chanyoung');
//이제 toBe를 살펴보자
const ages = [22, 23, 27];
allowedToDrink = ages;
// allowedToDrink와 ages를 비교하면 true가 나온다.
// 배열 변수 ages의 주소 값을 변수 allowedToDrink에 할당했기 때문이다.
expect(allowedToDrink === ages).toBe(true);
// 하지만 allowedToDrink와 [22,23,27]을 비교하면 값이 같은데 false가 나온다.
// allowedToDrink는 [22,23,27] 배열과 주소값이 다르기 때문이다.
expect(allowedToDrink === [22, 23, 27]).toBe(false);
// 같은 이유로 아래 결과도 false가 나온다.
const nums1 = [1, 2, 3];
const nums2 = [1, 2, 3];
expect(nums1 === nums2).toBe(false);

5. 얕은 복사 vs 깊은 복사


객체의 복사는 얕은 복사와 깊은 복사로 나뉘어 진다. 얕은 복사와 깊은 복사의 차이는 복사하고자 하는 객체의 속성(key)의 값으로 객체나 배열이 있을때, 그 내부 값에 대하여 참조(주소) 값을 복사하느냐 또는 그 값을 복사하느냐의 차이를 가지고 있다. 깊은 복사와 얕은 복사를 할 수 있는 방법이 각각 몇 가지 존재하지만 전부를 언급하진 못하니 참조 사이트를 아래에 남겨 놓겠다.

이렇게 적고 보니 더 헷갈릴 것 같기 때문에, 간단하게 예로 들어서 얕은 복사와 깊은복사를 설명 해보겠다!

얕은 복사

const testData = {a: {aa: 1}, b: [1, 2, 3]};
const copyData2 = Object.assign({}, testData);
testData.a.aa = 2;
testData.b[0] = 100;
console.log(copyData2);
// {a: {aa: 2}, b: [100, 2, 3]}
  • 위에서 이야기 했듯이 복사하고자 하는 객체가 객체나 배열을 담고 있으면 참조 값을 복사하기 때문에 ‘복사 대상 객체’의 값이 바뀌면 ‘복사한 객체’의 값이 바뀐다. 반대로도 마찬가지다.

깊은 복사

const testData = {a: {aa: 1}, b: [1, 2, 3]};
const copyData = JSON.parse(JSON.stringify(testData));
testData.a.aa = 2;
testData.b[0] = 100;
console.log(copyData);
// {a: {aa: 1}, b: [1, 2, 3]}
const testData = {a: {aa: 1}, b: [1, 2, 3]};
const copyData = eval('('+JSON.stringify(testData)+')');
testData.a.aa = 2;
testData.b[0] = 100;
console.log(copyData);
// {a: {aa: 1}, b: 2}
// {a: {aa: 1}, b: [1, 2, 3]}
  • 깊은 복사는 객체 안의 객체, 배열의 참조 값도 완전히 원시 데이터 값으로 복사하는 방법이다.
  • 가장 간단하게 사용할 수 있는 방법은 JSON 객체를 사용하거나, JSON 객체와 eval() 메서드를 사용하는 방법이다. JSON.stringify() 메서드로 객체의 데이터를 JSON 형태의 문자열로(원시 데이터)로 만들어서 복사한 후 JSON.parse() 또는 eval() 메서드를 사용해서 다시 객체로 만든다.


6. 객체.length


  • 객체 내부 속성이 몇개있는지 파악하기 위해서 length로 접근하면 undefined가 출력된다.
  • 객체변수.keys()는 객체의 속성을 배열의 형태로 반환시키기 때문에, 이 반환값으로 length를 사용하여 객체 내부 속성의 개수를 파악한다.
    let obj = {key1: 1, key2: 2, key3: 3};
    console.log(obj.length);                 // undefined
    let objKeyArr = obj.keys();
    console.log(objKeyArr.length)            // 3

7. Spread Operator / Rest Parameter


만약 서로 다른 배열을 붙이고 싶다면 어떻게 할 것인가? concat을 제외하고 말이다. spread operator는 배열 내부의 값을 마치 뿌리는 것 처럼 펼쳐주는 연산자이다. 이를 활용하여 함수의 정의 방법과 상관없이 함수를 호출할 때 넘겨주는 인수의 개수엔 제약이 없게 한 것이 rest parameter이다.

역시나 말로 설명하면 모르니 코드로 살펴보자!

Spread Operator

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [7, 8, 9];
const newArr = [...arr1, ...arr2, ...arr3];
document.write(newArr.length);                 // 9
document.write(newArr);                        // [1,2,3,4,5,6,7,8,9]
  • 위와 같이 ...(spread operator)를 사용하면 여러개의 객체, 배열을 합치는데에 용이하다.

Rest Parameter

function getAllParamsByRestParameter(...args) {
  return args;
}
// arguments를 통해 '비슷하게' 함수의 인자들을 다룰 수 있다. (spread syntax 도입 이전)
function getAllParamsByArgumentsObj() {
  return arguments;
}
/* arguments는 유사 배열 객체고 rest parameter(...)는 배열이다.
유사 배열 객체(array-like object)는 간단하게 순회가능한(iterable) 특징이 있고 length 값을 알 수 있는 특징이 있는 것이다. 즉, 배열처럼 사용할 수 있는 객체를 말한다.
무슨 말이냐면 arguments는 유사배열객체이기 때문에 Array 오브젝트의 메서드를 사용할 수 없다.
따라서 ES6에서는 arrow function에 arguments는 사용할 수 없을 뿐더러 Rest 파라미터를 사용하면 더 유연한 코드를 작성할 수 있는 것이기 때문에 Rest 파라미터 사용을 권장한다.
*/
const restParams = getAllParamsByRestParameter('first', 'second', 'third');
const argumentsObj = getAllParamsByArgumentsObj('first', 'second', 'third');
expect(restParams).toEqual(['first', 'second', 'third']);
expect(argumentsObj).toEqual(FILL_ME_IN);
// argumentsObj는 유사 배열 객체이기 때문에 표현하기 힘드므로 코드를 복사해서 결과를 확인 바란다.
// 이 아래는 함수에 인자로 rest parameter가 들어 갔을 때 나오는 배열과 arguments(유사 배열 객체)의 차이를 보여준다.
expect(restParams === argumentsObj).toEqual(false);
expect(typeof restParams).toEqual('object');
expect(typeof argumentsObj).toEqual('object');
expect(Array.isArray(restParams)).toEqual(true);
expect(Array.isArray(argumentsObj)).toEqual(false);
const argsArr = Array.from(argumentsObj);
expect(Array.isArray(argsArr)).toEqual(true);
expect(argsArr).toEqual(['first', 'second', 'third']);
expect(argsArr === restParams).toEqual(false);
  • Rest Parameter는 인자의 수가 정해져 있지 않은 경우에도 유용하게 사용할 수 있다.
  • Rest Parameter는 인자의 일부에만 적용할 수도 있다.
function abc(a,b ...c) {
  console.log(c);                       // arr1:[1,2,3,4]  arr2:[1,2]
  return [a, b, ...c];
}
let arr1 = abc(1, 2, 1, 2, 3, 4);
let arr2 = abc(1, 2, 1, 2);
console.log(arr1);                      // [1, 2, 1, 2, 3, 4]
console.log(arr2);                      // [1, 2, 1, 2]


8. 매개변수


  • 함수를 호출하면서 넘긴 인자는 호출된 함수의 지역변수로 (매 호출 시마다) 새롭게 선언된다.
function print(str){       // let str = 'hello'와 같다.
  console.log(str);
}
print('hello');
  • 변수 str은 print라는 function의 안쪽에서 선언되었기 때문에 함수를 나오면 str의 값은 소실된다.

9. for( ~ in A) vs for( ~ of B)


for( ~ in A)

let obj = {a: 1, b: 2, c: 3};
let result = [];
for(let key in obj) {
  result.push(key);
}
console.log(result);              // ['a', 'b', 'c']
  • for in 문은 객체의 키값을 차례로 넘기기 위해 사용한다.

for( ~ of A)

let arr = [1, 2, 3, 4];
let result = [];
for(let value of arr) {
  result.push(value);
}
console.log(result);              // [1, 2, 3, 4]
  • for of 문은 배열의 값을 차례로 넘기기 위해 사용한다.

10. 함수 호출 시 인자의 순서


  • 자바스크립트는 (named parameter를 지원하지 않기 때문에) 함수 호출 시 인자의 순서가 중요하다.
function returnFirstArg(firstArg) {
  return firstArg;
}
expect(returnFirstArg('first', 'second', 'third')).toBe('first');
// returnfirstArg 함수는 인자를 1개 밖에 받지 않으므로, 함수로 인자를 넘길 때 첫 번째 인자만 넘어간다.
function returnSecondArg(firstArg, secondArg) {
  return secondArg;
}
expect(returnSecondArg('only give first arg')).toBe(undefined);
// returnSecondArg 함수는 인자를 2개 받으므로, 함수의 인자를 넘길 때 첫 번째, 두 번째 인자만 넘긴다.
// 위에서 returnSecondArg에 한개의 문자열을 보내지만, 반환값은 secondArg이므로 undefined가 출력된다.

11. Spead Operator를 이용한 객체 복사


it('여러 개의 객체를 병합할 수 있습니다.', function () {
  const fullPre = {
    cohort: 7,
    duration: 4,
    mentor: 'hongsik',
  }
  const me = {
    time: '0156',
    status: 'sleepy',
    todos: ['coplit', 'koans']
  };
  const merged = { ...fullPre, ...me };
  /* 
  - 변수 'merged'에 할당된 것은 'obj1'과 'obj2'의 value일까요, reference일까요?
    -> 얕은 복사로써 primitive 자료형은 value가 저장되고, reference 자료형은 주소가 저장된다.
  - 만약 값(value, 데이터)이 복사된 것이라면, shallow copy일까요, deep copy일까요?
    -> shallow copy
  */
  expect(merged).toEqual({cohort: 7, 
                          duration: 4, 
                          mentor: 'hongsik',
                          time: '0156',
                          status: 'sleepy',
                          todos: ['coplit', 'koans'],
                         });
});

# Work Off


블로그를 작성할 수록 나의 지식을 쌓아나는 것에 보람을 느낀다. 잊어버리는 개념이 생기면 다시 돌아 볼 저장소는 큰 자산이다. 작성하는 것에 시간과 노력이 많이 들어가지만, 그 이상으로 돌아오는 것이 많다는 생각을 했다. 앞으로도 열심히, 잘, 꾸준히 블로깅해야겠다!

(블로그를 돌아보니까 말투가 엄청 딱딱하닼ㅋㅋㅋㅋㅋ실제로 그렇지 않은데말이지....자소서를 많이써서 그런가...군대로 돌아간거갔네... 다음부턴 친근하게 얘기해보련다ㅋㄷ 그래야 다른 사람들도 이해가 쉽지않을까?)

[참고문서]
"Javascript #6 객체의 얕은 복사와 깊은 복사에 대해 알아봅니다.", , https://falsy.me/javascript-6-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B4%85%EB%8B%88%EB%8B%A4/ 2019.01.23. (접속일: 2020.09.23 07:15AM)

"[자바스크립트] var를 사용할 때 발생하는 문제들", Dale Seo,https://www.daleseo.com/js-var-issues/ 2017.02.05. (접속일: 2020.09.23 07:15AM)

"JavaScript - 전개연산자(spread)와 와 나머지 매개변수(rest)", bigbrothershin,https://velog.io/@bigbrothershin/JavaScript-%ED%95%A8%EC%88%98-%EC%8B%AC%ED%99%94-%EB%82%98%EB%A8%B8%EC%A7%80-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%99%80-%EC%A0%84%EA%B0%9C-%EC%97%B0%EC%82%B0%EC%9E%90 2020.02.06. (접속일: 2020.09.23 07:15AM)

"자바스크립트 for in vs for of 반복문 정리", Kyoung Deok Kwon,https://jsdev.kr/t/for-in-vs-for-of/2938 2017.01.09. (접속일: 2020.09.23 07:15AM)

기본기가 탄탄한 풀스택 개발자가 되는 그날까지 🔥🔥🔥

profile
Communication : any

0개의 댓글