本帖最后由 wode490390 于 2018-10-12 02:33 编辑
排版后期再优化吧,有误请指出,部分内容参考MDN
§0 入坑准备
§0.1 服务端环境
需要插件:http://www.mcbbs.net/thread-813311-1-1.html
§0.2 开发环境
任意文本编辑器
小项目推荐用Notepad++:http://notepad-plus-plus.org/download/
§1 ECMAScript 语言
目前的NK模组加载器可能不兼容部分高级JS语法,请自行测试。
如果你对JavaScript已经了解,可以跳过本大章节内容。
§1.1 变量
声明变量
复制代码
行末的分号表示语句结束
另外大小写敏感,以下例子声明了两个不同的变量
复制代码
定义一个变量之后,你可以赋予它一个值:
复制代码
你也可以将这些操作写在同一行:
复制代码
你可以通过变量名称读取变量的值:
复制代码
在给变量赋值之后,你可以更改这个变量的值:
复制代码
§1.1.1 var 变量
复制代码
变量声明,无论发生在何处,都在执行任何代码之前进行处理。用var声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。如果你重新声明一个 JavaScript 变量,它将不会丢失其值。
将赋值给未声明变量的值在执行赋值时将其隐式地创建为全局变量(它将成为全局对象的属性)。声明和未声明变量之间的差异是:
1. 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。
复制代码
2. 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建。
复制代码
复制代码
3. 声明变量是它所在上下文环境的不可配置属性,非声明变量是可配置的(如非声明变量可以被删除)。
复制代码
由于这三个差异,未能声明变量将很可能导致意想不到的结果。因此,建议始终声明变量,无论它们是在函数还是全局作用域内。
§1.1.1.1 变量提升
由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明。这意味着变量可以在声明之前使用,这个行为叫做“hoisting”。“hoisting”就像是把所有的变量声明移动到函数或者全局代码的开头位置。
复制代码
因此,建议始终在作用域顶部声明变量(全局代码的顶部和函数代码的顶部),这可以清楚知道哪些变量是函数作用域(本地),哪些变量在作用域链上解决。
重要的是,提升将影响变量声明,而不会影响其值的初始化。当到达赋值语句时,该值将确实被分配:
复制代码
§1.1.1.2 例子
声明并初始化两个变量:
复制代码
给两个变量赋值成字符串值:
复制代码
留意其中的顺序:
复制代码
在这里,x和y在代码执行前就已经创建了,而赋值操作发生在创建之后。当"x = y"执行时,y已经存在,所以不抛出ReferenceError,并且它的值是'undefined'。所以x被赋予 undefined 值。然后,y被赋予'A'。于是在执行完第一行之后,x === undefined && y === 'A'才出现了这样的结果。
多个变量的初始化
复制代码
隐式全局变量和外部函数作用域
看起来像是隐式全局作用域的变量也有可能是其外部函数变量的引用。
复制代码
§1.1.2 let 局部变量
let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
复制代码
复制代码
let允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。
复制代码
在程序或者函数的顶层,let并不会像var一样在全局对象上创造一个属性,比如:
复制代码
§1.1.3 const 常量
常量是块级作用域,很像使用 let 语句定义的变量。常量的值不能通过重新赋值来改变,并且不能重新声明。
复制代码
此声明创建一个常量,其作用域可以是全局或本地声明的块。 与var变量不同,全局常量不会变为窗口对象的属性。需要一个常数的初始化器;也就是说,您必须在声明的同一语句中指定它的值(这是有道理的,因为以后不能更改)。
const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容(例如,其参数)。
关于“暂存死区”的所有讨论都适用于let和const。
一个常量不能和它所在作用域内的其他变量或函数拥有相同的名称。
示例:
复制代码
§1.2 数据类型
§1.3 注释
单行注释(//)
复制代码
多行注释(/**/)
复制代码
§1.4 运算
§1.4.1 加法 (+)
加法运算符的作用是数值求和,或者字符串拼接。
复制代码
§1.4.2 减法 (-)
减法运算符使两个操作数相减,结果是它们的差值。
复制代码
§1.4.3 除法 (/)
除法运算符的结果是操作数的商 ,左操作数是被除数,右操作数是除数。
复制代码
§1.4.4 乘法 (*)
乘法运算符的结果是操作数的乘积。
复制代码
§1.4.5 求余 (%)
求余运算符返回第一个操作数对第二个操作数的模,即 var1 对 var2 取模,其中 var1 和 var2 是变量。取模功能就是 var1 除以 var2 的整型余数。
复制代码
§1.4.6 幂 (**)
幂运算符返回第一个操作数做底数,第二个操作数做指数的乘方。即, var1var2 ,其中 var1 和 var2 是其两个操作数。幂运算符是右结合的。a ** b ** c 等同于 a ** (b ** c)。
复制代码
如果要反转求幂表达式结果的符号,你可以采用这样的方式:
复制代码
强制求幂表达式的基数为负数:
复制代码
§1.4.7 递增 (++)
递增运算符为其操作数增加1,返回一个数值。
复制代码
§1.4.8 递减 (--)
递减运算符将其操作数减去1,并返回一个数值。
复制代码
§1.4.9 一元负号 (-)
一元负号运算符位于操作数前面,并转换操作数的符号。
复制代码
§1.4.10 一元正号 (+)
一元正号运算符位于其操作数前面,计算其操作数的数值,如果操作数不是一个数值,会尝试将其转换成一个数值。 尽管一元负号也能转换非数值类型,但是一元正号是转换其他对象到数值的最快方法,也是最推荐的做法,因为它不会对数值执行任何多余操作。它可以将字符串转换成整数和浮点数形式,也可以转换非字符串值 true,false 和 null。小数和十六进制格式字符串也可以转换成数值。负数形式字符串也可以转换成数值(对于十六进制不适用)。如果它不能解析一个值,则计算结果为 NaN.
复制代码
§1.4.11 非
§1.5 比较
有两种比较方式:严格比较运算符和转换类型比较运算符。对于严格比较运算符(===)来说,仅当两个操作数的类型相同且值相等为 true,而对于被广泛使用的比较运算符(==)来说,会在进行比较之前,将两个操作数转换成相同的类型。对于关系运算符(比如 <=)来说,会先将操作数转为原始值,使它们类型相同,再进行比较运算。
§1.6 条件句
条件句是能够让你测试一个表达式是否返回 true 然后根据结果运行不同的代码的代码结构。
§1.6.1 if
复制代码
§1.6.2 if...else
复制代码
§1.6.3 if...else if...else
复制代码
§1.7 switch...case...break...default
判断句评估一个表达式,将表达式的值与case子句匹配,并执行与该情况相关联的语句。
复制代码
expression:一个用来与 case 子语句匹配的表达式。
case valueN(可选):用于匹配 expression 的 case 子句。如果 expression 与给定的 valueN 相匹配,则执行该 case 子句中的语句直到该 switch 语句结束或遇到一个 break 。
default(可选):一个 default 子句;如果给定,这条子句会在 expression 的值与任一 case 语句均不匹配时执行。default 可以放到 case 之间。
复制代码
一个 switch 语句首先会计算其 expression 。然后,它将从第一个 case 子句开始直到寻找到一个其表达式值与所输入的 expression 的值所相等的子句(使用 严格运算符,===)并将控制权转给该子句,执行相关语句。(如果多个 case 与提供的值匹配,则选择匹配的第一个 case,即使这些 case 彼此间并不相等。)如果没有 case 子句相匹配,程序则会寻找那个可选的 default 子句,如果找到了,将控制权交给它,执行相关语句。若没有 default 子句,程序将继续执行直到 switch 结束。按照惯例,default 子句是最后一个子句,不过也不需要这样做。
可选的 break 语句确保程序立即从相关的 case 子句中跳出 switch 并接着执行 switch 之后的语句。若 break 被省略,程序会继续执行 switch 语句中的下一条语句。
如果你忘记添加break,那么代码将会从值所匹配的 case 语句开始运行,然后持续执行下一个 case 语句而不论值是否匹配。
§1.8 循环
循环可以将代码重复运行。
§1.8.1 for
用于创建一个循环,它包含了三个可选的表达式,三个可选的表达式包围在圆括号中并由分号分隔, 后跟一个在循环中执行的语句(通常是一个块语句)。
复制代码
initialization:一个表达式 (包含赋值语句) 或者变量声明。典型地被用于初始化一个计数器。该表达式可以使用var关键字声明新的变量。初始化中的变量不是该循环的局部变量,而是与for循环处在同样的作用域中。该表达式的结果无意义。
condition:一个条件表达式被用于确定每一次循环是否能被执行。如果该表达式的结果为true, statement 将被执行。 这个表达式是可选的。如果被忽略,那么就被认为永远为真。如果计算结果为假,那么执行流程将被跳到for语句结构后面的第一条语句。
final-expression:每次循环的最后都要执行的表达式。执行时机是在下一次condition的计算之前。通常被用于更新或者递增计数器变量。
statement:只要condition的结果为true就会被执行的语句。 要在循环体内执行多条语句,使用一个块语句({ ... })来包含要执行的语句。没有任何语句要执行,使用一个空语句(;)。
以下例子声明了变量i并被初始赋值为0,for语句检查i的值是否小于9,如果小于9,则执行语句块内的语句,并且最后将i的值增加 1。
复制代码
for语句的所有的表达式都是可选的
例如,初始化块中的表达式没有被指定:
复制代码
像初始化块一样,条件块也是可选的。如果省略此表达式,则必须确保在循环体内跳出,以防创建死循环。
复制代码
你当然可以忽略所有的表达式。同样的,确保使用了break语句来跳出循环并且还要修改(增加)一个变量,使得break 语句的条件在某个时候是真的。
复制代码
for允许无语句,但循环条件后的分号是强制性的。如果没有分号,循环声明之后的行将被视为语句。
复制代码
§1.8.2 for...in
for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。
复制代码
§1.8.3 for...of
for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
复制代码
§1.8.4 while
while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为真时结束循环。
复制代码
§1.9 函数
函数声明定义一个具有指定参数的函数。
复制代码
参数是有限的。
你也可以使用 Function 构造函数和一个函数声明来定义函数。
复制代码
函数可以被有条件来声明,这意味着,在一个 if 语句里,函数声明是可以嵌套的。有的浏览器会将这种有条件的声明看成是无条件的声明,无论这里的条件是true还是false,浏览器都会创建函数。因此,它们不应该被使用。
默认情况下,函数是返回 undefined 的。想要返回一个其他的值,函数必须通过一个 return 语句指定返回值。
下面的例子定义了一个匿名函数并把它赋值给变量x。这个函数返回它参数的平方:
复制代码
§1.9.1 使用函数
复制代码
§1.9.2 函数提升
JavaScript 中的函数声明被提升到了函数定义。你可以在函数声明之前使用该函数:
复制代码
注意 :函数表达式function expressions 不会被提升:
复制代码
§1.9.3 命名函数表达式
如果你想在函数体内部引用当前函数,则需要创建一个命名函数表达式。然后函数名称将会且只会作为函数体(作用域内)的本地变量。
复制代码
§1.x 内置对象
待更新,可自行参考MDN!
§1.x Classes
待更新,可自行参考MDN!
§1.x 其它
持续填坑中,尽请期待!
§2 Nukkit 模组加载器
阅读本章内容前你应该熟练掌握上一大章节的内容并对Java有所了解。
可在本插件的GitHub源码上查看所有可调用的方法。
请以ANSI格式保存文件,否则中文字符串将乱码。
§2.1 全局变量
你应该避免对这些内置变量进行重新赋值。
复制代码
§2.2 命令
创建命令
使用manager的createCommand()方法,有3个参数
复制代码
§2.3 任务
§2.3.1 定时任务
创建Java定时任务
使用manager的createTask()方法,有2个参数
复制代码
§2.3.2 循环任务
创建Java循环任务
使用manager的createLoopTask()方法,有2个参数
复制代码
§2.4 事件
所有的事件类型请在Nukkit事件大全查阅。
用事件名命名的函数监听事件,参数为此事件
复制代码
§2.5 配置
创建配置
使用manager的createConfig()方法,有2个参数
复制代码
§3 练习
暂无
§4 作品
你可以在js文件头部使用注释添加版权等信息
复制代码
§4.1 混淆
你可以使用工具对代码进行加密,使抄袭者阅读不便
如源代码:
复制代码
混淆后:
复制代码
§4.2 加密
如源代码:
复制代码
加密后:
复制代码
§4.3 压缩
你可以使用一些工具进行压缩,简化代码
复制代码
§4.4 发布
恭喜你学完了本教程,快去制作属于自己的NK模组吧,如有更新记得回来看看哦。
来看看其他人写的模组吧(按优秀程度排序,欢迎广大版友提供)
排版后期再优化吧,有误请指出,部分内容参考MDN
§0 入坑准备
§0.1 服务端环境
需要插件:http://www.mcbbs.net/thread-813311-1-1.html
§0.2 开发环境
任意文本编辑器
小项目推荐用Notepad++:http://notepad-plus-plus.org/download/
§1 ECMAScript 语言
目前的NK模组加载器可能不兼容部分高级JS语法,请自行测试。
Q:目前暂时不支持的语法为什么还要写在帖内呢?
A:未来新版本的模组加载器可能会支持,所以先写在帖子内。
如果你对JavaScript已经了解,可以跳过本大章节内容。
§1.1 变量
声明变量
- var 变量a;
行末的分号表示语句结束
另外大小写敏感,以下例子声明了两个不同的变量
- var 变量a;
- var 变量A;
定义一个变量之后,你可以赋予它一个值:
- 变量a = '我是变量';
你也可以将这些操作写在同一行:
- var 变量a = '我是变量';
你可以通过变量名称读取变量的值:
- 变量a;
在给变量赋值之后,你可以更改这个变量的值:
- var 变量a = '我是变量';
- 变量a = '吾乃变量也';
§1.1.1 var 变量
- var 变量1 [= 值1 [, 变量2 [, 变量3 ... [, 变量N]]]];
变量声明,无论发生在何处,都在执行任何代码之前进行处理。用var声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。如果你重新声明一个 JavaScript 变量,它将不会丢失其值。
将赋值给未声明变量的值在执行赋值时将其隐式地创建为全局变量(它将成为全局对象的属性)。声明和未声明变量之间的差异是:
1. 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。
- function x() {
- y = 1;
- var z = 2;
- }
- x();
- console.log(y); // 打印"1" 。
- console.log(z); // 抛出ReferenceError: z未在x外部声明。
2. 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建。
- console.log(a); // 抛出ReferenceError。
- console.log('still going...'); // 永不执行。
- var a;
- console.log(a); // 打印"undefined"或"")。
- console.log('still going...'); // 打印"still going..."。
3. 声明变量是它所在上下文环境的不可配置属性,非声明变量是可配置的(如非声明变量可以被删除)。
- var a = 1;
- b = 2;
- delete this.a;
- delete this.b;
- console.log(a, b); // 抛出ReferenceError。
- // 'b'属性已经被删除。
由于这三个差异,未能声明变量将很可能导致意想不到的结果。因此,建议始终声明变量,无论它们是在函数还是全局作用域内。
§1.1.1.1 变量提升
由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明。这意味着变量可以在声明之前使用,这个行为叫做“hoisting”。“hoisting”就像是把所有的变量声明移动到函数或者全局代码的开头位置。
- bla = 2
- var bla;
- // ...
- // 可以理解为:
- var bla;
- bla = 2;
因此,建议始终在作用域顶部声明变量(全局代码的顶部和函数代码的顶部),这可以清楚知道哪些变量是函数作用域(本地),哪些变量在作用域链上解决。
重要的是,提升将影响变量声明,而不会影响其值的初始化。当到达赋值语句时,该值将确实被分配:
- function do_something() {
- console.log(bar); // undefined
- var bar = 111;
- console.log(bar); // 111
- }
- // is implicitly understood as:
- function do_something() {
- var bar;
- console.log(bar); // undefined
- bar = 111;
- console.log(bar); // 111
- }
§1.1.1.2 例子
声明并初始化两个变量:
- var a = 0, b = 0;
给两个变量赋值成字符串值:
- var a = "A";
- var b = a;
- // 等效于:
- var a, b = a = "A";
留意其中的顺序:
- var x = y, y = 'A';
- console.log(x + y); // undefinedA
在这里,x和y在代码执行前就已经创建了,而赋值操作发生在创建之后。当"x = y"执行时,y已经存在,所以不抛出ReferenceError,并且它的值是'undefined'。所以x被赋予 undefined 值。然后,y被赋予'A'。于是在执行完第一行之后,x === undefined && y === 'A'才出现了这样的结果。
多个变量的初始化
- var x = 0;
- function f(){
- var x = y = 1; // x在函数内部声明,y不是!
- }
- f();
- console.log(x, y); // 0, 1
- // x是全局变量。
- // y是隐式声明的全局变量。
隐式全局变量和外部函数作用域
看起来像是隐式全局作用域的变量也有可能是其外部函数变量的引用。
- var x = 0; // x是全局变量,并且赋值为0。
- console.log(typeof z); // undefined,因为z还不存在。
- function a() { // 当a被调用时,
- var y = 2; // y被声明成函数a作用域的变量,然后赋值成2。
- console.log(x, y); // 0 2
- function b() { // 当b被调用时,
- x = 3; // 全局变量x被赋值为3,不生成全局变量。
- y = 4; // 已存在的外部函数的y变量被赋值为4,不生成新的全局变量。
- z = 5; // 创建新的全局变量z,并且给z赋值为5。
- } // (在严格模式下(strict mode)抛出ReferenceError)
- b(); // 调用b时创建了全局变量z。
- console.log(x, y, z); // 3 4 5
- }
- a(); // 调用a时同时调用了b。
- console.log(x, z); // 3 5
- console.log(typeof y); // undefined,因为y是a函数的本地(local)变量。
§1.1.2 let 局部变量
let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
- let 变量A [= 值1] [, 变量2 [= 值2]] [, ..., 变量N [= 值N]];
- var a = 1;
- var b = 2;
- if (a === 1) {
- var a = 11; // the scope is global
- let b = 22; // the scope is inside the if-block
- console.log(a); // 11
- console.log(b); // 22
- }
- console.log(a); // 11
- console.log(b); // 2
let允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。
- function varTest() {
- var x = 1;
- if (true) {
- var x = 2; // 同样的变量!
- console.log(x); // 2
- }
- console.log(x); // 2
- }
- function letTest() {
- let x = 1;
- if (true) {
- let x = 2; // 不同的变量
- console.log(x); // 2
- }
- console.log(x); // 1
- }
在程序或者函数的顶层,let并不会像var一样在全局对象上创造一个属性,比如:
- var x = 'global';
- let y = 'global';
- console.log(this.x); // "global"
- console.log(this.y); // undefined
§1.1.3 const 常量
常量是块级作用域,很像使用 let 语句定义的变量。常量的值不能通过重新赋值来改变,并且不能重新声明。
- const 常量1 = 值1 [, 常量2 = 值2 [, ... [, 常量N = 值N]]];
此声明创建一个常量,其作用域可以是全局或本地声明的块。 与var变量不同,全局常量不会变为窗口对象的属性。需要一个常数的初始化器;也就是说,您必须在声明的同一语句中指定它的值(这是有道理的,因为以后不能更改)。
const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容(例如,其参数)。
关于“暂存死区”的所有讨论都适用于let和const。
一个常量不能和它所在作用域内的其他变量或函数拥有相同的名称。
示例:
- // 注意: 常量在声明的时候可以使用大小写,但通常情况下全部用大写字母。
- // 定义常量MY_FAV并赋值7
- const MY_FAV = 7;
- // 报错
- MY_FAV = 20;
- // 输出 7
- console.log("my favorite number is: " + MY_FAV);
- // 尝试重新声明会报错
- const MY_FAV = 20;
- // MY_FAV 保留给上面的常量,这个操作会失败
- var MY_FAV = 20;
- // 也会报错
- let MY_FAV = 20;
- // 注意块范围的性质很重要
- if (MY_FAV === 7) {
- // 没问题,并且创建了一个块作用域变量 MY_FAV
- // (works equally well with let to declare a block scoped non const variable)
- let MY_FAV = 20;
- // MY_FAV 现在为 20
- console.log('my favorite number is ' + MY_FAV);
- // 这被提升到全局上下文并引发错误
- var MY_FAV = 20;
- }
- // MY_FAV 依旧为7
- console.log("my favorite number is " + MY_FAV);
- // 常量要求一个初始值
- const FOO; // SyntaxError: missing = in const declaration
- // 常量可以定义成对象
- const MY_OBJECT = {"key": "value"};
- // 重写对象和上面一样会失败
- MY_OBJECT = {"OTHER_KEY": "value"};
- // 对象属性并不在保护的范围内,下面这个声明会成功执行
- MY_OBJECT.key = "otherValue";
- // 也可以用来定义数组
- const MY_ARRAY = [];
- // It's possible to push items into the array
- // 可以向数组填充数据
- MY_ARRAY.push('A'); // ["A"]
- // 但是,将一个新数组赋给变量会引发错误
- MY_ARRAY = ['B']
§1.2 数据类型
变量 | 解释 | 示例 |
String | 字符串(文字序列)。 要表示变量的值是字符串,你必须将它们用引号包裹起来。 | var myVariable = 'Bob'; |
Number | 数字。不用引号包围。 | var myVariable = 10; |
Boolean | 布尔(逻辑)值。一个 True/False (真 / 假)值。 true/false 是 JS 里的特殊关键字,不需要引号。 | var myVariable = true; |
Array | 数组,一种允许你存储多个值在一个引用里的结构。 | var myVariable = [1,'Bob','Steve',10]; |
Object | 对象,基本上 JavaScript 里的任何东西都是对象,而且都可以被储存在变量里。 | 上面所有示例都是对象。 |
§1.3 注释
单行注释(//)
- //我被注释了
- var 变量 = '我被声明了';//我被注释了
多行注释(/**/)
- /*我被注释了
- 我也被//注释了
- /*我亦被注释了//
- */
§1.4 运算
运算符 | 解释 | 符号 |
加/连接/正 | 数值求和或者字符串拼接。亦可以作正号。 | + |
减/负 | 使两个操作数相减,结果是它们的差值。亦可以作负号。 | - |
乘 | 结果是操作数的乘积。 | * |
除 | 结果是操作数的商 ,左操作数是被除数,右操作数是除数。 | / |
赋值 | 基于右值给左值赋值。 | = |
非 | 如果原本的值是 true 则返回 false ,反之亦然。 | ! |
求余 | 返回第一个操作数对第二个操作数的模。 | % |
幂 | 返回第一个操作数做底数,第二个操作数做指数的乘方。 | ** |
递增 | 为其操作数增加1,返回一个数值。 | ++ |
递减 | 将其操作数减去1,并返回一个数值。 | -- |
§1.4.1 加法 (+)
加法运算符的作用是数值求和,或者字符串拼接。
- // Number + Number -> 数字相加
- 1 + 2 // 3
- // Boolean + Number -> 数字相加
- true + 1 // 2
- // Boolean + Boolean -> 数字相加
- false + false // 0
- // Number + String -> 字符串连接
- 5 + "foo" // "5foo"
- // String + Boolean -> 字符串连接
- "foo" + false // "foofalse"
- // String + String -> 字符串连接
- "foo" + "bar" // "foobar"
§1.4.2 减法 (-)
减法运算符使两个操作数相减,结果是它们的差值。
- 5 - 3 // 2
- 3 - 5 // -2
- "foo" - 3 // NaN
§1.4.3 除法 (/)
除法运算符的结果是操作数的商 ,左操作数是被除数,右操作数是除数。
- 1 / 2 // 在 JavaScript 中返回 0.5
- 1 / 2 // 在 Java 中返回 0
- // (不需要数字是明确的浮点数)
- 1.0 / 2.0 // 在 JavaScript 或 Java 中都返回 0.5
- 2.0 / 0 // 在 JavaScript 中返回 Infinity
- 2.0 / 0.0 // 同样返回 Infinity
- 2.0 / -0.0 // 在 JavaScript 中返回 -Infinity
§1.4.4 乘法 (*)
乘法运算符的结果是操作数的乘积。
- 2 * 2 // 4
- -2 * 2 // -4
- Infinity * 0 // NaN
- Infinity * Infinity // Infinity
- "foo" * 2 // NaN
§1.4.5 求余 (%)
求余运算符返回第一个操作数对第二个操作数的模,即 var1 对 var2 取模,其中 var1 和 var2 是变量。取模功能就是 var1 除以 var2 的整型余数。
- 12 % 5 // 2
- -1 % 2 // -1
- NaN % 2 // NaN
- 1 % 2 // 1
- 2 % 3 // 2
- -4 % 2 // -0
- 5.5 % 2 // 1.5
§1.4.6 幂 (**)
幂运算符返回第一个操作数做底数,第二个操作数做指数的乘方。即, var1var2 ,其中 var1 和 var2 是其两个操作数。幂运算符是右结合的。a ** b ** c 等同于 a ** (b ** c)。
- 2 ** 3 // 8
- 3 ** 2 // 9
- 3 ** 2.5 // 15.588457268119896
- 10 ** -1 // 0.1
- NaN ** 2 // NaN
- 2 ** 3 ** 2 // 512
- 2 ** (3 ** 2) // 512
- (2 ** 3) ** 2 // 64
如果要反转求幂表达式结果的符号,你可以采用这样的方式:
- -(2 ** 2) // -4
强制求幂表达式的基数为负数:
- (-2) ** 2 // 4
§1.4.7 递增 (++)
递增运算符为其操作数增加1,返回一个数值。
- 如果后置(postfix)使用,即运算符位于操作数的后面(如 x++),那么将会在递增前返回数值。
- 如果前置(prefix)使用,即运算符位于操作数的前面(如 ++x),那么将会在递增后返回数值。
- // 后置
- var x = 3;
- y = x++;
- // y = 3, x = 4
- // 前置
- var a = 2;
- b = ++a;
- // a = 3, b = 3
§1.4.8 递减 (--)
递减运算符将其操作数减去1,并返回一个数值。
- 如果后置使用(如 x--),则在递减前返回数值。
- 如果前置使用(如 --x),则在递减后返回数值。
- // 后置
- var x = 3;
- y = x--; // y = 3, x = 2
- // 前置
- var a = 2;
- b = --a; // a = 1, b = 1
§1.4.9 一元负号 (-)
一元负号运算符位于操作数前面,并转换操作数的符号。
- var x = 3;
- y = -x; // y = -3, x = 3
§1.4.10 一元正号 (+)
一元正号运算符位于其操作数前面,计算其操作数的数值,如果操作数不是一个数值,会尝试将其转换成一个数值。 尽管一元负号也能转换非数值类型,但是一元正号是转换其他对象到数值的最快方法,也是最推荐的做法,因为它不会对数值执行任何多余操作。它可以将字符串转换成整数和浮点数形式,也可以转换非字符串值 true,false 和 null。小数和十六进制格式字符串也可以转换成数值。负数形式字符串也可以转换成数值(对于十六进制不适用)。如果它不能解析一个值,则计算结果为 NaN.
- +3 // 3
- +"3" // 3
- +true // 1
- +false // 0
- +null // 0
- +function(val){ return val;} //NaN
§1.4.11 非
§1.5 比较
有两种比较方式:严格比较运算符和转换类型比较运算符。对于严格比较运算符(===)来说,仅当两个操作数的类型相同且值相等为 true,而对于被广泛使用的比较运算符(==)来说,会在进行比较之前,将两个操作数转换成相同的类型。对于关系运算符(比如 <=)来说,会先将操作数转为原始值,使它们类型相同,再进行比较运算。
比较符 | 解释 | 符号 | 示例 |
大于 | 大于运算符仅当左操作数大于右操作数时返回true | > | 4 > 3; // true |
大等 | 大于等于运算符当左操作数大于或等于右操作数时返回true | >= | 3 >= 3; // true |
小于 | 小于运算符仅当左操作数小于右操作数时返回true | < | 3 < 4; // true |
小等 | 小于等于运算符当左操作数小于或等于右操作数时返回true | <= | 3 <= 4; // true |
相等 | 测试两个值是否相等,返回一个 true/false (布尔型)值。 | == | 1 == 2; |
全等 | 测试两个值与类型是否相等,返回一个 true/false (布尔型)值。 | === | "3" === 3; |
不等 | 测试两个值是否不等,返回一个 true/false (布尔型)值。 | != | "4" != 4; |
不全等 | 测试两个值与类型是否不等,返回一个 true/false (布尔型)值。 | !== | 5 !== "5"; |
§1.6 条件句
条件句是能够让你测试一个表达式是否返回 true 然后根据结果运行不同的代码的代码结构。
§1.6.1 if
- if ('对象' == '对象') {
- 变量A = "变量1";
- }
§1.6.2 if...else
- if (false) {
- 变量B = "变量2";
- } else {
- 变量B = "变量3";
- }
§1.6.3 if...else if...else
- if ('变量n' == 5) {
- 变量C = "变量4";
- } else if (5 === '5') {
- 变量C = "变量5";
- } else {
- 变量C = "变量6";
- }
§1.7 switch...case...break...default
判断句评估一个表达式,将表达式的值与case子句匹配,并执行与该情况相关联的语句。
- switch (expression) {
- case value1:
- // 当 expression 的结果与 value1 匹配时,执行此处语句
- [break;]
- case value2:
- // 当 expression 的结果与 value2 匹配时,执行此处语句
- [break;]
- ...
- case valueN:
- // 当 expression 的结果与 valueN 匹配时,执行此处语句
- [break;]
- [default:
- // 如果 expression 与上面的 value 值都不匹配时,执行此处语句
- [break;]]
- }
expression:一个用来与 case 子语句匹配的表达式。
case valueN(可选):用于匹配 expression 的 case 子句。如果 expression 与给定的 valueN 相匹配,则执行该 case 子句中的语句直到该 switch 语句结束或遇到一个 break 。
default(可选):一个 default 子句;如果给定,这条子句会在 expression 的值与任一 case 语句均不匹配时执行。default 可以放到 case 之间。
- switch (expr) {
- case '1':
- 变量a = 1;
- break;
- case '2':
- case '3':
- 变量a = 3;
- break;
- default:
- 变量a = 0;
- }
一个 switch 语句首先会计算其 expression 。然后,它将从第一个 case 子句开始直到寻找到一个其表达式值与所输入的 expression 的值所相等的子句(使用 严格运算符,===)并将控制权转给该子句,执行相关语句。(如果多个 case 与提供的值匹配,则选择匹配的第一个 case,即使这些 case 彼此间并不相等。)如果没有 case 子句相匹配,程序则会寻找那个可选的 default 子句,如果找到了,将控制权交给它,执行相关语句。若没有 default 子句,程序将继续执行直到 switch 结束。按照惯例,default 子句是最后一个子句,不过也不需要这样做。
可选的 break 语句确保程序立即从相关的 case 子句中跳出 switch 并接着执行 switch 之后的语句。若 break 被省略,程序会继续执行 switch 语句中的下一条语句。
如果你忘记添加break,那么代码将会从值所匹配的 case 语句开始运行,然后持续执行下一个 case 语句而不论值是否匹配。
§1.8 循环
循环可以将代码重复运行。
§1.8.1 for
用于创建一个循环,它包含了三个可选的表达式,三个可选的表达式包围在圆括号中并由分号分隔, 后跟一个在循环中执行的语句(通常是一个块语句)。
- for ([initialization]; [condition]; [final-expression])
- statement
initialization:一个表达式 (包含赋值语句) 或者变量声明。典型地被用于初始化一个计数器。该表达式可以使用var关键字声明新的变量。初始化中的变量不是该循环的局部变量,而是与for循环处在同样的作用域中。该表达式的结果无意义。
condition:一个条件表达式被用于确定每一次循环是否能被执行。如果该表达式的结果为true, statement 将被执行。 这个表达式是可选的。如果被忽略,那么就被认为永远为真。如果计算结果为假,那么执行流程将被跳到for语句结构后面的第一条语句。
final-expression:每次循环的最后都要执行的表达式。执行时机是在下一次condition的计算之前。通常被用于更新或者递增计数器变量。
statement:只要condition的结果为true就会被执行的语句。 要在循环体内执行多条语句,使用一个块语句({ ... })来包含要执行的语句。没有任何语句要执行,使用一个空语句(;)。
以下例子声明了变量i并被初始赋值为0,for语句检查i的值是否小于9,如果小于9,则执行语句块内的语句,并且最后将i的值增加 1。
- for (var i = 0; i < 9; i++) {
- 变量A = i;
- // more statements
- }
for语句的所有的表达式都是可选的
例如,初始化块中的表达式没有被指定:
- var i = 0;
- for (; i < 9; i++) {
- 变量A = i;
- // more statements
- }
像初始化块一样,条件块也是可选的。如果省略此表达式,则必须确保在循环体内跳出,以防创建死循环。
- for (var i = 0;; i++) {
- 变量A = i;
- if (i > 3) break;
- // more statements
- }
你当然可以忽略所有的表达式。同样的,确保使用了break语句来跳出循环并且还要修改(增加)一个变量,使得break 语句的条件在某个时候是真的。
- var i = 0;
- for (;;) {
- if (i > 3) break;
- 变量A = i;
- i++;
- }
for允许无语句,但循环条件后的分号是强制性的。如果没有分号,循环声明之后的行将被视为语句。
- var 变量B = 3,变量c = "笑";
- for (
- var 变量a = "哈";
- 变量B;
- 变量c += 变量a
- );
- var 变量D = "最终的变量c为:" + 变量c; //笑哈哈哈
- }
§1.8.2 for...in
for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。
- var obj = {a:1, b:2, c:3};
-
- for (var prop in obj) {
- console.log("obj." + prop + " = " + obj[prop]);
- }
- // Output:
- // "obj.a = 1"
- // "obj.b = 2"
- // "obj.c = 3"
§1.8.3 for...of
for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
- function* foo(){
- yield 1;
- yield 2;
- }
- for (let o of foo()) {
- console.log(o);
- // expected output: 1
- break; // closes iterator, triggers return
- }
§1.8.4 while
while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为真时结束循环。
- while (condition) {
- statement
- }
§1.9 函数
函数声明定义一个具有指定参数的函数。
- function 函数名a([参数名1,[,参数名2,[..., 参数名n]]]) {
- //do sth..
- }
参数是有限的。
你也可以使用 Function 构造函数和一个函数声明来定义函数。
- var 函数表达式 = function 函数名B([参数名1,[,参数名2,[..., 参数名n]]]) {
- //do sth..
- };
函数可以被有条件来声明,这意味着,在一个 if 语句里,函数声明是可以嵌套的。有的浏览器会将这种有条件的声明看成是无条件的声明,无论这里的条件是true还是false,浏览器都会创建函数。因此,它们不应该被使用。
默认情况下,函数是返回 undefined 的。想要返回一个其他的值,函数必须通过一个 return 语句指定返回值。
下面的例子定义了一个匿名函数并把它赋值给变量x。这个函数返回它参数的平方:
- var x = function(y) {
- return y * y;
- };
§1.9.1 使用函数
- function 函数1(参数1,参数2,参数3) {
- return 参数1 + 参数3 - 5 + 参数2 * 2;
- }
- 函数1(10,15,2);//37
§1.9.2 函数提升
JavaScript 中的函数声明被提升到了函数定义。你可以在函数声明之前使用该函数:
- hoisted(); // "foo"
- function hoisted() {
- console.log("foo");
- }
- /* equal to*/
- var hoisted;
- hoisted = function() {
- console.log("foo");
- }
- hoisted();
- // "foo"
注意 :函数表达式function expressions 不会被提升:
- notHoisted(); // TypeError: notHoisted is not a function
- var notHoisted = function() {
- console.log("bar");
- };
§1.9.3 命名函数表达式
如果你想在函数体内部引用当前函数,则需要创建一个命名函数表达式。然后函数名称将会且只会作为函数体(作用域内)的本地变量。
- var math = {
- 'factorial': function factorial(n) {
- if (n <= 1)
- return 1;
- return n * factorial(n - 1);
- }
- };
§1.x 内置对象
待更新,可自行参考MDN!
§1.x Classes
待更新,可自行参考MDN!
§1.x 其它
持续填坑中,尽请期待!
§2 Nukkit 模组加载器
阅读本章内容前你应该熟练掌握上一大章节的内容并对Java有所了解。
可在本插件的GitHub源码上查看所有可调用的方法。
请以ANSI格式保存文件,否则中文字符串将乱码。
§2.1 全局变量
你应该避免对这些内置变量进行重新赋值。
- /* 直接暴露 */
- var server; // 返回 Java 的 “getServer();”
- var plugin; // 返回 Java 的模组加载器插件的 “MainClass”
- var manager; // 返回 Java 的 “FunctionManager Class”, 用于创建命令或任务
- var logger; // 返回 Java 的控制台日记记录器
- var players; // 返回所有在线玩家
- /* 非直接暴露() */
- var me; // 返回触发者
- var level; // 返回世界
- var pos; // 返回坐标
§2.2 命令
创建命令
使用manager的createCommand()方法,有3个参数
- 命令
- 介绍
- 函数名
- manager.createCommand("test1", "测试1", "命令1");
- function 命令1(sender, args) { // 这个函数的第一个参数为命令执行者,第二个参数为执行命令附带的参数(数组)
- if(args.length < 1){ // 判断是否存在参数
- sender.sendMessage('亲,带个参数吧~'); // 给命令使用者发消息
- return; //返回
- }
- //如果存参数
- sender.sendMessage("你输入的第一个参数为:" + args[0]); // 给命令使用者发消息
- sender.sendMessage(
- manager.format("你输入的第一个参数为: %s", args[0]) // manager的format()方法用于格式化字符串,即“...{args[0]}”
- );
- }
§2.3 任务
§2.3.1 定时任务
创建Java定时任务
使用manager的createTask()方法,有2个参数
- 函数名
- 等待Tick
- manager.createTask("定时任务1", 6 * 20); // 1秒=20tick,所以这里就是等待6秒执行函数“定时任务1”
- function 定时任务1(currentTick) { // 这个函数的参数为当前的Tick
- print(currentTick); // 打印输出
- print('I tasknormal!');
- }
§2.3.2 循环任务
创建Java循环任务
使用manager的createLoopTask()方法,有2个参数
- 函数名
- 间隔Tick
- manager.createLoopTask("循环任务1", 9 * 20); // 1秒=20tick,所以这里就是每隔9秒执行函数“循环任务1”一次
- function 循环任务1(currentTick) { // 这个函数的参数为当前的Tick
- print(currentTick); // 打印输出
- print('I taskloop');
- }
§2.4 事件
所有的事件类型请在Nukkit事件大全查阅。
用事件名命名的函数监听事件,参数为此事件
- function PlayerJoinEvent(event) { // 监听玩家进服事件
- var player = event.getPlayer(); // 获取触发事件的玩家
- player.sendMessage("欢迎来到我的测试服务器!"); // 给玩家发送消息
- }
§2.5 配置
创建配置
使用manager的createConfig()方法,有2个参数
- 文件
- 类型
- var config = manager.createConfig(
- manager.getFile("测试文件夹", "配置.yml"), // manager的getFile()方法用于获取文件,第一个参数是文件夹,第二个参数是文件名
- 2); // 2为YAML语言
- config.set("键", "值"); // 设置配置
- config.save(); // 保存配置
§3 练习
暂无
§4 作品
你可以在js文件头部使用注释添加版权等信息
- /*
- * auther: @wode490390
- * 采用GPLv3开源许可证
- */
§4.1 混淆
你可以使用工具对代码进行加密,使抄袭者阅读不便
如源代码:
- function main(player,info) {
- player.sendMessage(info+(info*info-info));
- }
- main(wode490390,233);
- main(093094edow,666);
混淆后:
- function a(b,c) {
- b.sendMessage(c+(c*c-c));
- }
- a(wode490390,233);
- a(093094edow,666);
§4.2 加密
如源代码:
- function main(player,info) {
- player.sendMessage(info+(info*info-info));
- }
- main(wode490390,233);
- main(093094edow,666);
加密后:
- 持续更新中
§4.3 压缩
你可以使用一些工具进行压缩,简化代码
- 持续更新中
§4.4 发布
恭喜你学完了本教程,快去制作属于自己的NK模组吧,如有更新记得回来看看哦。
来看看其他人写的模组吧(按优秀程度排序,欢迎广大版友提供)
- 暂无
JS文件吗! JS不是Nukkit吧 是原版MOD
逗比的幻影 发表于 2018-8-17 06:53
JS文件吗! JS不是Nukkit吧 是原版MOD
nk的js模组,详见此插件:http://www.mcbbs.net/thread-813311-1-1.html
wode490390 发表于 2018-8-17 07:06
nk的js模组,详见此插件:http://www.mcbbs.net/thread-813311-1-1.html
类似于以前modpe的样子……
/..................../.............................../
感觉比NK插件简单多了
xiaozha13 发表于 2018-8-17 13:08
感觉比NK插件简单多了
这是肯定的
之前不知道为什么不能说话,早就很想问了,对于小部分的js,例如红石js,自定义生物js可以实现吗
堕落落 发表于 2018-8-21 10:23
之前不知道为什么不能说话,早就很想问了,对于小部分的js,例如红石js,自定义生物js可以实现吗 ...
nk不支持红石与生物,需要额外插件实现
wode490390 发表于 2018-8-21 13:09
nk不支持红石与生物,需要额外插件实现
我并不是说原版的红石,而是用js实现的红石,自定义方块换成原版的方块,能实现吗
堕落落 发表于 2018-8-24 18:58
我并不是说原版的红石,而是用js实现的红石,自定义方块换成原版的方块,能实现吗 ...
如果你是红石大神又精通js,可以试一试
wode490390 发表于 2018-8-24 19:22
如果你是红石大神又精通js,可以试一试
很早之前不是已经有过红石插件js了吗
堕落落 发表于 2018-8-29 15:18
很早之前不是已经有过红石插件js了吗
那个是bl的,不是nk的
wode490390 发表于 2018-8-29 16:23
那个是bl的,不是nk的
你不是转载了一个模组加载器的插件吗
堕落落 发表于 2018-8-29 23:58
你不是转载了一个模组加载器的插件吗
这玩意新出的,哪有红石js