Frontend Development 20 min read

Unlock ES6: Master Advanced Modules, Symbols, and Async Patterns

This article delves into ES6’s advanced features—including dynamic module loading, Symbol usage, iterators, Maps, Sets, Weak collections, Promises, async‑await, generators, and meta‑programming tools like Proxy and Reflect—providing concise explanations, code snippets, and practical guidance for modern JavaScript development.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
Unlock ES6: Master Advanced Modules, Symbols, and Async Patterns

Preface

This article continues from the previous piece, focusing on ES6 advanced features that were not covered before, highlighting often‑overlooked aspects to help readers correctly understand and use ES6.

As always, feedback and corrections are welcome.

Main Content

Module

Modularization has been discussed for a long time, with many patterns such as AMD and CommonJS.

Module

is a new ES6 feature that provides language‑level support for modules.

Unlike previous loading mechanisms, Module loads dynamically and imports variables as read‑only references , not copies.
<code>// 1. export default can be used for default export
// a.js
export default 5;      // default export
// b.js
import b, {a} from './a.js';   // default import, no braces needed
// 2. Dynamic loading
// a.js
export let a = 10;
export let b = 10;
export function add() {
  a = 15;
  b = 20;
  return a+b;
};
// b.js
import {a, b, add} from './a.js';
a+b;   // 20
add(); // 35
a+b;   // 35
</code>

Symbol

symbol

is a new ES6 primitive type. Since ES6, JavaScript has six primitive types:

string

,

number

,

boolean

,

null

,

undefined

, and

symbol

.

symbol

can be used as an

Object

key.

Symbols exist in the global registry;

Symbol.for(key)

creates or retrieves a global symbol, and

Symbol.keyFor(sym)

returns its key.

JavaScript uses many built‑in symbols internally, e.g.,

Symbol.iterator

to denote an object's iterator.

"New" is only from a front‑end developer’s perspective; the Symbol concept has long been used inside the language.
<code>// 1. Symbol(desc) creates a new symbol; desc is only a description, not a unique identifier
Symbol('abc') === Symbol('abc'); // false
// 2. Symbol.for(key) uses a global key; both calls return the same symbol
Symbol.for('abc') === Symbol.for('abc'); // true
Symbol.for('abc') === Symbol('abc'); // false
// 3. Symbols are not enumerable with for...in; use Object.getOwnPropertySymbols
const obj = {
  [Symbol('abc')]: 'abc',
  'abc': 'abc'
};
for (var i in obj) { // logs 'abc'
  console.log(i);
}
Object.getOwnPropertySymbols(obj); // [Symbol(abc)]
</code>

Iterator + For..Of

ES6 introduces a new iteration protocol consisting of the iterable protocol and the iterator protocol. Any object implementing the former can be used with a

for…of

loop.

String

,

Array

,

Map

, and

Set

are native iterables because they implement

Symbol.iterator

on their prototype.

for…of iterates over an object’s iterator, while for…in iterates over its enumerable properties.
<code>// 1. Iterator protocol
const iter = {
  counter: 0,
  next() {
    if (++this.counter < 10) {
      return { done: false, value: this.counter };
    } else {
      this.counter = 0;
      return { done: true };
    }
  }
};
// 2. Iterable protocol
const iterObj = {};
for (var i of iterObj) {} // TypeError: iterObj is not iterable
iterObj[Symbol.iterator] = function() { return iter; };
for (var i of iterObj) { console.log(i); } // 1,2,3,4,5,6,7,8,9
</code>

About Collections

Traditionally we used

Object

for collections. ES6 adds

Map

and

Set

to complement the concept.

Map

Common questions: why use

Map

instead of

Object

, and can it completely replace

Object

for data storage?

Map

is suited for data sets that frequently change (add/remove) or need to be iterated, whereas

Object

is better for static configuration data.

The key of an

Object

must be a

String

or

Symbol

;

Map

keys can be any value.

Map

provides an easy way to get the number of entries;

Object

requires extra work.

<code>// 1. Map constructor can take an iterable of key‑value pairs
const first = new Map([['a', 1], [{b: 1}, 2]]); // Map {"a" => 1, Object {b: 1} => 2}
// 2. Map keys can be any value, even undefined or null
first.set(null, 1).set(undefined, 0); // Map {"a"=>1, Object {b:1}=>2, null=>1, undefined=>0}
</code>

Set

Set

is a simple collection with the following characteristics:

It stores any type of value and preserves insertion order.

It contains no duplicate values.

<code>// 1. Set constructor can take an iterable
const first = new Set(['a', 1, {b:1}, null]); // Set {"a",1,Object{b:1},null}
// 2. Adding a duplicate value has no effect
first.add(1); // Set unchanged
</code>

WeakMap + WeakSet

WeakMap

and

WeakSet

introduce weak references. Unlike the strong references of

Map

and

Set

, weak references allow objects to be garbage‑collected when no other strong references exist, reducing memory waste.

Because they are weak, they cannot be iterated or have their size queried; they are only useful for storing values (WeakMap) or testing existence (WeakSet).

