
__proto__ 속성을 이용한 상속const user = {
name : 'Mike',
};
console.log(user.hasOwnProperty('name')); // true
console.log(user.hasOwnProperty('age'));
// false

hasOwnProperty() 메서드는 객체가 직접 가지고 있는 프로퍼티가 맞는지 여부를 알려준다. user 객체에서 hasOwnProperty() 메서드를 만들어준 적이 없는데, 어떻게 사용이 가능할까? 이는 user 객체가 Object.prototype 객체를 상속 받기 때문이다. 즉, hasOwnProperty() 메서드는 Object.prototype 객체에 존재한다.
사용할 프로퍼티가 객체에 있으면 탐색을 멈춘다. 만약, 객체에 찾고자 하는 프로퍼티가 없다면 해당 객체가 상속받는 객체에서 프로퍼티를 탐색한다. 위 코드의 경우, user 객체에 hasOwnProperty() 메서드가 없기에 부모 객체 Object.prototype에서 hasOwnProperty() 메서드를 탐색하여 메서드에 접근하였다.
📝 참고: 객체의 프로퍼티란 일반적으로 객체의 키-값 쌍에서 키(key)를 의미한다. 정확히는 키와 값의 쌍 자체를 프로퍼티라고 한다. 메스드 또한 프로퍼티에 해당한다. 메서드명이 키가 되고, 함수가 값으로 들어가기 때문에 메서드도 프로퍼티의 일종이다.
만약 객체가 hasOwnProperty() 메서드를 직접 가지고 있으면 어떻게 될까? 사용할 프로퍼티가 객체에 있으면 탐색을 멈추기에, Object.prototype 에서 해당 프로퍼티를 더 이상 찾지 않는다.

const bmw = {
color: "red",
wheels: 4,
navigation: 1,
drive() {
console.log("drive..");
},
};
const benz = {
color: "black",
wheels: 4,
drive() {
console.log("drive..");
},
};
const audi = {
color: "blue",
wheels: 4,
drive() {
console.log("drive..");
},
};
세 객체들은 모두 wheels와 drive()를 프로퍼티로 갖는다. 차 객체가 늘어날수록 새로운 변수가 만들어지며 코드가 길어진다. 따라서, __proto__ 속성을 이용하여 공통된 부분을 처리해보자.
const car = {
wheels: 4,
drive() {
console.log("drive..");
},
};
const bmw = {
color: "red",,
navigation: 1,
};
const benz = {
color: "black",
};
const audi = {
color: "blue",
};
bmw.__proto__ = car;
benz.__proto__ = car;
audi.__proto__ = car;

대략적인 클래스 다이어그램으로 나타내면 위와 같다. 함수를 제외한 모든 객체는 생성되면 __proto__ 속성을 가진다. 이때 __proto__ 속성은 상속 받을 객체를 가리킨다. bmw, benz, audi 객체는 car라는 객체를 상속받기 때문에 __proto__ 속성은 car 객체를 가리킨다. 그리고, 모든 객체는 Object.prototype이라는 객체를 상속 받는다. 따라서 car 객체의 __proto__ 속성은 Object.prototype 객체를 가리켜 상속받는다.
console.log(bmw.wheels); // 4
bmw 객체에서 wheels 프로퍼티를 찾는다. 이때, bmw 객체에는 해당 프로퍼티가 없기에, __proto__ 속성이 가리키는 car 객체에서 wheels 프로퍼티를 탐색한다. 그리고, 해당 프로퍼티를 찾아서 접근한다.
const car = {
wheels: 4,
drive() {
console.log("drive..");
},
};
const bmw = {
color: "red",,
navigation: 1,
};
bmw.__proto__ = car;
const x5 = {
color: "white",
name: "x5",
};
x5.__proto__ = bmw;
console.log(x5.name); // "x5"
console.log(x5.color); // "white"
console.log(x5.navigation) // 1
x5 객체에 name과 color 프로퍼티가 있기에 탐색을 멈추고, 값을 가져온다. 반면 navigation 프로퍼티는 x5 객체에 없기에, x5 객체의 부모 객체인 bmw에서 프로퍼티를 탐색을 하고 접근한다.

