프로퍼티와 생성자함수

hb-developer·2021년 6월 2일
0

JavaScript

목록 보기
8/10

본 내용은 책 모던 자바스크립트 Deep Dive 의 내용을 참조했습니다.

프로퍼티

자바스크립트 엔진은 프로퍼티를 만들때 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.

데이터 프로퍼티

데이터 프로퍼티는 키와 값으로 이루어진 흔히 아는 프로퍼티다.
프로퍼티 안에는 다음과 같은 속성이 있다.

프로퍼티 어트리뷰트프로퍼티 디스크립터설명
[[value]]value프로퍼티의 키값
[[Writable]]writable불리언 값, value값을 바꿀수 있게한다.
[[Enumerable]]enumerable불리언 값, 프로퍼티의 열거 가능여부를 나타낸다
[[Configurable]]configurable불리언 값, 프로퍼티의 재정의 가능 여부를 나타낸다

만약 프로퍼티의 속성을 확인하려면 ?
Object.getOwnPropertyDescriptor(오브젝트,프로퍼티 키) 를 사용

const person = {
  name: 'Lee'
};

// 프로퍼티 디스크립터 객체를 반환함
console.log(Object.getOwnPropertyDescriptor(person,'name'));
// {value: "Lee", writable: true, enumerable: true, configurable: true}

프로퍼티를 처음 생성할때 writable, enumerable, configurable 모두 true로 설정된다.

접근자 프로퍼티

프로퍼티 어트리뷰트프로퍼티 디스크립터설명
[[Get]]getgetter 함수를 호출하고 프로퍼티값을 읽는다
[[Set]]setsetter 함수를 호출하고 프로퍼티 값을 설정후 저장한다.
[[Enumerable]]enumerable불리언 값, 프로퍼티의 열거 가능여부를 나타낸다
[[Configurable]]configurable불리언 값, 프로퍼티의 재정의 가능 여부를 나타낸다

Object.getOwnPropertyDescriptor(오브젝트,프로퍼티 키)에서
get 과 set 이면 접근자 프로퍼티 , value 와 writable 이면 데이터 프로퍼티

프로퍼티 정의

그럼 프로퍼티는 어떻게 정의할까?

Object.defineProperty 를 사용한다.
인수 (객체,프로퍼티 키 , 디스크립터 객체) 를 받고 정의를 하면된다.

const person = {
  name: 'kim'
}
console.log(Object.getOwnPropertyDescriptor(person,'name'));
//{ value: 'kim', writable: true, enumerable: true, configurable: true }

Object.defineProperty(person,'name',{
  value: 'kim',
   writable: false,
    enumerable: false,
     configurable: true 
})

console.log(Object.getOwnPropertyDescriptor(person,'name'));
//{value: 'kim',writable: false,enumerable: false,configurable: true}

한번에 정의하려면 Object.defineProperties

객체 변경 방지

위와 같이 데이터프로퍼티 직접 정의할수 있지만.

객체안의 모든 프로퍼티값을, 또 재정의 자체를 막아버리는 메서드도 존재한다.

구분메서드프로퍼티 추가프로퍼티 삭제프로퍼티값 읽기프로퍼티 값 쓰기프로퍼티 재정의
객체 확장 금지Object.preventExtensionsXOOOO
객체 밀봉Object.sealXXOOX
객체 동결Object.freezeXXOXX

프리벤트 익스텐션을 사용하면 프로퍼티 동적추가 , Object.defineProperty 가 금지된다.

아래로 내려갈수록 강도가 세지며 freeze 까지 내려가면 읽기만 가능한 객체가 된다.

객체에 위의 메서드가 사용 됬는지 확인하려면
isExtensible isSealed isFrozen 를 사용한다.

🧨객체 변경 방지는 얕은 변경만 된다. 객체 안의 객체까지 효과를 줄 수 없기 때문에
재귀적 함수 호출로 깊은 변경을 이용해야 한다.

생성자 함수

