JavaScript - The Complete Guide 2022

Section 26: Meta-Programming - Symbols, Iterators & Generators, Reflect API, Proxy API

olivia_yj 2022. 11. 7. 23:54

The goals

๐Ÿ’ช๐ŸปWhat & How?

 

What?

Symbols Iterators & Generators Reflect API Proxy API
Primitive values Create your own "loopable" values API to control objects Create "traps" for object operations
Used as object property identifiers What arrays, string etc. use internally Standardiezed & grouped methods Step in and execute code
Built-in & creatable by developers   Control code usage / impact  
Uniqueness is guaranteed      

 

 

 

Iterators and generators

Iterators and Generators bring the concept of iteration directly into the core language and provide a mechanism for customizing the behavior of for...of loops.

 

Iterators

In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination.

Specifically, an iterator is any object which implements the Iterator protocol by having a next() method that returns an object with two properties:

value

The next value in the iteration sequence.

done

This is true if the last value in the sequence has already been consumed. If value is present alongside done, it is the iterator's return value.

Once created, an iterator object can be iterated explicitly by repeatedly calling next(). Iterating over an iterator is said to consume the iterator, because it is generally only possible to do once. After a terminating value has been yielded additional calls to next() should continue to return {done: true}.

The most common iterator in JavaScript is the Array iterator, which returns each value in the associated array in sequence.

While it is easy to imagine that all iterators could be expressed as arrays, this is not true. Arrays must be allocated in their entirety, but iterators are consumed only as necessary. Because of this, iterators can express sequences of unlimited size, such as the range of integers between 0 and Infinity.

Here is an example which can do just that. It allows creation of a simple range iterator which defines a sequence of integers from start (inclusive) to end (exclusive) spaced step apart. Its final return value is the size of the sequence it created, tracked by the variable iterationCount.

function makeRangeIterator(start = 0, end = Infinity, step = 1) {
  let nextIndex = start;
  let iterationCount = 0;

  const rangeIterator = {
    next() {
      let result;
      if (nextIndex < end) {
        result = { value: nextIndex, done: false };
        nextIndex += step;
        iterationCount++;
        return result;
      }
      return { value: iterationCount, done: true };
    }
  };
  return rangeIterator;
}

Using the iterator then looks like this:

const it = makeRangeIterator(1, 10, 2);

let result = it.next();
while (!result.done) {
 console.log(result.value); // 1 3 5 7 9
 result = it.next();
}

console.log("Iterated over sequence of size: ", result.value); // [5 numbers returned, that took interval in between: 0 to 10]

 

Generator functions

While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. Generator functions provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function whose execution is not continuous. Generator functions are written using the function* syntax.

When called, generator functions do not initially execute their code. Instead, they return a special type of iterator, called a Generator. When a value is consumed by calling the generator's next method, the Generator function executes until it encounters the yield keyword.

The function can be called as many times as desired, and returns a new Generator each time. Each Generator may only be iterated once.

We can now adapt the example from above. The behavior of this code is identical, but the implementation is much easier to write and read.

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
  let iterationCount = 0;
  for (let i = start; i < end; i += step) {
    iterationCount++;
    yield i;
  }
  return iterationCount;
}

1. ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ

๋ณธ๊ฒฉ์ ์œผ๋กœ Proxy๋ฅผ ์•Œ์•„๋ณด๊ธฐ์— ์•ž์„œ ํ•œ ๊ฐ€์ง€ ๊ฐœ๋…์„ ์†Œ๊ฐœํ•˜๋ ค ํ•œ๋‹ค. Proxy ๋˜ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ์–ด๋–ค ๊ธฐ๋Šฅ ๋˜๋Š” ๊ฐœ๋…์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜์˜จ ๋ฌธ๋ฒ•์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋ฐ”๋กœ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Metaprogramming) ์ด๋‹ค.

๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋ž€ ํ•˜๋‚˜์˜ ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋žจ์ด ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์„ ๋ฐ์ดํ„ฐ๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์ฆ‰, ์–ด๋–ค ํ”„๋กœ๊ทธ๋žจ์ด ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์„ ์ฝ๊ณ , ๋ถ„์„ํ•˜๊ฑฐ๋‚˜ ๋ณ€ํ™˜ํ•˜๋„๋ก ์„ค๊ณ„๋œ ๊ฒƒ์ด๋‹ค. ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ์ด์šฉ๋˜๋Š” ์–ธ์–ด๋ฅผ ๋ฉ”ํƒ€ ์–ธ์–ด๋ผ๊ณ  ํ•˜๊ณ , ์กฐ์ž‘ ๋Œ€์ƒ์ด ๋˜๋Š” ์–ธ์–ด๋ฅผ ๋Œ€์ƒ ์–ธ์–ด๋ผ๊ณ  ํ•œ๋‹ค. ๋ฉ”ํƒ€ ์–ธ์–ด์™€ ๋Œ€์ƒ ์–ธ์–ด๋Š” ๊ฐ™์„ ์ˆ˜๋„ ์žˆ๊ณ , ๋‹ค๋ฅผ ์ˆ˜๋„ ์žˆ๋‹ค.

1.1. ๋ฉ”ํƒ€ ์–ธ์–ด์™€ ๋Œ€์ƒ ์–ธ์–ด๊ฐ€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ

์•„๋ž˜ ์˜ˆ์ œ๋Š” JSP์—์„œ HTML์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ์ฝ”๋“œ์ธ๋ฐ ๋ฉ”ํƒ€ ์–ธ์–ด๋Š” Java์ด๊ณ , ๋Œ€์ƒ ์–ธ์–ด๋Š” HTML์ด ๋  ๊ฒƒ์ด๋‹ค. (๋‚˜๋ฆ„๋Œ€๋กœ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๋ณธ ๊ฑด๋ฐ ๋Œ€์ƒ์–ธ์–ด๊ฐ€ HTML์ด๋ผ ์•ฝ๊ฐ„ ์• ๋งคํ•˜๊ธด ํ•˜๋‹ค. ํ•˜์ง€๋งŒ ์ด ์•ˆ์—์„œ <script>๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด ์ข€ ๋” ํ™•์‹คํ•ด์ง€์ง€ ์•Š์„๊นŒ ํ•œ๋‹ค.)

out.println("<html><body>");

out.println("<h1>Hello World</h1>");
out.println("The current time is : " + new java.util.Date());
out.println("</body></html>");

1.2. ๋ฉ”ํƒ€ ์–ธ์–ด์™€ ๋Œ€์ƒ ์–ธ์–ด๊ฐ€ ๊ฐ™์€ ๊ฒฝ์šฐ

์•„๋ž˜ ์˜ˆ์ œ๋Š” ๋ฉ”ํƒ€ ์–ธ์–ด์™€ ๋Œ€์ƒ ์–ธ์–ด๋ฅผ ๋‘˜ ๋‹ค JavaScript๋กœ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด eval()์„ ์‚ฌ์šฉํ–ˆ๋‹ค. eval์€ ๋Ÿฐํƒ€์ž„ ์ค‘์— ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ‰๊ฐ€ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๊ฐ€ ์ž๊ธฐ ์ž์‹ ์˜ ๋ฉ”ํƒ€ ์–ธ์–ด๊ฐ€ ๋˜๋Š” ๊ฒƒ์„ ๋ฐ˜์˜(Reflection) ์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ์ด๋Š” ๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ์ž์‹ ์˜ ๊ตฌ์กฐ์™€ ํ–‰์œ„๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

> eval('1 + 1')
2

๊ทธ๋Ÿผ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ํ•œ ๊ฐˆ๋ž˜์ธ ๋ฐ˜์˜์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

1.3. ๋ฐ˜์‚ฌํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Reflective programming)

