[코어자바스크립트] prototype
📚 What is TIL?
프로토타입
자바스크립트는 프로토타입 기반 언어이다
클래스 기반 언어에서는 ‘상속’을 사용하지만 프로토타입 기반 언어에서는
어떤 객체를 원형으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다
🔎 어떤 생성자 함수를 new 연산자와 함께 호출하면
Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성된다
🔎 이때 instance에는 __proto__ 라는 프로퍼티가 자동으로 부여되는데
이 프로퍼티는 Constructor의 prototype이라는 프로퍼티를 참조한다
prototype와 이를 참조하는 __proto__ 는 모두 객체이다
prototype 객체 내부에는 인스턴스가 사용할 메서드를 저장한다
코드로 살펴보기
var Person = function (name) {
this._name = name;
};
Person.prototype.getName = function() {
return this._name;
};
var suzi = new Person('Suzi');
그럼 실제 코드로 한 번 살펴보자
Person 함수는 인자로 받은 name 값을 _name 속성값으로 가진다Person.prototype 프로퍼티에 getName 함수를 추가하고
getName 함수에는 this._name으로 해당 인스턴스의 _name 속성값을 반환하도록 작성되어있다suzi 변수에 new Person(‘Suzi’)을 할당해서,
Person 함수를 생성자로 사용해서 suzi 라는 인스턴스를 생성한다
여기서 생성자 함수를 호출할 때 this 는 새로 생성된 인스턴스 suzi를 가리키는데
suzi._name 속성에는 ‘Suzi’가 할당된다suzi 인스턴스에는 __proto__라는 프로퍼티가 자동으로 부여되고
Person.prototype 이라는 프로퍼티를 suzi.__proto__ 이라는 프로퍼티가 참조하고있다
개발자 도구로 디렉터리 구조 보기
console.dir(Person)
console.dir(suzi)
그럼 Person과 suzi가 각각의 디렉터리 구조가 어떠한지 살펴보자
출력결과, 첫 줄에는 함수라는 의미의 f 와 함수 이름인 Person이 보인다
그리고 두 번째 줄에는 suzi가 아닌 Person이라고 나와있다
💡 왜 Person이라고 나와있을까?
바로, 어떤 인스턴스가 있을때 그 인스턴스가 어떤 생성자 함수에 속해있는지
이름을 표기함으로써 해당 함수의 인스턴스임을 알려주기 위함이다 따라서, Person이 찍히게 되는 것이다
우선 Person 함수의 디렉터리부터 열어보면,
그 안에는 여러가지 속성들이 존재하는걸로 보여지는데
그 중에서 name 속성과 prototype 속성을 살펴보면
name 이라는 속성에는 Person 이라고 나와있다
🔎 이 name 이라는 속성은 함수를 정의할때 자동으로 생성되는데
🔎 설명하자면, 함수를 익명 함수로 정의하면 name의 값은 빈 문자열이 되고
🔎 함수의 이름을 지정해서 정의하면 그 이름이 name의 값이 된다
그 다음 prototype을 열어보면
아까 Person.prototype 속성에 추가했던 함수인 getName 이 있는 것을 확인할 수 있다
이제 이어서 suzi 인스턴스 의 디렉터리를 열어보면,
_name의 속성이 ‘Suzi’라고 보여지는데
이것은, 아까 코드를 통해 suzi 라는 인스턴스를 생성할때
suzi._name 속성에는 ‘Suzi’라고 할당되었기 때문이다
이제
[[prototype]]
라는 내부슬롯도 한번 열어보자
🔎 [[prototype]]
(내부슬롯) 에는 상위 객체가 저장하고 있는 prototype 속성에서 상속받은정보가 존재한다
🔎 Person 함수가 가지고있던 prototype 속성의 내부와 같은 형태인것을 확인 할 수 있다
🔎 이것을 통해서 알 수 있는 것은 상위 객체의 prototype 속성에서 상속 받은 정보를 참조하고 사용할 수 있다는 것이다
var suzi = new Person('Suzi');
suzi.__proto__._name = 'SUZI__PROTO__'
suzi.__proto__.getName(); // SUZI__PROTO__
🔎 따라서 __proto__ 을 통해 상속 받은 정보를 사용할 수 있다
각 속성의 색상을 살펴보면 색이 짙은색과 옅은색으로 나뉘어져있는데
이것은 enumerable 이라는 속성 때문이다
🔎 객체의 프로퍼티에 대한 정보를 제공하는 디스크립터(descriptor)에는 enumerable 속성도 포함되어있다
🔎 해당 속성이 열거가 가능한지 불가능한지의 여부 에따라 구분을 짓고자 enumerable(이넴뭐러블) 속성이 존재한다
🔎 여기서 말한 열거가 가능하다는 것은 객체의 속성를 순회하거나 조작하는 작업을 얘기한다
🔎 따라서, 이 속성값을 통해 방금과 같이 개발자도구에서 직관적으로 확인이 가능하다
요약
💡 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면
인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근 할 수 있다
Array 생성자 함수
var literalArray = [1, 2];
// var literalArray2 = new Array;
console.dir(literalArray);
console.dir(Array);
// console.dir(literalArray2);
이번에는 내장 생성자 함수인 Array와 배열 리터럴로 생성된 배열을 비교하며 살펴보자
1과 2를 원소로 가지고있는 literalArray와 생성자 함수인 Array가 있다
🔎 각각의 디렉터리를 콘솔로 확인해보면 마찬가지로 literalArray의 상속받은 정보들이
Array 생성자 함수의 prototype 속성에서 상속을 받아 생성되었다는 것을 확인할 수 있다
🔎 Array를 new 연산자와 함께 호출해서 인스턴스를 생성했을 때도 마찬가지의 결과를 보여줄것이다
literalArray.push(...);
literalArray.pop(...);
literalArray.map(...);
literalArray.filter(...);
🔎 따라서, 인스턴스가 push, pop, map 등의 메서드를 마치 자신의 것 처럼 호출 할 수 있다
🔎 이러한 Array 객체도 사용자 정의 객체와 동일하게 __proto__를 생략해서 사용이 가능하다
Array 생성자 함수의 prototype을 상속받은 인스턴스를 이용해서 호출할경우
__proto__가 생략 가능하도록 설계되어있기 때문에 __proto__를 생략할 수 있다
🔎 하지만 예외적으로 Array 생성자 함수의 prototype 내부에 존재하지 않는 from이나 isArray 등의 메서드들은
인스턴스를 통해 호출하는 것이 아니라 Array 생성자 함수에서 직접 접근해야 실행이 가능하다
var arr = [1, 2];
arr.forEach(function (){});
Array.isArray(arr);
arr.isArray(); // TypeError: arr.isArray is not a function
이런식으로 객체에 직접적으로 접근해서 사용할 수 있는 메서드들을 정적 메서드 라고한다
💡 왜 이런식으로 메서드를 다른방식으로 접근하도록 만들었을까?
그 이유 중 하나는 인스턴스와 구분하기 위함인데
정적 메서드들은 생성자 함수 자체에 속하는데, 인스턴스와는 직접적인 연관이 없기 때문에
객체의 인스턴스에서 호출되는 인스턴스 메서드와 구분하기 위한 목적이 있기 때문이라고 할 수 있다
댓글남기기