We found closures to be very powerful, making asynchronous programming much easier and clearer. However, subtle errors can occur if developers are not fully aware of their behavior.
For example, quite a common mistake is to create closures inside a loop, referencing variables that change during the execution of that loop. This can lead to confusing behavior since variables referenced by a closure contain the value at the point the closure is executed, not at the point of its creation.
Objects can be assigned as prototypes of other objects. If a property is not found on an object then the runtime will check the prototype object, and so on. As functions can be stored on objects and objects can share prototypes, the prototype mechanism allows methods and code reuse in a familiar way. Functions also behave as objects, and can be used to store and retrieve properties by name.
As a best practice, we recommend establishing a clear and well-defined policy of object ownership. This makes it easier to point to the code that is responsible for maintaining (and releasing) the reference to a given object.
(0x80 << 24) !== 0x80000000
(0x80 << 24) === -2147483648
Both would be false if unsigned integers were used. Note the use of the triple-equal operator, because it does not perform type conversion if the operands have different types. In contrast, the double-equal operator does perform type conversion and could hide errors in the code.
The language has evolved considerably from its origin and some features are not entirely standardized across browsers. Any recently added feature should be checked for before use. For example, the ability to define getters and setters for object properties with Object.defineProperty has, for us, become one of the most useful features added recently, however we need to provide extra code paths for when support does not exist. In some cases testing for the existence of a function is enough to check for a supported feature, as in the case of Object.defineProperty. In other cases, the code must try to actually use the feature inside a try / catch block and check whether an exception was raised.
At Turbulenz, we try to minimize development mistakes by employing a development process focused on code quality: strict coding standards, frequent code reviews and automatic unit testing. We also routinely use static analysis tools to check our code for common mistakes. So far we have not found analysis tools with the same level of inspection as those available for C and C++, but the following tools have been helpful in that they do catch a fairly large class of bugs, and they do improve over time: Closure Linter, JSHint, and JSLint.
Editors for static typed languages can provide a lot of functionality that is harder to provide for dynamic languages.
- Syntax highlighting
- Auto-completion suggestions for:
- Default global objects.
- Well-known external libraries like jQuery.
- Variables and functions defined within the current function.
- Global variables and global functions defined within the current file.
- Refactoring at the current file level.
- Integration with JSLint or JSHint.