객체를 확장하는 extend()
는 복사용도로도 쓸 수 있는 정말 편리한 메소드지만, 사용하면서 주의할 점이 하나 있는데, 바로 인자 순서이다.
예를 들어, 여러 명의 사람 정보를 저장한 배열에 똑같은 프로퍼티를 추가하는 로직을 짠다고 가정하자. 물론 이 경우 extend()
를 사용해야할 필요는 없지만, 굳이 사용해야만 하는 상황이라고 가정하자. 그러면 다음과 같이 코드를 짤 수 있을 것이다.
1 | const _ = require('underscore'); |
언뜻 봐서는 적절한 코드로 보인다. 하지만 이 코드를 실행시키면 배열 내 모든 객체의 데이터가 동일한 데이터로 변경되는 문제가 생긴다.
왜일까? 이유는 인자의 순서에 따라 extend()
의 결과물이 다르기 때문이다. underscore의 문서를 다시보면,
extend
_.extend(destination, *sources)
source 객체에 있는 모든 프로퍼티를 destination 객체에 복사하고, destination 객체를 리턴합니다. source는 순서대로 처리하므로, 마지막 source의 프로퍼티가 앞의 인자들이 가진 같은 이름의 프로퍼티를 덮어쓸 수 있습니다.
이 서술에 의하면, 앞에서 예로 든 코드는 base
라는 변수를 확장하고 리턴하게 된 것이다. 게다가 extend()
메소드는 새로운 객체를 만들어 리턴하지 않고, 주어진 객체를 확장하기 때문에 이 경우에는 base
가 확장되고 리턴되는 것만 세 번 반복되는 것이다. 따라서 새롭게 만들어진 배열은 그냥 base
가 세 번 연속 extend()
된 결과물 일 뿐이다.
코드가 의도한 대로 동작하도록 바꾸려면 그냥 인자 순서만 교체해주면 된다.
1 | people = people.map((person) => _.extend(person, base)); |
위의 사항은 동일한 용도인 lodash의 assignIn(), jQuery의 extend()에서도 동일하다.
“extend는 매개변수의 순서에 유의해서 사용해라”라는 말로 축약될 수 있는 이야기를 이렇게 늘어 놓을 필요가 있나 싶지만, 이 문제로 몇 시간 가량을 날려먹었기 때문에 이렇게 포스팅 기록을 남긴다. 역시 API 문서는 영어라고 읽기 싫다고 넘기지말고 주의 깊게 보아야겠다.