다음 같은 조건을 만족하는 객체를 일급 객체라고 한다.
함수도 일급객체이다
let f = function () {
return 'hi';
};
function foo() {
return 'foo 입니다';
}
function bar(f) {
return f;
}
console.log(bar(foo)); // [Function: foo]
console.log(bar(foo())); // foo 입니다
그렇기 때문에 함수는 일급 객체이다.
함수는 객체이며 , 객체는 프로퍼티와 프로퍼티 값으로 이뤄진 것을 의미한다고 하였다.
그럼 함수도 프로퍼티를 가질까 ?
가진다
function sum(x, y, z) {
return x + y + z;
}
console.log(Object.getOwnPropertyDescriptors(sum));
{
length: { value: 3, writable: false, enumerable: false, configurable: true },
name: {
value: 'sum',
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 }
}
함수는 내가 프로퍼티를 설정해주지 않아도 내장 프로퍼티가 존재하는데 그것이 length , arguments , caller , prototype
이다.
Property | Description |
---|---|
length |
The number of parameters expected by the function. |
arguments |
An array-like object containing the arguments passed to the function. |
caller |
(Deprecated) Reference to the function that invoked the currently executing function. |
prototype |
A property that allows the addition of properties and methods to the function's prototype object. |
length
는 함수 가 정의될 때 기대되는 매개변수 개수
를 의미한다.
argument
는 유사 배열 객체 로 함수가 받는 인수
들을 담는 자료구조이다.
자세한 내용은 밑에서 더 이야기 하도록 하겠다.
caller
는 현재 호출한 함수 자체를 나타낸다.
prototype
은 생성자 함수
가 생성한 객체들이 공유하는 프로퍼티와 메소드를 나타낸다.
위 함수는 x,y,z
3개의 매개변수를 기대하고 있기 때문에 length : 3
이다.
현재 argument : null
로 나오고 있는데 이는 현재 받은 인수
가 존재하지 않기 때문에 null
이다. 자세한 내용은 밑에서 추후 추가하도록 하겠다.
caller
는 현재 호출한 함수를 나타내며 stric mode
에선 사용이 제한된다.
arguments
함수는 매개 변수를 이용해 코드 로직을 작성하고, 함수를 호출 할 때 받은 인수를 매개변수에 할당해
반환값을 반환한다.
하지만 함수는 함수를 선언 할 때 설정한 매개 변수의 수와 인수의 수가 달라도 상관 없었는데
이는 arguments
라는 유사 배열 객체에 전달 받은 모든 인수를 받기 때문이다.
function sum(x, y, z) {
console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
return x + y + z;
}
sum(1, 2, 3, 4);
argument
는 함수 내부에서 지역 변수처럼 사용되어 함수 외부에서는 참조 할 수 없다.
유사 배열 객체처럼 취급 받는 이유는 다음과 같다.
arguments
는 들어온 인수의 순서대로 프로퍼티와 프로퍼티 값으로 객체를 저장한다.
그렇기 때문에 마치 인덱스로 arguments
객체의 값을 참조 할 수 있는 것처럼 보인다.
arguments
도 결국엔 객체다.
그럼 arguments
도 내부 프로퍼티를 갖는가 ?
갖는다.
function sum(x, y, z) {
console.log(arguments);
console.log(Object.getOwnPropertyDescriptors(arguments));
}
sum(1, 2, 3, 4);
{
'0': { value: 1, writable: true, enumerable: true, configurable: true },
'1': { value: 2, writable: true, enumerable: true, configurable: true },
'2': { value: 3, writable: true, enumerable: true, configurable: true },
'3': { value: 4, writable: true, enumerable: true, configurable: true },
length: { value: 4, writable: true, enumerable: false, configurable: true },
callee: {
value: [Function: sum],
writable: true,
enumerable: false,
configurable: true
},
[Symbol(Symbol.iterator)]: {
value: [Function: values],
writable: true,
enumerable: false,
configurable: true
}
}
arguments
의 내부 프로퍼티들을 살펴보면 인수로 전달받은 값들을 제외하고도 length , callee , Symbol
이 존재하는 것을 볼 수 있다.
length
프로퍼티length
는 arguments
자료구조의 길이를 나타내는 것으로 arguments
내부에 존재하는 프로퍼티들의 길이와 같다.
다음과 같은 특성으로 마치 배열처럼
function sum(x, y, z) {
let argLength = arguments.length;
for (let i = 0; i < argLength; i++) {
console.log(`${i}번째 값 : ${arguments[i]}`);
}
}
sum(1, 2, 3, 4);
0번째 값 : 1
1번째 값 : 2
2번째 값 : 3
3번째 값 : 4
처럼 배열을 순회하듯이 확인 할 수 있다.
하지만 배열과의 차이점은 생김새를 보면 알겠지만 argumets
는 배열이 아닌 배열과 유사한 성질을 가진 객체이다.
[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
그로 인해 배열에서 사용 가능한 메소드
들은 사용 불가능한 경우가 많다.
Racap
함수의 프로퍼티 length
는 전달받기를 기대하는 매개변수의 수를 의미한다.
함수 내부 프로퍼티 arguments 의 length
는 전달받은 인수의 수를 의미한다.
Symbol(Symbol.iterator)
Symbol(Symbol.iterator)
프로퍼티는 arguments
객체를 순회 가능한 자료구조인 itertable
로 만들기 위한 프로퍼티이다.
function sum(x, y, z) {
let iterator = arguments[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
}
sum(1, 2, 3);
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }
arguments
에 담긴 객체들을 iterator
형태로 만들어 next()
로 순회 할 수 있다.
Rest
파라미터자바스크립트에서 함수의 가장 큰 특징은 선언 할 때 설정한 매개 변수의 수와 상관 없는 인자들을 넣을 수 있다는 것이였다.
만약 모든걸 더하는 함수를 만들고 싶다면
function sum() {
let argLength = arguments.length;
let res = 0;
for (let i = 0; i < argLength; i++) {
res += arguments[i];
}
return res;
}
console.log(sum(1, 2, 3)); // 6
이런 식으로 arguments
에 담긴 인수들을 모두 더해가며 값을 구할 수 있을 것이다.
하지만 이는 배열자료구조라면 reduce
함수를 이용해 쉽게 구할 수 있기에
인수들이 담기는 자료구조가 arguments
인 유사배열객체가 아니라 정말 배열이면 좋겠다는 생각이 든다.
그래서 ES6
에서 나타난게 Rest
파라미터이다.
function sum(...args) {
console.log(Array.isArray(args));
console.log(args);
return args.reduce((pre, cur) => pre + cur, 0);
}
console.log(sum(1, 2, 3));
true
[ 1, 2, 3 ]
6
...자료구조명
을 매개변수로 설정하면 유사배열객체
가 아닌 배열
로서 받을 수 있다.
name
프로퍼티 name
프로퍼티는 함수명을 나타내는 프로퍼티이다.
function foo() {
console.log('foo!');
}
console.log(foo.name); // foo
name
프로퍼티는 함수명을 나타내는 것임을 잊지 말자
function foo() {
console.log('foo!');
}
let boo = foo;
console.log(boo.name); // foo
boo
라는 객체는 foo
를 가리키고 있기 때문에 boo.name
은 결국 foo.name
과 같다.
boo
는 foo
를 가리키는 식별자일 뿐이다.
__proto__
, prototype
모든 객체는 [[Prototype]]
이라는 내부 슬롯을 갖는다.
객체 지향 프로그래밍의 상속을 구현하는 프로토타입 객체를 가리킨다.
프로토타입 객체
상속을 구현하는 핵심 개념 중 하나로, 다른 객체로부터 상속되는 프로퍼티와 메소드를 포함하는 객체를 의미한다.
모든 객체는 프로토타입 체인을 통해 하나 이상의 프로토타입 객체로 연결되어 있으며, 객체가 프로토타입 체인을 통해 상위 객체의 프로퍼티와 메소드에 접근 할 수 있게 해준다.
__proto__
프로퍼티는 [[Prototype]]
내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티
이다.
접근자 프로퍼티
객체의 동작을 정의 할 수 있는 프로퍼티
간단한 예시를 통해 객체의 prototype
과 생성자 함수 객체가 가질 수 있는 prototype
프로퍼티를 살펴보자
function person(name) {
this.name = name;
}
person.prototype.sayHello = function () {
console.log(`hello i am ${this.name}`);
};
person.prototype.star = 'earth';
let tom = new person('tom');
위 코드에서 person
이란 생성자 함수는 name
을 인수로 받아 객체를 생성하는 생성자 함수
이다.
이 때 person
의 prototype
으로 인사를 하는 sayHello
함수와 어느 행성에 사는지를 나타내는 star
라는 프로퍼티에 함수와 값을 추가해주었다.
그리고 tom
이라는 식별자에 tom
이란 이름을 가진 객체를 new person
생성자 함수를 이용해 생성해주었다.
person
생성자 함수가 선언 되었을 때는 prototype
이 존재하지 않고 이름을 가진 객체를 생성하는 생성자 함수였다.person
의 prototype
에 메소드(sayHello
)와 프로퍼티(star
)를 추가해줌으로서 person
이란 생성자 함수에 기능을 추가해주었다.person
을 상속 받아 생성된 인스턴스 tom
은 person
을 통해 객체가 생성 되었을 뿐 아니라 person
의 prototype
또한 상속 받는다.prototype
console.log(person.prototype); // { sayHello: [Function (anonymous)], star: 'earth' }
person
생성자 함수에는 동적으로 추가된 프로토타입 객체들이 존재한다.
프로토타입 객체에는 메소드
와 프로퍼티
들로 이뤄져있는 모습을 볼 수 있다.
property
console.log(tom); // person { name: 'tom' }
console.log(typeof tom); // object
console.log(tom.__proto__); // { sayHello: [Function (anonymous)], star: 'earth' }
person
을 통해 생성된 인스턴스 tom
은 객체로 생성되며
tom.__proto__
를 통해 tom
이 상속받은 프로토타입 객체
에 접근 할 수 있다.
tom.sayHello(); // hello i am tom
console.log(tom.star); // earth
person
의 prototype
객체를 상속받은 tom
은 prototype
객체의 메소드와 프로퍼티를 사용 할 수 있다.
console.log(tom.__proto__ === person.prototype); // true
정리
객체의
__proto__
는 상속받은프로토타입
을 가리킨다.__proto__
를 통해 자신의 부모인 프로토타입 객체에 접근 할 수 있다.
생성자 함수의prototype
은 상속 시킬 객체를 가리킨다. 이 프로토타입 객체에 추가된 메소드나 프로퍼티는 생성자 함수로소부터 생성된 모든 객체에게 공유된다.