forked from linuxmint/cjs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStyle_Guide.txt
243 lines (174 loc) · 10.2 KB
/
Style_Guide.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
= Coding style =
Our goal is to have all JavaScript code in GNOME follow a consistent style. In a dynamic language like
JavaScript, it is essential to be rigorous about style (and unit tests), or you rapidly end up
with a spaghetti-code mess.
== Semicolons ==
JavaScript allows omitting semicolons at the end of lines, but don't. Always end
statements with a semicolon.
== js2-mode ==
If using Emacs, try js2-mode. It functions as a "lint" by highlighting missing semicolons
and the like.
== Imports ==
Use CamelCase when importing modules to distinguish them from ordinary variables, e.g.
<pre>
const Big = imports.big;
const GLib = imports.gi.GLib;
</pre>
== Variable declaration ==
Always use one of "const", "var", or "let" when defining a variable. Always use "let" when block scope is intended; in particular, inside for() and while() loops, let is almost always correct.
<pre>
// Iterating over an array
for (let i = 0; i < 10; ++i) {
let foo = bar(i);
}
// Iterating over an object's properties
for (let prop in someobj) {
...
}
</pre>
If you don't use "let" then the variable is added to function scope, not the for loop block scope.
See [http://developer.mozilla.org/en/docs/index.php?title=New_in_JavaScript_1.7&printable=yes#Block_scope_with_let What's new in JavaScript 1.7]
A common case where this matters is when you have a closure inside a loop:
<pre>
for (let i = 0; i < 10; ++i) {
mainloop.idle_add(function() { log("number is: " + i); });
}
</pre>
If you used "var" instead of "let" it would print "10" a bunch of times.
Inside functions, "let" is always correct instead of "var" as far as we know. "var" is useful when you want to add something to the with() object, though... in particular we think you need "var" to define module variables, since our module system loads modules with the equivalent of "with (moduleObject)"
== "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 Lang.bind, eg:
<pre>
const Lang = imports.lang;
let closure = Lang.bind(this, function() { this._fnorbate() });
</pre>
A more realistic example would be connecting to a signal on a
method of a prototype:
<pre>
const Lang = imports.lang;
MyPrototype = {
_init : function() {
fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
},
_onFnorbFrobate : function(fnorb) {
this._updateFnorb();
},
};
</pre>
== Object literal syntax ==
JavaScript allows equivalently:
<pre>
foo = { 'bar' : 42 };
foo = { bar: 42 };
</pre>
and
<pre>
var b = foo['bar'];
var b = foo.bar;
</pre>
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']".
== Variable naming ==
# We use javaStyle variable names, with CamelCase for type names and lowerCamelCase for variable and method names. However, when calling a C method with underscore-based names via introspection, we just keep them looking as they do in C for simplicity.
# Private variables, whether object member variables or module-scoped variables, should begin with "_".
# True 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 in it, like "BigFoo"
# When you assign a module to an alias to avoid typing "imports.foo.bar" all the time, the alias should be "const TitleCase" so "const Bar = imports.foo.bar;"
# If you need to name a variable something weird to avoid a namespace collision, add a trailing "_" (not leading, leading "_" means private).
# For GObject constructors, always use the lowerCamelCase style for property names instead of dashes or underscores.
== Whitespace ==
* 4-space indentation (the Java style)
* No trailing whitespace.
* No tabs.
* If you chmod +x .git/hooks/pre-commit it will not let you commit with messed-up whitespace (well, it doesn't catch tabs. turn off tabs in your text editor.)
== JavaScript "classes" ==
Keep in mind that JavaScript does not "really" have classes in the sense of C++ or Java; you can't create new types beyond the built-in ones (Object, Array, RegExp, String). However, you can create object instances that share common properties, including methods, using the prototype mechanism.
Each JavaScript object has a property __proto__; if you write obj.foo and "foo" is not in "obj", JavaScript will look for "foo" in __proto__. If several objects have the same __proto__, then they can share methods or other state.
You can create objects with a constructor, which is a special function. Say you have:
<pre>
function Foo() {}
let f = new Foo();
</pre>
For "new Foo()" JavaScript will create a new, empty object; and execute Foo() with the new, empty object as "this". So the function Foo() sets up the new object.
"new Foo()" will also set __proto__ on the new object to Foo.prototype. The property "prototype" on a constructor is used to initialize __proto__ for objects the constructor creates. To get the right __proto__ on objects, we need the right prototype property on the constructor.
You could think of "f = new Foo()" as:
<pre>
let f = {}; // create new object
f.__proto__ = Foo.prototype; // doing this by hand isn't actually allowed
Foo.call(f); // invoke Foo() with new object as "this"
</pre>
Our pattern for writing classes is:
<pre>
function Foo(arg1, arg2) {
this._init(arg1, arg2);
}
Foo.prototype = {
_init : function(arg1, arg2) {
this._myPrivateInstanceVariable = arg1;
},
myMethod : function() {
},
myClassVariable : 42,
myOtherClassVariable : "Hello"
}
</pre>
This pattern means that when you do "let f = new Foo()", f will be a new object, f.__proto__ will point to Foo.prototype, and Foo.prototype._init will be called to set up the object.
{{Note|Again, on the JavaScript language level, Foo is not a class in the sense of Java or C++; it's just a constructor function, which means it's intended for use with the "new Foo()" syntax to create an object. Once the object is created, from a JavaScript language perspective its type is the built-in type "Object" - though we're using it and thinking of it as if it had type "Foo", JavaScript doesn't have a clue about that and will never do any type-checking based on which constructor was used to create something. All typing is "duck typing." The built-in types, such as Object, String, Error, RegExp, and Array, ''are'' real types, however, and do get type-checked.}}
{{Note|If a constructor function has a return value, it is used as the value of "new Foo()" - replacing the automatically-created "this" object passed in to the constructor. If a constructor function returns nothing (undefined), then the passed-in "this" is used. In general, avoid this feature - constructors should have no return value. But this feature may be necessary if you need the new instance to have a built-in type other than Object. If you return a value from the constructor, "this" is simply discarded, so referring to "this" while in the constructor won't make sense. }}
== JavaScript "inheritance" ==
There are lots of ways to simulate "inheritance" in JavaScript. In general, it's a good idea to avoid class hierarchies in JavaScript. But sometimes it's the best solution.
Our preferred approach is to use a Spidermonkey-specific extension and directly set the __proto__ member of the subclass's prototype to point to the prototype of the base class. Looking up a property in the subclass starts with the properties of the instance. If the property isn't there, then the prototype chain is followed first to the subclass's prototype and then to the base class's prototype.
<pre>
const Lang = imports.lang;
function Base(foo) {
this._init(foo);
}
Base.prototype = {
_init : function(foo) {
this._foo = foo;
},
frobate : function() {
}
};
function Sub(foo, bar) {
this._init(foo, bar);
}
Sub.prototype = {
__proto__ : Base.prototype,
_init : function(foo, bar) {
// here is an example of how to "chain up"
Base.prototype._init.call(this, foo);
this._bar = bar;
}
// add a newMethod property in Sub.prototype
newMethod : function() {
}
}
</pre>
{{Note|You cannot use this mechanism to inherit from a built-in type, such as String or Error, because the methods on those objects expect them to have primitive type String or Error, while your constructor will create an Object instead.}}
{{Note|You cannot use this mechanism to inherit from a GObject. For that to work, we would need to create a new GType in C that was a subclass of the base class GType, and we'd need to override the class methods in the new GType so they called into JavaScript. Tons of work. For now, GObject subclasses must be implemented in C.}}
In portable JavaScript code you'll often see a different technique used to get this prototype chain:
<pre>
function Base(foo) ...
Base.prototype = ...
function Sub(foo, bar) ...
// Do NOT do this - do not use an instance of Base as the Sub prototype
Sub.prototype = new Base();
</pre>
The problem with this pattern is that you might want to have side effects in the Base() constructor. Say Base() in its constructor creates a window on the screen, because Base() is a dialog class or something. If you use the pattern that some instances of Base() are just prototypes for subclasses, you'll get extra windows on the screen.
The other problem with this pattern is that it's just confusing and weird.
== JavaScript attributes ==
Don't use the getter/setter syntax when getting and setting has side effects, that is, the code:
<pre>
foo.bar = 10;
</pre>
should not do anything other than save "10" as a property of foo. It's obfuscated otherwise; if the setting has side effects, it's better if it looks like a method.
In practice this means the only use of attributes is to create read-only properties:
<pre>
get bar() {
return this._bar;
}
</pre>
If the property requires a setter, or if getting it has side effects, methods are probably clearer.