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. 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; // 35Symbol
symbolis 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,9About 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
Setis 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 unchangedWeakMap + WeakSet
WeakMapand 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 GCAsynchronous 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
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.
// 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
Generatoris 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
Proxycan “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
Reflectprovides 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/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
