Last active
August 29, 2015 14:14
-
-
Save pelonpelon/8fe956746d33d5220411 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Returns a function, that, as long as it continues to be invoked, will not | |
// be triggered. The function will be called after it stops being called for | |
// N milliseconds. If `immediate` is passed, trigger the function on the | |
// leading edge, instead of the trailing. | |
function debounce(func, wait, immediate) { | |
var timeout; | |
return function() { | |
var context = this, args = arguments; | |
var later = function() { | |
timeout = null; | |
if (!immediate) func.apply(context, args); | |
}; | |
var callNow = immediate && !timeout; | |
clearTimeout(timeout); | |
timeout = setTimeout(later, wait); | |
if (callNow) func.apply(context, args); | |
}; | |
}; | |
// Usage | |
var myEfficientFn = debounce(function() { | |
// All the taxing stuff you do | |
}, 250); | |
window.addEventListener('resize', myEfficientFn); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The problem with switch is that it's a statement, | |
// i.e. it cannot be nested inside expressions, which is what our templates are made of. | |
// We can create a switch-like expression instead using a little trick from the world of Python: dictionary switches | |
var subject = "John"; | |
var choice = { | |
"John": "lemonade", | |
"Bob": "orange juice", | |
"Mary": "tea", | |
}[subject]; | |
//choice is "lemonade" | |
//This is just a regular Javascript object, | |
// with the twist that we immediately access one of its properties to get a value that corresponds to the key we accessed. | |
// We can use this technique to create a tiny helper function: | |
//helper function | |
app.choose = function(key, options) { | |
var option = options[key]; | |
return option[0](option[1]); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var getAbsoluteUrl = (function() { | |
var a; | |
return function(url) { | |
if(!a) a = document.createElement('a'); | |
a.href = url; | |
return a.href; | |
}; | |
})(); | |
// Usage | |
getAbsoluteUrl('/something'); // http://davidwalsh.name/something |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var sheet = (function() { | |
// Create the <style> tag | |
var style = document.createElement('style'); | |
// Add a media (and/or media query) here if you'd like! | |
// style.setAttribute('media', 'screen') | |
// style.setAttribute('media', 'only screen and (max-width : 1024px)') | |
// WebKit hack :( | |
style.appendChild(document.createTextNode('')); | |
// Add the <style> element to the page | |
document.head.appendChild(style); | |
return style.sheet; | |
})(); | |
// Usage | |
sheet.insertRule("header { float: left; opacity: 0.8; }", 1); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function() { | |
// Used to resolve the internal `[[Class]]` of values | |
var toString = Object.prototype.toString; | |
// Used to resolve the decompiled source of functions | |
var fnToString = Function.prototype.toString; | |
// Used to detect host constructors (Safari > 4; really typed array specific) | |
var reHostCtor = /^\[object .+?Constructor\]$/; | |
// Compile a regexp using a common native method as a template. | |
// We chose `Object#toString` because there's a good chance it is not being mucked with. | |
var reNative = RegExp('^' + | |
// Coerce `Object#toString` to a string | |
String(toString) | |
// Escape any special regexp characters | |
.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&') | |
// Replace mentions of `toString` with `.*?` to keep the template generic. | |
// Replace thing like `for ...` to support environments like Rhino which add extra info | |
// such as method arity. | |
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' | |
); | |
function isNative(value) { | |
var type = typeof value; | |
return type == 'function' | |
// Use `Function#toString` to bypass the value's own `toString` method | |
// and avoid being faked out. | |
? reNative.test(fnToString.call(value)) | |
// Fallback to a host object check because some environments will represent | |
// things like typed arrays as DOM methods which may not conform to the | |
// normal native pattern. | |
: (value && type == 'object' && reHostCtor.test(toString.call(value))) || false; | |
} | |
// export however you want | |
module.exports = isNative; | |
}()); | |
// Usage | |
isNative(alert); // true | |
isNative(myCustomFunction); // false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function matchesSelector(el, selector) { | |
var p = Element.prototype; | |
var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) { | |
return [].indexOf.call(document.querySelectorAll(s), this) !== -1; | |
}; | |
return f.call(el, selector); | |
} | |
// Usage | |
matchesSelector(document.getElementById('myDiv'), 'div.someSelector[some-attribute=true]') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function once(fn, context) { | |
var result; | |
return function() { | |
if(fn) { | |
result = fn.apply(context || this, arguments); | |
fn = null; | |
} | |
return result; | |
}; | |
} | |
// Usage | |
var canOnlyFireOnce = once(function() { | |
console.log('Fired!'); | |
}); | |
canOnlyFireOnce(); // "Fired!" | |
canOnlyFireOnce(); // nada |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function poll(fn, callback, errback, timeout, interval) { | |
var endTime = Number(new Date()) + (timeout || 2000); | |
interval = interval || 100; | |
(function p() { | |
// If the condition is met, we're done! | |
if(fn()) { | |
callback(); | |
} | |
// If the condition isn't met but the timeout hasn't elapsed, go again | |
else if (Number(new Date()) < endTime) { | |
setTimeout(p, interval); | |
} | |
// Didn't match and too much time, reject! | |
else { | |
errback(new Error('timed out for ' + fn + ': ' + arguments)); | |
} | |
})(); | |
} | |
// Usage: ensure element is visible | |
poll( | |
function() { | |
return document.getElementById('lightbox').offsetWidth > 0; | |
}, | |
function() { | |
// Done, success callback | |
}, | |
function() { | |
// Error, failure callback | |
} | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Hi Leo, the OtherConstructor.call( this ) approach is interesting, | |
but it only works as expected if you don't use constructors' | |
most powerful feature – prototypal inheritance. | |
In order to bind the mixin constructor's prototype properties, you'll need something like the following: | |
for(var prop in GoogleAnalyticsCtrl.prototype){ | |
ProjectDetailsCtrl.prototype[ prop ] = GoogleAnalyticsCtrl.prototype[ prop ] | |
} | |
…but seeing as all the examples above have all their methods defined in the constructor anyway, | |
we may as well use an abstraction of the code above to merge 2 instances together using factories. | |
Here's code that behaves identically to your cross-pollinated example: | |
// Utility function: add all object2's properties to object1 | |
var extend = function( object1, object2 ){ | |
for ( var property in object2 ){ | |
object1[ property ] = object2[ property ]; | |
} | |
} | |
var ProjectDetailsCtrl = function() { | |
var instance = {}; | |
extend( instance, GoogleAnalyticsCtrl() ); | |
instance.name = m.prop("My Project"); | |
instance.save = function() { | |
Project.save({name: this.name()}); | |
} | |
return instance; | |
} | |
var GoogleAnalyticsCtrl = function() { | |
var instance = {}; | |
instance.name = m.prop("default"); | |
instance.track = function() { | |
console.log("tracked " + this.name()); | |
} | |
return instance; | |
} | |
var controller = new ProjectDetailsCtrl(); | |
controller.track(); | |
Rather than assigning properties to this, we explicitly create an empty object called instance | |
(to be fair, I always started off constructors with var instance = this to avoid the .bind( this ) your methods so often involve), | |
and assign properties to that. There's an implication in your article that the choice of factories or constructors | |
will change how you deal with private / encapsulated variables, | |
but that isn't the case: in both instances, you either keep data as var variables, | |
in which case they are available exclusively to code within the factory / constructor or exposed by it, | |
or you nail them down to the instance by assigning them to properties of this or whatever the factory returns. | |
The 2 significant factors for me are: | |
• Factories are more flexible in execution: | |
you can use apply on them, and also (I love this!) invoke them as an iterator in maps. | |
Moreover, new doesn't affect the behaviour of factories. | |
• Constructor-instantiated objects will show their constructor's name and omit their prototype's methods when logged, | |
which makes it easy to identify objects at a glance during development. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment