JavaScript pitfalls
-
Upload
claudiocicali -
Category
Documents
-
view
2.195 -
download
0
description
Transcript of JavaScript pitfalls
SOME JAVASCRIPTPITFALLS
by Claudio Cicali (Sponsorpay)
@gmail.com // @caludio
DISCLAIMERThe following examples are meant to run
inside the global scope
I'm also not a "always use the === test operator" kind of guy [1]
[1] long time PHP developer
DECLARATIONS AND EXPRESSIONS// A variable declarationvar A;
// An assignment expression (definition / initialization)A = 0;
// Declaration and definitionvar A = 0;
DON'T TOUCH UNDECLARED VARIABLES// Reference error: A is not defined (just for the harmless test)if ( A == undefined ) { console.log("Hello");}
// This will fail as well (very common mistake!)if ( !A ) { console.log("Hello");}
DON'T TOUCH UNDECLARED VARIABLES// To test if a variable is declared, always* use the typeof operatorif ( typeof A == "undefined" ) { console.log("hello");}
// * Unless you are testing an object property
var A;
// No errorsif ( A == undefined ) { console.log( A ); // "undefined"}
UNDEFINED PROPERTIES
any object property is declared within the object itself
var myObj = { };
// No errors (we can skip the typeof test) if ( myObj.A == undefined ) { console.log( typeof myObj.A ); // undefined }
VARIABLE HOISTING// No errorsif (A === 0) { console.log("Have a Coke");}
// In the same scope, variables can be declared *after* being usedvar A = 0;
// The declaration is "hoisted" at the very top of the scope+--> var A;|| // The test fails nonetheless| if (A === 0) {| console.log("Have a Coke");| }|| // The definition is NOT hoisted (thus, no Coke for us)+--- A = 0;
VARIABLE HOISTING// Error (declaration out of scope, no "hoisting")if (A) { console.log("hello");}
function foobar() { var A = 0;}
var A = 42;
function foobar() { console.log( A ); // Can you guess?
var A = 0; // Declaration hoisted, NOT the assignment}
foobar();
VARIABLE HOISTINGvar A = 42;
function foobar() { console.log( A ); // 42
// var A = 0;}
foobar();
FUNCTION DECLARATIONS ANDEXPRESSIONS
// This a function declaration (a name, no assignment)function FOOBAR() {}
// This is a function expression (assignment)var FOOBAR = function() {};
// This is still a function expression (a "named" one)var FOOBAR = function something() { // The "something" symbol can be used only in this scope};
FUNCTION DECLARATIONS ANDEXPRESSIONS
(function something() { // Recursion without using arguments.callee (deprecated) something();})();
// This is wrong (syntax error)function() {};
// But those are - again - valid expressions-function() {};+function() {};!function() {};0,function() {};(function() {});
FUNCTION DECLARATION HOISTING// Available (declaration has been hoisted)foo(); // "One"
// Function declaration (w/o this we'd get an error)function foo() { console.log('One');}
// Function expression (evaluated at run-time, not hoisted)var foo = function() { console.log("Two");}
// Will log "Two"foo();
HIDDEN GLOBALSfunction sillyOperation() {
var A = B = 0;
// Will be parsed as follows var A; A = 0; B = 0;
// thus creating a global variable B
}
HIDDEN GLOBALS// Will create a global Pfor (P in myObject) { console.log(P);}
// Always use varfor (var P in myObject) { console.log(P);}
ARRAYS: DO NOT USE AS DICTIONARIES!var A = [];
// There are no "associative arrays", in JavaScript
A['key1'] = 100; // The same as A.key1 = 100;A['key2'] = 200; // The same as A.key2 = 200;
console.log(A.length); // 0, still 0
// Use plain objects insteadvar A = {};
A['key1'] = 100;A['key2'] = 200;
console.log( Object.keys(A).length ); // 2 (ES5)
ARRAYS: THEY ARE NOT SPARSEvar A = [];
A[999] = 47;
console.log( A.length ); // 1000 (with 999 undefined values)
USE USE STRICT// Switch on "strict mode" with just an harmless string
"use strict";
// This, now illegal, assignment of an undefined variable will // throw an error instead of implicitely declaring a (global) variable
A = 49;
"use strict";
// Duped properties: this will now throw an errorvar A = { P1: "foo", P2: "bar", P1: "zot" };
USE USE STRICT"use strict";
// "with" does not exist anymore (syntax error)with (object) {
}
"use strict";
function foobar() { console.log( this ); // undefined}
USE USE STRICT"use strict";// "Inner functions" are not permitted really ANYWHEREif (true) { function innerFoobar() { } // syntax error}// In not strict-mode, you could write a monstruosity likeif (true) { function innerFoobar() { console.log(1); }} else { function innerFoobar() { console.log(2); }}innerFoobar(); // Can you guess the result?
function outerFoobar() { function innerFoobar() { } // Still permitted}
A NOTE OF WARNING ON USE STRICTStrict mode applies to the context it is used
Scrict mode changes semantics: be sure to apply it only oncode you control
<script>// global context"use strict";...</script>
<script src="legacy_lib.js"></script>
// Now the code inside legacy_lib will run in strict mode.// Don't do that.
( PARENTHESIS )// Let's get the integer part of this numbervar A = 3.14;
// The usual wayconsole.log( Math.floor( A ) );
// Sligthly faster, compresses betterconsole.log( 0|A );
( PARENTHESIS )// What I "discovered", using this techniquevar A = 3.14;var B = 3.14;
console.log( 0|A === 0|B ); // true? No, "3" (true-ish)
// My bug was exploited by something likevar A = 3.14;var B = 13.14;
console.log( 0|A === 0|B ); // false? No, "13", still true-ish
( PARENTHESIS )What happened then?
Operators precedence, of course.
0|A === 0|B // is 0|false|13.14
// When unsure, use parenthesis
var A = 3.14;var B = 13.14;
console.log( (0|A) === (0|B) ); // false? Yes!
IIFE ARE LOVELY. USE THEM.Immediately-Invoked Function Expression ("iffy")
;(function(w, $, undefined) {
// This is your courtyard: here you can play safe and sound
// Better compression // Slightly faster when accessing global objects (which are local) // Not even one single global symbol declared
"use strict"; // Strict mode for your code only
})(window, jQuery);
IIFE ARE LOVELY. USE THEM.More than one style
Technically, we need to coerce the function to be anexpression, not a declaration
Other "formats" exist, but you betterstick with those for convention sake.
// Doug Crockford recommends this one( function() { }() );
// This one will work as well( function() {} )();