[JavaScript] 11. First-Class Object

100tick·2022년 12월 23일
0

JavaScript Deep Dive

목록 보기
11/16
post-thumbnail

1. First-Class Object

다음과 같은 조건을 만족하는 objectFirst-Class Object, 일급 객체라 부른다.
용어가 어려워 보이지만, 이는 JS 뿐만 아니라 다른 언어에서도 통용되는 개념이다.

  1. Anonymous Literalruntime에 생성이 가능하다.
  2. 변수, 자료구조(객체, 배열 등)에 저장할 수 있다.
  3. 함수의 매개변수에 전달할 수 있다.
  4. 함수의 반환값으로 사용할 수 있다.

앞선 여러장을 거치며 Function Expression에서 함수를 변수에 담을 수 있다는 것을 알게 되었다.
그리고 함수를 다른 함수의 매개변수로 전달하고, return하기도 했다.

C, C++ 등과 달리 JS의 함수가 일급객체이기 때문에 가능한 것이었다.

예시를 보자.

const fn = function(num) {}; // 함수를 변수에 할당

const obj = { fn: fn }; // 함수를 객체의 Property로 할당

JS, Python 등의 언어만 사용했다면 이와 같이 함수를 사용한다는 것이 당연하게 느껴지겠지만 해당 언어 내에서 함수가 일급객체로 취급되기 때문에 가능했던 것임을 알아두자.

object는 값이고, 함수가 일급객체JS에서는 함수도 값이다.
단, [[Call]], [[Construct]] 등의 내부 Method를 가지고 있기 때문에 함수 or 생성자로서 호출될 수 있다.


2. Properties of Function Objects

아래 코드를 통해 함수 PropertyAttribute를 확인할 수 있다.
arguments, caller, length, name, prototype 등은 일반 object에는 없는 함수 고유의 Property다.
그러나 Accessor Property__proto__는 함수 고유의 Property가 아니라 Object.prototype objectProperty를 상속받은 것임을 알 수 있다.

function fn(n) { return n * n; }

console.log(Object.getOwnPeropertyDescriptiors(fn));
/*
{
 length: {value: 1, writable: false, enumerable: false, configurable: true},
 name: {value: "square", writable: false, enumerable: false, configurable: true},
 arguments: {value: null, writable: false, enumerable: false, configurable: false},
 caller: {value: null, writable: false, enumerable: false, configurable: false},
 prototype: {value: {...}, writable: true, enumerable: false, configurable: false}
}
*/

1. arguments Property

함수 objectarguments Property 값은 argument object다.
이는 함수 호출 시 전달된 argument들의 정보를 담고 있는 iterable 유사 배열 객체이며, 함수 내부에서 지역 변수처럼 사용된다.

즉, 함수 외부에서는 참조할 수 없다.

function add(x, y) {
	console.log(arguments);
  	return x + y;
}

그냥 함수 내부에서 지역 변수 arguments가 하나 생성되며, 그 안에 매개변수가 index: value 형태로 존재한다고 보면 된다.
argument objectcallee라는 Property는 함수 자체를, length는 인수의 개수를 나타낸다.

이는 가변 인자 함수를 구현할 때 유용하다.

function sum() {
 let res = 0;
 // arguments 객체는 length 프로퍼티가 있는 유사 배열 객체이므로 for 문으로 순회할 수 있다.
 for (let i = 0; i < arguments.length; i++) {
 res += arguments[i];
 }
 return res;
}
console.log(sum()); // 0
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3)); // 6

단, 배열이 아니라 유사 배열 객체인 점을 주의하자.(ES6 이후 arguments는 유사 배열 객체이면서 Iterable이기도 함)
그래서 배열 메소드 사용 시 오류가 발생한다.

그래서 배열 메소드를 사용할 경우, 아래와 같이 Rest Parameter로 받으면 arg을 배열로 재정의하면 된다.

// ES6 Rest parameter
function sum(...args) {
 return args.reduce((pre, cur) => pre + cur, 0);
}
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4, 5)); // 15

2. caller Property

ECMAScript 스펙에 비포함, 표준화 예정이 없기 때문에 생략한다.


3. length Property

함수 object가 가진 매개변수의 개수를 출력한다.
주의할 점은 arguments.length는 인자의 개수를, 함수 objectlength는 매개변수의 개수를 나타내기 때문에 값이 다를 수 있다는 것이다.

function foo() {}
console.log(foo.length); // 0
function bar(x) {
 return x;
}
console.log(bar.length); // 1
function baz(x, y) {
 return x * y;
}
console.log(baz.length); // 2

4. name Property

함수 이름을 나타내는 Property다.
ES6 이후 표준이 되었으며 이전 버전과 동작이 다르다.
익명 함수 표현식일 경우, ES5에서는 빈 문자열을 값으로 갖지만, ES6에서는 함수 object를 가리키는 _identifier를 값으로 갖는다.

// 기명 함수 표현식
var namedFunc = function foo() {};
console.log(namedFunc.name); // foo
// 익명 함수 표현식
var anonymousFunc = function() {};
// ES5: name 프로퍼티는 빈 문자열을 값으로 갖는다.
// ES6: name 프로퍼티는 함수 객체를 가리키는 변수 이름을 값으로 갖는다.
console.log(anonymousFunc.name); // anonymousFunc
// 함수 선언문(Function declaration)
function bar() {}
console.log(bar.name); // bar

5. proto Accessor Property

모든 object[[Prototype]] 내부 슬롯을 갖는다.
이는 OOP를 위해 JS에서 상속을 구현하는 prototype object를 가리킨다.

__proto__ Property는 이 [[Prototype]] 내부 슬롯이 가리키는 prototype object에 접근하기 위해 사용하는 Accessor Property다.

내부 슬롯에 직접 접근할 수 없기 때문에 __proto__ Accessor Property를 통해 간접적으로 prototype object에 접근하는 것이다.

const obj = { a: 1 };
// 객체 리터럴 방식으로 생성한 객체의 프로토타입 객체는 Object.prototype이다.
console.log(obj.__proto__ === Object.prototype); // true
// 객체 리터럴 방식으로 생성한 객체는 프로토타입 객체인 Object.prototype의 프로퍼티를 상속받는다.
// hasOwnProperty 메서드는 Object.prototype의 메서드다.
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('__proto__')); // false

hasOwnProperty는 전달받은 Key가 해당 object 고유의 Property라면 true를, 상속 받은 Property거나 없다면 false를 반환한다.


6. prototype Property

prototype PropertyConstructor Function으로 호출할 수 있는 function object.
즉, Constructor만이 소유하는 Property다.
Non-Constructorprototype Property가 없다.

// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty('prototype'); //  true
// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty('prototype'); //  fals

prototype Property는 함수가 object를 생성하는 Constructor Function으로 호출될 때 Constructor Function이 생성할 instanceprototype object를 가리킨다.

앞서 살펴봤던 것처럼, 함수는 모두 내부 슬롯 [[Call]]을 갖고, [[Construct]]는 갖거나 갖지 않는다.
[[Construct]]를 갖는 경우가 바로 Constructor Function인 경우인데, Function Declaration, Function Expression, Class인 경우에 해당한다.

그리고 Method 축약 표현, Arrow Function인 경우는 [[Call]]만 내부 슬롯으로 갖는다고 하였다.

0개의 댓글