Well, if you have seen this line, I will assume that you are interested in the various magic skills of [KJS] Kubejs.But this article is not to introduce things that Kubejs can implement, but to introduce what kind of method can simplify your development when implementing the function.
If there is no special explanation in this article, the Kubejs used is Kubejs-FORGE-1605.3.19-Build.299, Rhino (Rhino) is Rhino-Forge-1605.1.5-Build.75, and the corresponding Minecraft version is 1.16.The higher version of the Kubejs and Rhino functions can be described as there are only a lot. Choosing this slightly old version is to find a script developer who is used to 1.16.
Some of the following briefs that may be used below: kubejs == kjs, javascript == js, typeScript == TS
The "class" method of the function
Someone is called "Look at your New New".
Base
If you write a function, and the "accidental" brain supplements an object named this, and gives a certain (few) attributes to it, and watch this code in VSCODE, such as:::
Function WHO (the, F, is, ZZZANK) {This.Name = zzzank
}
You may find that the function name ("WHO") has changed from common yellow to green, and the mouse pointer hovers on the function name. You can also find a sentence:
The first letter in the figure is a lowercase, but it will not affect the function
"This constructor may be converted into a class statement."
This sentence sounds awkward, because the original "might" is actually "May", which is translated into "can" here. Maybe you will be a bit clearer.
Note that although it can be converted, Rhino actually does not support the traditional statement grammar in the traditional sense, only supports the alternative grammar we are introducing, so don't rush to "fast repair".
Using the three -line code just now, we have created a class. The usage is consistent with the class of Java. All uses New to construct an object that conforms to this type of characteristics.The created objects can obtain data like ordinary objects:
const exactlywho = new who (null, null, null, "zank")that just providedConsole.info (Exactlywho.name) // Output to ZANK, which is the "zank"
Function in the "class" function
If such a class can only be used to organize data, then it is better to use JS's own object.The essence of the class is that the class can have its own functions. Only objects that belong to the same class can simply call this/function, while other objects need to be touched if they want to callAt first, the functions facing all objects are separated from those objects that do not need this function.
This is not because of any data security (you have used JS), but to avoid investigation of pain when you accidentally call the wrong.
It seems a bit too much, so just look at the grammar first:
who.prototype = {SAY: Function (Words) {
Return `$ {this.name} says: $ {words}`
},
Run: () => {{
Return "No"
}}
This is to add two function members to the WHO class.Both functions can be called. The function names used are the key name corresponding to the function of the function to the object to the prototype. In fact, the previous SAY and Run.Let's try:
console.info (exactlywho.say ("wow")) // Output to: zank says: wowconsole.info (exactlywho.run ()) // Output as: No
Function members also support the defined functions, as long as the defined function name replace the function constructed by our own on -site structure:
Function Max3 (A, B, C) {Return math.max (math.max (a, b), C)}WHO.PROTOTYPE = {
Say: Function (Words) {Return `$ {This.zzzank} Says: $ {words}`},
Run: () => "No",
MAX3: MAX3
}
The objects assigned to Prototype can also be filled with ordinary members, such as numbers and string, which can achieve the effect of establishing the default value.But I also recommend that the constructor of the class (Rhino does not, the equivalent function here is the function that is regarded as a class statement, such as the WHO here), set the default value, because the initialization of the data is best placed on the same one on the same oneLocal, convenient for subsequent search and markings.
this
Now let's look at the SAY function again, you may find that this object that should not exist out of thin air.And it is clearly called this instead of Exactlywho, but when the SAY function is called by Exactlywho, this.Name and exactlywho.name are completely consistent.(If you don't mind doing more research, you will find that it is really "complete", it is to point to the same thing)
This is because this is not an object in essence, but similar to a code, representing an object that calls the function that belongs to this.In our example, the function of this belongs is SAY. The object of calling the SAY function is Exactlywho, so this represents Exactlywho at this time.
Using this can let JS know that what you want to use is a member of the object (whether it is a value or a function), not a function parameter or a variable of the same name defined by yourself.More importantly, this member function can process the data of the caller, and the class will not be just a new place to store functions.
What if you do not use the object and directly call the This function?
At this time, this generally points to a shared object called Globalthis.But this is the behavior of the JS engine browser. As for MC's Rhino?Rhino's GitHub historical records did not record changes involving Globalthis, but how can you try it yourself.
This should be in the function of the usual writing format, not where it is in the function of the rough arrow, because this in the rough arrow function will only represent Globalthis.In other words, in the following two lines, the first line can be placed on the class prototype, and the second line should not be placed in the class prototype:
Function Readx () {Return this.x}Let Ready = (() => {Return this.y})
WHO.PROTOTYPE = {
Read_x: readx, // feasible read_y: ready // No suggestion
}
I personally advocate that this is not used in functions in the form of rough arrows, because this here can only represent Globalthis, but Rhino has provided Globalthis without martial arts.It's useless.
Chain call and delay value value
Maybe you have seen this usage:
Generalfluids.Foreach ((FLUID) => {Event
.Create (Fluid.id)
.TextureStill (fluid.still)
.Textureflowing (flow.flowing)
.Bucketcolor (floid.color);
});
It is very comfortable to write, because there is no need to give a variable for assigning a variable when creating (Create), and you do not need to repeat the variable name-> Consider which function to use-> Newly opens a process of preparing to write variable names.We might as well make a comparison to see how much the chain call is short and easy to read:
Event.create (FLUID.ID) const builder = event.create (fluid.id);.Texturesstill (fluid.still) builder.textureStill (fluid.still);
.Textureflowing (flow.flowing) builder.textureflowing (fluid.flowing);
.Bucketcolor (floid.color); Builder.Bucketcolor (FLUID.COLOR);
accomplish
Each call -called function of chain call is modified the attributes of the initial objects created, which means that chain calls and delay value value generally requires use.The ring will give an output, and the next loop will need to return the output explanation to the input, and then the output is too troublesome.
In order to truly realize the chain call, the return value of each function called on the chain should be a modified object, so that the return value can call the next function, such as:
who.prototype = {Addname: Function (name) {
This.Name = this.Name+''+name
Return this
}
}
This is this this, that is, the caller itself. Any WHO class object returns after calling is the who class, so you can then call the function under the WHO class, so that the chain call is implemented:
console.log (exactlywho.addname ("mark"). Addname ("John"). addname ("daming") // The output is: zank mark john daming
Give an example
Function Grid (IDPREFIX) {This._ = {{
ROWS: -1,
Columns: -1,
IDPREFIX: IDPREFIX,
Dx: 0,
DY: 0
}
}
Grid.prototype = {
ROWS: Function (ROWS) {
This ._. ROWS = ROWS
Return this
},
Columns: Function (color) {
This ._. Columns = color
Return this
},
Movex: function (dx) {
This ._. DX+= DX
Return this
},
Movey: Function (dy) {this ._. DY+= DY
Return this
},
Build: Function () {
If (this ._. ROWS <0 || This ._. Columns <0) {{
Throw "Invalid Size"
}
Const grid = [];
Const {dx, dy, columns, rows, idprefix} = this._;
For (let i = 0; i
For (let j = 0; j
Grid.push ({{
X: DX+I*18,
Y: DY+J*18,
ID: `$ {idprefix} _ $ {i+j*rows}`
});}
}
Return grid;
}
}
This is a simplified version of CMGrid. Although it is simplified, it can still reflect the details of chain calls and delay value.
Each function that is not used to find the ultimate value will return to the caller itself (this), so you can constantly call chain until call build for value.None of the functions other than BUILD will calculate the output, but the data used to calculate the results at that time.Data can be modified only by functions other than BUILD, and will not be used to modify the uses outside the original data. They will only be used in the built function.
Try:
const big = trueconst slots = new grid ('grid_what')
.Movex (20)
.Movey (8)
. ROWS (Big? 3: 1)
.Columns (big? 7: 4)
.Build ()
Slots will be an array with 3*7 = 21 objects at this time. Each object has three attributes: x, y, ID.X are at least 20, Y to 8, IDs start with grid_what.Its source should be displayed very clearly in the Build function.
And, by modifying the value of the BIG, you can also generate GRIDs of different sizes.This is one of the good ways to pack a large number of control variables into a single "switch".
JS type system
Some people regard the type as a shackle, but in fact, the automatic completion of the type of explicit type and the convenience brought by the syntax check.
Probejs
If you are developing for MC1.18 or higher, see Probejs.If you are developing for MC1.16.5Forge, see Probejs Legacy.If you are using MC1.16.5fabric, uh, for the time being to ask for more blessings.
Probejs can provide type prompts for JS. For example, when calling event.shaped (), the type that indicates the first parameter should be things that can be the item. The second parameter is the character series list, and the third parameter is a JS object.
In addition, input@can be triggered by the code fragment.If the VSCode plug -in corresponding to the Probejs is installed, the mouse is hovering on the string of the item/fluid/language key, etc., and it can also display its detailed information.
Type annotation
Positioning the cursor to the previous empty row of the function/class/variable statement. If there is no, add a line by yourself, enter one/input two*. At this time, you should be able to see VSCODE with a display of/** display/***/Suggestion:
Enter the annotation in the figure.In parentheses in the note, the first bracket should be Any.Change it to Number, so that A, B, C will be regarded as a variable of Number, so you can accept automatic completion for Number.
You can also further be the value of each parameter, the return value, the function of the function as a whole, and automatically call it when used:
Type Note: Type parameters
However, there are many restrictions on JS types. For example, if there is a function in the maximum value in the array, no matter what the type of array elements is feasible, how should the return value be marked?Since JS does not use the type parameter to automatically identify the function of the input type, we can only mark the type of return value as any, but this type of annotation is almost gone.
But do you remember the Probejs just now?With the work area settings set by Probejs, we can add our own documents to the User folder under the Probe folder under the Kubejs folder.Use the TypeScript type annotation in this file.For example,
/***Get the "bigget" element among provided entries, "big" is defined by `comparator`
*@Param list provided entries
*@Param comparator if not specify, will use `(a, b) => a-b`
*@Return the "Biggest" Entry
*/
declare function maxof
(list: t [], comparator: (a: t, b: t) => 1 | -1 | 0): t
The TS comes with the grammar of the type label, so the type annotation is simpler than the JS, and the part of the type label is transferred to the code.
After that, we do not need to write documents again when the Maxof function is implemented. VSCODE will automatically repeat the defined TS document.
Grammar
After using Probejs to generate a document, you should find a JSON file called JSCONFIG under the Kubejs folder.If the PROBEJS version you use is relatively new, the CheckJS option should exist and open the default.But if not, open it and add a line:
"Checkjs": true,
The result should grow like this:
After that, you can see that the grammatical examination is running, and the use of non -type inspection will be marked.For example, the three parameters of the MAX3 function that we previously implemented were marked with the type annotation that the type must be Number, then the input different types will be marked with red:
However, note: The type document generated by Probejs has been not perfect for a long time, so opening Checkjs may cause the correct usage to be mistaken for errors. Please consider it yourself.
Knot
If you just change the synthetic table slightly, then the above part will not have any use.But if you want to do something larger and deeper, the above methods will greatly standardize the code you wrote, reduce your mental burden and facilitate subsequent development.
I wish you can really turn "My World" into a unique brand -new world with Kubejs.