JavaScript: 객체 Object(3)
안녕하세요. 저번 포스팅에서 프로토타입의 상속에 대해 간략하게 살펴보았습니다.
이번 포스팅에서는 생성자를 new
연산자로 호출하여 인스턴스를 생성했을 때 객체 내부에서 어떤 변화가 일어나는지 알아보도록 하겠습니다.
new
연산자의 역할?
가령 아래와 같은 코드가 있다고 가정해봅시다.
// 1
function Circle (center, radius) {
this.center = center;
this.radius = radius;
}
// 2
Circle.prototype.area = function () {
return Math.PI * this.radius * this.radius;
}
첫번째 함수는 원의 정보에 대해 나타내주는 함수입니다.
두번째 함수는 첫번째 함수 객체를 불러와 원의 넓이를 구해주는 함수입니다.
본래 우리가 new
연산자로 Circle
이라는 생성자를 사용한다고 할 때 아래와 같은 과정을 거쳤습니다.
// center(중심)이 (0, 2)이고 반지름이 2.0인 원
let circle = new Circle({x: 0, y: 2}, 2.0)
이렇게 new
연산자를 사용할 때 다음과 같은 절차를 거치게 됩니다.
let circleObj = {};
circleObj.__proto__ = Circle.prototype;
Circle.apply(circleObj, arguments);
return circleObj;
우선 빈 객체를 만들어 줍니다. 그리고 Circle.prototype
을 새로 생성된 circleObj
의 프로토타입으로 지정합니다.
이 때 Circle.prototype
이 객체가 아닌 경우에는 Object.prototype
을 프로토타입으로 설정해주게됩니다.
그 다음 Circle
생성자를 실행하여 circleObj
를 초기화 해줍니다. 여기에서 apply()
는 주어진 this
값과 배열로 제공되는 arguments
를 호출합니다. arguments
는 객체이며 함수에 전달된 인수에 해당하는 Array
형태의 객체입니다.
마지막으로 완성된 객체를 결과값으로 반환하게 됩니다.
우리가 circleObj.__proto__ = Circle.prototype;
와 같이 지정하는 부분에 대해 유심히 살펴보아야 합니다. 생성자의 프로토타입 프로퍼티를 인스턴스의 __proto__
로 대입하고 있는데, 이렇게 하여 인스턴스의 프로토타입 체인이 정의되고, 생성자로 생성한 모든 인스턴스가 생성자의 프로토타입 객체의 프로퍼티를 사용할 수 있게 됩니다.
이와 같이 생성자를 new
연산자로 호출하게 되면 객체의 생성과 프로토타입 설정, 객체 초기화를 수행할 수 있게 되는 것입니다.
프로토타입 객체의 프로퍼티?
자바스크립트의 함수 객체는 기본적으로 prototype
프로퍼티를 갖고 있습니다. 이는 프로토타입 객체를 가리키고 이 프로토타입 객체는 constructor
프로퍼티와 내부 프로퍼티인 [[Prototype]]
또는 __proto__
를 갖습니다.
constructor
프로퍼티란?
constructor
프로퍼티는 함수 객체의 참조를 값으로 갖습니다.
function P () {};
console.log(P.prototype.constructor); // Function P () {}
생성자와 생성자의 프로토타입 객체는 서로를 참조하는 형태입니다. 생성자의 prototype
프로퍼티가 프로토타입 객체를 가리키고 이 프로토타입 객체의 constructor
프로퍼티가 생성자를 가리키는 체인으로 묶여 있습니다.
그러나 생성자로 생성한 인스턴스는 생성될 때 프로토타입 객체의 참조만 가지고 있지 생성자와 직접적인 체인관계는 없습니다.
인스턴스가 어떤 생성자로 생성되었는지 확인하는 방법으로 인스턴스가 가진 프로토타입의 constructor
프로퍼티를 확인하는 방법이 있습니다.
인스턴스가 프로토타입에서 constructor
프로퍼티를 상속받기 때문에 이를 인스턴스의 프로퍼티로 참조할 수 있는 것입니다.
function P () {};
myObj = new P();
console.log(myObj.constructor); // Function P () {}
[[Prototype]]
이란?
[[Prototype]]
은 함수 객체가 가진 프로토타입 객체의 내부 프로퍼티입니다.
이것은 기본적으로 Object.prototype
을 가리키는데 프로토타입 객체의 프로토타입이 됩니다.
예를 들어,
function P () {};
console.log(P.prototype.__proto__);
라고 한다면 콘솔에는 Object {} : Object.prototype
이 출력될 것입니다.
생성자로 생성한 인스턴스가 Object.prototype
의 프로퍼티를 사용할 수 있는 것, Object.prototype
의 프로토타입은 null
을 가리킨다는 사실을 꼭 기억해둡시다!
프로토타입 객체를 교체하는 방법과 constructor
프로퍼티
생성자가 가진 prototype
을 개로운 객체로 교체할 때 주의해야할 점이 있습니다. 프로퍼티만 정의된 새로운 객체를 prototype
으로 대입하게 되면 인스턴스와 생성자 사이의 체인이 끊어집니다. 왜냐하면 그 객체에는 constructor
가 부재하게 되기 때문입니다.
이 때문에 인스턴스와 생성자 사이의 체인을 유지하고자 한다면 prototype
으로 사용할 객체에 constructor
를 정의하고 그 프로퍼티에 생성자의 참조를 대입해 주는 과정이 필요합니다.
이전에 사용했던 예시를 다시 가져와 보겠습니다.
function Circle (center, radius) {
this.center = center;
this.radius = radius;
}
이 함수의 프로토타입 객체에 constructor
를 직접 지정해보겠습니다. 생성자를 constructor
로 대입하는 것입니다.
Circle.prototype = {
constructor: Circle,
area: function () { return Math.PI * this.radius * this.radius }
}
이제 circle
에 Circle
함수를 사용하여 원의 넓이를 구하는 함수를 변수로 지정해보겠습니다.
let circle = new Circle({x: 0, y: 2}, 2.0);
자 그럼 결과를 확인해보겠습니다.
console.log(circle.constructor); // Function Circle
console.log(circle instanceof Circle); // true
첫번째는 constructor
즉 인스턴스의 프로토타입을 만든 참조를 반환하는데 Function Circle
인 것을 보아 circle
은 Circle
함수의 프로토타입을 참조하고 있다는 사실이 명확해집니다.
두번째는 instanceof
즉 인스턴스 circle
이 생성자 Circle
로 생성된 것인지 확인해주는 연산자인데 true
로 반환되는 것으로 보아 circle
은 Circle
로 생성되었음을 알 수 있습니다.
인스턴스 생성 이후 생성자의 프로토타입을 수정하거나 교체했을 때
인스턴스의 프로토타입은 생성자가 인스턴스를 만들 때 갖고 있던 프로토타입 객체입니다. 인스턴스를 생성한 후 생성자의 prototype
프로퍼티를 바꾸더라도 인스턴스의 프로토타입은 바뀌지 않습니다.
인스턴스의 프로퍼티는 생성될 당시 시점의 프로토타입을 상속받기 때문에 생성된 이후에 생성자의 프로토타입을 바꾼다고 해도 교체한 객체로부터 프로퍼티를 상속받지 않습니다.
예를 들어 위에서 사용한 코드를 다시 가져와보겠습니다.
function Circle (center, radius) {
this.center = center;
this.radius = radius;
}
let circle = new Circle({x: 0, y: 2}, 2.0);
Circle.prototype = {
constructor: Circle,
area: function () { return Math.PI * this.radius * this.radius }
}
여기에서 바로 circle.area()
를 호출해보면 어떻게 나올까요?
circle.area();
// Uncaught TypeError: circle.area is not a function
이렇게 에러가 발생하지요? 그렇지만 생성자가 기존에 갖고 있던 프로토타입 객체에 프로퍼티를 추가한 경우에는 생성자와 인스턴스의 체인이 끊어지지 않습니다.
Circle.prototype.area = function () {
return Math.PI * this.radius * this.radius;
}
circle.area() // 12.56637.....
자 이렇게 new
연산자의 역할 그리고 프로토타입 객체의 프로퍼티에 대해 살펴보았습니다.
constructor
로 생성자와 생성자의 프로토타입 객체가 서로를 참조하는지 확인할 수 있고, 인스턴스와 생성자의 체인관계를 유지하기 위해 이것을 별도로 정의해 주어야 한다는 점까지 알 수 있었습니다. 또한 인스턴스를 생성한 후 프로토타입 프로퍼티를 수정하더라도 인스턴스 자체의 프로토타입은 바뀌지 않는다는 점도 알 수 있었지요.
다음 포스팅에서는 프로토타입을 확인하는 방법에 대해 살펴보겠습니다.
Author And Source
이 문제에 관하여(JavaScript: 객체 Object(3)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@htwenty-1/JavaScript-객체-Object3저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)