다음과 같은 조건을 만족하는 object
를 First-Class Object, 일급 객체라 부른다.
용어가 어려워 보이지만, 이는 JS 뿐만 아니라 다른 언어에서도 통용되는 개념이다.
앞선 여러장을 거치며 Function Expression에서 함수를 변수에 담을 수 있다는 것을 알게 되었다.
그리고 함수를 다른 함수의 매개변수로 전달하고, return
하기도 했다.
C, C++ 등과 달리 JS의 함수가 일급객체이기 때문에 가능한 것이었다.
예시를 보자.
const fn = function(num) {}; // 함수를 변수에 할당
const obj = { fn: fn }; // 함수를 객체의 Property로 할당
JS, Python 등의 언어만 사용했다면 이와 같이 함수를 사용한다는 것이 당연하게 느껴지겠지만 해당 언어 내에서 함수가 일급객체로 취급되기 때문에 가능했던 것임을 알아두자.
object
는 값이고, 함수가 일급객체인 JS에서는 함수도 값이다.
단, [[Call]], [[Construct]] 등의 내부 Method를 가지고 있기 때문에 함수 or 생성자로서 호출될 수 있다.
아래 코드를 통해 함수 Property의 Attribute를 확인할 수 있다.
arguments
, caller
, length
, name
, prototype
등은 일반 object
에는 없는 함수 고유의 Property다.
그러나 Accessor Property인 __proto__
는 함수 고유의 Property가 아니라 Object.prototype object
의 Property를 상속받은 것임을 알 수 있다.
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}
}
*/
함수 object
의 arguments Property 값은 argument object
다.
이는 함수 호출 시 전달된 argument
들의 정보를 담고 있는 iterable
유사 배열 객체이며, 함수 내부에서 지역 변수처럼 사용된다.
즉, 함수 외부에서는 참조할 수 없다.
function add(x, y) {
console.log(arguments);
return x + y;
}
그냥 함수 내부에서 지역 변수 arguments
가 하나 생성되며, 그 안에 매개변수가 index: value 형태로 존재한다고 보면 된다.
또 argument object
에 callee
라는 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
ECMAScript 스펙에 비포함, 표준화 예정이 없기 때문에 생략한다.
함수 object
가 가진 매개변수의 개수를 출력한다.
주의할 점은 arguments.length
는 인자의 개수를, 함수 object
의 length
는 매개변수의 개수를 나타내기 때문에 값이 다를 수 있다는 것이다.
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
함수 이름을 나타내는 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
모든 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
를 반환한다.
prototype
Property는 Constructor Function으로 호출할 수 있는 function object
.
즉, Constructor만이 소유하는 Property다.
Non-Constructor는 prototype
Property가 없다.
// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty('prototype'); // true
// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty('prototype'); // fals
prototype
Property는 함수가 object
를 생성하는 Constructor Function으로 호출될 때 Constructor Function이 생성할 instance의 prototype object
를 가리킨다.
앞서 살펴봤던 것처럼, 함수는 모두 내부 슬롯 [[Call]]을 갖고, [[Construct]]는 갖거나 갖지 않는다.
[[Construct]]를 갖는 경우가 바로 Constructor Function인 경우인데, Function Declaration, Function Expression, Class인 경우에 해당한다.
그리고 Method 축약 표현, Arrow Function인 경우는 [[Call]]만 내부 슬롯으로 갖는다고 하였다.