일급객체 / 함수객체의 프로퍼티
# 일급객체란?
일단 일급객체 용어부터 정리하면
1. 무명의 리터럴로 생성할 수 있음. 런타임 생성 가능
const fuc = function(){
return
}
뭐 이런뜻인듯?
중첩함수에서 만들어지는게 런타임중 만들어지는 함수인가..? 암튼
2. 변수나 자료구조에 저장할 수 있다.
3. 함수의 매개변수에 전달할 수 있다.
4. 함수의 반환값으로 사용될 수 있다.
2,3,4번은 그냥 함수리터럴이 메모리에 함수객체로 저장되는 표현식이니까 당연한거..
3번은 자주쓰이는 콜백함수다.
뭐 이렇단다.. 아직 중요한지는 모르겠음.
# 함수객체의 프로퍼티
함수객체를 console.dir로 찍어보자
const fuc = function () {
return;
};
console.dir(fuc);
저렇게 arguments, caller, length, name, prototype이라는 프로퍼티가 들어있다.
# 화살표 함수의 프로퍼티
화살표함수 객체를 console.dir로 찍어보자
const arrow = () => {
return;
};
console.dir(arrow);
저렇게 length, name, arguments, caller이라는 프로퍼티가 들어있다.
근데 arguments와 caller프로퍼티는 화살표함수에서 (...) 이고
일반함수에서는 null이 들어있다. 저 쩜쩜쩜은 뭐야?
펼쳐보면
저렇게 TypeError머시기가 들어있다.
화살표함수에서 저 arguments, caller프로퍼티를 참조하려고하면
console.log(arrow.caller); //타입에러
console.log(arrow.arguments); //타입에러
아까 저기 담겨있던 타입에러가 그대로 뜬다.
# 정확히는 화살표함수객체의 프로퍼티에는 arguments caller prototype이 없다
- arguments, caller프로퍼티
위에서 썼듯이 브라우저상에선 프로퍼티가 있으나 참조하려고하면 저런 에러를 띄워준다.(노드환경에서도 똑같은에러뜸)
딥다이브에서는 아예 저 프로퍼티가 없다고 뜬다.
없는 프로퍼티를 참조하려고하면 undefined를 띄우는게 상식이지만 특별하게 에러를 띄워준다.
아예없다고 생각하든, 참조시 에러터뜨려주는거든 좋은대로 생각하면 될거다.
# prototype 프로퍼티
https://jacobowl.tistory.com/182
메서드가 뭐야?
흔히들 메서드는 객체안에 존재하는 함수? 같은걸로 생각을 한다. 뭐 기능면에서는 동작이야 다 잘되겠지만.. 일단 아래코드를 보자. const obj = { 화살표함수: () => { console.log("화살표함수"); }, 메
jacobowl.tistory.com
이건 예전 포스팅에서 설명했듯이 화살표함수는 생성자로써 호출할수 없다.
메서드도 마찬가지로 prototype프로퍼티가 없다.
걍 둘다 non-constructor이라서 그렇다
const obj = {
method() {
return;
},
};
console.dir(obj.method);
메서드 객체도 console.dir로 찍어보면 이렇게 prototype프로퍼티가 없는걸 확인할 수 있다.
근데 신기하게 메서드에도 arguments, caller프로퍼티가 없다고 뜬다.
화살표함수 프로퍼티랑 똑같이 생김 ㅎ
# 클래스의 프로퍼티
클래스도 궁금해서 console.dir로 찍어봤다.
const cls = class {
constructor() {}
};
console.dir(cls);
그렇다. 클래스도 함수다.
그냥 함수객체와 클래스객체는 함수로써 비슷하게 생겼다. 뭐 다만 나중에 클래스배우면 isClassConsturctor인가 있다는데 이건 나중에 배우면되고..
다만 클래스에서도 arguments와 caller프로퍼티는 타입에러뜨게 되어있다.
암튼 이렇다
일반함수 | 화살표함수 | 메서드 | 클래스 | |
arguments프로퍼티 | O | X | X | X |
caller프로퍼티 | O | X | X | X |
prototype프로퍼티 | O | X | X | O |
뭐 대충 이렇게 정리되는듯?
# 좀 더 파고들어가보자
- 클래스와 일반함수(생성자함수)의 프로토타입
console.log(cls.__proto__ == fuc.__proto__); //true뜸
이 코드찍어보면 클래스랑 일반함수에서 프로토타입 내부슬롯은 똑같대.
브라우저상에서 [[Prototype]]을 펼쳐보면
얘 정체는 바로
console.log(Function.prototype == cls.__proto__);//true
Function prototype이다.
뭐 클래스객체나 함수객체나 둘다 함수라니까 당연한 결과
- 그럼 클래스와 일반함수의 프로퍼티들의 다른점은 무엇일까?
아까 말했던 isClassConstructor차이는 뭐 아직 안배운거니까 스킵하고,
먼저 두 생성자에 프로토타입 메서드를 추가해보자.
const 생성자 = function () {};
const 클래스 = class {
constructor() {}
test() {
console.log("헬로우 클래스");
}
};
생성자.prototype.test = function () {
console.log("헬로우 생성자");
};
일단 둘다 이름을 한글로 바꿔봤다.
그리고 둘다 프로토타입메서드로 test를 추가해줬다.
그리고 이 프로토타입메서드 함수객체를 뜯어보면?
console.log(
"생성자함수꺼",
Object.getOwnPropertyDescriptor(생성자.prototype, "test")
);
console.log(
"클래스꺼",
Object.getOwnPropertyDescriptor(클래스.prototype, "test")
);
어랏? 여긴 좀 다르다. 생성자함수의 프로토타입메서드인 test는 enumerable이 true이다.
클래스의 프로토타입메서드인 test는 enumerable이 false이다.
이전 프로퍼티 포스팅에서 썼듯이 저건 열거할수 있냐이건데 코드로 테스트해보면
for (a in 생성자.prototype) {
console.log("생성자에서");
console.log(a);
console.log("생성자끝");
}
for (a in 클래스.prototype) {
console.log("클래스에서");
console.log(a);
console.log("클래스 끝");
}
생성자 반복문만 코드가 동작하고 안에있는 test메서드가 뜬다.
클래스의 프로토타입 메서드인 test는 enumerable이 false라서 반복문에선 안돌아가는거다.
하지만 이게 클래스의 프로토타입 객체자체가 반복문이 안돌아간다는 의미는 아니다.
클래스.prototype.value = 1;
for (a in 클래스.prototype) {
console.log("클래스에서");
console.log(a);
console.log("클래스 끝");
}// value잘띄워줌
클래스의 프로토타입객체에 임의의 프로퍼티를 넣어주고 돌려주면 잘 동작한다.
그냥 해당객체에 enumerable이 true인 프로퍼티가 없으면 아예 for in 반복문이 동작을 안하는듯.
여기서 for in 반복문 돌리기전에 돌릴 객체의 프로퍼티들 속성들을 체크한다는걸 알 수있는듯?
# arguments프로퍼티
위에서 살펴봤지만 함수에는 arguments프로퍼티가 있다.
얘는 null로 뜬다. 설명했듯이 화살표함수, 클래스, 메서드에는 없다. 브라우저상으로 옅은 색으로 보이긴하지만 에러띄우게 담긴거임. (에러객체인듯)
이 null이 담긴 프로퍼티는 어따가 써먹을까?
# arguments프로퍼티의 사용법
const showArguments = function () {
console.log(arguments);
};
showArguments();
이 코드를 실행하면
요렇게 arguments객체가 뾰로롱 나온다.
분명 함수 밖에서 함수객체의 arguments를 참조했을땐 (프로퍼티로 봤을때는) null이었다.
그럼 함수에다가 인수를 전해넣어보자.
const showArguments = function () {
console.log(arguments);
};
showArguments('무찌');
이젠 조금 바꼈다. 0의 프로퍼티에 '무찌'가 담겨졌다.
showArguments("무찌", "도리");
이번엔 인수를 두개 넣어줬을때
인수 두개 넣어주니 무찌도리가 둘다 들어갔다.
이 함수내에서 참조한 arguments객체에는 함수 호출시 전해준 인수들이 뾰로롱 나타난다.
저기서 callee와 Symbol(Symbol.iterator)프로퍼티를 펴보면?
callee프로퍼티는 이 arguments 객체를 생성한 함수인 fucArguments함수가 바인딩된다.
# arguments객체의 callee, Symbol(Symbol.iterator)프로퍼티
const fucArguments = function () {
console.log(arguments);
const 순회시키자 = arguments[Symbol.iterator]();
console.log(순회시키자.next()); //무찌 출력
console.log(순회시키자.next()); //도리 출력
console.log(순회시키자.next()); // undefined
console.log(순회시키자.next()); // undefined
};
fucArguments("무찌", "도리");
순회가능하게 바꿔준다고해서 난 어레이로 바뀌는건줄알았는데 그건아니고 Symbol.iterator프로퍼티에 바인딩된 함수를 호출하면
요런 객체로 바뀜.. 그리고 next메서드를 호출하면 인수로 넣은 값들 하나씩 돌아가면서 순회하는듯.
이터러블을 아직 안배워서 어따써먹는지는 잘 모르겠따.
# arguments객체의 응용법
사실 이 객체는 위에서 정리한것을 보면 화살표함수, 클래스 등등 최신?(사실 최신도아님..) 문법의 함수들에선 사라졌다..
이건 예전에 가변인자함수를 구현할때 썼던 객체이다.
예를들어보면
const fucArguments = function () {
for (let i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
};
fucArguments("무찌"); // 무찌 출력
fucArguments("무찌", "도리"); // 무찌, 도리 둘다 출력
fucArguments("무찌", "도리", "똥개"); // 무찌 도리 똥개 전부다 출력
맨처음 함수에 파라미터들을 안정해줬음에도 요렇게 매개변수를 여러개 전해줘도 알아서 반복문 다 돌아간다.
이럴때 arguments객체를 써야한다.
여기서 특이한점은 뭔가 배열?어레이같은데 arguments에 바인딩된건 배열이 아닌 '객체'다.
인수 두개 넣은 arguments객체는 length프로퍼티가 2이다. 그래서 배열처럼 length프로퍼티를 참조할 수 있지만 이건 배열이 아니라 유사배열객체라고 하는거다. 때문에 어레이처럼 arguments.length, argument[1] 등등 쓸수 있다.
이름 참 고급스럽고 어렵게 지어놨다.
요즘 안쓰는 이유는 REST파라미터를 쓸 수 있기 때문이다.
const fucArguments = function (...arg) {
console.log(arg); //함수내에서는 전해준 인수들이 배열에 담겨짐. ['무찌','도리',~~~]
for (let i = 0; i < arg.length; i++) {
console.log(arg[i]);
}
};
fucArguments("무찌"); // 무찌 출력
fucArguments("무찌", "도리"); // 무찌, 도리 둘다 출력
fucArguments("무찌", "도리", "똥개"); // 무찌 도리 똥개 전부다 출력
함수의 매개변수로 정의한 ...arg는 인수가 담길때 갯수상관없이 이걸 함수내부에서 배열로 만들어주고 이 배열은 arg로 참조할수 있다.
arguments객체가 사라진 이유는 저 arguments객체가 배열이 아닌 객체라서다. 물론 length프로퍼티와 인수들 프로퍼티이름이 0,1,2등등 넘버값으로 지정이 되기때문에 배열처럼 쓰고 length도 제대로 찍어지지만, 다른 배열로써 쓰이는 메서드들은 쓸수가 없어서 아예 안에서 배열로 만들어준거다.
최신문법이고 꽤 유용하다.
const fucArguments = function () {
arguments.pop(); // 에러뜸!
};
fucArguments('무찌','도리') // 타입에러. arguments에는 pop프로퍼티가 없어서 undefined인데 호출하려하니.
애초에 배열로 담아주는데 굳이 유사배열객체인 arguments를 쓸필요는 없다
REST파라미터는 최신문법이고 꽤 유용하다.
# fucArguments.arguments로 참조하면 왜 null일까?
const fucArguments = function () {
console.log(arguments); //제대로 뜸
};
console.log(fucArguments.arguments); // null
arguments객체는 함수가 호출되고 그 함수가 동작할때만 제대로 생성되서다.
즉 함수 실행컨텍스트가 생성되고 없어질때까지만 제대로 존재하고 나머지는 null로 바인딩되어있다.
# 화살표함수에서 arguments참조
const fucArguments_arrow = () => {
console.log(arguments);
};
fucArguments_arrow();
위에서 설명했듯이 화살표함수에는 arguments객체가 없다.
떄문에 함수내에서 참조하려고하면 에러뜨는게 당연하다.
때문에 브라우저환경에서 저 코드를 돌리면 저렇게 참조에러가 뜬다.
근데 노드js환경에서는 참조가된다? 뭔가 굉장히 긴 객체가 튀어나온다. 이건 뭘까?
노드 js에서는 모듈로 돌아가기때문에 전역에서 arguments객체가 존재한다. 아직 이걸 제대로 안배워서 뭔지는 모르겠지만, 저 위의 화살표함수가 동작할때는 먼저 함수 내에서 arguments식별자로 검색후 없으니 상위스코프인 전역스코프로 올라가서 arguments를 검색해서 참조해주기때문에 그런거다.
때문에 아래처럼 직접 프로퍼티로 참조하면 에러가 제대로 뜬다.
fucArguments_arrow.arguments; // 타입에러
# caller프로퍼티
const fuc = function () {
console.log(fuc.caller);
};
fuc(); // 브라우저에서는 null, 노드에서는 뭔 함수담겨있음. 뭔지모름.
console.log(fuc.caller); //null
얘는 일단 함수내에서 그냥 caller식별자로만 참조하면 참조에러가 뜬다.
함수객체의 프로퍼티는 함수내에서는 식별자로써 다 참조할수 있는줄 알았는데 caller는 안되더라.
함수이름.caller로 참조해야 됨. 근데 궁금해서 보니까 prototype, length도 안되긴하더라.
암튼 함수내에서 프로퍼티 키 그대로 참조할 수 있는건 arguments와 name뿐이다.
그럼 이건 어따 쓸까?
이건 해당함수를 호출한 상위함수객체를 가져온다.
const fuc = function () {
console.log(fuc.caller); // fuc함수를 호출한 함수는 없으니 null
const innerFuc = function () {
console.log(innerFuc.caller); // innerFuc을 호출한 fuc함수가 담겨있음.
};
innerFuc();
};
fuc();
//이코드 스택오버플로 재귀함수임
const fuc = function () {
console.log(fuc.caller);
const innerFuc = function () {
innerFuc.caller()
};
innerFuc();
};
fuc();
이따구로 쓰면 innerFuc.caller가 fuc이라서 스택오버플로우가 발생함. 직접돌리지말긴바란다..
하지말라면 하지마!
const innerFuc = function () {
console.log(innerFuc.caller);
};
const fuc1 = function (callback) {
console.log("1번함수에서 호출된 innerFuc");
callback();
};
const fuc2 = function (callback) {
console.log("2번함수에서 호출된 innerFuc");
callback();
};
fuc1(innerFuc); // fuc1함수객체
fuc2(innerFuc); // fuc2함수객체
이렇게 콜백으로 넣어준 함수에서 caller프로퍼티에는 이 함수를 호출한 함수를 바인딩해준다.
솔직히 이 caller프로퍼티는 비표준 프로퍼티란다. 그래서 호환성도 안좋고.. 안쓰는게 좋을것같다.
위에서 nodejs환경에서 caller참조가 된건 아마 이것도 모듈이라서 그런거같다. 무슨함수가 참조됐는지는 모름 ㅋ
# length프로퍼티
length 프로퍼티는 함수정의시 설정한 매개변수의 개수이다.
const fuc = function (para1, para2) {
return;
};
console.dir(fuc.length); // 2
이렇단다..
REST파라미터로 정의한 함수의 length는?
const fuc = function (...arg) {
// console.log(fuc.length);
return;
};
console.dir(fuc.length); // 0
앤 0이 뜬다.
매개변수와 인수를 헷갈리지 말긴 바란다.
포스팅 하나 쓸거임.. 나도 예전엔 파라미터(매개변수)와 아규먼트(인수)를 헷갈렸었다.
const fuc = function (para1, para2, ...arg) {
// console.log(fuc.length);
return;
};
console.dir(fuc.length); // 2
아예 REST파라미터는 숫자로 안세는듯.
저거 나중에 나오지만 파라미터 저따구로 정하면 세번째부터 arg배열에 담기는거다.
# name프로퍼티
예전에 설명함
https://jacobowl.tistory.com/172
함수의 진짜 이름
# 기명함수와 익명함수console.log(function fuc() { return "뿩!"; }, ": 기명함수/ 이름이 있는 함수래~"); console.log(function () { return "뿩!"; }, ': 익명함수/ 이름이 없는 함수래~');둘다 함수리터럴로 평가되서
jacobowl.tistory.com
뭐 이거보면된다. 귀찮아서 다시 반복해서 안씀
+ 추가해서 딥다이브에선 __proto__프로퍼티도 있다는데
내가 직접 브라우저에서 확인한결과 이 프로퍼티는 없었다.
애초에 저건 Object.prototype에 들어있어서 모든객체들(Object.create null말구)이 쓸수 있는 프로토타입 메서드인 접근자프로퍼티다. 딥다이브가 옛날책이라서 옛날에는 있었거나 내가 브라우저를 크롬으로 쓰니까 크롬에만 없는거거나 이런경우겠지?
이것도 저 바로위에 저 포스팅 들어가보면 써놨다
끗