16장 프로퍼티 어트리뷰트
내부 슬롯과 내부 메서드
내부 슬롯과 내부 메서드는 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티(pseudo property)와 의사 메서드(pseudo method)다.
개발자가 직접 접근할 수 있도록 외부로 공개된 객체의 프로퍼티는 아니지만, 간접적으로 접근할 수 잇는 방법을 제공한다.
프로퍼티 상태
프로퍼티의 상태란 프로퍼티의 값(value), 값의 갱신 가능 여부(writable), 열거 가능 여부(enumerable), 재정의 가능 여부(configurable)를 말한다.
프로퍼티 어트리뷰트
자바스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯이다.
Object.getOwnPropertyDescriptor 메서드
하나의 프로퍼티 어트리뷰트 정보를 제공한다. 즉, 프로퍼티 디스크립터 객체를 반환한다. 프로퍼티 디스크립터 객체는 프로퍼티 어트리뷰트 정보를 제공한다.
만약 존재하지 않는 프로퍼티나 상속받은 프로퍼티에 대한 프로퍼티 디스크립터를 요구하면 undefined가 반환된다.
데이터 프로퍼티와 접근자 프로퍼티
데이터 프로퍼티 data property
키와 값으로 구성된 일반적인 프로퍼티
지금까지 살펴본 모든 프로퍼티는 데이터 프로퍼티
[[Value]] 내부 슬롯이 있다.
접근자 프로퍼티 accessor property
자체적으로 값을 가지지 않고 다른 데이터 프로퍼티 값을 읽거나 저장할 때 호출되는 접근자 함수 accessor function으로 구성된 프로퍼티
[[Value]] 내부 슬롯이 없다.
메서드가 아니고 프로퍼티다.
따라서 접근자 프로퍼티를 사용할 때는 프로퍼티 키를 () 없이 사용한다.
접근자 프로퍼티로 프로퍼티 값에 접근시 동작
Object.definedProperty
프로퍼티의 어트리뷰트를 정의할 수 있다.
인수로 객체의 잠조, 데이터 프로퍼티의 키인 문자열, 프로퍼티 디스크립터 객체 를 전달한다.
프로퍼티 정의란
새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의 하는 것
객체 변경을 방지하는 다양한 메서드
불변 객체
앞에서 살펴본 변경 방지 메서드는 얕은 변경 방지 (shallow only)로
직속 프로퍼티만 변경이 방지되고 중첩 객체까지는 영향을 주지 못한다.
따라서 재귀를 사용해서 중첩된 객체까지 동결해야함.
불변 객체
중첩 객체 까지 동결해서 변경이 불가능한 읽기 전용의 불변 객체 (재귀적으로 Object.freeze 메서드 호출)
17장 생성자 함수에 의한 객체 생성
Object 생성자 함수
new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성해서 반환한다.
생성자 함수란
new 연산자와 함께 호출해서 객체(인스턴스)를 생성하는 함수
인스턴스
생성자 함수에 의해 생성된 객체
객체 리터럴에 의한 객체 생성 방식의 문제점
객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성하기 때문에,
동일한 프로퍼티구조를 갖는 객체를 여러 개 생성해야 하는 경우 비효율적임.
생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성
아래 코드의 문제점은?
// 생성자 함수
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
동일한 동작을 하는 메서드가 중복 생성된다.
인스턴스 마다 프로퍼티 값이 똑같으면 인스턴스 내에 들어있을 필요가 없다!!!!
행위인 함수는 자신이 가지고있을 필요가 없다. 함수는 프로토타입으로 올리면 된다!
상속돼서 사용가능함. 메모리 사용률 감소!
수정버전
// 생성자 함수
function Circle(radius) {
this.radius = radius;
}
// Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를
// 공유해서 사용할 수 있도록 프로토타입에 추가한다.
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.
Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};
바인딩이란
식별자와 값을 연결하는 과정. 변수 선언은 변수 이름과 확보된 메모리 공간의 주소를 바인딩하는 것이다.
this 동적 바인딩
생성자 함수의 인스턴스 생성 과정
1. 인스턴스 생성과 this 바인딩
암묵적으로 빈 객체를 생성하고 빈 객체, 즉 인스턴스는 this에 바인딩된다.
생성자 함수 내부의 this가 생성자 함수가 생성할 인스턴스를 가리키는 이유는 this 바인딩 때문이다.
함수 몸체의 코드가 한 줄씩 실행되는 런타임 이전에 실행된다.
2. 인스턴스 초기화
생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스를 초기화한다.
즉, this에 바인딩 되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고
생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당한다.
3. 인스턴스 반환
생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
명시적으로 다른 객체을 반환하면 this는 무시되고 명시한 객체가 반환된다.
명시적으로 원시 값을 반환하면, 원시 값 반환은 무시되고 암묵적으로 this가 반환된다.
함수 객체와 일반 객체의 차이점
일반 객체는 호출할 수 없지만, 함수는 호출할 수 있다.
함수 객체는
일반 객체가 가지는 내부슬롯, 내부 메서드 + 함수 객체만을 위한 내부 슬롯, 내부 메서드
함수 객체의...
내부 슬롯 : [[Environment]]. [[FormalParameters]]
내부 메서드 : [[Call]], [[Construct]]
함수 객체의 특징

