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.
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.
Moduleis 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
symbolis a new ES6 primitive type. Since ES6, JavaScript has six primitive types:
string,
number,
boolean,
null,
undefined, and
symbol.
symbolcan be used as an
Objectkey.
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.iteratorto 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…ofloop.
String,
Array,
Map, and
Setare native iterables because they implement
Symbol.iteratoron 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
Objectfor collections. ES6 adds
Mapand
Setto complement the concept.
Map
Common questions: why use
Mapinstead of
Object, and can it completely replace
Objectfor data storage?
Mapis suited for data sets that frequently change (add/remove) or need to be iterated, whereas
Objectis better for static configuration data.
The key of an
Objectmust be a
Stringor
Symbol;
Mapkeys can be any value.
Mapprovides an easy way to get the number of entries;
Objectrequires 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
Setis 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
WeakMapand
WeakSetintroduce weak references. Unlike the strong references of
Mapand
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
Promiseis 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
Generatoris an ES6 language feature with these traits:
Uses
yieldto 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
Proxyand
Reflect, which extend JavaScript’s object capabilities.
Proxy
Proxycan “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
Reflectprovides methods to manipulate an object’s native behavior, mirroring the operations that
Proxycan 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/
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.