모던JS딥다이브 공부정리용 글입니다.

18.1_ 일급 객체

일급 객체란?

  1. 무명의 리터럴로 생성 가능(런타임에 생성 가능)
  2. 변수나 자료구조(객체, 배열)에 저장 가능
  3. 함수의 매개변수에 전달 가능
  4. 함수의 반환값으로 사용 가능
// 1. 함수는 무명의 리터럴로 생성할 수 있다.
// 2. 함수는 변수에 저장할 수 있다.
// 런타임(할당 단계)에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당된다.
(선언문은 평가단계에서...
 표현식은 평가단계에서 변수 선언 후 런타임 때 할당이 이루어짐)
const increase = function (num) { //무명
  return ++num;
};

const decrease = function (num) {
  return --num;
};

// 2. 함수는 객체에 저장할 수 있다.
const auxs = { increase, decrease };

// 3. 함수의 매개변수에게 전달할 수 있다.
// 4. 함수의 반환값으로 사용할 수 있다.
function makeCounter(aux) {
  let num = 0;

  return function () {
    num = aux(num);
    return num;
  };
}

// 3. 함수는 매개변수에게 함수를 전달할 수 있다.
const increaser = makeCounter(auxs.increase);
console.log(increaser()); // 1
console.log(increaser()); // 2

// 3. 함수는 매개변수에게 함수를 전달할 수 있다.
const decreaser = makeCounter(auxs.decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

함수가 일급 객체라는 것은 함수를 객체와 동일하게 사용 가능하다는 의미.
객체는 값! -> 함수 또한 값으로 취급 가능!
즉, 변수 할당문, 객체의 프로퍼티 값, 배열요소, 함수 호출의 인수, 함수 반환문에 리터럴로 정의가 가능하며 런타임에 함수 객체로 평가됨.

  • 일급 객체로서의 함수의 가장 큰 특징!
  1. 일반 객체처럼 함수의 매개변수에 전달 가능
  2. 함수의 반환값으로도 사용 가능
  • 이는 함수형 프로그래밍을 가능케 하는 자바스크립트의 장점 중 하나!

함수형 프로그래밍이란?

function add(sum,count) {
  sum += count;
  if(count > 0) {
    return add(sum, count - 1); //10 + 9 + 8....= 55
  }else {
    return sum;
  }
}
add(0, 10) //55

위의 재귀함수처럼 add를 add 내에서 재호출하고
함수 재사용을 적극적으로 활용하는 것이다.
이것이 가능한 이유가 함수를 반환값으로 사용 가능하기 때문이다.

18.2_ 함수 객체의 프로퍼티

함수는 객체이다. 따라서 함수도 프로퍼티를 가질 수 있다.

🔎 함수 객체의 내부 들여다보기

function square(number){
  return number * number;
}

console.dir(square);


//결과
ƒ square(number)
arguments: null
caller: null
length: 1
name: "square"
prototype: {constructor: ƒ}
[[FunctionLocation]]: VM50:1
[[Prototype]]: ƒ ()

square 함수의 모든 프로퍼티의 프로퍼티 어트리뷰트를 확인해보자
(Object.getOwnPropertyDescriptors(객체))

console.log(Object.getOwnPropertyDescriptors(square));
/*
{
  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}
}
*/![](https://velog.velcdn.com/images/bigwave-cho/post/0ff6885d-2f57-4245-b656-0b045db9a83e/image.png)

// proto는 square 함수의 프로퍼티가 아니다.
console.log(Object.getOwnPropertyDescriptor(square, 'proto')); // undefined

// proto는 Object.prototype 객체의 접근자 프로퍼티다.
// square 함수는 Object.prototype 객체로부터 proto 접근자 프로퍼티를 상속받는다.
console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'proto'));
// {get: ƒ, set: ƒ, enumerable: false, configurable: true}

`__proto__`는 square함수의 프로퍼티가 아니라
Object.prototype 객체의 접근자 프로퍼티이다.
proto를 이용하여 부모 객체의 프로토타입에 접근 가능하단 의미.