함수 객체는 반드시 callable이여야한다. 모든 함수 객체는 내부 메서드 [[Call]]을 가진다.
하지만 함수 객체는 constructor 일 수도 있고, non-constructor 일 수도 있다.
일반 함수로서 호출되면 함수 객체의 내부 메서드 [[Call]] 가 호출된다
생성자 함수로 호출되면 내부 메서드 [[Construct]] 가 호출된다.
생성자 함수로 사용할 수 있는 함수
constructor을 생성자 함수로 사용할 수 있다.
constructor : 생성자 함수로 사용 가능
함수 선언문, 함수 표현식, 클래스(클래스도 함수)
[[Call]], [[Construct]] 존재
new 연산자를 사용하여 호출할 수 있다.
non-constructor : 생성자 함수로 사용 불가
메서드(ES6 메서드 축약 표현), 화살표 함수
[[Call]]는 있지만, [[Construct]]가 없다.
억지로 new로 호출하려고 한다면 TypeError가 발생한다.
new연산자 없이 생성자 함수를 호출한다면
일반 함수로 호출한것으로 해석된다.
1) new.target
따라서 !new.target 연산자를 사용하여 new를 사용하지 않고 생성자 함수를 호출하였을 때, 따로 처리할 수 있다.
new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new.target은 함수 자신을 가리킨다.
new 연산자 없이 일반함수로 호출된 함수 내부의 new.target은 undefined다.
// 생성자 함수
function Circle(radius) {
// 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target === undefined
if(!new.target){
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// 인스턴스의 생성
const circle1 = Circle(5); // 반지름이 5인 Circle 객체를 생성
2. 스코프 세이프 생성자 패턴 (instance of)
new.target은 ES6에서 도입된 최신 문법으로써 IE에서는 지원하지 않기때문에
new.target을 사용할 수 없는 상황이라면 스코프 세이프 생성자 패턴을 사용했다.
// Scope-Safe Constructor Pattern
function Circle(radius) {
// 생성자 함수가 new 연산자와 함께 호출되면 함수의 선두에서 빈 객체를 생성하고
// this에 바인딩한다. 이때 this와 Circle은 프로토타입에 의해 연결된다.
// 이 함수가 new 연산자와 함께 호출되지 않았다면 이 시점의 this는 전역 객체 window를 가리킨다.
// 즉, this와 Circle은 프로토타입에 의해 연결되지 않는다.
if (!(this instanceof Circle)) {
// new 연산자와 함께 호출하여 생성된 인스턴스를 반환한다.
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// new 연산자 없이 생성자 함수를 호출하여도 생성자 함수로서 호출된다.
const circle = Circle(5);
console.log(circle.getDiameter()); // 10
Built in 생성자 함수를 new 연산자 없이 호출한 경우
Object, String, Number, Boolean, Function, Array, Date, RegExp
는 new 연산자와 함께 호출되었는지를 확인한 후 적절한 값을 반환한다.
String, Number, Boolean는
new 연산자와 함께 호출 하하면 String, Number, Boolean객체를 반환
new 연산자 없이 호출하면 데이터 타입을 변환
18장 함수와 일급 객체
자바스크립트에서 함수가 일급객체라는 것은 무엇을 의미하는가
자바스크립트는 Functional Programming Language 라는 의미!
일급 객체는 객체랑 똑같다는 말임.
대부분의 다른 언어에서는 런타임 이전에 함수를 생성한다.
하지만 자바스크립트는 런타임에 변수에 함수를 할당할 때 생성할 수 있다.
(런타임에 함수를 생성하는 것이 핵심)
일급객체의 4가지 조건
- 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
- 변수나 자료구조에 저장할 수 있다.
- 함수의 매개변수에 전달할 수 있다.
- 함수의 반환값으로 사용할 수 있다.
함수 객체의 데이터 프로퍼티
arguments, caller, length, name, prototype
arguments 프로퍼티
함수 호출 시 전달된 인수들의 정보를 담고 있는 순회 가능한 유사배열 객체
ES3부터 표준에서 폐지됨
화살표 함수에서는 arguments를 사용할 수 없고, 대신에 Rest 파라미터를 사용하면된다.
가변인자함수란?
매개변수를 확정할 수 없는 함수