一.Global
- 全局域中的this = window.
myglobal = "hello"; // antipattern console.log(myglobal); // "hello" console.log(window.myglobal); // "hello" console.log(window["myglobal"]); // "hello" console.log(this.myglobal); // "hello", 全局域中的this = window.
?? - Another antipattern that creates implied globals is to chain assignments??as part of a var declaration. In the following snippet, a is local but b??becomes global, which is probably not what you meant to do:
// antipattern, do not use function foo() { var a = b = 0; // ... }
?
??? If you’re wondering why that happens, it’s because of the right-to-left evaluation.??First,the expression b = 0 is evaluated and in this case b is not declared. The return?value of this expression is 0, and it’s assigned to?the new local variable declared with var a.?In other words, it’s as if you’ve typed:
??var a = (b = 0);
?
?If you’ve already declared the variables, chaining assignments is fine and??doesn’t create unexpected globals. Example:function foo() { var a, b; // ... a = b = 0; // both local }
? - Side Effects When Forgetting var:
??There’s one slight difference between implied globals and explicitly defined ones―the difference?is in the ability to undefine these variables using the delete operator:
?????? ? Globals created with var (those created in the program outside of any function) cannot be deleted.
????????? Implied globals created without var (regardless if created inside functions) can?be?deleted.
????This shows that implied globals are technically not real variables, but they are properties of??the global object. Properties can be deleted with the delete operator whereas variables cannot:// define three globals var global_var = 1; global_novar = 2; // antipattern (function () { global_fromfunc = 3; // antipattern }()); // attempt to delete delete global_var; // false delete global_novar; // true delete global_fromfunc; // true // test the deletion typeof global_var; // "number" typeof global_novar; // "undefined" typeof global_fromfunc; // "undefined"
?? ?In ES5 strict mode, assignments to undeclared variables (such as the two antipatterns in the preceding snippet) will throw an error.
- Hoisting: A Problem with Scattered vars
????JavaScript enables you to have multiple var statements anywhere in a function, and?they all act as if the variables were declared at the top of the function. This behavior is??known as hoisting. This can lead to logical errors when you use a variable and then you?declare it further in the function. For JavaScript, as long as a variable is in the same?scope (same function), it’s considered declared, even when it’s used before the var?declaration. Take a look at this example:// antipattern myname = "global"; // global variable function func() { alert(myname); // "undefined" var myname = "local"; alert(myname); // "local" } func();
?? ?In this example, you might expect that the first alert() will prompt “global” and the?second will prompt “local.” It’s a reasonable expectation because, at the time of the?first alert, myname was not declared and therefore the function should probably “see”?the global myname. But that’s not how it works. The first alert will say “undefined”?because myname is considered declared as a local variable to the function. (Although the?declaration comes after.) All the variable declarations get hoisted to the top of the?function. Therefore to avoid this type of confusion, it’s best to declare upfront all variables?you intend to use.?The preceding code snippet will behave as if it were implemented like so:myname2 = "global"; // global variable function func_fixed() { var myname2; // same as -> var myname = undefined; alert(myname2); // "undefined" myname2 = "local"; alert(myname2); // "local" } func_fixed();
? - for Loops
?? ?In for loops you iterate over arrays or array-like objects such as arguments and ?HTMLCollection objects. The usual for loop pattern looks like the following:// sub-optimal loop for (var i = 0; i < myarray.length; i++) { // do something with myarray[i] }
?? ?A problem with this pattern is that the length of the array is accessed on every loop?iteration. This can slow down your code, especially when myarray is not an array but?an HTMLCollection object. HTMLCollections are objects returned by DOM methods such as:? document.getElementsByName() ? document.getElementsByClassName() ? document.getElementsByTagName()
?? ?There are also a number of other HTMLCollections, which were introduced before the?DOM standard and are still in use today. There include (among others):? document.images All IMG elements on the page ? document.links All A elements ? document.forms All forms ? document.forms[0].elements All fields in the first form on the page
???The trouble with collections is that they are live queries against the underlying??document(the HTML page). This means that every time you access any collection’s length, you’re querying the live DOM, and DOM operations are expensive in general.
??That’s why a better pattern for for loops is to cache the length of the array??(or collection) you’re iterating over, as shown in the following example:for (var i = 0, len = myarray.length; i < len; i++) { // do something with myarray[i] }
??This way you retrieve the value of length only once and use it during the whole loop.Note that when you explicitly intend to modify the collection in the loop (for example,by adding more DOM elements), you’d probably like the length to be updated and not constant.
- Function constructor
?? Using the new Function() constructor is similar to eval() and should be approached with care. It could be a powerful construct but is often misused. If you absolutely must use eval(), you can consider using new Function() instead. There is a small potential benefit because the code evaluated in new Function() will be running in a local function scope, so any variables defined with var in the code being evaluated will not become globals automatically.
?? Another way to prevent automatic globals is to wrap the?eval() call into an immediate function. Consider the following example. Here only un remains as a global variable polluting?the namespace:console.log(typeof un); // "undefined" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined" var jsstring = "var un = 1; console.log(un);"; eval(jsstring); // logs "1" jsstring = "var deux = 2; console.log(deux);"; new Function(jsstring)(); // logs "2" jsstring = "var trois = 3; console.log(trois);"; (function () { eval(jsstring); }()); // logs "3" console.log(typeof un); // "number" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined"
?
???? Another difference between eval() and the Function constructor is that eval() can?interfere with the scope chain whereas Function is much more sandboxed. No matter?where you execute Function, it sees only the global scope. So it can do less local variable?pollution. In the following example, eval() can access and modify a variable in its outer?scope, whereas Function cannot (also note that using Function or new Function is?identical):(function () { var local = 1; eval("local = 3; console.log(local)"); // logs 3 console.log(local); // logs 3 }()); (function () { var local = 1; Function("console.log(typeof local);")(); // logs undefined }());
? - Number Conversions with parseInt()
?? Using parseInt() you can get a numeric value from a string. The function accepts a second radix parameter, which is often omitted but shouldn’t be. The problems occur when the string to parse starts with 0: for example, a part of a date entered into a form field. Strings that start with 0 are treated as octal numbers (base 8) in ECMAScript 3;however, this has changed in ES5. To avoid inconsistency and unexpected results, always specify the radix parameter:var month = "06", year = "09"; month = parseInt(month, 10); year = parseInt(year, 10);
?? ?In this example, if you omit the radix parameter like parseInt(year), the returned value will be 0, because “09” assumes octal number (as if you did parseInt(year, 8)) and 09 is not a valid digit in base 8. Alternative ways to convert a string to a number include:+"08" // result is 8 Number("08") // 8
?? These are often faster than parseInt(), because parseInt(), as the name suggests, parses and doesn’t simply convert. But if you’re expecting input such as “08 hello”, parseInt() will return a number, whereas the others will fail with NaN.