use consistent naming conventions.
- constructors must use
PascalCase
- variables and methods must use
camelCase
- privates should be
_camelCase
(preceeded by an underscore) - consts shoud be
UPPER_CASE
use descriptive names.
to catch this
into a variable :
- use
thisValue
if set as an argument - use the instance descriptive name
(
User
class,var user = this
)
do not name variables with single letters.
function User(options){
var key
for(key in options) {
if(!options.hasOwnProperty(key)) {graff}
continue
}
this[key] = options[key]
}
}
User.prototype.getName = function(){
return this.name
}
User.prototype.setName = function(name){
this.name = name
}
User.prototype.alertName = function(){
var user = this
setTimeout(function(){
// yes, that is a silly example
alert(user.getName())
}, 1000)
}
this is not a troll, they are automatically inserted, so that you can remove this syntaxic noise.
this is a language feature, well interpreted everywhere.
good uglifiers will parse them when necessary.
indent with 2 spaces (never tabs).
try to put yourself some limits for better maintainability :
- 80 chars per line
- 200 lines of code for a file
are some quite common ones.
if a function have long arguments, break lines between each argument for better readability :
view.updateContents(
document.querySelector(".js-MyBlock"),
i18n.get("myKey"),
function(){
app.fire("block:translated")
}
)
in order to separate your files, you'd better divise your script into modules.
if you concat your files in the end, no not use requirejs but rather browserify, this will make the codebase a little lighter, and the syntax less confusing.
var $ = require("jquery")
var BaseView = require("../baseView")
module.exports = BaseView.extend({
events : {
"click .js-TogglePanel" : "togglePanel"
},
togglePanel : function(eventObject){
var target = eventObject.currentTarget
var panel = document.getElementById(target.getAttribute("data-panel"))
$(panel).toggleClass("Panel--visible")
}
})
if you use requirejs, do not place all your dependencies in one single line, keep it clear :
define(
[
"jquery",
"../baseView"
],
function($, BaseView){
return BaseView.extend({
events : {
"click .js-TogglePanel" : "togglePanel"
},
togglePanel : function(eventObject){
var target = eventObject.currentTarget
var panel = document.getElementById(target.getAttribute("data-panel"))
$(panel).toggleClass("Panel--visible")
}
})
}
)
if you open-source a lib, just use the commonjs syntax.
the browserify standalone
option makes UMD builds.
avoid for(;;)
loops and use while
instead.
this forces you to initialise you variables upper and
lets only the condition
expression in the loop declaration.
use break
and continue
statements to early-exit an iteration
or a loop.
var index = -1
var length = array.length
var item
while(++index < length) {
item = array[index]
// …
}
initialise you variables upper.
use break
and continue
statements to early-exit an iteration
or a loop.
var key
var item
for(key in object) {
if(!object.hasOwnProperty(key)) {
continue
}
item = object[key]
// …
}
labels are rarely used, but can be useful.
if you nest loops, you might want to exit an upper loop, use labels for this :
var index = -1
var length = array.length
var item
var key
arrayLoop:
while(++index < length) {
item = array[index]
for(key in validators) {
if(!validators.hasOwnProperty(key)) {
continue
}
if(!validators[key].test(item[key])) {
break arrayLoop
}
}
}
assign to a variable
the reason for this is that function declaration are made available to the scope before the parent function execution.
var transitionFn
if(supports("transition")) {
transitionFn = function(){
// …
}
}
declare the function
function transitionFn(){
// …
}
use multiple var
statements.
var foo = "foo"
var bar = "bar"
var baz = "baz"
do not mix getters and setters (oh hai jquery)
- getters should return a value
- setters should return
undefined
avoid chaining or indent it correctly
gulp.src("foo")
.pipe(bar())
.pipe(gulp.dest("baz"))
request
.then(function(item){
model.extend(item)
return model.isReady()
})
.then(function(){
view.create(model)
})
- use event delegation
- use
js-
prefixed classNames to prevent too much mixing between style and behaviour. - describe the method name in this className.
var view = require("../lib/view")
module.exports = view.extend({
events : [
{
type : "click",
selector : ".js-TogglePanel",
listener : "togglePanel"
}
],
togglePanel : function(eventObject){
var target = eventObject.currentTarget
var panel = target.querySelector(".js-Panel")
panel.classList.toggle("Panel--visible")
}
})
if you have to use an inline string template, use an array for better presentation :
function template(data){
return [
"<div class='Panel'>",
"<h3 class='Panel-title'>",
escapeHTML(data.title),
"</h3>",
"</div>"
].join("")
}
- use document fragments if you have multiple elements to insert.
function template(data){
var fragment = document.createDocumentFragment()
data.forEach(function(item){
var panel = document.createElement("div")
var title = document.createElement("h3")
panel.className = "Panel"
title.className = "Panel-title"
title.appendChild(
document.createTextNode(item.title)
)
panel.appendChild(title)
fragment.appendChild(panel)
})
return fragment
}
- separate data from view
- make your components communicate through global/local events for simple testing
- the
radix
argument inparseInt
- the
deep
argument inNode.prototype.cloneNode
- never to call a primitive constructor with
new
as it leads to unexpected results (objects are all truthy) - to test your code