
그림이 잘안보이겠지만 프로토타입에서가장중요한건 이 삼각형(?)이다.
그러니까 윗변엔 Contructor(생성자 함수)를 오른쪽 꼭짓점엔 Constructore.prototype 이라는 프로퍼티 왼쪽 꼭짓점 아래 화살표 중간엔 new 그 끝엔 instance가 존재 다시 오른쪽 꼭짓점 에서 내려오는 화살표 끝에는 instance.__proto__프로퍼티가 존재 한다.
어떤 순서로 진행되는지 보면
이과정을 코드로 보자면
var Count = (num)=>{
this.num=num
}//생성자 함수(Constructor)
let ten = new Count(10)
//인스턴스(instance) -> Constructor + new
__proto__ 라는 프로퍼티가 자동부여되고 이 프로퍼티는 Constructor.prototype이라는 프로퍼티 참조
__proto__가아닌 [[prototype]]라는 명칭으로 정의되어 있다. 또, instance.__proto__와 같은 방식으로 직접 접근하는것은 허용x Object.getPrototype(instance) 등을 통해서만 접근할수있도록 정의 했었다. 그러나, 이런 명세에도 불구하고 대부분의 브라우저들이__proto__을 포기하지 않앗고 es6에서는 결국 이를 브라우저에서동작하는 레거시 코드에 대한 호환성 유지차원에서인정하기에 이르렀다. 어찌됐든 계속 사용은하겠지만 실무에선 가급적 Object.getPrototype(instance) 등을 사용하라는 말이있다
예제를 통해 살펴보자 Person 이라는 생성자 함수의 prototype에 getName 메서드 지정한경우
let Person = function(name){
this._name = name;
}
Person.prototype.getName = function(){
return this._name
}
let susan = new Person('susan')
susan.```__proto__``` .getName() //undefined
Person.prototype === susan.```__proto__``` //true
첫번째로 볼것은 getName() 부분이 undefined라는점이다. 에러가 없다는건 호출할수 있는 함수에 해당하는것으로 어쨋든 getName이 함수라는것이다. 아니엿다면 TypeError가 나왓을것
다음으로 this.name을 리턴하는 부분인데 this에 할당되는것이 메서드인경우엔 바로앞의 객체고 곧 this가 된다 그러니깐 this._name -> susan._name 형태로 되는것이다. 그런데 위에보면 susan.__proto__ 는 this -> susan.__proto__ 가되어 susan.__proto__ .name의 형태 식별자가 정의되어있지 않을때인 undefined가 나왔던것이다.
그렇다면 어떻게 하면될까? 간단하다
susan.getName() //proto를 제거한다.
//번외
let susan = new Person('susan')
susan.__proto__._name = 'sss'
susan.__proto__.getName() //'sss'
//원하는대로 잘 출력이 됐다. 관건은 this이다. this를 인스턴스로 쓸수 있으면 마찬가지로 원하는결과 proto를 제거했을때 susan이 출력되는 결과를 얻을수 있다.
이상한점이 있을것이다 위의 삼각형에서 분명 constructor.prototype을 instance.__proto__ 이 참조한다고 했기때문이다. 근데 왜 없어야만 하는걸까? 사실 별거아니다 이런걸 보통 규칙이라고 할지 모르겟는데 그냥 생략 가능한 프로퍼티 이기때문이다. 원래부터 그렇게 설계되어 왔다고 한다.

코어 자바스크립트 참조
let arr = [1,2]
arr.forEach(()=>{}) //(0)
Array.isArray(arr); //(0)
arr.isArray()//(X)
Array를 new연산자와 함께 arr = new Array(1,2) 해서 인스턴스를 생성하든, 그냥 배열 리터럴을 하든 insatnce인 [1,2] 가 만들어진다. 앞서 __proto__는 constructor.prototype을 참조한다고 하였으니 위 에선 Array.prototype을 참조 할것이다. __proto__가 생략 가능하므로 push, pop등의 메서드를 자신의 것처럼 호출 할 수 있다.
Array의 prototype 프로퍼티 내부에 있지않은 from, isArray 등의 메서드들은 인스턴스가 직접 호출할 수 없다. 이유는 위에서 보다시피 instance가 참조하는건 array.prototype인데 해당되지 않기때문에 instance는 사용할수 없다 그렇기 때문에 Array 생성자 함수를 통해 직접 실행해야 한다.
생성자 함수의 프로퍼티인 prototype 객체 내부에 constructor 프로퍼티 가 존재한다 __proto__ 도 마찬가지 이다. 이 프로퍼티의 역할은 생성자 함수(자기 자신)을 참조한다.
let arr = [1,2]
Array.prototype.constructor===Array //true
arr.`__proto`.constructor===Array; //true
arr.constructor===Array//true
마찬가지로 __proto__ 생략 가능하기 때문 직접 접근 가능,자기 자신을 가리키므로 위사항들이 성립
contructor는 읽기 전용 속(기본형 리터럴 변수 number, boolean, string) 제외하고는 값을 바꿀 수 있다.
let NewCon = function(){
console.log('this')
}
var dataTypes=[1,'test',true,{},[],function(){},/test/,new Number(), new String()]
dataTypes.forEach((d)=>{
d.constructor = NewCon;
console.log(d.constructor.name,'&',d instanceof NewCon)
})
Number & false
String & false
Boolean & false
NewCon & false
NewCon & false
NewCon & false
NewCon & false
NewCon & false
NewCon & false
d instanceof 에 대해서 false 반환 contstructor를 변경하더라도 참조하는 대상이 변경될뿐 이미 만들어진 인스턴스의 원형이 바뀐다거나 탑이 변하진 않는다. 어떤 인스턴스의 생성자 정보를 알아내기 위해 contructor 프로퍼티에 의존하는게 항상 안전하지는 않은것이다.
프로토타입 객체를 참조하는 __proto__를 생략하면 인스턴스는 프로토타입에 정의된 프로퍼티나 메서드를 자신의 것처럼 사용할수 있다고 했다. 인스턴스가 만약 동일한 이름의 프로퍼티, 메서드라면?
let Person = function(name){
this.name = name
}
Person.prototype.getName = function(){
return this.name;
}
let gd = new Person('지용');
gd.getName = function(){
return '아니' + this.name
}
console.log(gd.getName()) //아니 지용
console.log(gd.__proto__.getName()) //this가 prototype 객체(gd.__proto__)를 가리키는데 property상에는 name property가 존재하지 않는다.
getName의 메서드가 아니 지용이 호출됐다. 이러한 현상을 메서드 오버라이드(덮어씌우기) 라고한다. 제거하기 보단, 원본이 있는 상태에서 그위에 얹는 느낌이다.
js엔진이 getName이라는 메서드를 찾는 방식은 가장 가까운 대상인 자신의 프로퍼티를 검색하고, 없으면 그다음으로 가까운 대상인 __proto__를 검색하는 순서로 진행 되기 때문에 우선순위에서 밀린것이다.
아까 언급했듯 원본이 유지되어 있기때문에 원본에 접근할수 있는 방법도 있다.
//프로퍼티 에 name 프로퍼티가 있다면 그값을 출력
Person.prototype.namae = '권지용'
Person.prototype.getName = function(){
return this.namae;// 위값은 이걸 반영하게 된다.
}
console.log(gd.__proto__.getName()) //권지용
//person.prototye <-this
//__proto__ 는 생정자함수.prototype을 참조하기때문에
//마지막으로 this가 prototype 바라보는데 -> 인스턴스를 바라보도록하는 방법은 call, apply로
gd.__proto__.getName.call(gd)
prototype.getName 메서드가 호출되고 있다.