jQuery 소스코드 분석과 함께하는 자바스크립트 공부 – jQuery.extend: Objects Inherit from Objects

이 글은 Decoding jQuery series 를 보고 공부한 점을 정리한 글입니다. 원문의 내용과 똑같은 부분도 있고 제가 알고 있는 내용을 따로 덧붙인 부분도 있으니 원문 – Decoding jQuery – jQuery.extend: Objects Inherit from Objects 을 꼭 보시기 바랍니다.

——————————————————————————————————————–

jQuery.extend()
jQuery에서 $.extend() 메서드를 사용하여 객체를 확장할 수 있습니다.(한 객체가 다른 객체의 프로퍼티를 상속받을 수 있습니다.) 이 때 shallow copy(얕은 복사)와 deep copy(깊은 복사)의 두 가지 방법 중 하나를 사용할 수 있습니다. shallow copy를 할 경우 확장한 객체를 수정하면 원본 객체도 같이 수정됩니다. deep copy에서는 새로 확장한 객체를 수정해도 원본 객체는 영향을 받지 않습니다. 아래에서 shallow copy와 deep copy의 사용예를 보여드리겠습니다.

var artist = {
	name: 'Serge Gainsbourg',
	tags: ['french', 'chanson francaise', 'chanson'],
	similar: {
		name: 'Jane Birkin'
	}
};
var myShallow = {};				// 빈 객체 myShallow
$.extend(myShallow, artist);	// shallow copy, myShallow 객체는 artist 객체의 프로퍼티들을 가지게 됩니다.
myShallow.tags.push('pop');		// tags 프로퍼티에 'pop' 이라는 항목을 추가하면
console.log(myShallow.tags);	// 확장한 객체 myShallow.tags 에도 추가가 되고
console.log(artist.tags);		// 원본 객체 artist.tags 에도 추가가 되어 있습니다.
 
var myDeep = {};				// 빈 객체 myDeep
$.extend(true, myDeep, artist);	// deep copy, myDeep 객체는 artist 객체의 프로퍼티들을 가지게 됩니다.
myDeep.tags.push('french pop');	// tags 프로퍼티에 'french pop' 이라는 항목을 추가하면
console.log(myDeep.tags);		// 확장한 객체 myDeep.tags 에는 추가가 되어 있지만
console.log(artist.tags);		// 원본 객체 artist.tags 에도 추가가 되어 있지 않습니다.

위 코드를 실행한 결과는 코드에 주석으로 달아놓은 것처럼 아래와 같이 나타납니다.

["french", "chanson francaise", "chanson", "pop"]
["french", "chanson francaise", "chanson", "pop"]
["french", "chanson francaise", "chanson", "pop", "french pop"]
["french", "chanson francaise", "chanson", "pop"]

shallow copy와 deep copy가 어떤 원리로 이루어지는지 간단하게 살펴보겠습니다. 먼저 shallow copy 입니다.

var obj = {		// obj라는 객체를 하나 만듭니다.
	prop: 'value'
}

var obj2 = obj;		// obj 객체를 obj2에 할당합니다.
					// obj2는 obj의 프로퍼티를 그대로 가지는 객체가 되는데
					// 바로 이것이 shallow copy 입니다.

console.log(obj2.prop);		// 'value'
obj2.prop = 'new value';	// 복사한 객체의 프로퍼티 값을 변경합니다.

console.log(obj.prop);		// 'new value'
							// 원본 객체의 프로퍼티 값이 변경되어 있습니다.
							// 복사한 객체를 수정하면 원본 객체도 수정됩니다.

원본 객체가 복사한 객체의 영향을 받는 shallow copy와 달리 deep copy를 하면 원본 객체는 복사한 객체의 영향을 받지 않습니다.

var obj = {		// obj라는 객체를 하나 만듭니다.
	prop: 'value'
}

var obj2 = {};		// obj2 객체를 만듭니다.

for (var i in obj) {	// for in 문을 사용하여 obj 객체 안의 프로퍼티를 하나씩 꺼냅니다.
	obj2[i] = obj[i];	// obj2 객체에 obj 객체가 가지고 있는 프로퍼티를 만들고
						// 프로퍼티 값도 원본 객체인 obj 객체와 같은 값으로 할당합니다.
}

console.log(obj2.prop);		// 'value'
obj2.prop = 'new value';	// 복사한 객체의 프로퍼티 값을 변경합니다.

console.log(obj.prop);		// 'value'
							// 복사한 객체 obj2의 프로퍼티 값을 변경해도 원본 객체의
							// 프로퍼티 값은 그대로 유지됩니다.
							// 이 것이 deep copy 입니다.

위의 예제 코드처럼 단순히 “새 객체 = 원본 객체” 와 같은 형태로 값을 할당하는 shallow copy와 달리 원본 객체의 프로퍼티의 이름과 값을 얻어서 복사할 객체에 새로 만들어 넣는 형식입니다. 그래서 복사한 객체의 값을 수정해도 원본 객체는 영향을 받지 않습니다.

jQuery 소스코드의 extend 메서드 부분을 보면 deep 인자의 값을 확인해서 true일 경우 deep copy를 하고 그렇지 않을 경우 shallow copy를 합니다. jQuery.extend 메서드의 경우 복사할 대상 객체가 위의 예처럼 빈 객체 {} 가 아닌 경우 등, 여러 가지 경우에 대한 처리가 되어 있어 소스코드가 약간 복잡하지만 크게 보면 구현 원리는 위에서 다룬 내용과 같습니다.

  • Woosung Chu

    정말 설명이 잘되어 있네요. 감사합니다!