Open peterorosz opened 8 years ago
I know this is old, but I just was writing on the subject:
JavaScript's context (this
) is implicit, meaning:
// function born w/o context (not required)
function getCategory() { return `I'm ${this._category}` }
// let's create objects with implied context
// so calls to `Porsche.getCategory` automatically wire up `this === Porsche`
var Porsche = {
_category: 'fast',
getCategory: getCategory
}
var Dodge = {
_category: 'american',
getCategory: getCategory
}
// Here are some examples how context works & can be manipulated:
Porsche.getCategory() // => I'm fast
getCategory.call(Porsche) // => I'm fast
// Override at runtime using `.call(this, ..args)`
Porsche.getCategory.call(Dodge) // => I'm american
// "Extract" getCategory from its implicit parent `Porsche` object.
var getCat = Porsche.getCategory
// `getCat` is merely a context-free alias of the `getCategory` method.
getCat() //=> I'm undefined
// Attach/Save context for later using `Function.bind`
var imposter = getCat.bind(Dodge)
imposter() // => I'm american
imposter.call(Porsche) // => I'm american
// ^^ Re-binding won't work like this
// .bind essentially returns a **new function** wrapped with the scope specified.
// Watch out for this pitfall: passing a callback like this will lose the fn's implied context:
setTimeout(Porsche.getCategory, 10); //=> I'm undefined
setTimeout(getCategory, 10); //=> I'm undefined
// ^^ Previous 2 lines show how subtle these bugs can be
// 1st Fix: using `.bind` - returns new func pointer with context locked in
setTimeout(getCategory.bind(Porsche), 5); // I'm fast
setTimeout(getCategory.bind(Dodge), 1000); // I'm american
// 2nd Fix: using a closure, call with implied JS context
setTimeout(() => Porsche.getCategory(), 5); // I'm fast
// WARNING: Be careful designing with this complexity however, it's rarely necessary.
// +++ Plus: attackers can send surprises:
var payload = {_category: 'here for your Porsche', name: '', email: ''}; // from unchecked form
getCategory.call(payload) // => I'm here for your Porsche
// Ahh!!
@peterorosz question's answer needs a bit more info, the console
is a little different in NodeJS vs Chrome.
In the browser implementation of console
, its methods are usually pre-bound to console
.
console.log
- as I often only discover moving to synthetic browser or unit tests. Unit tests in particular will need console.log.bind(console)
.
Why is the official solution
console.log.apply(console, [namespace].concat(slice.call(arguments)))
?This works just as well:
console.log.apply(null, [namespace].concat(slice.call(arguments)))
Thank you for the answer.