When a weak reference’s object has no other strong references, it will be reclaimed by the GC.
<code>// 1. WeakMap keys must be objects
const err = new WeakMap([['a',1]]); // TypeError
// 2. WeakMap/WeakSet weak references
const wm = new WeakMap([[{a:1},1]]); // object can be GC'd
const ws = new WeakSet();
ws.add({a:1}); // object can be GC'd
const obj = {'b':1};
ws.add(obj); // not GC'd while strong reference exists
obj = undefined; // now eligible for GC
</code>

Asynchronous Programming

Before ES6, asynchronous JavaScript relied on callbacks, which become hard to maintain in complex scenarios due to nesting.

<code>// Callback nesting example (Node.js)
const git = require('shell').git;
const commitMsg = '...';
git.add('pattern/for/some/files/*', (err) => {
  if (!err) {
    git.commit(commitMsg, (err) => {
      if (!err) {
        git.push(pushOption);
      } else {
        console.log(err);
      }
    });
  } else {
    console.log(err);
  }
});
</code>

Promise

Promise

is an ES6 feature for asynchronous programming with these characteristics:

Still based on callbacks.

Separates success and failure callbacks, reducing inner‑level condition checks.

Facilitates conversion from callback style to Promise style.

Transforms nested callbacks into a vertical chain, easier to understand and maintain.

Although advantageous, Promise code still differs from synchronous code.

<code>// Promise version of the previous example
git.add('pattern/for/some/files/*')
  .then(() => git.commit(commitMsg))
  .then(git.push)
  .catch((err) => { console.log(err); });
</code>

Generator

Generator

is an ES6 language feature with these traits:

Uses

yield

to pause execution and return a value.

next(val)

resumes execution, optionally passing a value.

Shares local variables across pauses.

Returns an iterator object that is also iterable, allowing generators to be used for custom iteration.

Because of these properties, generators implement coroutines (semi‑coroutine) in JavaScript.

Coroutines can be symmetric or asymmetric; JavaScript implements asymmetric coroutines, which are clearer and easier to understand.
<code>// Generator example with async flow
const co = require('co');
const git = require('shell').git;
co(function* () {
  const commitMsg = '...';
  yield git.add('pattern/for/some/files/*');
  yield git.commit(commitMsg);
  yield git.push();
}).catch((err) => { console.log(err); });
</code>

Generators are not the best async solution because they require an executor, but they still produce clearer code than callbacks or Promises.

Async‑Await

Although introduced in ES7, async‑await is the current best way to handle asynchronous JavaScript.

<code>// Async‑await version
const git = require('shell').git;
(async function () {
  const commitMsg = '...';
  try {
    await git.add('pattern/for/some/files/*');
    await git.commit(commitMsg);
    await git.push();
  } catch (err) {
    console.log(err);
  }
})();
</code>

Metaprogramming

Metaprogramming lets developers program the language itself via APIs such as

Proxy

and

Reflect

, which extend JavaScript’s object capabilities.

Proxy

Proxy

can “proxy” native object behavior and replace it with custom behavior, allowing any object to be proxied and enabling reversible proxies via

Proxy.revocable

.

<code>// Simple element cache using Proxy
const cacheElement = function(target, prop) {
  if (target.hasOwnProperty(prop)) {
    return target[prop];
  } else {
    return target[prop] = document.getElementById(prop);
  }
};
const elControl = new Proxy(cacheElement, {
  get: (target, prop) => cacheElement(target, prop),
  set: (target, prop, val) => { cacheElement(target, prop).textContent = val; },
  apply: (target, thisArg, args) => Reflect.ownKeys(target)
});
elControl.first;   // div#first
elControl.second;  // div#second
elControl.first = 5;   // div#first => 5
elControl.second = 10;  // div#second => 10
elControl();   // ['first','second']
</code>

Reflect

Reflect

provides methods to manipulate an object’s native behavior, mirroring the operations that

Proxy

can intercept.

<code>// Using Reflect inside a Proxy handler
const customProxy = new Proxy({ custom: 1 }, {
  get: (target, prop) => {
    console.log(`get ${prop} !`);
    return Reflect.get(target, prop);
  }
});
customProxy.custom; // logs "get custom !" and returns 1
const symb = Symbol('b');
const a = { [symb]: 1, b: 2 };
if (Reflect.has(a, symb) && Reflect.has(a, 'b')) {
  console.log('good');
}
Reflect.ownKeys(a); // ["b", Symbol(b)]
</code>

Further Reading

For deeper exploration of ES6, consider the following resources:

Compatibility tables: https://kangax.github.io/compat-table/es6/

Performance benchmarks: http://kpdecker.github.io/six-speed/

Comprehensive MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript

Feature examples and polyfills: http://es6-features.org/#Constants

Full ECMA‑262 specification (PDF): http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

Latest spec online: https://tc39.github.io/ecma262/

Conclusion

The basic and advanced sections have covered the main ES6 features. ES6 is the present, and future articles may explore ES2016+ to keep up with newer changes.

Hope the content helps you understand and use ES6 effectively. Thanks for reading!

References

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

https://babeljs.io/docs/learn-es2015/

https://www.stackoverflow.com

http://es6.ruanyifeng.com/

https://kangax.github.io/compat-table/esnext/

http://www.ecma-international.org/ecma-262/6.0/

JavaScriptProxyAsync/AwaitpromisesModulesES6GeneratorsReflect
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.