JS Deep Dive | 2021.12.18

invidia·2021년 12월 18일
0

TIL

목록 보기
8/29

18 함수와 일급 객체

정리

일급 객체

  • 일급 객체
    • 조건
      1. 런타임에 생성이 가능
      2. 변수나 자료구조에 저장이 가능
      3. 함수의 매개변수로 전달이 가능
      4. 함수의 반환값으로 사용이 가능
    • 의미
      • 일반적인 변수들은 일급 객체의 조건을 만족한다. 즉 일급 객체라는 건 값과 동일한 취급을 받는다. 함수가 일급객체라는 말은 즉, 함수는 값과 동일한 취급을 받을 수 있다는 것을 보장한다는 의미이다.

함수의 데이터 프로퍼티

  • arguments 프로퍼티

    • 함수호출 시 전달한 인자의 정보들을 유사 배열 객체로 담고있는 프로퍼티이다.
    • 유사 배열 객체: 순회는 가능하나배열 메서드를 사용할 수 없다.
  • caller 프로퍼티

    • 함수 본인을 호출한 다른 함수를 가리킨다.
  • length 프로퍼티

    • 함수 정의 과정에서 선언한 매개변수의 개수를 가리킨다.
  • name 프로퍼티

    • 함수의 이름을 가리킨다.
    • 함수 이름과 함수 객체 식별자는 엄연히 다르다.
  • __proto__프로퍼티

    • 프로토타입 객체에 간접 접근하기 위해 필요한 프로퍼티이다.
  • prototype 프로퍼티

    • 생성자함수로 실행될 때, 생성할 인스턴스의 프로토타입 객체를 가리킨다. (?)

느낀점

  • 함수형 프로그래밍과 프로토타입에 대해 좀 더 배워야겠다는 생각이 든다.
    • 설명은 이해하였는데 의미는 이해하지 못한 느낌이다.

19. 프로토타입

정리

용어 정리

  • 객체지향 프로그래밍

    • 객체의 상태와 동작을 묶은 자료구조로 프로그램을 표현
    • 명확히 구분되는 요소가 많고, 실행해야 할 연산이 비교적 적을 때 유용하다.
  • 상속과 프로토타입

    • 생성자 함수의 문제점: 동일한 함수를 중복 생성한다.
    • 상속을 이용한 해결방안: 프로토타입을 이용하여 동일함수를 공유하게끔한다.
      • 공유를 하더라도 순차적으로 실행되기에 겹치거나 하는 문제가 없다.
      • this가 항상 다른 값을 가리키기에 계산에 문제가 없다.
  • 프로토타입과 상속

    • 상속방식: 생성자 함수가 생성한 인스턴스는 상위역할을 하는 프로토타입의 프로퍼티와 메서드를 상속한다.
    • 접근방법
      • [[Prototype]] 은 내부슬롯이며, 내부슬롯은 프로퍼티가 아니므로 접근할 수 없다.
      • __proto__라는 프로퍼티를 이용해 간접접근한다.
      • __proto__접근자 프로퍼티이므로, setter, getter 함수를 이용해 프로토타입을 취득 or 교체가 가능하다.
      • __proto__는 생성된 객체 소유가 아닌 Object.prototype의 프로퍼티이며 객체는 상속받는 것이다.
      • 간접참조를 하는 까닭은 순환오류를 막기 위해서이다. (__proto__는 순환오류를 체크한다.)
      • __proto__는 직접 사용보다는 Object.getPrototypeOf(obj), Object.setPrototypeOf(obj, proto) 를 이용한 취득 & 교체를 권장한다..

프로토타입 관련 프로퍼티

  • 함수 객체만이 가지는 prototype 프로퍼티: 생성자 함수가 가지며, 인스턴스의 프로토타입을 가리킨다.

    • 인스턴스의 __proto__와 역시 동일하다.
      • fun.__proto__ === [[Prototype]] === Function.prototype 이지만, fun.prototype과는 다르다.
      • fun.prototype은 fun의 인스턴스의 생성자이다.
    • ()햇갈리면 286p의 그림 19-18 참조
  • 프로토타입의 constructor 프로퍼티

    • 생성자 함수를 가리킨다.
    • 인스턴스가 가지는 것이 아니라 인스턴스의 프로토타입이 가진다.
    • 인스턴스는 이를 상속한다.
  • 생성자함수로 생성된 객체는 constructor이 생성자를 가리킨다.

  • 리터럴로 생성한 객체는 가상적인 생성자 함수를 가지나, constructor이 반드시 생성자함수를 가리키지 않는다.

    • 생성자 함수에 인수전달 없이 생성하면 추상연산(OrdinaryObjectCreate)을 통해 리터럴에 맞는 프로토타입을 지니게한다.

프로토타입의 생성시점

  • 생성시점
    • 함수: 런타임 (함수 호이스팅) = 함수정의 시점 | non-const : 생성X
  • 프로토타입도 객체이므로 Object.prototype을 프로토타입으로 가진다.

객체 생성 방식과 프로토타입 결정

  • 객체 리터럴: 프로토타입: Object.prototype 구성자: 생성자 함수
  • Object 생성자 함수: "
  • 자체 생성자 함수: 프로토타입: 생성자함수의 프로토타입 구성자: 생성자 함수
    | 생성방식 | 객체 리터럴 | Ojbect 생성자 함수 |자체 생성자 함수|
    | :------: | :--------------: | :--------------------: |:--:|
    | 프로토타입 | Object.prototype | Object.prototype |자체 생성자 함수의 프로토타입|
    | constructor |자체 생성자 함수 | 자체 생성자 함수 |자체 생성자 함수|
    | Example | const obj = {}; | const obj = new Object(); | const person = new Person();|

