해당 내용은 '코어 자바스크립트' 서적을 참고하여 작성되었으며, 초보 개발자에게 유익한 책을 지필해주신 지은이 정재남 님께 감사의 말씀을 드립니다.
// 생성자함수 정의
var Person = function (name) {
this.name = name;
};
// 메서드 정의
Person.prototype.getName = function () {
return this.name;
};
// 인스턴스 정의
var instance = new Person('son');
// 인스턴스의 메서드 정의(프로토타입 메서드와 이름이 같다)
instance.getName = function () {
return '새로운' + this.name;
};
console.log(instance.getName()); // 새로운 son (출력값)
//위 예시에서 보면 기존 생성자 함수의 메서드가 아닌 인스턴스의 메서드가 실행되었음을 알 수 있다.
그렇다면 기존 생성자 함수의 메서드는 없어졌을까? 아니다
이런 현상을 메서드 오버라이드 라고 한다.
인스턴스 자체의 메서드 식별자와 생성자 함수의 메서드 식별자 같을 때, 인스턴스에서는 자신이 가진 메서드를 실행시켜 기존에 받아왔던 메서드를 덮어버리는 현상이다.
그렇다면 우회적인 접근이 가능할까? 그렇다
console.log(instance.__proto__.getName.call(instance)); // son
// 이렇게 __proto__ 프로퍼티를 활용하고, call 메서드로 this를 지정해주어 실행시킬 수 있다.
Array
객체 구조를 참고할 수 있다.console.dir(Array);
/* 굉장히 길게 출력되는데 여기서 주요하게 확인하는 부분은 __proto__ 이다.
ƒ Array()
arguments: (...)
caller: (...)
from: ƒ from()
isArray: ƒ isArray()
length: 1
name: "Array"
of: ƒ of()
prototype: [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
Symbol(Symbol.species): (...)
get Symbol(Symbol.species): ƒ [Symbol.species]()
__proto__: ƒ () // 앞서 배웠다시피 해당 프로퍼티가 존재한다. 그런데 그 안을 보면
apply: ƒ apply()
arguments: (...)
bind: ƒ bind()
call: ƒ call()
caller: (...)
constructor: ƒ Function()
length: 0
name: ""
toString: ƒ toString()
Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
get arguments: ƒ ()
set arguments: ƒ ()
get caller: ƒ ()
set caller: ƒ ()
__proto__: Object // 여기 똑같은 프로퍼티 하나 더 있는 것을 알 수 있다.
[[FunctionLocation]]: <unknown>
[[Scopes]]: Scopes[0]
[[Scopes]]: Scopes[0]
*/
왜냐하면 prototype 또한 하나의 객체이기 때문에 Object 생성자의 prototype을 받는 것이다.
이런식으로 __proto__
속성이 연쇄적으로 이어진 것을 프로토타입 체인 이라고 하며,
이 체인을 따라가는 것을 프로토타입 체이닝 이라고 한다.
그렇다면 모든 생성자 함수에는 prototype 프로퍼티가 존재하는데, 그럼 모두 Object prototype을 상속받는가? 그러하다
// 객체 인스턴스 정의
var instance = new Object();
// 객체 메서드 정의
Object.prototype.getArr = function () {
var result = [];
for (prop in this) {
if (this.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
};
// 데이터 정의
var data = [{ a: 1, b: 2, c: 3 }, [1, 2, 3], 'abc', true];
// 각 요소별로 객체 메서드 실행
data.forEach((el) => {
console.log(el.getArr());
});
/*
[ 'a', 'b', 'c' ]
[ '0', '1', '2' ]
[ '0', '1', '2' ]
[]
모두 에러없이 실행된다.
여기 주목해야할 점은 객체가 아닌 데이터 타입인데도 불구하고 객체 메서드가 실행된다는 점이다.
*/
Object.freeze()
) 로써 두며,// 생성자 함수 정의
var Constructor = function () {
let num = 0;
for (el in arguments) {
this[el] = arguments[el];
num++;
}
this.length = num;
};
var instance = new Constructor(1, 2, 3);
console.dir(instance);
/* 출력값을 보면 데이터 형태는 배열과 유사하다.
Constructor
0: 1
1: 2
2: 3
length: 3
__proto__:
constructor: ƒ ()
__proto__: Object // 하지만 배열 메서드를 사용할 수 없다(유사배열객체).
*/
Constructor.prototype = []
명령어만 추가하면 된다.// 기존
var instance = new Constructor(1, 2, 3);
instance.pop() // instance.pop is not a function
// 변경(추가)
Constructor.prototype = [];
var instance = new Constructor(1, 2, 3);
console.log(instance.pop()); // 3 출력