객체의 모든 것이라고 적었지만 프로토타입과 상속에 대한 설명

Eamon·2021년 7월 23일
2

JavaScript

목록 보기
7/8
post-thumbnail

객체의 모든 것이라는 어그로성 제목의 글이지만 내가 면접을 보는데 정말 아무 것도 모르는 미지의 세계인 객체, 상속 , 프로토타입 , 생성자 함수 등을 다루는 글이다. 이미 아는 이야기를 할 수 도있고 이상한 이야기를 할 수도있다. 최대한 많은 조사를 해서 사실 기반으로 다루기 위해서 노력했다는 것을 알아주시고 잘못된 정보에 대한 피드백은 언제나 환영입니다.

자바스크립트에서는 거의 모든게 객체입니다. 함수도 객체... 배열도 객체.. 이 글에서는 명확한 호칭을 정리하기 위해 객체라는 것과 최상위 Object 를 다르게 생각하고 글을 써내려갔습니다.

✏️ 객체(Object)란?

자바스크립트는 객체기반의 언어이다. 라는 이야기는 프로그래밍 세계에서 속담처럼 내려오는 구문이다. 그럼 객체가 무엇인지 부터 생각해보자.

자바스크립트에서 객체(Object)는 프로퍼티와 메서드의 집합체 이다.

  • 객체는 변경가능한(mutable) 값(value) 이다.

🔥 프로퍼티

  • 프로퍼티는 key 와 value 로 이루어져있습니다. (객체의 상태를 담당)

🔥 메서드

  • 객체의 프로퍼티에 value 에 함수가 들어간 경우 메서드라고 합니다. (동작을 담당)

🔥 내부 슬롯과 내부 메서드

ECMAScript 사양에서 사용하는 이중괄호[[]]로 감싼 이름들이 내부 슬롯과 내부 메서드이다.

내부 슬롯과 내부 메서드들은 자바스크립트 엔진에서 실제로 동작하지만 개발자가 직접 접근할수 있도록 외부로 공개된 객체의 프로퍼티는 아니다. 그러나 [[prototype]] 의 경우 자바스크립트에서 __proto__ 프로퍼티를 동해 간접적으로 접근할수있다.

데이터 프로퍼티

  • 키와 값으로 구성된 일반적인 프로퍼티다.

[[value]]

  • 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값.
  • 프로퍼티 키를 통해 값을 변경하면 [[value]] 값에 재할당한다.

[[writable]]

  • 프로퍼티값의 값의 변경 가능 여부를 나타내며 불리언 값을 가진다.

[[Enumerable]]

  • 프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 가진다.

열거가능 하다 라는 말은 for ...in , forEach 메소드를 사용 할 수 있다는 것이다.

[[Configurable]]

  • 프로퍼티의 재정의 가능여부를 나타내며 불리언 값을 가진다.
  • false 인경우 해당 프로퍼티의 삭제 값의 변경이 금지된다.
  • 단 [[writable]] 이면 값의 변경은 가능하다.


접근자 프로퍼티

  • 접근자 프로퍼티는 객체가 자체적으로는 값을 가지지 않고 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수이다.

[[Get]]

  • 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수이다.

[[Set]]

  • 접근자 프로퍼티를 통해 데이터 프로퍼티 값을 저장할때 호출 되는 접근자 함수이다.

