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.
// 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

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.
// 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)]

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.
// 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

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.

// 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}

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.

// 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

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.
// 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

Asynchronous Programming

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

// 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);
  }
});

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.

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

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.
// 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); });

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.

// 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);
  }
})();

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.

// 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']

Reflect

Reflect

provides methods to manipulate an object’s native behavior, mirroring the operations that Proxy can intercept.

// 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)]

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/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptProxyasync/awaitpromiseses6Generatorsreflect
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

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.