1 | 18.06.2016:
|
---|
2 | -----------
|
---|
3 |
|
---|
4 | Start of Refactoring.
|
---|
5 |
|
---|
6 | The entire FACT++/scripts folder contains ~5k lines of JS code, hardly documented at all. Yes, there are comments, but this is mostly commented out code, not anything that tries make the design choices clear to the reader.But this is not what worries me most.
|
---|
7 |
|
---|
8 | Mostly I am worried about, what I like to call "global include with side-effects".
|
---|
9 | AFAIK in ECMAScript 5 there is nothing like an include command, but in our FACT++ JS dialect, we have include(). From what I infer, this function will simply insert the mentioned script at its own position. Much like the C-preprocessor would execute a #include.
|
---|
10 |
|
---|
11 | This means, by including another script, one does not only make new functions and objects available, but one can also (unknowingly) execute code. Even more, this inserted script is inserted into the global scope (mostly), so it has access to all the variables and functions, previously defined and can alter them. So that after an include(), one cannot be sure about the state ones variables. I can imagine, a nice source of error would be the following:
|
---|
12 |
|
---|
13 | As script works nicely, but somebody needs additional output, so a new variable is created in the top of Main.js:
|
---|
14 |
|
---|
15 | var i_am_already_used_inside_an_included_script = some_value;
|
---|
16 |
|
---|
17 | Further down the line, there is an include(), which alters the value of "i_am_already_used_inside_an_included_script", because it happened to use the same variable name.
|
---|
18 |
|
---|
19 | Further down, our new variable has a different value, and we just don't see why.
|
---|
20 |
|
---|
21 | #-------------------------------------------------------------
|
---|
22 |
|
---|
23 | So to sum it up. I would like it best, when.
|
---|
24 |
|
---|
25 | A) There are no scripts to be included, that have side-effects.
|
---|
26 | I.e. All scripts, which are included into the Main.js or any other main-like script, should only create new functions, or reference types (aka classes), but never call any function or instanciate new objects.
|
---|
27 |
|
---|
28 | B) Even better: Scripts, which are written, to be included into a main-like script, should simply create their own scope. If they have their own scope, of course they may create instances of new classes, or any other variable, which stays bound to the
|
---|
29 | scope of the script. They still *should not* execute any code, I'd say.
|
---|
30 |
|
---|
31 | Example:
|
---|
32 | # great_functions.js
|
---|
33 |
|
---|
34 | var great_functions = {
|
---|
35 |
|
---|
36 | scoped_variable1 : "foo",
|
---|
37 |
|
---|
38 | great_function1 : function(parameter1){
|
---|
39 | console.out(parameter1);
|
---|
40 | },
|
---|
41 |
|
---|
42 | great_function2 :function(){
|
---|
43 | console.out(this.scoped_variable1);
|
---|
44 | },
|
---|
45 | };
|
---|
46 |
|
---|
47 | Use the functions from great_functions.js like:
|
---|
48 |
|
---|
49 | include("great_functions.js");
|
---|
50 | great_functions.great_function1("foo"); // --> foo
|
---|
51 | great_functions.scoped_variable1 = "bar";
|
---|
52 | great_functions.great_function2(); // --> bar
|
---|
53 |
|
---|
54 |
|
---|
55 | C) If one wants to create a lot of funtions, which should go into one single scope, like a library for example. I'd suggest to do it like this:
|
---|
56 |
|
---|
57 | Create a folder for the library and inside that folder create a file called like the library as well.
|
---|
58 |
|
---|
59 | new_lib/
|
---|
60 | |
|
---|
61 | |----new_lib.js
|
---|
62 | |
|
---|
63 | |----part1.js
|
---|
64 | |
|
---|
65 | |----part2.js
|
---|
66 |
|
---|
67 | Let new_lib.js create the scope and include all parts of the library inside new_lib.js:
|
---|
68 |
|
---|
69 | # new_lib.js
|
---|
70 |
|
---|
71 | var new_lib = {};
|
---|
72 | include("part1.js");
|
---|
73 | include("part2.js");
|
---|
74 |
|
---|
75 | Then inside the parts of the library (names can be chosen arbitraryly of course.) attach the new functions or reference types to the namespace created in new_lib.js:
|
---|
76 |
|
---|
77 | # par1.js
|
---|
78 |
|
---|
79 | new_lib.a_function = function(){
|
---|
80 | // do something astonishing!
|
---|
81 | };
|
---|
82 |
|
---|
83 |
|
---|
84 | Of course all this heavily relies on convention, and not to break the convention. But I think, it would work. In the main-like script, which wants to use a_function() just do:
|
---|
85 |
|
---|
86 | # main_like.js
|
---|
87 |
|
---|
88 | include("new_lib/new_lib.js");
|
---|
89 | new_lib.a_function();
|
---|
90 |
|
---|
91 |
|
---|
92 |
|
---|
93 | # --------------------------------------------------------------
|
---|
94 |
|
---|
95 | So there we are ... I am now on the mission of scoping scripts and removing side-effects.
|
---|
96 |
|
---|
97 | I'll start with side-effects, because they are much worse, than missing scope.
|
---|
98 |
|
---|
99 | So I'll start with "CheckFTU.js", which is included in "Startup.js"
|
---|
100 |
|
---|
101 | Okay, took me 5 minutes. not bad.
|
---|
102 |
|
---|
103 | next is:
|
---|
104 | "CheckUnderflow.js"
|
---|
105 |
|
---|
106 | Okay so, this is more tricky. CheckUnderflow.js includes 4 more scripts.
|
---|
107 | CheckStates.js is included in CheckUnderflow.js *and* in Startup.js
|
---|
108 |
|
---|
109 | I think, it is time to invent something like include-guards. So, when a library is included another time, it checks, weather it was already included. Maybe like this:
|
---|
110 |
|
---|
111 | # library.js
|
---|
112 | // this should be the global context, *if* include('library.js')
|
---|
113 | // was called in a global context.
|
---|
114 | if (!('library' in this)){
|
---|
115 | var library = {
|
---|
116 | some_func : function(){
|
---|
117 |
|
---|
118 | },
|
---|
119 | another_func : function(){
|
---|
120 |
|
---|
121 | },
|
---|
122 | };
|
---|
123 | }
|
---|
124 | //maybe even:
|
---|
125 | else{
|
---|
126 | console.out("multiple include of 'library.js' ");
|
---|
127 | }
|
---|
128 |
|
---|
129 | So I add, this new "include guard" to CheckFTU.js quickly.
|
---|
130 |
|
---|
131 | Still we need to decide, what we want to do, when include() is called
|
---|
132 | inside a libraries scope. Currently it would be included, into that namespace, and stay there. Which is probably just fine. So...
|
---|
133 | let's got for it, and see how it works.
|
---|
134 |
|
---|
135 | # ------------------------------------------------------------------------
|
---|
136 | So CheckUnderflow.js is done. Now I look at: CheckStates.js which is included by CheckUnderflow.js.
|
---|
137 |
|
---|
138 | Okay, I'm done inside CheckStates.js, now I need to see, how the functions are called.
|
---|
139 |
|
---|
140 | While doing CheckStates.js, I realized, that functions which are part of a scope now, cannot refer to each other directly, as they could do before.
|
---|
141 | Now when once function inside a library wants to call another function, it must refer to it, using this. This is a bit more to write, but it makes it also clear, where the function, which is called, comes from. So for the sake of expliciteness. I'll do it.
|
---|
142 |
|
---|
143 | Now I do Hist1D.js and Hist2D.js. Also I must say, I found a little library, being defined inside CheckUnderflow, which is the Func object.
|
---|
144 |
|
---|
145 | So I will create a Func.js for this.
|
---|
146 | # ----------------------------------------------------------------------
|
---|
147 |
|
---|
148 | Okay now I look at takeRun.js as it is included in CheckUnderflow.
|
---|
149 | I had to create a new function, which would handle the attachment of
|
---|
150 | the inchange function to the FACT_CONTROL/INCOMPLETE subscription.
|
---|
151 | I don't know a good name for this, but I think I wrote it correctly.
|
---|
152 |
|
---|
153 | I don't like that the new function takeRun.attachFadControlIncompleteOnChange
|
---|
154 | has a member named takeRun.attachFadControlIncompleteOnChange.incomplete.
|
---|
155 |
|
---|
156 | I also don't like, that in CheckUnderflow, I call this new function,
|
---|
157 | which will attach a callback function to the FACT_CONTROL/INCOMPLETE subscription.
|
---|
158 | But when reading the code, one can hardly understand that.
|
---|
159 |
|
---|
160 | I mean, this is just like when takeRun.js was included before.
|
---|
161 | Nobody could have even guessed, that there was a callback function attached to
|
---|
162 | a Subscription instance made just before takeRun.js was included.
|
---|
163 |
|
---|
164 | Okay that's it for the moment. Next is probably CheckUnderflow itself,
|
---|
165 | since it is a kind of library itself.
|
---|
166 |
|
---|
167 | # ----------------------------------------------------------------------
|
---|
168 |
|
---|
169 | Okay so I looked at CheckUnderflow.js. It was a pretty large mess, Which I broke up into
|
---|
170 | 11 separate functions, none of them longer than one page.
|
---|
171 | But there was no possibility to reuse anything. So the code in total got larger.
|
---|
172 | Well, we will see if there is any possibility to reuse later, when refactoring is done.
|
---|
173 |
|
---|
174 | At least, I could imagine, that some of the function would in principle be testable,
|
---|
175 | while I have no idea, how to test anything, which is dim dependent, without the overhead
|
---|
176 | of writing a total mock up.
|
---|
177 |
|
---|
178 | Next up is: Statup.js, because this uses CheckUnderflow.
|
---|
179 |
|
---|
180 | Then I will look at all these handlers. Let's see.
|
---|
181 |
|
---|
182 | # ----------------------------------------------------------------------
|
---|
183 |
|
---|
184 | I just remembered, I should mention what I donÄt like about Hist1D.js and Hist2D.js
|
---|
185 | (I mean apart from the fact, that this code would not be needed,
|
---|
186 | when we had used Python+PyDim instead of Javascript and a custom made JS interpreter.)
|
---|
187 | I don't like that Hist1D() and Hist2D() *claim* to be a constructors, but in fact they are not.
|
---|
188 |
|
---|
189 | If you did:
|
---|
190 | var h = Hist1D.Hist1D(100, 0, 100);
|
---|
191 | console.out(h instanceof Hist1D);
|
---|
192 | you would get a 'false'.
|
---|
193 |
|
---|
194 | Also the fact, that one always has to call constructors with new, does not apply for Hist1D or Hist2D, why?
|
---|
195 | In strict mode, not calling a constructor with new, will even cause an exception. But calling Hist1D() with or without new, does not make a difference at all.
|
---|
196 |
|
---|
197 | Should be changed.
|
---|
198 |
|
---|
199 | # ----------------------------------------------------------------------
|
---|
200 |
|
---|
201 |
|
---|