GJS Style Guide
This guide represents the official coding style of GJS and all official GNOME projects which utilize GJS.
The goal of this guide is to provide a flexible, balanced JavaScript syntax for all projects, GNOME or otherwise, which utilize our platform.
In an amazing, dynamic language like JavaScript, a correct style guide (and unit tests) are critical or your codebase rapidly becomes a spaghetti-code mess.
Rule List
Semicolons
While JavaScript allows omitting semicolons at the end of lines, we do not. Always end statements with a semicolon.
Variable naming
- We use CamelCase variable names, with CamelCase for type names and lowerCamelCase for variable, property, and method names.
- Private variables, whether object member variables or module-scoped variables, should begin with
_
. - Global variables (in the global or 'window' object) should be avoided whenever possible. If you do create them, the variable name should have a namespace.
Imports
Always use CamelCase when importing modules and classes to distinguish them from other variables.
const Lang = imports.lang;
1Use quick object syntax when importing multiple classes from one source.
const { GLib, GObject } = imports.gi; const Lang = imports.lang;
1
2Always separate library imports from local imports.
const { GLib, GObject } = imports.gi; const Lang = imports.lang; const LocalClass = imports.localClass;
1
2
3
4
Variable declaration
Always use one of const
, let
, or var
when defining a variable. Always use let
when block scope is intended; in particular, inside for()
and while()
loops, let
or const
is almost always correct.
var
var
declares variables at the beginning of the closest function, module, or script, regardless of where the declaration occurs. var
should be used with caution and typically only when you need to export a variable to other modules. Never use var
in while
or for
loops.
const
vs. let
const
and let
both ensure that variables are only accessible after they are declared in the code. const
should always be used unless you absolutely need to change the variable value. This prevents bugs from accidental reassignments.
Examples:
/* Iterating over any iterable object (Array, etc)
* This is valid because the logic of this loop is:
* { const prop = someobj[0]; ~your code~ }
* { const prop = someobj[1]; ~your code~ }
*/
for (const prop of someobj) {
...
}
// vs.
/* Using an actual counter which logically functions as...
* let i = 0;
* if (i < 10) ~your code~
* i++;
* if (i < 10) ~your code~
* i++;
*/
for (let i = 0; i < 10; i++) {
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
If you don't use let
/const
then the variable is added to function scope, not the for loop block scope. See [What's new in JavaScript 1.7][1]
A common case where this matters is when you have a closure inside a loop:
for (let i = 0; i < 10; ++i) {
mainloop.idle_add(() => {
log(`Printing i: ${i}`);
});
}
2
3
4
5
If you used var
instead of let
it would print "10" numerous times.
GObject Properties
WARNING
These rules only apply to GObject properties!
GJS provides numerous ways to access properties on GObject classes.we recommend using camelCase
for getting properties and utilizing the provided setter function instead of setting the property directly.
Use lowerCamelCase when getting or setting a simple property
Getting a property value
/* incorrect */ let a = obj['property-name']; /* incorrect */ let b = obj.property_name; /* correct */ let c = obj.propertyName;
1
2
3
4
5
6
7
8Setting a property value
/* incorrect */ obj['property-name'] = 10; /* incorrect */ obj.property_name = 10; /* correct */ obj.propertyName = 10;
1
2
3
4
5
6
7
8
Use theC
setter function when setting a property that has side effectsIf setting the property causes change beyond simply changing the value, prefer the setter function for clarity.
/* incorrect */ obj['property-name-has-side-effects'] = 10; /* incorrect */ obj.property_name_has_side_effects = 10; /* incorrect */ obj.propertyNameHasSideEffects = 10; /* correct */ obj.set_property_name_has_side_effects(10);
1
2
3
4
5
6
7
8
9
10
11
JavaScript Properties
Do not use JavaScript's getter and setter syntax if your code has side effects.
For example, the code foo.bar = 10;
should not do anything other than set foo.bar
to 10
. If the setting has side effects use a setter method:
function setBar(value) {
this._bar = value;
}
2
3
The same logic applies to getters.
In practice, this means getters are only used to make internal properties public or for creating static properties on the object.
class Demo {
get bar() {
return this._bar;
}
/* and */
static get bar() {
return 'bar?';
}
}
const demo = new Demo();
let staticBar = Demo.bar;
let bar = demo.bar;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
this
in closures
this
will not be captured in a closure; this
is relative to how the closure is invoked, not to the value of this where the closure is created, because this
is a keyword with a value passed in at function invocation time, it is not a variable that can be captured in closures.
To solve this, use arrow functionsopen in new window or Function.prototype.bind()
:
let closure = () => {
this._fn();
};
// or
let closure = function() {
this._fn();
}.bind(this);
2
3
4
5
6
7
An example of this would be connecting to a signal on a GObject:
const { Gtk } = imports.gi;
const button = new Gtk.Button({ label: 'Click Me!' });
button.connect('clicked', () => {
/* do something */
});
2
3
4
5
6
Object literal syntax
JavaScript allows equivalently:
foo = { 'bar' : 42 };
foo = { bar: 42 };
2
and
var b = foo['bar'];
var b = foo.bar;
2
If your usage of an object is like an object, then you're defining "member variables." For member variables, use the no-quotes no-brackets syntax, that is, { bar: 42 }
and foo.bar
.
If your usage of an object is like a hash table (and thus conceptually the keys can have special chars in them), don't use quotes, but use brackets, { bar: 42 }
, foo['bar']
.
Whitespace
General Rules
- 4-space indentation (the Java style)
- No trailing whitespace.
- No tabs.
No whitespace when calling a function.
eslint: func-call-spacing
/* very incorrect */
fn
();
/* incorrect */
fn ();
/* perfect */
fn();
2
3
4
5
6
7
8
9
No spaces between brackets and elements.
eslint: array-bracket-spacing
/* incorrect */
let arr = [ 1, 2, 3 ];
/* correct */
let arr = [1, 2, 3];
2
3
4
5
Spacing in a function signature.
eslint: space-before-function-paren, space-before-blocks
Enforce spacing between keys and values in object literal properties.
eslint: key-spacing
/* incorrect */
const objA = { foo:42 };
const objB = { foo : 42 };
const objC = { foo :42 };
/* correct */
var obj = { foo: 42 };
/* incorrect */
const a = function (){};
const b = function (){};
const c = function(){};
/* correct */
const d = function() {};
const e = function a() {};
/* incorrect */
const arr = [ 1, 2, 3 ];
log(arr[ 0 ]);
/* correct */
const foo = [1, 2, 3];
log(foo[0]);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Use spaces inside curly braces.
eslint: object-curly-spacing
/* incorrect */
const foo = {clark: 'kent'};
/* correct */
const foo = { clark: 'kent' };
2
3
4
5