객체를 만드는 방법은 2가지가 있다.

객체리터럴 - const a = { }
생성자 함수 - csont b = new Object();

리터럴 방식은 간단하지만 프로퍼티 값과 메서드를 계속 적어줘야 한다.

동일한 코드는 항상 배제해야 하이 때문에 쉽게 만들수 있는 생성자함수(템플릿)를 사용한다.

// 생성자 함수
function Circle(radius) {
  // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

// 인스턴스의 생성
const circle1 = new Circle(5);  // 반지름이 5인 Circle 객체를 생성
const circle2 = new Circle(10); // 반지름이 10인 Circle 객체를 생성

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

🔨여기서 this 란? 자기 참조 변수를 뜻한다.
또 호출 방식에 따라 바인딩 되는 값이 다르다.

함수 호출 방식this가 가르키는 값
일반 함수로 호출전역 객체
메서드로 호출메서드를 호출한 객체
생성자 함수로 호출생성자 함수가 만들 인스턴스

생성자 함수를 사용하면 다음과 같은 일이 벌어진다.

  1. 인스턴스 생성과 this 바인딩
    • 빈 객체(인스턴스)를 만들고 this 값을 바인딩한다.
  2. 인스턴스 초기화
    • 인수로 받은 값과 프로퍼티를 인스턴스에 할당한다.
  3. 인스턴스 반환
    • 인스턴스를 반환한다.

🧨생성자 함수는 자동으로 return이 되기때문에 return 문을 생략 해야된다.
리턴문 뒤에 원시값을 넣으면 무시가 되지만
리턴뒤에 새로운 객체를 명시하면 명시한 객체가 리턴된다.

[[call]] 과 [[construct]]

함수 와 객체의 차이점은 뭘까?
함수는 호출 가능한 객체 이고 객체는 호출 불가능한 객체다.

따라서 모든 함수는 내부 메서드로 call을 가지고있다.
그렇다면 construct 는 뭐지?

함수앞에 new 를 붙이면 생성자 함수가 되곤 했다. 하지만 정확히 구분하자면 다음과 같다.

constructor - 함수 선언문 , 함수 표현식 , 클래스
non-constructor - 축약형 메서드 , 화살표 함수

콜론과 펑션을 생략한 축약형 메서드화살표 함수를 생성자함수로 호출하게 되면 오류가 발생한다.

// 일반 함수 정의: 함수 선언문, 함수 표현식
function foo() {}
const bar = function () {};
// 프로퍼티 x의 값으로 할당된 것은 일반 함수로 정의된 함수다. 이는 메서드로 인정하지 않는다.
const baz = {
  x: function () {}
};

// 일반 함수로 정의된 함수만이 constructor이다.
new foo();   // -> foo {}
new bar();   // -> bar {}
new baz.x(); // -> x {}

// 화살표 함수 정의
const arrow = () => {};

new arrow(); // TypeError: arrow is not a constructor

// 메서드 정의: ES6의 메서드 축약 표현만을 메서드로 인정한다.
const obj = {
  x() {}
};

new obj.x(); // TypeError: obj.x is not a constructor

new 를 붙이느냐 마느냐에 따라 생성자가 되기도 일반함수가 되기도 한다.
그렇다면 정의된 함수만 보고 생성자함수로 사용될 예정인지 아닌지를 어떻게 구분할수있을까?

개발자들은 생성자 함수에는 파스칼케이스 컨벤션을 사용하기로 한다.
대문자로 시작하는 함수들은 생성자 함수라고 약속 한것

그럼에도 실수가 발생한다면? 이때
new.target 을 사용하면 new 연산자로 호출됬는지 아닌지를 감지할수 있다.

// 생성자 함수
function Circle(radius) {
  // 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined다.
  if (!new.target) {
    // new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.
    return new Circle(radius);
  }

  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

// new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출된다.
const circle = Circle(5);
console.log(circle.getDiameter());
profile
배우면 바뀐다

0개의 댓글