Objects are coerced to numbers via valueOf() and to strings via toString().
- Warning: valueOf takes precedence over toString
var obj = {value:1}
log( obj + 1) [object Object]1
obj.valueOf = function(){ return this.value}
log( obj + 1) 2
Variable declarations within a block are implicitly hoisted to the top
of their enclosing function.
Redeclarations of a variable are treated as a single variable.
Consider manually hoisting local variable declarations to avoid confusion.
My addendum: Don't forget let.
Closures store their outer variables by reference, not by value.
Immediate invoked functions are a work around for lack of block scoping.
let is the solution of block level scoping
function wrapElements( a ) {
var result = [], i, n;
for ( let i = 0, n = a.length; i < n; i++ ) {
result[ i ] = function() {
return a[ i ];
};
}
return result;
}
var wrapped = wrapElements( [ 10, 20, 30, 40, 50 ] );
var f = wrapped[ 0 ];
log(f())
Depending on where it appears, this could be either a function declaration
or a named function expression.
Nevermind: If you are shipping in properly implemented ES5 environments, you've got nothing to worry about!
function double(x) { return x * 2; }
var obj = { a: 1 }
var fun = function() { log( this.a ) }
fun.call(obj)
Use the following idiom to get a copy
Beware that extracting a method does not bind the method's receiver to its object.
Use bind as a shorthand for creating a function bound to the appropriate receiver.
var buffer = {
entries: [],
add: function( s ) {
this.entries.push( s );
},
join: function() {
return this.entries.join( "" );
},
clear: function(){
this.entries = []
}
};
var source = [ "867", "-", "5309" ];
source.forEach( buffer.add.bind( buffer ) );
log( buffer.join() ) 867-5309
buffer.clear()
source.forEach( buffer.add, buffer )
log( buffer.join() ) 867-5309
var obj = {}
var proto = Object.getPrototypeOf( obj )
proto.fun = function() {
return "fun"
}
log( obj.fun() ) fun
function Dir() {
}
Dir.prototype = Object.create(Array.prototype);
function MyArray(array){
this.array = array
}
MyArray.prototype.forEach = function( fun, context ) {
context = context ? context : this
context.array.forEach( fun, context )
}
var a = new MyArray([1,2,3])
a.forEach( x => log( x ) )
1
2
3
Item 44: Use null Prototypes to Prevent Prototype Pollution
- In ES5, use Object.create(null) to create prototype-free empty
objects that are less susceptible to pollution.
function C() { }
C.prototype = null;
var o = new C
log( Object.getPrototypeOf( o ) === Object.prototype ) true , didn't work
var o = Object.create( null )
log( Object.getPrototypeOf( o ) === Object.prototype ) false
log( Object.getPrototypeOf( o ) === null ) true
Item 48: Avoid Modifying an Object during Enumeration
- ECMAScript Standard: If new properties are added to the object being enumerated
during enumeration, the newly added properties are not guaranteed
to be visited in the active enumeration.
Item 51: Reuse Generic Array Methods on Array-Like Objects
- Any object can be used with generic Array methods if it has indexed
properties and an appropriate length property.
function fun1(){
[].forEach.call( arguments, x => log( x ) )
}
fun1(1,2,3)
1
2
3
function fun2(){
var args = [].slice.call(arguments)
args.forEach( x => log( x ) )
}
fun2(1,2,3)
1
2
3
Item 54: Treat undefined As "No Value"
- The undefined value is special: Whenever JavaScript has no specific
value to provide it just produces undefined. Unassigned variables
start out with the value undefined
- Avoid using undefined to represent anything other than the absence
of a specific value.
- Test for undefined instead of checking arguments.length to provide
parameter default values.
- Never use truthiness tests for parameter default values that should allow
0, NaN, or the empty string as valid arguments.
var x;
log(x) undefined
log( ( function(){ return } )() ) undefined
log( ( function() {} )() ) undefined
log( ( function( x ) { return x } )() ) undefined
function Element( width, height ) {
this.width = width === undefined ? 320 : width;
this.height = height === undefined ? 240 : height;
}
var c1 = new Element( 0, 0 );
this.width = _.condUndefined(width, 320)
this.width = _.cond()
_.condUndefined = function(value, other){
return _isUndefined(value) ? other : value
}
Item 55: Accept Options Objects for Keyword Arguments
- use some implementation of extend to merge defaults
function Constructor( argsObject ) {
var defaults = { x: 0, y: 0 }
argsObject = _.extend(defaults, argsObject)
log(argsObject)
}
new Constructor( { x: 100 } ) { x: 100, y: 0 }