[javascript] 객체의 속성을 삭제하는 방법들 - delete 연산자 사용 방법

객체에서 속성을 사용할 때는 대표적으로 delete 연산자를 사용합니다.

객체의 속성 자체를 삭제하려는 것이 아니라면 속성 값을 초기화하는 다음 방법을 사용하는 것인 delete 연산자를 사용하는 방법보다 속도 면에서 훨씬 유리합니다. 평균적으로 delete 연산자를 사용하는 방법보다 두 배이상 빠릅니다.

목적이 속성 값을 초기화 하는 것인지 속성 자체를 삭제하려는 것인지 구분해서 꼭 필요한 경우에만 delete 연산자로 속성을 삭제해야 합니다.

obj[key] = null;
obj[key] = undefined;
obj[key] = false;

delete 연산자로 삭제되어 존재하지 않는 속성에 접근할 경우 자바스크립트는 undefined를 반환합니다. 속성 값에 undefined를 대입해서 초기화를 하면 delete 연산자로 삭제한 것과 같은 값을 반환하기 때문에 굳지 속성을 삭제할 필요가 없으면 undefined를 대입하는 방법이 더 좋은 코딩 방법입니다.

delete 연산자를 사용할 때 삭제할 속성을 표현하는 방법은 다음과 같습니다.

delete obj[key];
delete obj.key;

delete 연산자는 삭제하려는 속성이 존재하지 않아도 에러를 발생시키지 않습니다. 조용히 실행을 종료하기 때문에 실제로 속성을 삭제했는지, 또는 속성이 존재하지 않아서 그냥 종료했는지를 알 수 없습니다.

속성의 존재 여부를 확인하려면 hasOwnProperty() 함수를 사용해 객체에 속성이 있는지 따로 확인(true/false 반환)을 해야합니다.

다음 코드는 실제로 아무런 에러를 발생시키지 않습니다.

let obj = {
    title: "어포스트 기술 블로그",
    url: "https://apost.dev",
    backend: {os: "ubuntu", cms: "ghost"}
}
delete obj.url;
delete obj.url; // 이미 삭제한 속성을 다시 삭제하려고 시도하지만, 에러는 발생하지 않음.

결과는 동일하지만 앞의 코드는 다음처럼 속성 체크를 한 후 속성 삭제를 하는 세이프 코드로 작성해야 합니다.

let obj = {
    title: "어포스트 기술 블로그",
    url: "https://apost.dev",
    backend: {os: "ubuntu", cms: "ghost"}
}
if(obj.hasOwnProperty("url")){
    delete obj.url;
    console.log("Porperty deleted!");
}

delete 연산자로 하위 객체 삭제

delete 연산자는 객체의 속성 뿐만 아니라 객체도 삭제할 수 있습니다. 앞의 예에서 backend 키가 가리키는 것은 속성 값이 아니라 객체이며, delete 연산자로 backend를 삭제하면 backend가 가리키는 하위 객체까지 삭제됩니다.

let obj = {
    title: "어포스트 기술 블로그",
    url: "https://apost.dev",
    backend: {os: "ubuntu", cms: "ghost"}
}
if(obj.hasOwnProperty("backend")){
    delete obj.backend;
    console.log("Porperty deleted!");
    console.log(obj.backend);
}

delete 연산자로 하위 객체를 삭제할 때는 삭제되는 대상이 하위 객체 자체가 아니라 하위 객체의 참조 주소라는 것에 주의해야 합니다. backend 속성이 가리키는 것이 하위 객체의 참조 주소이고 backend 속성이 삭제되면서 참조 주소가 삭제되어 더 이상 참조가 되지 않는 하위 객체에 할당된 메모리 영역(실제 하위 객체 데이터)은 자바스크립트의 가비지 콜렉션에 의해서 별도로 삭제됩니다.

하위 객체를 삭제할 때는 객체의 참조에 주의해야 합니다.

다음처럼 하위 객체에 대한 참조가 추가로 있을 경우 delete 연산자로 하위 객체를 삭제해도 다른 참조 변수에 의해서 하위 객체 메모리 영역의 참조가 유지되기 때문에 하위 객체의 데이터에 계속 접근할 수 있습니다.

let obj = {
    title: "어포스트 기술 블로그",
    url: "https://apost.dev",
    backend: {os: "ubuntu", cms: "ghost"}
}
let ref = obj.backend;
if(obj.hasOwnProperty("backend")){
    delete obj.backend;
    console.log("Porperty deleted!");
    console.log(obj.backend);
    console.log(ref);
}

콘솔출력
--------
Porperty deleted!
undefined
{os: 'ubuntu', cms: 'ghost'}

펼침 연산자와 화살표 함수로 속성 삭제

펼침 연산자를 사용하면 특정 속성을 삭제한 나머지 객체를 얻을 수 있습니다. 다음처럼 변수를 선언하면 나머지 파라미터로 선언한 변수인 rest 변수에 url 속성이 삭제된 나머지 속성들만으로 구성된 객체가 할당됩니다.

let obj = {
    title: "어포스트 기술 블로그",
    url: "https://apost.dev",
    backend: {os: "ubuntu", cms: "ghost"}
}
const {url, ...rest} = obj;
obj = rest;
console.log(obj);

첫 번째 변수 이름으로 삭제할 속성 키를 선언합니다. 여러 개의 속성을 삭제하려면 앞쪽의 변수명으로 삭제할 객체의 속성들을 먼저 나열하면 됩니다.

let obj = {
    title: "어포스트 기술 블로그",
    url: "https://apost.dev",
    backend: {os: "ubuntu", cms: "ghost"}
}
const {url, title, ...rest} = obj;
obj = rest; // {backend: {…}}

나머지 파라미터로 선언한 변수(rest)를 원래 객체 변수(obj)에 다시 대입하는 과정은 필수적인 것은 아니지만, 변수의 재활용이라는 측면에서 이전/이후의 코드 일관성에 유리하기 때문에 관행적으로 이렇게 사용합니다.

이 방식과 유사하게 나머지 파라미터와 화살표 함수로 구현할 수도 있습니다.

const deleteProperty = (propKey, {[propKey], ...rest})=>rest;
obj = deleteProperty("url", obj)

함수로 구현되었기 때문에 함수를 재활용해서 여러 객체에 재활용할 수 있는 장점이 있고, rest 변수가 로컬 변수로 선언되기 때문에 사용후 반환되어 메모리 활용성이 더 좋습니다.