대표적인 접근자 프로퍼티는 __proto__ 가 있는데 get __proto__ 와 set __proto__ 를 이용해서 [[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있다.

📝 함수가 자바스크립트의 1급 객체다 📝

  • 이 역시 자주 등장하는 자바스크립트 속담 중 하나인데 , 여기서 1급 객체라는 말을 아래 조건을 만족하는 객체를 말한다.
  1. 런타임에 생성이 가능하다.
  2. 변수에 저장이 가능하다.
  3. 함수의 매개변수에 전달 할 수 있다.
  4. 함수의 반환 값으로 사용할 수있다.
  • 이는 자바스크립트의 함수가 객체와 동일하게 사용할 수 있다는 것이고 객체는 (할당, 전달, 반환 가능) 으로 평가 될 수 있습니다.
  • 위의 조건들을 만족하는 자바스크립트 함수의 특징은 값으로 평가 될 수 있다. 라고 축약해서 말할수 있겠습니다.


✏️ 프로토 타입

자바스크립트는 프로토 타입객체를 이용해서 상속을 구현한다. 모든 객체는 [[Prototype]] 이라는 내부슬롯을 가지며 이 내부 슬롯의 값은 프로토타입의 참조다.

[[Prototype]]에 저장되는 값은 객체가 생성될때 객체 생성 방식에 따라 프로토 타입이 결정되고 저장된다.

✏️ __proto__ 접근자 프로퍼티

proto 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티다.

[[Prototype]]에 접근자 프로퍼티를 이용해서 접근하는 이유는 사옿 참조에 의해 프로토 타입 체인이 생성되는 것을 방지 하기 위해서 이다.

상호 참조라는 것은 자식의 프로토타입이 부모를 가리키고있지만 부모의 프로토 타입도 자식을 가리키고있을 때를 말한다.

그러나 코드 내에서 proto 접근자 프로퍼티를 이용해서 상속을 구현하는 것은 추천되지 않는다.

몇가지 예외사항이 있기 때문인데. 직접 상속을 통해 Object.prototype 을 상속 받지 않는 객체를 생성 할 수있기때문에 proto 에 접근하지 못하는 경우가 생긴다.

함수 객체의 prototype 프로퍼티

함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토 타입을 가리킨다.

prototype 의 contstructor 프로퍼티 와 생성자 함수

모든 프로토 타입은 constructor 프로퍼티를 가진다.



✏️ 객체를 생성하는 방법

  • 객체 리터럴
  • Object 생성자함수
  • Object.create
  • 생성자 함수
  • 클래스 (ES6)

객체를 생성하는 방법과 그 방법에 따른 상속은 어떻게 이뤄지는지 알아보자.



🔥 객체리터럴

리터럴 은 사람이 이해할수있는 문자를 사용해서 값을 생성하는 표기법을 말합니다 .객체리터럴은 중괄호 안의 {} 키와 value 로 선언된 프로퍼티들을 해석해서 객체를 생성한다.

const obj = {
    key : 'value'
}

선언된 프로퍼티를 해석해서 Object.prototype 을 프로토 타입으로 가지게 되며 상속받는다. 그렇기 때문에 obj 는 접근자 프로퍼티__proto__와 constrictor 등의 프로퍼티를 자신의 것처럼 쓸 수 있습니다.

  • 문제점 : 동일한 프로퍼티를 가진 객체를 여러개 생성해야하는 경우 매번 같은 프로퍼티를 기술해야한다. (비효율적)


🔥 Object 생성자함수

new 연산자와 함께 생성자 함수를 호출하면 빈객체를 생성하여 반환한다. 빈객체를 생성한 이후 프로퍼티와 메서드들을 추가하여 객체를 완성할 수 잇다 . 생성자 함수(constructor)에 의해서 생성된 객체를 인스턴스 라고 한다.

const obj = new Object()

Object 생성자 함수에 의해 생성된 obj 객체는 객체리터럴에 의한 생성과 같이 Object.prototype을 프로토 타입으로 가지게 되고 상속받습니다.

constructor 프로퍼티는 생성자함수 Object를 가르킨다.

constructor - Object

obj.constructor === Object // true

Object.protoype 는 생성된 객체의 프로토타입에 저장된다.

[[Prototype]] <- Object.protoype

obj.__proto__ === Object.prototype // true


🔥 생성자 함수 and Class

Object , String, Number, Array 등 자바스크립트의 빌트인 생성자 함수 뿐만이 아니라, 우리가 만든 함수에도 new 를 붙이면 생성자 함수가 된다.

함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토 타입도 더불어 생성된다.

생성자 함수에서의 객체생성은 다음과 같은 과정으로 생성된다.

function Person(name) {
    this.name = name;
}

const me = new Person('hoo');
  1. 인스턴스 생성과 this 바인딩
  • 생성자 함수가 호출되면 암묵적으로 빈객체를 생성하고 this 생성된 객체(인스턴스)를 바인딩한다.
  1. 인스턴스 초기화
  • 생성된 빈 인스턴스에 개발자가 함수 몸체에적은 코드대로 초기화를 시켜준다.
  1. 인스턴스 반환
  • 함수 내부의 처리가 끝나면 완성된 인스턴스인 this 가 반환된다.

Person 생성자 함수에 의해 생성된 me 객체는 Person.prototype을 프로토 타입으로 가지게 되고 상속받습니다.

constructor 프로퍼티는 생성자함수 Person 을 가르킨다.

constructor - Person

me.constructor === Person // true

Person.protoype 는 생성된 객체의 프로토타입에 저장된다.

[[Prototype]] <- Person.protoype

obj.__proto__ === Object.prototype // true

me 객체는 프로토타입 체인을 통해서 최상위 객체의 prototype에 도달할수있다.

me.__proto__.__proto__ === Object.prototype //ture

class 에 의한 객체 생성 또한 생성자 함수의 객체 생성 에서의 prototype 과 constructor 규칙과 유사하다.

class animal {}

const cat = new animal
cat.constructor === animal // true

animal.protoype 는 생성된 객체의 프로토타입에 저장된다.

[[Prototype]] <- animal.protoype

cat.__proto__ === animal.prototype // true

함수가 호출되는 방식에 따라 달라지는 함수 안의 this

  • 일반 함수 호출 : this === 전역 객체
  • 메서드 호출 : this === 메서드 호출 객체
  • 생성자 함수 호출 : this === 생성자 함수가 생성할 인스턴스


🔥 Object.create

  • Object 객체에 있는 create 함수를 이용한 객체를 생성한다.
  • 직접상속이라고도 부른다

Object.create(proto[, propertiesObject])

let obj = Object.create(null)

create 의 인자에 null을 넣으면 프로퍼티가 하나도 없는 빈객체가 생성된다. 이 객체는 prototype 이 null 이기 때문에 __proto__ 프로퍼티도 접근이 불가능하다.

let bam = Object.create(Object.prototype)

proto 라는 인자에 Object.prototype 객체를 넣으면 객체리터럴이나 Object 생성자함수에서 생성되는 객체와 같이 Object.prototype 를 프로토타입으로 가지는 객체가 생성됩니다.


프로토 타입 체인(상속)

  • 자바스크립트는 객체의 프로퍼티에 접근하려고할대 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토 타입의 프로퍼티를 순차적으로 검색한다.

❓ 리터럴 표기법에 의해 생성되는 객체도 생성자 함수와 연결되어지기 때문에 모든 객체는 생성자함수에 연결되어있다. 자바스크립트는 왜 Function.prototype 을 만들어서 생성자 함수들의

profile
Steadily , Daily, Academically, Socially semi-nerd Front Engineer.

0개의 댓글