- 객체의 프로토타입 체인
-
1.
객체 생성자 함수 또한
__proto__
링크를 통해 모든 객체의 최상위 객체인 Object 객체로 연결되고(이는 연결을 통한 접근 이지 복사 가 아니다!), 이러한 프로토타입 체인을 통해 그 최종 단계에 위치한 Object 객체의 메서드와 프로퍼티는 모든 하위 객체로 전파되며, 모든 객체 인스턴스에서 사용할 수 있게 된다: -
2.
new (와 객체 생성자 함수)로 인스턴스 변수를 정의하면;
스크립트는 내부적으로, 우선 빈 객체를 생성하고(
let 인스턴스= {}
), constructor.prototype 을 인스턴스의 프로토타입으로 설정한다. 다음으로, 인스턴스를 초기화하는데, 이때 this 는 인스턴스에 바인딩되고 인수는 new 와 함께 사용한 인수를 그대로 사용하여(생성자 함수.apply(인스턴스, argunents)
) 완성된 인스턴스 객체를 반환한다(return 인스턴스객체
):
function Test(a, b, c, d) { // 생성자 함수 Test 정의
// 속성들 정의..
} Test.prototype.x= function() { // 생성자 함수 Test의 x() 메서드 정의
// ..
}
☞
객체 인스턴스 __proto__
constructor.prototype __proto__
Object.prototype
← 여기서
constructor 는 인스턴스 객체의 생성자 함수를 가리킨다
✓
객체의 데이터 프로퍼티는 프로토타입 체인이 아니라 인스턴스에 정의해야 한다.
따라서, 주의 겸 항시 Object.hasOwn(인스턴스 객체, 속성)
(인스턴스 객체 가 특정 속성을 자체적으로 가지고 있는가?)으로 체크해주는 것이 좋다!
const ex= { Kjc: "jc", Kjh: "jh" }
for(const name in ex) {
if(Object.hasOwn(ex, name)) {
console.log(name) // Kjc Kjh
}
}
for(const name of Object.keys(ex)) {
console.log(name) // Kjc Kjh
}
☞
참고로, 객체의 프로퍼티 나열 시, 위와 같이 Object.keys
를 사용하면;
프로토타입 체인상에만 정의된(해당 객체 자체적으로는 보유하지 않은, 상속된) 프로퍼티를 건너뛸 수 있다!
function Circle(center, radius) { // 객체 생성자 함수 Circle()
this.radius= radius // 이 this는 ins 인스턴스의 this다!
this.area= function() {
return Math.PI * this.radius * this.radius;
}
}
let ins= new Circle({x: 0, y: 0}, 2) // 객체 생성자 함수 Circle의 인스턴스 변수 ins
console.log(ins.area()) // 12.566370614359172
☞ 이렇게 인스턴스로부터 constructor.prototype 을 거쳐서 (모든 함수의 프로토타입은 Object 이므로)Object 객체로 이어지는 프로토타입 체인이 만들어지며, 이를 통해 인스턴스가 Object 객체의 프로퍼티를 사용할 수 있게 된다!
객체 리터럴 방식으로 만들어지는 인스턴스 객체는 {} __proto__
Object.prototype
링크를 따라 Object.prototype이 자신의 프로토타입 객체가 된다.
따라서 모든 객체 인스턴스는 Object 객체가 지닌 스크립트 기본 내장 메서드와 프로퍼티를 이용할 수 있게 된다
←
단, Object 객체의 모든 메서드와 속성들을 상속받는 것은 아니고,
Object.prototype 객체에 포함된 것들만이다
- 곧, prototype
속성은 상속시키려는 멤버들만 정의해둔 객체이다!
Object 객체는 모든 객체의 최상위 객체이므로 이 객체의 프로토타입에 속성이나 메서드를 추가하면
스크립트 내 모든 곳에서 사용할 수 있게 된다.
스크립트 기본 내장 객체인 Number, String, Array 등 또한 프로토타입 체인을 통해
모든 객체의 최종 프로토타입인 Object.prototype으로 연결되어 있다:
[] __proto__
Array.prototype __proto__
Object.prototype
✓
자바스크립트 표준 스펙에서 [[prototype]]
으로 표현되는 프로토타입 객체에 대한 링크는
다수의 최신 브라우저들이 __proto__
속성을 통해 특정 객체의 프로토타입에 접근할 수 있도록 구현하였는데,
ES 6) 문법에서는 Object.getPrototypeOf(obj)
메서드를 통해 객체의 프로토타입에 바로 접근할 수 있다:
let proto= {}
let obj= Object.create(proto) // obj는 proto를 상속받는다
Object.getPrototypeOf(obj) === proto // true
- 객체 생성자 함수와 this
- 객체 생성자 함수는 객체를 생성하고 초기화하는데, 객체 생성자 함수가 new 로 호출되면; 빈 객체를 생성하여 프로토타입을 설정하고, 객체 초기화를 수행한 다음 자신을 호출한 인스턴스에게 돌려주게 된다. new 로 특정 함수를 호출하게 되면; 해당 함수는 객체 생성자 함수로 작동하게 되는데, 먼저 빈 객체를 생성한 다음 코드 내부에서 this 를 사용하여 동적으로 프로퍼티나 메서드를 생성한다. ← 따라서, 생성자 함수 내부에서 사용되는 this 는 이 빈 객체에 묶이게 된다!
const Fnc= function(name) { // 이 함수는 new로 호출되면 객체 생성자 함수가 된다!
this.name= name,
this.inFnc= function() {
console.log(this.name)
}
} // 여기서 this는 자신을 호출한 인스턴스에 바인딩된다 ← 내부에서 정의한 메서드 안에서 사용되는 this 또한 마찬가지이다!
const Kim= new Fnc('Kjc') // 인스턴스 변수객체 Kim
Kim.inFnc() // Kjc
☞ 함수는 반드시 리턴값 을 반환하는데.. return 문 없는 일반 함수에서는 undefined 를 반환하며, 객체 생성자 함수에서는 별도의 return 문이 없으면 this 로 바운딩된 새로 생성된 객체가 반환된다 ← 때문에 생성자 함수에서는 일반적으로 리턴값을 지정하지 않는다!
✓ 일반 함수 호출의 경우 this 가 Window 전역객체에 바인딩되는 반면, 객체 생성자 함수 호출의 경우에는 새로 생성되는 빈 객체에 바인딩된다는 점에서 함수 호출 시 new 키워드의 잘못된 사용으로 인해 문제를 일으킬 위험성이 생겨난다!
function Car(maker, color) {
this.carMaker= maker
this.carColor= color
}
function myCar(maker, color) { // 변수 객체 myNewCar의 생성자 함수
Car.call(this, maker, color) // 이 this는 Car() 함수에 묶인다!
this.age= 5 // 이 this는 myNewCar 인스턴스에 묶인다!
}
const myNewCar= new myCar('제네시스', '검정색') // new를 써서 myCar를 자신의 생성자 함수로 삼는다
console.log(myNewCar.carMaker + ": " + myNewCar.carColor + ", " + myNewCar.age) // 제네시스: 검정색, 5