이번에는 참조 타입의 특성을 알아보도록 하자
1)참조 타입의 특성
객체를 참조 타입이라고 부르는데 그 이유는
객체의 모든 연산이 실제 값이 아닌 참조 값으로 처리되기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 | var objA = { val : 40 }; var objB = objA; console.log(objA.val); // 40 console.log(objB.val); // 40 objB.val = 50; console.log(objA.val); // 50 console.log(objB.val); // 50 | cs |
처음 objA 객체를 객체 리터럴 방식으로 생성한다.(객체 자체를 저장하고 있는 것이 아니라 생성된 객체를 가르키는 참조값을 저장하고 있다.)
objB에 objA값을 할당한다. (objB에는 objA와 같은 참조값이 저장된다.)
변수 objB의 객체의 값을 50으로 갱신하고 console.log를 찍어보면 .val값이 50으로 변경된걸 확인 할 수 있다.
1-1)객체비교
동등 연산자(==)를 사용하여 두 객체를 비교할 때도 객체의 프로퍼티값이 아닌 참조값을 비교한다.
1 2 3 4 5 6 7 8 9 10 | var a = 100; var b = 100; var objA = { value : 100 }; var objB = { value : 100 }; var objC = objB ; console.log(a == b); // true(1) console.log(objA == objB); // false(2) console.log(objB == objC); // true(3) | cs |
(1)의 경우 a,b 값이 숫자 100을 저장하는 기본 타입의 변수이기 때문에 비교할때 당연히 true가 나온다.
(2)의 경우 value의 값이 둘다 100이지만 여기서는 참조 값을 비교하기 때문에 false가 나온다.
(3)의 경우 objC가 objB의 참조 값을 할당 받았기 때문에 (2)와 다르게 false가 아닌 true가 나오는 것이다.
1-2)참조에 의한 함수 호출 방식
기본 타입과 참조 타입의 경우 함수 호출 방식도 다르다.
기본 타입의 경우 값에 의한 호출(Call by Value) 방식이고 참조 타입의 경우 참조에 의한 호출(call by Reference) 방식이다.
값에 의한 호출은 함수를 호출할 때 인자로 기본 타입의 값을 넘기는걸 말하는건데 호출된 함수의 매개변수로 복사된 값이 전달된다. 함수내부에서 매개변수를 이용해 값을 변경해도 실제로 호출된 변수의 값이 변경되지 않는다.
참조에 의한 호출은 함수를 호출할때 인자로 참조 타입인 객체를 전당할 경우, 객체의 프로퍼티 값이 함수의 매개변수로 복사되지 않고, 인자로 넘긴 객체의 참조값이 그대로 함수 내부로 전달된다. 함수내부에서 참조값을 이용해서 인자로 넘긴 실제 객체의 값을 변경할 수 있는 것이다.
(책에 나와있는 글로 설명된걸 읽고 한번에 이해하기가 불가능하다...)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var a = 100; var b = { value : 100 }; function change(num, obj) { num = 200; obj.Value = 200; console.log(num); // 200 (1) console.log(obj); // value : 200 (2) } change(a, b);// (3) console.log(a); // 100; (4) console.log(b); // value : 200 (5) | cs |
(3)에서 change()의 함수를 호출하면서 a, b를 넘겼다.
(1),(2)의 함수 내부의 매개변수 num, obj를 이용해 인자로 전달된 a,b의 값을 100에서 200으로 바꿨다.
(4)에서 a의 값이 100으로 변하지 않은걸 확인 할 수 있고 (5)에서 b의 값이 value : 200 으로 바뀐걸 볼 수 있다.
1-3)프로토타입
모든 객체는 자신의 부모 역활을 하는 객체와 연결되어 있다.
이것은 마치 객체 지향의 상속 개념과 같은 부모 객체의 프로퍼티를 마치 자신의 것처럼 쓸 수 있는 것 같은 특징이 있다.
부모객체를 프로토타입 객체 (프로토타입)
1 2 3 4 5 6 7 8 | var foo = { name : 'foo', age : 30 }; foo.toString(); // (1) console.dir(foo); // (2) | cs |
객체 리터럴 방식으로 foo객체 생성하고 (1)은 toString() 메서드를 출력한 것이다.
foo 객체에는 toString() 메서드가 없어서 에러가 발생해야 하지만 정상적으로 결과가 출력되는걸 확인 할 수 있다.
그 이유는 foo 객체의 프로토 타입에 toString() 메서드가 이미 정의되어 있고 foo 객체가 상속처럼 toString() 메서드를 호출 했기 때문이다.
크롬 개발자 도구에서 확인하면 위와 같은 코드가 나오는데 name과 age 프로퍼티 이외에도 foo 객체에 __proto__프로퍼티가 있다는걸 확인 할 수 있다. __proto__프로퍼티가 바로 foo객체의 부모인 프로토타입 객체를 가르킨다.
자바스크립트는 모든 객체는 자신의 프로토타입을 가르키는 [[Prototype]]라는 숨겨진 프로퍼티를 가지고 있다.
즉 foo객체는 자신의 부모 객체를 __proto__라는 내부 프로퍼티로 연결하고 있는 것이다.
모든 객체의 프로토타입은 자바스크립트의 룰에 따라 객체를 생성할 때 결정된다.
객체 리터럴 방식은 Object.prototype 객체가 프로토타입 객체가 된다는 것을 기억하자.
객체를 생성할 때 결정된 프로토타입 객체는 임의의 다른 객체로 변경하는 것도 가능하다.
부모 객체를 동적으로 바꿀 수도 있다는 것이다.
자바스크립트는 이러한 특징을 활용해서 객체 상속 등의 기능을 구현한다.(6장에서)