diff --git a/book/_toc.yml b/book/_toc.yml index b207425..1149d9f 100644 --- a/book/_toc.yml +++ b/book/_toc.yml @@ -21,6 +21,7 @@ parts: - file: docs/chapter_15 - file: docs/chapter_16 - file: docs/chapter_17 + - file: docs/chapter_18 - caption: 개인 공간 chapters: - file: 공희재/main @@ -37,6 +38,7 @@ parts: - file: 공희재/chapter_14 - file: 공희재/chapter_15 - file: 공희재/chapter_17 + - file: 공희재/chapter_18 - file: 김현우/main sections: - file: 김현우/chapter_04 diff --git a/book/docs/chapter_18.md b/book/docs/chapter_18.md index c320a92..729ad3a 100644 --- a/book/docs/chapter_18.md +++ b/book/docs/chapter_18.md @@ -1,321 +1,113 @@ # Chapter 18 - 함수와 일급 객체 -아래의 조건을 만족하는 객체를 일급 객체라고 합니다. -- 무명의 리터럴로 생성이 가능하다. (= 런타임에 생성이 가능하다) - - 코드 내에서 특정 값을 바로 생성하거나 정의할 수 있는 방식 -- 변수나 자료구조(객체, 배열 등)에 저장할 수 있다. -- 함수의 매개변수에 전달할 수 있다. (= 매개변수로 함수를 받을 수 있다) -- 함수의 반환값으로 사용할 수 있다. (= 리턴값으로 함수를 리턴할 수 있다) +## 1. 일급 객체 +일급 객체란, 자바스크립트에서 다음 같은 조건을 만족하는 객체를 뜻한다. +1. 무명의 리터럴로 생성할 수 있다. 즉, **런타임에 생성**이 가능하다. +2. **변수나 자료구조**(객체, 배열 등)**에 저장**할 수 있다. +3. 함수의 **매개변수에 전달**할 수 있다. +4. 함수의 **반환값으로 사용**할 수 있다. -
+**자바스크립트의 함수는** 위의 조건을 모두 만족하므로 **일급 객체**다.
+즉, 함수는 **값을 사용할 수 있는 곳**(변수 할당문, 객체의 프로퍼티 값, 배열의 요소, 함수 호출의 인수, 함수 반환문)이라면
+**어디서든지 리터럴로 정의**할 수 있으며, **런타임에 함수 객체로 평가**된다. -자바스크립트의 함수는 위 조건을 모두 만족하므로 일급 객체입니다. +이러한 자바스크립트 함수의 특징은, 함수형 프로그래밍을 가능케 하며 자바스크립트의 장점 중 하나다. -``` js -// 1. 함수는 무명의 리터럴로 생성 가능하다. -// 2. 함수는 변수에 저장할 수 있다. -const increase = function (num) { - return ++num; -}; -const decrease = function (num) { - return --num; -}; +## 2. 함수 객체의 프로퍼티 +함수는 객체다. 따라서 함수도 프로퍼티를 가진다.
+함수 객체의 프로퍼티를 하나씩 살펴보자. -// 2. 함수는 객체에 저장할 수 있다. -const auxs = function (num) { - return --num; -}; +### 2-1. `arguments` 프로퍼티 +함수 객체의 `arguments` 프로퍼티 값은, `arguments` 객체다.
+**`arguments` 객체**는 함수 호출 시 전달된 **인수(arguments)들의 정보**를 담은, **순회 가능한(iterable) 유사 배열 객체**이며,
+함수 내부에서 지역변수처럼 사용한다. 즉, **함수 외부에서는 참조할 수 없다**. -// 3. 함수의 매개변수에 전달할 수 있다. -// 4. 함수의 반환값으로 사용할 수 있다. -function makeCounter(aux) { - let num = 0; - return function () { - num = awx(num); - return num; - }; -} - -// 3. 함수는 매개변수에게 함수를 전달할 수 있다. -const increase = makeCounter(auxs.increase); -console.log(increaser()); // 1 -console.log(increaser()); // 2 -``` - - -
- -일급 객체로서 함수가 가지는 가장 큰 특징은 -일반 객체와 같이 함수의 매개변수에 전달할 수 있으며, 함수의 반환값으로도 사용할 수 있다는 것입니다. -이는 함수형 프로그래밍을 가능하게 하는 자바스크립트의 장점 중 하나입니다. - -