프로토타입 체인

  • 인스턴스(최하위)에서부터 Object.prototype(최상위)까지 차례대로 검색해가며 프로퍼티(메서드도 포함)를 찾는 매커니즘.

    • 종점은 Object.prototype 다음인 null이다.
  • 오버라이딩과 프로퍼티 섀도잉

    • 인스턴스에 상위 클래스의 메서드가 재정의되어 상위 클래스의 메서드가 가려질 때
      • 재정의된 인스턴스의 메서드가 오버라이딩했다
      • 프로퍼티 섀도잉이 일어났다 고 한다.
    • 삭제: 인스턴스는 삭제 가능 프로토타입 메서드를 삭제 X
      • 프로토타입의 메서드 삭제를 위해선 프로토타입의 메서드를 통해 접근해야 함
      • 프로퍼티(값)은 인스턴스 삭제시 프로토타입까지 삭제
  • 오버로딩은 매개변수의 타입 or 개수가 다름에 따라 다른 내용의 함수가 호출되는 방식 (JS에 내장 X)

프로토타입의 교체

  • 생성자 함수에의한 프로토타입의 교체

    • 즉시실행함수로 선언하는 이유: 모듈화 (변수 중복 피함, 캡슐화)
    • 교체된 프로토타입은 constructor이 연결되지 않아 Object의 constructor을 상속받음.
      • 교체시 프로토타입의 constructor생성자 함수로 상속해줘야 함.
  • 인스턴스에 의한 프로토타입의 교체

    • 생성자 함수
    • 교체된 프로토타입은 constructor이 연결되지 않고, 생성자 함수는 prototype이 연결되지 않음
      • 교체시 프로토타입의 constructor를 생성자 함수로 상속해줘야 함.
      • 교체시 생성자 함수의 prototype을 프로토타입으로 설정해줘야 함.

객체 상속관계 변경을 위한 프로토타입 교체는 꽤나 번거롭다.

따라서, 직접 상속을 이용하는 것이 편리하고 안전하다.

instanceof 연산자

  • Ex) 객체 instanceof 생성자 함수
  • 좌변의 프로토타입 체인의 상위 === 우변의 객체 ? 참 : 거짓;
  • 생성자 함수 내에서 한 프로토타입 교체는 instanceof에 영향을 주지 않는다.
    • 생성자 함수의 prototype === 인스턴스의 프로토타입

직접 상속

Object.create() 이용

  • Object.create(prototype,propertiesObject);

    • prototype을 프로토타입으로 가지고
    • propertiesObject를 프로퍼티로 가지는 (x: {value : 1, . . .} 꼴)
    • 객체를 리턴한다.
  • 장점

    1. new 연산자 없이 객체 생성
    2. 프로토타입을 지정하면서 객체 생성
    3. 객체 리터럴로 생성된 객체를 상속가능
  • ESLint에서는 create로 생성한 객체가 빌트인 메서드(hasOwnProperty() 등) 호출을 권장치 않는다.

    • 왜냐하면 종점 위치에 있는 객체는 비릍인 메서드를 사용할 수 없기 때문이다.
      const obj = Object.create(null);
      obj.x = 1;
      obj.hasOwnProperty('x'); // 에러
    • 따라서 Object.prototype을 통한 직접호출을 권장한다.

__proto__ 이용

```
const myProto = {x : 1};
const obj = {
    y: 2,
    __proto__ : myProto
}
// const obj = Object.create(myProto,{y : 2}) 와 동일
```

정적 프로퍼티와 메서드

  • 생성자 함수 정의 이후 생성자 함수에 프로퍼티와 메서드를 정의하면 생성자 함수만이 사용할 수 있는 정적 프로퍼티와 메서드가 된다.

    • 프로토타입에 정의가 안되어있어, 인스턴스는 사용이 불가능하다.
      • Ex) Object.creata() 등의 함수는 Object 생성자의 정적 메서드이므로, (당연히 Object.prototype을 상속받은) 다른 객체들임에도 사용할 수 없다.
  • 정적 프로퍼티/메서드와 프로토타입 프로퍼티/메서드의 구분이 필요하다.

    • 프로토타입 프로퍼티/메서드를 #으로 표기하기도 한다. (obj.prototype 대신 obj#으로)

프로퍼티 확인

  • in 연산자 : key in object
    • 프로토타입 체인까지 검색
  • Object.prototype.hasOwnProperty(key)
    • 객체의 고유 프로퍼티만을 검색

프로퍼티 열거

for key in obj

  • 프로토타입 체인까지 열거
  • [[Enumable]]만을 열거하므로 Object.prototype의 프로퍼티가 열거 X
  • Symbol은 열거 X
  • 순서 보장 X -> 배열에서는 사용 자제 권장 (배열 원소 + 프로퍼티까지 열거)

Object.keys / values / entries

  • 권장하는 방법
  • 각각 객체 고유의 프로퍼티 키, 프로퍼티 값, 프로퍼티 키 + 프로퍼티 값 을 배열로 반환
  • 순서 보장 X

느낀점

  • 개인적으로 가장 어렵게 느껴졌던 파트였다.
    • 이유
      1. 프로토타입으로 구현되어있어, 프로토타입을 공부하는데 프로토타입을 이해가 필요하다.
      2. 양이 방대하다.
      3. 많이 활용해보지 않어, 어디에 활용되는 지 감이 안잡힌다.
  • 메소드가 아니라 메서드가 표준용어 였다.

0개의 댓글