이처럼 객체는 자신의 프로퍼티뿐만 아니라, __proto__ 속성이 가리키는 객체의 속성 및 메서드에도 접근할 수 있다. 이것이 바로 프로토타입 체인(Prototype Chain)이라고 한다.
for(p in x5) {
console.log(p);
} // color name navigation wheels drive
Object.keys(x5); // ["color", "name"]
Object.values(x5); // ["white", "x5"]
Object.keys()와 Object.values() 메서드는 객체 자신이 가진 프로퍼티만을 반환한다.
for(p in x5) {
if (x5.hasOwnProperty(p)) {
console.log('o', p);
} else {
console.log('x', p);
}
} // o color, o name, x navigation, x wheels, x drive
hasOwnProperty()는 객체가 직접 가지고 있는 프로퍼티들만 true를 반환한다.
function Person(){};
var Person = new Function();
📝 참고: 자바스크립트에서는 함수는 객체다. 따라서 new 연산자와 함께 Function()이라는 생성자 함수를 호출하면 함수를 표현할 수 있다. 이처럼, 함수는 객체이기에 프로퍼티를 가질 수 있다.
이때, 일반 함수와 생성자 함수를 구분해야 한다. 자바스크립트에서 함수는 객체이기에 직접 속성과 메서드를 가질 수 있다. 함수 객체에 추가한 속성과 메서드는 그 함수 자체에서 접근이 가능하다.
function myFunction() {}
myFunction.prop = "I am a property";
myFunction.sayHello = function() {
console.log('Hello!');
};
console.log(myFunction.prop); // "I am a property"
myFunction.sayHello(); // "Hello!"
하지만 생성자 함수는 객체를 생성하기 위해 사용되므로 this 키워드를 사용해서, 생성된 인스턴스에 속성을 추가한다. 해당 속성은 생성된 인스턴스에만 저장되고 생성자 함수 객체에는 저장되지 않는다. 생성자 함수의 메서드는 생성자 함수의 프로토타입 객체에 저장된다.
__proto__ 속성은 생성자 함수의 프로토타입을 가리킨다는 것을 이용해서 코드 줄이기 예시const car = {
wheels: 4,
drive() {
console.log("drive..");
},
};
const Bmw = function(color) {
this.color = color;
};
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
x5.__proto__ = car;
z4.__proto__ = car;
위 코드를 사용해도 되지만 __proto__ 속성의 값을 지정해야하는 번거로움이 있다. 생성자 함수로 만든 객체의 __proto__ 속성은 기본적으로 생성자함수의 프로토타입을 가리키고 있다는 사실을 이용해서 코드를 더 간단하게 줄일 수 있다.
const Bmw = function(color) {
this.color = color;
};
Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function() {
console.log("drive..");
};
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
console.log(x5.wheels); // 4
console.log(z4.drive()); // "drive.."
const Bmw = function(color) {
this.color = color;
};
Bmw.prototype = {
wheels: 4,
drive() {
console.log("drive..");
},
};
const z4 = new Bmw("blue");
console.log(z4.constructor === Bmw); // false
⚠️ Bmw.prototype, 즉 Bmw 생성자 함수의 프로토타입에 객체를 할당할 때, 반드시 생성자 함수를 가리키는 constructor 속성이 있어야한다. 그래야, 생성자 함수의 프로토타입 객체가 원래의 생성자 함수와 올바른 연결을 유지할 수 있기 때문이다. 본래, 생성자 함수는 생성자 함수의 프로토타입 객체를 가리키는 prototype이라는 속성을 가진다. 그리고, 생성자 함수의 프로토타입 객체는 생성자 함수를 가리키는 constructor 속성을 가진다.
z4 객체의 __proto__ 속성은 본디 해당 객체를 만든 생성자 함수의 프로토타입 객체를 가리킨다. 따라서, Bmw 생성자함수의 프로토타입에 값을 할당할 때, 생성자 함수를 가리키는 constructor 속성이 없다면, z4.constructor은 Bmw를 반환하지 못한다. 즉, 올바른 생성자 함수를 가져올 수 없다. 생성자 함수의 prototype 속성은 생성자 함수의 프로토타입 객체를 가리키고 생성자 함수의 프로토타입 객체의 constructor 속성은 생성자 함수를 가리킨다.

const Bmw = function(color) {
this.color = color;
};
Bmw.prototype = {
constructor: Bmw, // constructor 속성을 수동으로 적어주자.
wheels: 4,
drive() {
console.log("drive..");
},
};
const z4 = new Bmw("blue");
console.log(z4.constructor === Bmw); // true
생성자 함수가 새로운 객체를 만들어낼 때, 새로 만들어진 객체는 생성자의 인스턴스라고 한다. instanceof 연산자는 사용자가 넘겨준 객체가 특정 생성자 함수로 만들어졌는지true 혹은 false로 반환한다.
z4 instanceof Bmw // true
z4 객체는 Bmw 생성자 함수로 만들어낸 객체로 true를 반환한다.