[javascript koans] 얕은 복사 vs 깊은 복사와 arguments 객체를 빈칸 채우기로 공부해보자.

이민선(Jasmine)·2023년 3월 6일
0
post-thumbnail

Object

  it("Object를 함수의 전달인자로 전달할 경우, reference가 전달됩니다.", function () {
    const obj = {
      mastermind: "Joker",
      henchwoman: "Harley",
      relations: ["Anarky", "Duela Dent", "Lucy"],
      twins: {
        "Jared Leto": "Suicide Squad",
        "Joaquin Phoenix": "Joker",
        "Heath Ledger": "The Dark Knight",
        "Jack Nicholson": "Tim Burton Batman",
      },
    };

    const copiedObj = Object.assign({}, obj);
    delete obj.twins["Jared Leto"];
    // expect("Jared Leto" in copiedObj.twins).to.equal(1️⃣);
  });
});

1️⃣에 들어갈 단어는 true인가 false인가??

깊은 복사와 얕은 복사를 나름 열심히 공부했다고 생각했는데 막상 이런 문제를 만나니까 깊게 생각 못하고 오답을 냈다.

  • 처음에 낸 답 : false
  • 왜 false라고 썼는가?
    단순히 객체를 Object.assign()으로 얕은 복사했으니, 복사본에서 프로퍼티를 삭제한다고 해서 원본에도 반영되지는 않을 것이라고 착각함.
  • 답이 왜 true인가?
    Object.assign()은 얕은 복사를 해준다. 그리고 문제의 obj에는 중첩된 객체 twins가 있다. 얕은 복사로는 중첩된 객체까지 복사해줄 수 없다. 그렇기 때문에 힙 안에 twins라는 객체는 복사되지 않고 하나만 존재한다. 하나만 존재하는 twins객체에 있는 "Jared Leto"라는 프로퍼티를 지웠다? 그럼 복사본 내의 중첩된 객체 프로퍼티인 "Jared Leto"도 사라지게 된다.
    그러므로 복사본에 "Jared Leto"가 있냐고 물어보면 false이다.

Spread syntax

it("Rest Parameter는 함수의 전달인자를 배열로 다룰 수 있게 합니다.", function () {
    // rest parameter는 spread syntax를 통해 간단하게 구현됩니다.
    function getAllParamsByRestParameter(...args) {
      return args;
    }

    // arguments를 통해 '비슷하게' 함수의 전달인자들을 다룰 수 있습니다. (spread syntax 도입 이전)
    // arguments는 모든 함수의 실행 시 자동으로 생성되는 '객체'입니다.
    function getAllParamsByArgumentsObj() {
      return arguments;
    }

    const restParams = getAllParamsByRestParameter("first", "second", "third");
    const argumentsObj = getAllParamsByArgumentsObj("first", "second", "third");

    expect(restParams).to.deep.equal(["first", "second", "third"]);
    expect(Object.keys(argumentsObj)).to.deep.equal(1️⃣);
    expect(Object.values(argumentsObj)).to.deep.equal(2️⃣);

    // arguments와 rest parameter를 통해 배열로 된 전달인자(args)의 차이를 확인하시기 바랍니다.
    expect(restParams === argumentsObj).to.deep.equal(false);
    expect(typeof restParams).to.deep.equal("object");
    expect(typeof argumentsObj).to.deep.equal(3️⃣);
    expect(Array.isArray(restParams)).to.deep.equal(true);
    expect(Array.isArray(argumentsObj)).to.deep.equal(4️⃣);

    const argsArr = Array.from(argumentsObj);
    expect(Array.isArray(argsArr)).to.deep.equal(true);
    expect(argsArr).to.deep.equal(5️⃣);
    expect(argsArr === restParams).to.deep.equal(false);
  });

막상 풀 때 arguments 객체를 떠올리지 못해서 시간이 좀 걸렸던 문제이다. arguments 객체에 대한 이해가 있으면 어렵지 않게 풀 수 있다.

arguments 객체란?

모든 인수는 매개변수의 숫자와는 무관하게 암묵적으로 arguments 객체의 프로퍼티로 보존된다. 살짝 극단적으로 매개변수는 0개이고 인수가 100개이면 모두 arguments 객체의 프로퍼티로 보존되는 것이다.

arguments 객체의 value : 인수들
arguments 객체의 key : 인수의 순서(0부터 시작)
arguments 객체는 length property도 가진다. 인수의 개수를 의미한다.
이거 배열 아냐?? arguments 객체는 유사 배열 객체이다.

참고: arguments 객체는 배열 형태로 인자 정보를 갖고 있지만 실제 배열이 아닌 유사배열 객체다. 유사배열 객체란, length 프로퍼티를 가지고 for문으로 순회할 수 있는 객체를 의미한다.

그렇다면 문제에서 의미하는 arguments도 arguments 객체가 된다.

function getAllParamsByArgumentsObj() {
   return arguments;
}
const argumentsObj = getAllParamsByArgumentsObj("first", "second", "third");
console.log(argumentsObj); // arguments 객체 출력

이제 문제로 돌아와보자.
1️⃣

expect(Object.keys(argumentsObj)).to.deep.equal(1️⃣);

arguments 객체의 키만 뽑아내서 배열로 만든다면?
['0','1','2']가 될 것이다. (string type임을 주의!!)

2️⃣

expect(Object.values(argumentsObj)).to.deep.equal(2️⃣);

같은 이유로 values만 뽑아내서 배열로 만들면
['first', 'second', 'third']가 될 것이다.

3️⃣

expect(typeof argumentsObj).to.deep.equal(3️⃣);

arguments 객체는 유사배열'객체'이고, type은 object이다.
따라서 정답은 "object"

4️⃣

expect(Array.isArray(argumentsObj)).to.deep.equal(4️⃣);

3️⃣과 같은 맥락으로, 유사배열 객체는 배열이 아닌 객체이므로,
정답은 false

5️⃣

const argsArr = Array.from(argumentsObj);
expect(argsArr).to.deep.equal(5️⃣);

이 문제의 핵심은 Array.from()에 인수로 유사배열 객체를 전달했을 때 무엇이 반환되느냐이다.
무엇이 반환되냐구? values로 이루어진 배열을 반환한다.
따라서 정답은
["first", "second", "third"]
이다.

자바스크립트를 빈칸 채우기로 접근해보니 좀 더 확실하게 내 지식으로 만들 수 있는 것 같아서 의미있는 시간이었다.

profile
기록에 진심인 개발자 🌿

0개의 댓글