- -### 함수 객체의 프로퍼티 -함수는 객체입니다. 따라서 함수도 프로퍼티를 가질 수 있습니다. - -``` js -function square(number) { - return number * number; -} - -console.dir(square); -``` - - - -
- -`square` 함수의 모든 프로퍼티에 대해 -프로퍼티 어트리뷰트를 `Object.getOwnPropertyDescriptors` 메소드로 확인해보면 다음과 같습니다. - -``` js -Object.getOwnPropertyDescriptors(square); -``` - - - -
- -``` js -// __proto__는 square 함수의 프로퍼티가 아니다. -Object.getOwnPropertyDescriptor(square, '__proto__'); // undefined - -// __proto__는 Object.prototype 객체의 접근자 프로퍼티이다. -// square 함수는 Object.prototype 객체로부터 __proto__ 접근자 프로퍼티를 상속받는다. -Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'); -// {enumerable: false, configurable: true, get: ƒ, set: ƒ} -``` - -
- -이와 같이 `arguments`, `caller`, `length`, `name`, `prototype` 프로퍼티는 모두 함수 객체의 데이터 프로퍼티입니다. -이 프로퍼티들은 일반 객체에는 없는 함수 객체 고유의 프로퍼티입니다. - -다만, `__proto__`는 접근자 프로퍼티이며, 함수 객체 고유의 프로퍼티가 아니라 -`Object.prototype` 객체의 프로퍼티를 상속받은 것을 알 수 있습니다. - -`Object.prototype` 객체의 프로퍼티는 모든 객체가 상속받아 사용할 수 있습니다. -즉, `Object.prototype` 객체의 `__proto__` 접근자 프로퍼티는 모든 객체가 사용할 수 있습니다. -> 상속은 19장 "프로토타입"에서 자세히 다룰 것이다. - - -

