Symbol.unscopables in JavaScript
Symbol.unscopables
is a built-in symbol that’s used to specify object properties that should not be accessible as variables in with
statement scopes. It’s a specialized feature primarily used to maintain backward compatibility.
Basic Concept
The Symbol.unscopables
property is an object whose properties are the names of properties that should be excluded from the environment bindings of a with
statement.
// Object with Symbol.unscopables
const obj = {
x: 1,
y: 2,
[Symbol.unscopables]: {
y: true // Prevents 'y' from being accessible in a with statement
}
};
with (obj) {
console.log(x); // 1 - accessible
// console.log(y); // ReferenceError: y is not defined
}
Purpose and Origin
Symbol.unscopables
was introduced to solve compatibility issues when new methods were added to built-in prototypes. The most notable example is Array.prototype
:
// Without Symbol.unscopables
Array.prototype.keys = function() {
console.log('Custom keys method');
};
const array = [1, 2, 3];
with (array) {
// This would call our custom method, breaking existing code
keys(); // 'Custom keys method'
}
// With Symbol.unscopables
Array.prototype[Symbol.unscopables] = {
keys: true,
// Other methods that shouldn't be accessible in with statements
values: true,
entries: true,
find: true,
findIndex: true,
// ...etc
};
with (array) {
// Now this would throw a ReferenceError
// keys(); // ReferenceError: keys is not defined
}
Built-in Unscopables
Several built-in objects have predefined Symbol.unscopables
properties:
Array.prototype[Symbol.unscopables]
console.log(Array.prototype[Symbol.unscopables]);
// {
// copyWithin: true,
// entries: true,
// fill: true,
// find: true,
// findIndex: true,
// flat: true,
// flatMap: true,
// includes: true,
// keys: true,
// values: true
// }
This prevents newer Array methods from being accessible in with
statements, maintaining backward compatibility with code that might use variables with the same names.
Implementing Symbol.unscopables
You can define Symbol.unscopables
for your own objects:
class MyClass {
constructor() {
this.visible = 'I am visible in with';
this.hidden = 'I should be hidden in with';
}
visibleMethod() {
return 'I am a visible method';
}
hiddenMethod() {
return 'I should be a hidden method';
}
}
// Define unscopables
MyClass.prototype[Symbol.unscopables] = {
hidden: true,
hiddenMethod: true
};
const instance = new MyClass();
with (instance) {
console.log(visible); // 'I am visible in with'
console.log(visibleMethod()); // 'I am a visible method'
// These would throw ReferenceError
// console.log(hidden);
// console.log(hiddenMethod());
}
Practical Use Cases
1. Library Backward Compatibility
// Adding new methods to a library object while maintaining compatibility
const myLibrary = {
// Existing methods
oldMethod() { return 'old'; },
// New methods that shouldn't break existing with statements
newMethod() { return 'new'; },
// Configure unscopables
[Symbol.unscopables]: {
newMethod: true
}
};
2. Preventing Accidental Variable Shadowing
const dataProcessor = {
data: [1, 2, 3],
process() {
// Prevent internal methods from being accessible
return this.data.map(item => item * 2);
},
[Symbol.unscopables]: {
// Ensure map, filter, etc. refer to global functions, not Array methods
map: true,
filter: true,
reduce: true
}
};
with (dataProcessor) {
// map would refer to global map function, not Array.prototype.map
}
Best Practices
- Avoid
with
statements: They’re considered bad practice and are disallowed in strict mode - Use
Symbol.unscopables
for compatibility: Only when maintaining backward compatibility with legacy code - Document unscopable properties: Make it clear which properties are intentionally excluded
- Consider alternatives: Use destructuring or explicit property access instead of
with
// Instead of with statements, use destructuring
const { x, z } = obj;
console.log(x, z);
// Or explicit property access
console.log(obj.x, obj.z);
Limitations and Considerations
- Strict Mode: The
with
statement is not allowed in strict mode, makingSymbol.unscopables
less relevant in modern code - Performance: Using
with
statements can negatively impact performance - Readability: Code using
with
andSymbol.unscopables
can be harder to understand - Debugging: Issues in
with
blocks can be difficult to debug
Interview Tips
- Explain that
Symbol.unscopables
is primarily a compatibility feature for thewith
statement - Describe how it helps prevent conflicts when new methods are added to built-in prototypes
- Mention that
with
statements are discouraged and not allowed in strict mode - Explain why Array.prototype has several unscopable methods
- Demonstrate knowledge of Symbol properties and their role in JavaScript’s meta-programming features
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.