- Published on
모던 자바스크립트 Deep Dive 복습 7
- Authors

- Name
- Byeong Jun An
함수와 일급 객체
일급 객체
함수는 일급 객체다. 함수가 일급객체라는 것은 함수를 객체와 동일하게 사용할 수 있다는 걸 말하는데 객체는 값이므로 함수를 값과 동일하게 취급할 수 있다는 걸 말한다. 따라서 어디서든지 리터럴로 정의할 수 있으며 런타임에 함수 객체로 평가된다.
일급 객체는 다음과 같은 조건을 가진다.
- 무명의 리터럴로 생성할 수 있다.
- 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
- 함수의 매개변수에 전달 할 수 있다.
- 함수의 반환값으로 사용할 수 있다.
함수와 객체는 호출할 수 있는지 여부에 따라 결정된다고 보면 되고 함수는 함수 고유의 프로퍼티를 갖는다.
함수 객체의 프로퍼티
함수는 객체다. 따라서 함수도 프로퍼티를 가질 수 있다. 함수의 모든 프로퍼티의 프로퍼티 어트리뷰트를 Object.getOwnPropertyDescriptors 메서드로 확인해보면 arguments, caller, length, name, prototype, 프로퍼티를 확인할 수 있고 이들은 함수 객체 고유의 프로퍼티다. 하지만 추가로 __proto__ 도 확인할 수 있는데 이는 함수 객체 고유의 프로퍼티가 아니라 Object.prototype 객체에 상속받은 프로퍼티이고 접근자 프로퍼티이다. 이제 이 프로퍼티에 대해 하나씩 알아보자
arguments 프로퍼티
함수 객체의 arguments 프로퍼티 값은 arguments 객체다. arguments 객체는 함수 호출 시 전달된 인수(argument)들의 정보를 담고 있는 순회 가능한(iterable) 유사 배열 객체이다.
이 arguments 객체 안에는 인수를 프로퍼티 값으로 소유하며, callee, length, Symbol(Symbol.iterator)가 있는데 callee는 호출되어 arguments 객체를 생성한 함수(함수 자신)를 가리키고 length는 인수의 개수, Symbol(Symbol.iterator)는 객체를 이터러블로 만들기 위한 프로퍼티로 보면 된다.
arguments 객체는 배열이 아닌 유사 배열 객체라 배열에서 사용가능한 slice 등의 메서드는 사용할 수 없다. 이를 해결하기 위해 간접 호출 call로 해결해왔는데 ES6부터 Rest 파라미터를 도입해 이를 해결 햇다. 이에 대해서는 나중에 알아보고자 한다.
caller 프로퍼티
함수 객체의 caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.
length 프로퍼티
함수 객체의 length 프로퍼티는 함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.
참고로 arguments 객체의 length는 인자(argument)의 개수를 가리키고 함수 객체의 length는 매개변수의 개수를 가리키므로 헷갈리지 않도록 하자
name 프로퍼티
함수 객체의 name 프로퍼티는 함수 이름을 나타낸다. (함수 이름은 함수 객체를 가리키는 식별자가 아니다.) 특징으로는 ES5 이전에는 익명함수의 name 프로퍼티를 빈 문자열로 값을 가졌지만 ES6부터는 함수 객체를 가리키는 변수 이름을 값으로 갖는다.
__proto__ 접근자 프로퍼티
모든 객체는 [[property]] 라는 내부 슬롯을 갖는다. 이 내부슬롯은 상속을 구현하는 프로토타입 객체를 가리킨다. __proto__ 프로퍼티는 이 [[property]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티다. 내부 슬롯에 직접 접근 할 수 없기 때문에 간접적으로 프로토타입 객체에 접근하기 위한 프로퍼티라고 보면 된다.
prototype 프로퍼티
prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 constructor만이 소유하는 프로퍼티이다. 이 프로퍼티는 함수가 생성자 함수로 호출될 때 함수가 생성할 인스턴스의 프로토타입 객체를 가리킨다.
Q&A
Q. 일급 객체란 뭐고 함수가 일급 객체인 이유가 뭔가요??
A: 일급 객체는 무명의 리터럴로 생성할 수 있고 변수나 자료구조에 저장할 수 있으며 함수의 매개변수에 전달하고 함수의 반환값으로 사용할 수 있는것을 말하는데 이는 값처럼 사용할 수 있는 객체를 말하고 자바스크립트에서 함수는 값처럼 사용 할 수 있기 때문에 이를 일급 객체로 취급합니다.
Q. 함수 객체 프로퍼티의 length와 arguments 객체의 프로퍼티 length는 각각 무얼 가리키나요?
A: 함수 객체 프로퍼티의 length는 함수를 정의할 때 선언한 매개변수를 가리키고 arguments 객체의 프로퍼티의 length는 함수 호출 시 전달된 인수를 가리킵니다.
프로토타입
자바스크립트는 다른 클래스 기반 객체지향과는 다른 프로토타입 기반의 객체지향 프로그래밍 언어다. 프로토타입에 대해 알아보기 전에 객체지향 프로그래밍에 대해 간략하게 알아보고 프로토타입에 대해 알아보고자 한다.
객체지향 프로그래밍
객체지향 프로그래밍은 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 말한다. 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조다. 여기서 상태는 이름, 주소, 성별 같은 특징이나 성질을 나타내는 속성을 말하고 동작은 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 것을 말한다. 이러한 상태와 동작을 통해 자신의 고유한 기능을 수행 할 수 있고 다른 객체와 구별할 수 있다. 이를 통해 데이터를 주고 받거나 처리하고 다른 객체의 상태 데이터나 동작을 상속 받아 사용하는 등의 다른 객체와의 관계성을 가질 수 있다.
상속과 프로토타입
상속은 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다. 이 상속은 불필요한 중복을 제거하는 것을 목적으로 둔다. 자바스크립트는 프로토타입(prototype)을 기반으로 상속을 구현한다.
프로토타입 객체
프로토타입 객체는 객체 간 상속을 구현하기 위해 사용하는 객체다. 프로토타입 객체는 어떤 객체의 상위(부모) 객체의 역할을 하는 객체로서 다른 객체에 프로퍼티를 제공한다. 프로토타입 객체를 상속받은 하위(자식) 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있다. (자식 프로퍼티에 부모 프로퍼티가 존재하는 것은 아님)
__proto__ 접근자 프로퍼티
모든 객체는 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입 객체, 즉 [[prototype]] 내부 슬롯에 간접적으로 접근할 수 있다.
__proto__는 접근자 프로퍼티다.
Object.prototype의 접근자 프로퍼티인 __proto__ [[Get]], [[Set]] 프로퍼티 어트리뷰트로 구성된 프로퍼티다. 만약 이 내부 슬롯의 값에 접근하고자 한다면 __proto__ 통해 접근 할 수 있는데 __proto__으로 접근하게 된다면 __proto__의 getter함수가 호출되어 값을 간접적으로 접근 할 수 있게 된다. 만약 __proto__로 새로운 프로퍼티를 할당하면 setter 함수가 호출되어 값을 간접적으로 변경할 수 있게 된다.__proto__ 접근자 프로퍼티는 상속을 통해 사용된다.
__proto__ 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티다. 모든 객체는 상속을 통해 Object.prototype.__proto__ 접근자 프로퍼티를 사용할 수 있다.__proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
[[prototype]] 내부 슬롯의 값, 즉 프로토타입에 접근하기 위해 접근자 프로퍼티를 사용하는건 상호참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서다. 프로토타입 체인은 프로퍼티 검색 방향이 한쪽 방향으로 흘러가야 한다. 만약 서로 참조하고 있는 프로토타입 체인이 만들어지면 프로토타입 체인 종점이 존재하지 않기 때문에 무한루프에 빠진다. 그렇기 때문에 아무런 체크 없이 무조건적으로 프로토타입을 교체할 수 없도록 __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하고 교체하도록 구현되어 있다.__proto__ 접근자 프로퍼티를 프로퍼티 코드 내에서 직접 사용하는 것은 권장하지 않는다.
__proto__ 접근자 프로퍼티는 직접 상속을 통해 Object.prototype을 상속 받지 않는 객체를 생성할 수도 있기 때문에 모든 객체가 해당 프로퍼티를 사용할 수 있는건 아니다. 그렇기 때문에 __proto__ 대신 프로토타입을 참조할 땐 Object.getPrototypeOf 메서드를 사용하고, 프로토타입을 교체하고 싶은 경우엔 Object.setPrototypeOf 메서드를 사용할 것을 권장한다.
함수 객체의 prototype 프로퍼티
함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킨다. 따라서 생성자 함수로서 호출할 수 없는 함수, 즉 non-constructor인 함수는 prototype 프로퍼티를 소유하지 않으며 생성하지도 않는다. 함수 선언문, 함수 표현식 처럼 생성자 함수로 호출하기 위해 정의하지 않은 일반 함수는 prototype 프로퍼티를 소유하지만 해당 프로퍼티는 사용하지 않기 때문에 아무런 의미가 없다
__proto__ 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입 객체를 가리킨다. 하지만 __proto__ 접근자 프로퍼티는 모든 객체가 사용하지만 prototype 프로퍼티는 생성자 함수가 사용하므로 사용 주체가 다름을 인지해야 한다.
프로토타입 객체의 constructor 프로퍼티와 생성자 함수
모든 프로토타입 객체는 constructor 프로퍼티를 갖는다. 이 constructor 프로퍼티는 자신을 참조하고 있는 생성자 함수를 가리킨다. 쉽게 말해서 생성자 함수는 prototype을 통해 프로토타입 객체를 참조하고 있고 프로토타입 객체는 constructor로 생성자 함수를 참조(서로 참조)하고 있다. 이 연결은 생성자 함수가 생성될 때(런타임 이전) 프로토타입 객체가 만들어지고 곧바로 연결된다. 이후에 생성자 함수가 인스턴스를 만들게 되면 인스턴스는 프로토타입 객체와 연결해야하는데 그 과정을 자세히 설명하자면
- 처음에 생성자 함수가 prototype으로 프로토타입 객체를 가리키고 있다.
- new 연산자를 이용해 생성자 함수의 인스턴스를 생성
- 인스턴스에는 자바스크립트 엔진에 의해 내부슬롯인 [[Prototype]] (일반적으로 __proto__로 접근 가능)에 생성자 함수의 prototype 속성을 참조하도록 설정한다.
- 그렇기 때문에 인스턴스와 프로토타입 객체는 서로 연결된다.
- 프로토타입 객체는 constructor라는 프로퍼티로 생성자 함수를 참조하고 있었다.
- 인스턴스가 해당 constructor 프로퍼티를 상속받아 인스턴스와 생성자 함수를 연결한다.
리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입 객체
앞에서 살펴본 경우는 new 연산자를 이용해서 생성자 함수로 객체를 생성하는 과정을 살펴보았다. 하지만 함수를 생성하는 방법에는 리터럴 표기법에 의한 객체 생성 방법과 같이 new 연산자와 함께 호출하여 인스턴스를 생성하지 않는 객체 생성 방식도 있다. 해당 방식은 ECMAScript 사양에 따르면 Object 생성자 함수 호출과 객체 리터럴의 평가는 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성하는 점에서 동일하나 new.target의 확인이나 프로퍼티를 추가하는 처리 등 세부 내용은 다르다.
따라서 객체 리터럴에 의해 생성된 객체는 Object 생성자 함수가 생성한 객체가 아니다. 하지만, 자바스크립트 엔진이 내부적으로 객체 리터럴을 해석할 때 Object 생성자 함수를 사용하여 객체를 생성하기 때문에, 결과적으로 생성된 객체는 Object 생성자 함수와 동일한 프로토타입 체인을 가지게 된다. 즉, 객체 리터럴은 단순히 객체를 생성하는 더 간편한 문법일 뿐, 실제로는 Object 생성자 함수를 통해 객체가 생성된다.
Q&A
Q. 상속을 사용하는 이유?
A: 동일한 메서드를 중복해서 사용하는걸 제거하기 위해서
Q. 프로토타입 객체란?
A: 객체 간 상속을 구현하기 위해 사용하는 것으로 어떤 객체의 부모 역할을 하는 객체로서 다른 객체에 공유 프로퍼티를 제공합니다. 하위 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있습니다.
Q. __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유는?
A: 상호 참조에 의해 프로토타입 체인이 생성되어 무한루프에 빠지지 않도록 하기 위함
Q. 함수 객체의 property란?
A: 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 기리키는 프로퍼티 키입니다.
Q. 프로토타입 객체의 constructor란?
A: 프로토타입 객체를 가리키고 있는 생성자 함수를 가리키는 프로퍼티 키입니다.
Q. 객체 리터럴로 생성하는 생성자 함수는 어떤 생성자 함수에 의해 객체가 생성되나요?
A: Object
오늘의 한줄평: 프로토라는 단어가 많아서 헷갈리기 쉽다. [[prototype]], __proto__, 생성자 함수의 prototype 프로퍼티, 프로토타입 객체 등등.. 헷갈리지 않게 정리를 잘 해놔야겠다.