[접근자 프로퍼티? 16장 2절](https://velog.io/@bigwave-cho/JS16%EC%9E%A5-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B8#1632_-%EC%A0%91%EA%B7%BC%EC%9E%90-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0)

이처럼 arguments, caller, length, name, prototype 프로퍼티는 모두 함수 객체의 데이터 프로퍼티이다.
이들은 일반 객체에는 없는 함수 객체의 고유 프로퍼티

But!! __proto__는 접근자 프로퍼티로 함수 객체 고유가 아닌 Object.prototype 객체의 프로퍼티를 상속받은 것이다.
따라서 위 접근자는 모든 객체가 사용가능하다.

  • (prototype은 객체이고 get,set이란 접근자 프로퍼티를 가지고 있으며 prototype은 자식 객체들에 상속됨) -19장에서 자세히 알아보기!

18.2.1_ arguments 프로퍼티

함수 객체의 argumenets 프로퍼티 값은 argumenets 객체이다.
arg는 함수 호출 시 인수들의 정보를 담고 있는 순회가능한(interable) 유사 배열 객체이고 함수 내부에서 지역변수처럼 사용된다.

함수 내부의 argumenets 객체 확인!

function multiply(x, y) {
  console.log(arguments);
  return x * y;
}

console.log(multiply());        // NaN
console.log(multiply(1));       // NaN
console.log(multiply(1, 2));    // 2
console.log(multiply(1, 2, 3)); // 2

함수를 정의할 때 선언한 매개변수는 함수 몸체 내부에서 변수와 동일하게 취급 됨.

즉 함수가 호출되면 함수 몸체 내에서 암묵벅으로 매개변수가 선언되고 undefined로 초기화된 이후 인수가 할당됨

인수가 적게 전달되면 undefined 초과되면 무시!

무시한다고 버리는게 아니라 arguments 객체의 프로퍼티로 보관된다.

  • arguments객체 프로퍼티의 키는 인수 순서, 값은 인수
  • calle 프로퍼티는 호출되어 arguments를 생성한 함수(즉 함수 자신)
  • length 프로퍼티는 인수의 개수
  • arguments 객체의 Symbol(Symbol.iterator)프로퍼티
    : arguments 객체의 Symbol(Symbol.iterator)프로퍼티는
    arguments 객체를 순회가능한 iterable로 만들기 위한 프로퍼티이다.
    Symbol.iterator를 사용하면 이터러블이 된다.


자세한 것은 34장 iterable에서 알아보기.

❓arguments 객체는 어디 사용할까요?

  • 선언된 매개변수의 개수와 함수를 호출할 때 전달하는 인수의 개수를 확인하지 않는 JS의 특성으로 인수 개수를 확인하고 이에 따라 함수의 동작을 다르게 정의할 필요가 있을 때 유용함.
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

arguments객체는 배열 형태로 인자 정보를 담고 있는 배열이 아닌 유사 배열 객체이다.(유사배열객체 : 객체지만 배열처럼 순회(iterable)가능한 것)

유사배열객체와 이터러블
ES6에서 도입된 이터레이션 프로토콜을 준수하면 이터러블이 된다.
ES6이전의 arguments객체는 유사배열객체로 분류되었지만 ES6부터는 유사배열객체이자 이터러블이다.

유사배열개체는 배열이 아니라서 배열 메서드를 사용하면 에러가 발생함
(call, apply 간접호출에 대해서는 22장에서 다룰 것)

  • 예제 18-07은 22장에 대해 공부한 후 다시 정리해보기

ES6의 경우 Rest 파라미터를 도입하여 이를 해결했다.

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

Rest파라미터에 대해서는 26장 4절에서 알아보자.

18.2.2 caller 프로퍼티

caller 프로퍼티는 ECMAScript 사양에 포함되지 않은 비표준 프로퍼티.
표준화될 예정도 없는 프로퍼티로 알아만 두자!

caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.

function foo(func) {
  return func();
}

function bar() {
  return 'caller : ' + bar.caller;
}

// 브라우저에서의 실행한 결과
console.log(foo(bar)); // caller : function foo(func) {...}
console.log(bar());    // caller : null

18.2.3_ length 프로퍼티

  • 함수를 정의할 때 선언한 매개변수의 개수를 가리킴.
    foo() //0
    foo(x,y)//2

  • arguments 객체의 프로퍼티 길이와 함수 객체 길이는 다를 수 있다.

function A(x,y){} //함수 객체 길이  2

A(1,2,3) // arguments.length 3

18.2.4_ name프로퍼티

함수 객체의 name 프로퍼티는 함수 이름(ES6부터 표준이 되었다)

  • name프로퍼티는 ES5와 6에서 다르게 동작하니 주의

    익명 함수 표현식
    const fn = function () {}
    ES5 : 빈문자열
    ES6 : 함수 객체를 가리키는 식별자(fn)

18.2.5_ proto 접근자 프로퍼티

모든 객체는 [[Prototype]]이라는 내부 슬롯을 가짐.
프로토타입 내부 슬롯은 객체지향 프로그래밍의 상속을 구현하는 프로토타입 객체를 가리킨다.

❗️내부슬롯은 접근자 프로퍼티를 통해 간접적으로 허용된 경우에만 접근이 가능하다.
prototype에 접근하기 위한 __proto__ 접근자 프로퍼티

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

18.2.6_ prototype 프로퍼티

프로토타입 프로퍼티는 생성자 함수로 호출 가능한 함수 객체(constructor)
만이 소유하는 프로퍼티이다.

일반 객체나 non-constructor에는 프로토타입 프로퍼티가 존재하지 않는다.

// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty('prototype'); // -> true

// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty('prototype'); // -> false

프로토타입 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 호출될 때
생성자 함수가 생성할 인스턴스의 포로토타입 객체를 가리킨다.

profile
주먹구구식은 버리고 Why & How를 고민하며 프로그래밍 하는 개발자가 되자!

0개의 댓글