- -#### arguments 프로퍼티 -함수 객체의 `arguments` 프로퍼티 값은 `arguments` 객체입니다. -`arguments` 객체는 함수 호출 시 전달된 인수(`argument`)들의 정보를 가지고 있는 순회 가능한(`iterable`) 유사 배열 객체이며, 함수 내부에서 지역 변수처럼 사용됩니다. - -자바스크립트는 함수의 매개변수와 인수의 개수가 일치하는지 확인하지 않습니다. -따라서 함수 호출 시 매개변수 개수만큼 인수를 전달하지 않아도 에러가 발생하지 않습니다. - -``` js -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 - -``` - -
- -다만 초과된 인수가 그냥 버려지는 것은 아닙니다. -모든 인수는 암묵적으로 `arguments` 객체의 프로퍼티로 보관됩니다. - - - -
- -`arguments` 객체는 인수를 프로퍼티 값으로 소유하며 프로퍼티 키는 인수의 순서를 나타냅니다. -`arguments` 객체의 `callee` 프로퍼티는 호출되어 함수 자기자신을 가리키고 -`arguments` 객체의 `length` 프로퍼티는 인수의 개수를 가리킵니다. - - -
- -※ arguments 객체의 Symbol(Symbol.iterator) 프로퍼티 -`arguments` 객체의 `Symbol(Symbol.iterator)` 프로퍼티는 `arguments` 객체를 -순회 가능한 자료구조인 이터러블(`iterable`)로 만들기 위한 프로퍼티입니다. +![image](https://github.com/user-attachments/assets/388943be-09ef-42b1-9841-187967fe6595) -``` js -function multiply(x, y) {d - // itertor - const iterator = arguments[Symbol.iterator](); +위 콘솔 사진에서 빨간 네모 표시된 부분이 인수의 정보가 보여지는 부분이다.
+`arguments` 객체는 다음과 같이 매개변수 개수를 확정할 수 없는, **가변 인자 함수를 구현할 때 유용**하다. - // iterator의 next() 메소드를 호출하여 이터러블 객체 `arguments`를 순회 - console.log(iterator.next()); // {value: 1, done: false} - console.log(iterator.next()); // {value: 2, done: false} - console.log(iterator.next()); // {value: 3, done: false} - console.log(iterator.next()); // {value: undefined, done: true} - - return x * y; -} - -multiply(1, 2, 3); -``` - -
- -`arguments` 객체는 매개변수 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 특히 유용합니다. - -``` js +```javascript function sum() { - let res = 0; - - 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 객체는 유사 배열 객체이기 때문에 -배열과 같이 사용하고 싶다면, `Function.prototype.call`, `Function.prototype.apply`와 같이 간접 호출해야 하는 번거로움이 있습니다. - -``` js -function sum(){ - // arguments 객체를 배열로 반환 - const array = Array.prototype.slice.call(arguments); - return array.reduce(function (pre, cur) { - return pre + cur; - }, 0); -} - -console.log(sum(1, 2)); // 3 -console.log(sum(1, 2, 3, 4, 5)); // 15 -``` - -
- -이런 번거로움을 해결하기 위해 ES6에서는 Rest 파라미터가 도입되었습니다. - -``` js -function sum( ...args) { - return args.reduce((pre, cur) => pre + cur, 0); -} + let res = 0; -console.log(sum(1, 2)); -console.log(sum(1, 2, 3, 4, 5)); -``` - -

- -#### caller 프로퍼티 -`caller` 프로퍼티는 ECMAScript 사양에 포함되지 않은 비표준 프로퍼티입니다. -이후 표준화될 예정이 없는 프로퍼티이므로 사용하지 않고 참고만 하는 것을 추천합니다. - -`caller` 프로퍼티는 함수 자신을 호출한 함수를 가리킵니다. - -``` js -function foo(func) { - return func(); -} + // arguments 객체는 length 프로퍼티가 있는 유사 배열 객체이므로, for문으로 순회할 수 있다. + for (let i = 0; i < arguments.length; i++) { + res += arguments[i]; + } -function bar() { - return 'caller : ' + bar.caller; + return res; } -console.log(foo(bar)); // caller : function foo(func) {...} -console.log(bar()); // caller : null +console.log(sum()) // 0 +console.log(sum(1, 2)) // 3 +console.log(sum(1, 2, 3)) // 6 ``` -

+`arguments` 객체는 앞서 말했듯 실제 배열이 아닌, **array-like object(유사 배열 객체)이다**.
+유사 배열 객체란, length 프로퍼티를 지니며 for문으로 순회할 수 있는 객체를 말한다.
+(이터러블이 도입된 ES6부터 `arguments` 객체는 유사 배열 객체이면서 **이터러블**(이후 34장 '이터러블' 참고)이기도 하다.) -#### length 프로퍼티 -함수 객체의 `length` 프로퍼티는 함수를 정의할 때 선언한 매개변수의 개수를 가리킵니다. +### 2-2. `caller` 프로퍼티 +`caller` 프로퍼티는 ECMAScript 사양에 포함하지 않는 비표준 프로퍼티이므로 생략한다. -``` js +### 2-3. `length` 프로퍼티 +함수 객체의 `length` 프로퍼티는 다음 예제와 같이 **함수를 정의할 때 선언한 매개변수의 개수**를 가리킨다. +```javascript function foo() {} -console.log(foo.length); // 0 +console.log(foo.length); // 0 function bar(x) { - return x; + return x; } -console.log(bar.length); // 1 +console.log(foo.length); // 1 -function bar2(x, y) { - return x * y; +function baz(x, y) { + return x * y } -console.log(bar2.length); // 2 +console.log(foo.length); // 2 ``` +**`arguments` 객체**의 `length` 프로퍼티와, **함수 객체**의 `length` 프로퍼티는 다른 개념인 걸 반드시 숙지하자.
+전자는 **arguments(인자)의 개수**를 가리키고, 후자는 **parameters(매개변수)의 개수**를 가리킨다. +### 2-4. `name` 프로퍼티 +함수 객체의 `name` 프로퍼티는 아래 예제처럼 **함수 이름을 나타낸다**. ES6부터 정식 표준이 되었다.
+주의해야 할 점은, `name` 프로퍼티는 **ES5와 ES6에서 동작을 달리한다**는 점이다.
+ES5에서는 익명 함수 표현식의 `name` 프로퍼티의 값이 **빈 문자열**이고, ES6에서는 함수 객체를 가리키는 **식별자**다. -

- -#### name 프로퍼티 -함수 객체의 `name` 프로퍼티는 함수 이름을 나타냅니다. -`name` 프로퍼티는 ES5에서는 익명 함수의 경우 빈 문자열을 값으로 갖지만, -ES6에서는 함수 객체를 가리키는 식별자를 값으로 갖습니다. - -``` js -// 가명 함수 표현식 -const namedFunc = function foo() (); -console.log(namedFunc.name); // foo +```javascript +// 기명 함수 표현식 +var namedFunc = function foo() {}; +console.log(namedFunc.name); // foo // 익명 함수 표현식 -const anonymousFunc = function() {}; -console.log(anonymousFunc.name); // anonymousFunc - -// 함수 선언문(Function declaration) -function bar() {} -console.log(bar.name); // bar +var anonymousFunc = function() {}; +// ES5: name 프로퍼티의 값이 빈 문자열이다. +// ES6: name 프로퍼티의 값이 함수 객체를 가리키는 식별자다. +console.log(anonymousFunc.name); // anonymousFunc + +// 함수 선언문 +function bar() {}; +console.log(bar.name); // bar ``` +### 2-5. `__proto__` 접근자 프로퍼티 +모든 객체는 [[Prototype]]이라는 내부 슬롯을 갖는다. [[Prototype]]은 프로토타입 객체를 가리킨다. (프로토타입에 관한 자세한 내용은 19장 '프로토타입' 참고)
+`__proto__`는 [[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하고자 사용하는 **접근자 프로퍼티**다. -

- - -#### __proto__ 접근자 프로퍼티 -모든 객체는 `[[Prototype]]` 이라는 내부 슬롯을 가집니다. -`__proto__` 프로퍼티는 `[[Prototype]]` 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티입니다. -내부 슬롯에는 직접 접근할 수 없고 간접적인 접근 방식을 제공하는 경우에 한하여 접근할 수 있습니다. +[[Prototype]] 내부 슬롯에는 직접 접근할 수 없고, `__proto__` 접근자 프로퍼티를 통해 **간접적으로** 프로토타입 객체에 접근할 수 있다. -``` js -conse obj = { a: 1 }; +### 2-6. `prototype` 프로퍼티 +`prototype` 프로퍼티는 **생성자 함수가 생성할 인스턴스의 프로토타입 객체**를 가리킨다. -// 객체 리터럴 방식으로 생성한 객체의 프로토토압 객체는 Object.prototype 이다. -console.log(obj.__proto__ === Object.prototype); // true +`prototype` 프로퍼티는 **constructor만이 소유하는** 특별한 프로퍼티다.
+따라서 일반 객체와 생성자 함수로 호출할 수 없는 non-constructor에는 `prototype` 프로퍼티가 없다. +```javascript +// 함수 객체는 prototype 프로퍼티를 갖는다. +(function () {}).hasOwnProperty('prototype'); // true -// 객체 리터럴 방식으로 생성한 객체는 프로토타입 객체인 Object.prototype의 프로퍼티를 상속받는다. -console.log(obj.hasOwnProperty('a')); // true -console.log(obj.hasOwnProperty('__proto__')); // false +// 일반 객체는 prototype 프로퍼티를 갖지 않는다. +({}).hasOwnProperty('prototype'); // false ``` - -

- -#### prototype 프로퍼티 -`prototype` 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 `constructor`만이 소유하는 프로퍼티입니다. -일반 객체와 생성자 함수로 호출할 수 없는 `non-constructor`에는 `prototype` 프로퍼티가 없습니다. - -``` js -// 함수 객체는 `prototype` 프로퍼티를 소유한다. -(function() {}).hasOwnProperty('prototype'); // true - -// 일반 객체는 `prototype` 프로퍼티를 소유하지 않는다. -({}).hasOwnProperty('prototype'); // false -``` - -
- -`prototype` 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 호출될 때, -생성자 함수가 생성할 인스턴스의 프로토타입 객체를 생성합니다. - -``` js -function Person(name) { - this.name = name; -} - -const person1 = new Person("Lee"); -``` \ No newline at end of file +끝.