Tests if property/method exists in object or prototype chain
Check API availability before use
Type Check
typeof obj.method === 'function'
Verifies property exists and is callable function
Ensure method is executable
Feature Test
!!window.feature
Coerces to boolean to check truthy existence
Quick availability check
hasOwnProperty
obj.hasOwnProperty('prop')
Tests if property exists directly on object (not inherited)
Avoid prototype chain lookups
Constructor Check
typeof Constructor !== 'undefined'
Verifies constructor/class is available
Test for API constructors like Promise, Map
Document Test
'prop' in document.createElement('tag')
Tests HTML element feature support
Check DOM element capabilities
Example: Comprehensive feature detection pattern
// Check for fetch APIconst hasFetch = typeof fetch === 'function' && typeof window.fetch !== 'undefined';// Check for Promise supportconst hasPromise = typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1;// Check for Array.prototype.includesconst hasArrayIncludes = 'includes' in Array.prototype;// Check for modern DOM APIconst hasQuerySelector = 'querySelector' in document;// Check for Storage APIconst hasLocalStorage = (function() { try { const test = '__storage_test__'; localStorage.setItem(test, test); localStorage.removeItem(test); return true; } catch(e) { return false; }})();
2. typeof and in Operator Usage Patterns
Pattern
Syntax
Returns
Best Practice
typeof undefined
typeof variable === 'undefined'
Safe check for undefined variables
Use for variables that may not be declared
typeof function
typeof func === 'function'
Checks if value is callable function
Validate methods before invocation
typeof object
typeof obj === 'object' && obj !== null
Object check excluding null
Always check for null separately
in operator (prototype)
'method' in Object.prototype
Checks entire prototype chain
Detect inherited properties
in operator (instance)
'prop' in instance
Checks own and inherited properties
Most common feature detection
hasOwnProperty
obj.hasOwnProperty('prop')
Only own properties, not inherited
Avoid prototype pollution checks
Example: typeof vs in operator comparison
// typeof - Safe for undeclared variablesif (typeof Promise !== 'undefined') { // Promise is available}// in operator - Checks property existenceif ('fetch' in window) { // window.fetch exists}// Combined approach for methodsif ('map' in Array.prototype && typeof Array.prototype.map === 'function') { // Array.map is available and callable}// Avoid false positivesif (typeof document.querySelector === 'function') { // Safe to use querySelector}// Check constructor availabilityif (typeof Map !== 'undefined' && typeof Map === 'function') { const myMap = new Map();}
3. try-catch Blocks for Safe Feature Testing
Scenario
Pattern
Error Handling
Use Case
Storage Access
try { localStorage.test } catch(e) {}
SecurityError, QuotaExceededError
localStorage may throw in private mode
Feature Execution
try { new Constructor() } catch(e) {}
Constructor not available or throws
Test constructor support safely
Property Access
try { obj.method() } catch(e) {}
Method doesn't exist or not callable
Safe method invocation
API Test
try { API.test() } catch(e) {}
API not supported or restricted
Browser security restrictions
Eval Alternative
try { new Function('...') } catch(e) {}
CSP violations, syntax errors
Test CSP-restricted features
Example: Safe storage detection with try-catch
// localStorage detection with private mode handlingfunction hasLocalStorage() { try { const test = '__test__'; localStorage.setItem(test, test); localStorage.removeItem(test); return true; } catch(e) { return false; }}// IndexedDB detectionfunction hasIndexedDB() { try { return !!window.indexedDB; } catch(e) { return false; }}// Service Worker registration checkfunction canUseServiceWorker() { try { return 'serviceWorker' in navigator && typeof navigator.serviceWorker.register === 'function'; } catch(e) { return false; }}// Safe feature test with fallbackfunction detectFeature() { try { // Attempt to use feature new IntersectionObserver(() => {}); return true; } catch(e) { // Feature not available return false; }}
Warning: try-catch blocks have performance overhead. Use only when necessary for features that
may throw exceptions. Prefer simple typeof and in
checks for most feature detection.
4. CSS.supports() for Style Feature Detection
Method
Syntax
Returns
Browser Support
CSS.supports()
CSS.supports('property', 'value')
Boolean - true if supported
Modern Browsers
Condition String
CSS.supports('display: grid')
Tests property:value declaration
Single string format
Two Arguments
CSS.supports('display', 'grid')
Tests property and value separately
Property/value pair format
Complex Query
CSS.supports('(display: grid) and (gap: 1rem)')
Tests multiple conditions with logic
Supports and/or/not operators
Vendor Prefix
CSS.supports('-webkit-appearance', 'none')
Tests vendor-prefixed properties
Check legacy prefixed features
Example: CSS feature detection patterns
// Check for CSS Grid supportconst hasGrid = CSS.supports('display', 'grid') || CSS.supports('display: grid');// Check for CSS Custom Propertiesconst hasVars = CSS.supports('--custom-prop', 'value') || CSS.supports('color', 'var(--custom-prop)');// Check for Flexboxconst hasFlex = CSS.supports('display', 'flex');// Check for sticky positioningconst hasSticky = CSS.supports('position', 'sticky') || CSS.supports('position', '-webkit-sticky');// Complex condition with logical operatorsconst hasModernLayout = CSS.supports( '(display: grid) and (gap: 1rem)');// Fallback for browsers without CSS.supportsfunction cssSupports(property, value) { if (typeof CSS !== 'undefined' && CSS.supports) { return CSS.supports(property, value); } // Fallback: test on element style const el = document.createElement('div'); el.style[property] = value; return el.style[property] === value;}
Note: CSS.supports() is available in all modern browsers. For legacy browser support, implement
a fallback using element style testing as shown in the example above.
Note: Modern alternatives like @supports in CSS and feature detection APIs have
reduced reliance on Modernizr. Consider if native solutions meet your needs before adding Modernizr dependency.
6. Dynamic Import for Feature-based Loading
Pattern
Syntax
Use Case
Benefit
Conditional Import
if(!feature) import('./polyfill.js')
Load polyfill only when needed
Reduce bundle size for modern browsers
Promise-based
import('module').then(m => m.fn())
Async module loading with promises
Non-blocking polyfill loading
Async/Await
const m = await import('mod')
Clean async loading syntax
Better readability and error handling
Feature Check
feature || await import('poly')
Short-circuit loading
Skip import if feature exists
Multiple Polyfills
Promise.all([import(...)])
Load multiple polyfills in parallel
Faster initialization
Lazy Initialization
() => import('heavy')
Defer loading until first use
Improve initial page load
Example: Dynamic polyfill loading strategies
// Basic conditional polyfill loadingasync function loadPolyfills() { const polyfills = []; if (typeof Promise === 'undefined') { polyfills.push(import('es6-promise-polyfill')); } if (!('fetch' in window)) { polyfills.push(import('whatwg-fetch')); } if (!Array.prototype.includes) { polyfills.push(import('./array-includes-polyfill')); } await Promise.all(polyfills);}// Initialize app after polyfills loadedloadPolyfills().then(() => { // Start application initApp();});// Lazy load heavy polyfill on demandlet intersectionObserverPolyfill = null;async function getIntersectionObserver() { if ('IntersectionObserver' in window) { return window.IntersectionObserver; } if (!intersectionObserverPolyfill) { const module = await import('intersection-observer'); intersectionObserverPolyfill = module.default; } return intersectionObserverPolyfill;}// Usageconst Observer = await getIntersectionObserver();const observer = new Observer(callback, options);// Differential serving patternif (supportsModernFeatures()) { // Load modern build (no polyfills) await import('./app.modern.js');} else { // Load legacy build (with polyfills) await import('./app.legacy.js');}
Key Takeaways - Feature Detection
Use typeof for safe checks of potentially undefined variables
Prefer in operator for property existence in objects/prototypes
Wrap risky operations (storage, CSP) in try-catch blocks
Use CSS.supports() for modern CSS feature detection
Load polyfills conditionally with dynamic imports for optimal performance
Always test features before use - never assume browser support