์šฐ๋ฆฌ๊ฐ€ ์•Œ์•„๋ณผ Proxy์™€ Reflect๋Š” ๋ฐ˜์˜์„ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๊ณ , ์—ฌ๊ธฐ์—๋Š” ๋˜๋‹ค์‹œ ์„ธ ๊ฐ€์ง€์˜ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค.

  • Type introspection
    ๋Ÿฐํƒ€์ž„์—์„œ ํ”„๋กœ๊ทธ๋žจ์ด ์ž์‹ ์˜ ๊ตฌ์กฐ์— ์ ‘๊ทผํ•˜์—ฌ ํƒ€์ž…์ด๋‚˜ ์†์„ฑ์„ ์•Œ์•„๋‚ด๋Š” ๋Šฅ๋ ฅ์„ ๋œปํ•œ๋‹ค. ์˜ˆ์‹œ๋กœ Object.keys()๊ฐ€ ์žˆ๋‹ค.
  • Self-modification
    ๊ตฌ์กฐ๋ฅผ ์Šค์Šค๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ๋กœ, ์˜ˆ์‹œ๋กœ ์–ด๋–ค ์†์„ฑ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด [] ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๊ณ , delete ์—ฐ์‚ฐ์ž๋กœ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ ๋“ฑ์ด ์žˆ๋‹ค.
  • Intercession
    ๋ง ๊ทธ๋Œ€๋กœ ์–ด๋–ค ๊ฒƒ์„ ๋Œ€์‹ ํ•˜์—ฌ ๊ฐœ์ž…ํ•˜๋Š” ํ–‰์œ„๋ฅผ ๋œปํ•˜๋ฉฐ, ์–ธ์–ด๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š” ์ผ๋ถ€ ์˜๋ฏธ๋ฅผ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์ด๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ES2015์—์„œ Proxy๊ฐ€ ๋งŒ๋“ค์–ด์กŒ๋‹ค. (2ality์˜ ์šด์˜์ž์ธ Axel Rauschmayer์˜ ํ‘œํ˜„(๋งํฌ ์ฐธ์กฐ)์ธ๋ฐ, ์‚ฌ์‹ค Object.defineProperty() ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋“ค์ด ์ด๋ฏธ ๊ตฌํ˜„์ด ๋˜์–ด ์žˆ์–ด ๊ธฐ์กด์— ์—†์—ˆ๋‹ค๊ณ  ๋ณด๊ธฐ์—๋Š” ์ข€ ์• ๋งคํ•˜์ง€๋งŒ, Intercession์˜ ๊ฐœ๋…์ด ๋Œ€์ƒ์— ๋ณ€์ด๋ฅผ ์ผ์œผํ‚ค์ง€ ์•Š๋Š” ๊ฒƒ์— ์ดˆ์ ์ด ๋งž์ถฐ์ง„๋‹ค๋ฉด ๋งž๋Š” ๋ง์ผ ์ˆ˜๋„ ์žˆ๊ฒ ๋‹ค.)

2. Proxy๋ž€

ํ”„๋ก์‹œ ๊ฐ์ฒด(Proxy object)๋Š” ๋Œ€์ƒ ๊ฐ์ฒด(Target object) ๋Œ€์‹  ์‚ฌ์šฉ๋œ๋‹ค. ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ , ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์‚ฌ์šฉ๋˜๋ฉฐ ๊ฐ ์ž‘์—…์„ ๋Œ€์ƒ ๊ฐ์ฒด๋กœ ์ „๋‹ฌํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ๋ ค์ค€๋‹ค.


์ด๋Ÿฌํ•œ ๋ฐฉ์‹์„ ํ†ตํ•ด ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” JavaScript์˜ ๊ธฐ๋ณธ์ ์ธ ๋ช…๋ น์— ๋Œ€ํ•œ ๋™์ž‘์„ ์‚ฌ์šฉ์ž ์ •์˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•œ๋‹ค. ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” ํŠน์ • ๋ช…๋ น์„ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฐ ๋ช…๋ น์˜ ์ข…๋ฅ˜๋Š” ์†์„ฑ ๊ฒ€์ƒ‰, ์ ‘๊ทผ, ํ• ๋‹น, ์—ด๊ฑฐ, ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋“ฑ์ด ๋Œ€ํ‘œ์ ์ด๋‹ค.

 

 

 

Sources

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#well-known_symbols

 

Symbol - JavaScript | MDN

Symbol is a built-in object whose constructor returns a symbol primitive — also called a Symbol value or just a Symbol — that's guaranteed to be unique. Symbols are often used to add unique property keys to an object that won't collide with keys any ot

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators

 

Iterators and generators - JavaScript | MDN

Iterators and Generators bring the concept of iteration directly into the core language and provide a mechanism for customizing the behavior of for...of loops.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect

 

Reflect - JavaScript | MDN

Reflect is a built-in object that provides methods for interceptable JavaScript operations. The methods are the same as those of proxy handlers. Reflect is not a function object, so it's not constructible.

developer.mozilla.org

https://meetup.toast.com/posts/302

 

JavaScript Proxy. ๊ทผ๋ฐ ์ด์ œ Reflect๋ฅผ ๊ณ๋“ค์ธ : NHN Cloud Meetup

Vue 3 Reactivity๋Š” Proxy๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๊ทธ ์•ˆ์— Reflect๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ์š”. ์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” Proxy์™€ Reflect๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ ์ด์œ ๋ฅผ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

meetup.toast.com