【杂谈】Koishi插件教程 #5 编程基础与插件写作

asakurasayori 发布于 2025-10-06 307 次阅读


虽然说Koishi的插件市场已经十分丰富,能够满足我们大部分的需求,但仍然存在一些个性化的功能,需要我们自己动手写作,丰衣足食。所以接下来这几节会教授编程的基础以及怎么进行插件开发。

编程基础

接下来的部分会讲的非常的详尽,大抵是不用担心听不懂。如果实在觉得存在困难,可以在同好群中寻求帮助或者联系群主或者管理一对一辅导,以及也可以前往Runoob JS教程进行学习,上面有更加详尽的教程。

背景知识

我们现在需要了解的编程背景知识是:

  • 我们将使用Javascript以及Typescript进行编程。
  • Typescript是Javascript的一个超集,支持更加丰富的功能,同样也意味着Javascript的代码交给Typescript完全能运行。底层原理是,Typescript的编译器会将Typescript代码翻译成Javascript代码再执行。
  • 之后Typescript简称TS,Javascript简称JS。
  • JS是一门面向对象语言,程序运行的底层逻辑更加关心对象之间的关联、从属、继承关系,从而达到完成任务的目的。相反的,面向过程语言(比如说C语言)关心的是一件事可以拆解成若干个步骤去完成。

环境配置

我们可以先在电脑上安装一下编程环境,包括Node.js和任何一个支持Javascript的IDE,这里推荐VS Code。

我们打开VS Code(下简称vsc)后会出现如上界面。我们直接点击File->Open Folder,在弹出的窗口中创建一个文件夹用来学习JS基础。

出现以上窗口时,勾选并点击蓝色按钮即可。

上图为我们开发时界面。左侧是资源管理器,用来查看我们存放代码和运行时环境的文件夹,中间是我们写代码的地方,右侧是Copilot,一个专门为编程服务的AI助手。如果需要使用Copilot可以根据上图Welcome页的指引,或者寻求群友或管理的帮助。

JS基础

接下来关闭Welcome页与Copilot,右键资源管理器,点击New File,名称叫做index.js,其中index是文件名,js是JS的源码文件后缀。

初识 JS 之 Hello World

我们输入以下代码:

console.log("Hello World!")

随后按Ctrl+S(Mac下为Command+S)保存文件

点击Terminal->New Terminal新建终端

输入并回车,node指令表示运行JS源代码文件:

node index.js

我们会发现在控制台终端中输出了 Hello World。代码中console.log是一个函数。函数在之前的文章中提到过,是封装了某些功能的工具。而这个函数的作用是在控制台中输出一段文本,括号中代表了函数的参数,参数是由逗号进行隔开的。通常来说,函数会规定它的参数个数,有可能是一个,或者有限个,也有可能是无限个。console.log可以接受无限个参数,表示需要输出的内容,他们在控制台中会以空格进行分割,如下图:

JS 速通 #1

接下来会以尽量清晰,以及尽量短的篇幅介绍我们目前来说最常用的JS功能。

变量的声明

let a=5
console.log(a)

let b=a+3
console.log(a+b+4)

a="你好"
console.log(a)

运行结果是:

PS D:\code\js-learning> node index.js
5
17
你好

上面即为变量的声明方式。变量使用let 来声明,我们可以使用let =[初始值]来对变量进行初始化。其中 = 为赋值符号,请留意这一点,意义为将右侧的值(简称右值或者rv)复制到左侧的变量(简称左值或者lv)上。变量名只可以包含字母数字下划线,并且只能以下划线或者字母开头。

变量有许多类型,在JS中有数字(Number),字符串(String),数组(Array),对象(Object),函数(Function),布尔(Boolean,储存真或者假的类型),空(Null),未定义(Undefined)等等。而以上的一切类型都继承于Object,可以说对象是整个JS的基础,后面会详细教学面向对象编程。

JS拥有动态类型,你可以一个变量初始化为一个类型,比如说数字,后续可以赋值为任何你想要的类型。

可以用typeof获取变量的数据类型,下面例子引用自Runoob:

typeof "John"                // 返回 string
typeof 3.14                  // 返回 number
typeof false                 // 返回 boolean
typeof [1,2,3,4]             // 返回 object
typeof {name:'John', age:34} // 返回 object 这是Javascript的设计缺陷,这其实是array

变量的运算 & 注释的使用

JS有许多类型的运算符,比如 = + - * / ( ),这些是进行运算的。其中 + 比较特殊,比如说,我们不仅可以对数字进行相加,比如说1+2=3,还可以对字符串进行相加,比如说"1"+"2"="12";还有一些如==  !=  是对值进行比较的,比如== 为判断相等,!= 为判断不等。所以再次声明,需要注意运算符=和运算符==的区别!下面为运算符以及注释的示例:

//用两个斜杠可以写单行注释
let a=1+2 //也可以跟在行结尾
// 以及我们习惯性会在双斜杠后面跟一个空格以达到美观

// 空行不会影响代码运行
/*
这是多行注释
这是多行注释
*/

// 接下来会在log语句后写这一行运行的答案以便对照
console.log(a) // 3
a="abc"+'abc'
console.log(a) // 'abcabc'
a=1==2
console.log(a) // false
let 中文=1 // 不要写中文名称的变量!!!

常量

常量顾名思义,不可改变的量。我们可以用常量去储存一些常用字面量,比如说在计算圆的面积时可以声明一个PI常量:

const PI=3.14 // 常量的声明
let r=5
console.log(PI*r**2) // 78.5
// 其中**是幂运算符

字符串

"abc"
'abc' // 单引号双引号引起来都是字符串
"'abc'" // 表示字符串'abc' 可以用A引号包含B引号来在字符串中包含B引号
// 不要使用中文引号,无法识别
let a=30
let b=`老王今年:
${a}`
console.log(b) // 老王今年30
// 可以用反引号(即大键盘数字键左侧)包含表达式(比如说一个变量)以及可以包含换行
console.log(b.length) // 6 这是字符串长度
console.log("\n") // 这是换行符\n

函数

function cmp(a,b){
    return a<b;
}
/*
具名函数声明格式
function 函数名(参数){
    函数体
}

return 代表函数的返回值
*/
console.log(cmp(1,2)) // true

let func=function(a,b){
    console.log(a,b);
}
/*
具名函数也可以这样声明
函数也可以没有返回值
*/

let func1=(a,b)=>{
    console.log(`这是匿名函数: ${a+b}`)
}
// 匿名函数又称箭头函数,格式为: (参数)=>{函数体}
// 匿名函数在代码写作方面是一种简便写法
// 不要在对象中使用箭头函数,否则将不能使用this,后面会讲解

对象

对象描述一个事物的属性和方法。

let person = {
    name: "张三",
    age: 18,
    speak: function(){
        console.log(`我叫
        ${this.name}`);
        // 语句后 可以且推荐 跟一个分号,这是别的语言留下的好习惯
    }
};

console.log(person.name); // '张三'
console.log(person["age"]); // 18
let varName = "age"
console.log(person[varName]); // 18
person.speak(); // '我叫张三'
console.log(person.speak); // [Function: speak] 在浏览器环境中会有不同的行为

我们使用大括号包含一些属性和方法(函数),这些属性可以是数字,字符串等等。其中,this指向外层定义域,比如说上面这个例子的this指向的就是person,所以输出的是person.name的值。

访问对象的属性有两种方法,一个是静态访问,比如说person.name,即在代码中写死需要访问的属性名;一个是动态访问,比如说可以根据字面量或者变量的值来确定访问的是什么属性,如上面例子中的person[varName]。

访问对象的方法,加了括号表示调用,会返回这个函数的返回值;不加括号会将一个定义函数的字符串返回。

条件语句

let x = 2;

// if / else if / else:按条件分支执行
if (x > 3) console.log("大");
else if (x === 3) console.log("等于3");
else console.log("小");

// switch:多分支判断,匹配 case 值
switch (x) {
  case 1: console.log("一"); break;   // break 跳出 switch
  case 2: console.log("二"); break;
  default: console.log("其他");
}

循环语句

// for(初始化; 条件; 更新)
// 三个部分用分号隔开:
// 1. 初始化:循环开始前执行一次(如定义计数变量)
// 2. 条件:每次循环前判断是否继续
// 3. 更新:每次循环体执行完后执行(通常 i++)
for (let i = 0; i < 3; i++) console.log(i); // 输出 0 1 2

// for...in:遍历对象的键(属性名)
let obj = {a:1, b:2};
for (let key in obj) console.log(key); // a b

// for...of:遍历可迭代对象的值(数组、字符串等)
let arr = [10, 20, 30];
for (let v of arr) console.log(v); // 10 20 30

// break:立即结束整个循环
// continue:跳过本次循环,继续下一次
for (let i = 0; i < 5; i++) {
  if (i === 2) continue; // 跳过 2
  if (i === 4) break;    // 停止循环
  console.log(i);        // 输出 0 1 3
}

// while(条件)
// 先判断条件,true 时执行循环体;false 时结束循环
let i = 0;
while (i < 3) {           // 判断 i<3
  console.log(i);         // 输出 0 1 2
  i++;                    // 更新变量,防止死循环
}

// do...while(条件)
// 先执行一次循环体,再判断条件(至少执行一次)
let j = 0;
do {
  console.log(j);         // 先执行,再判断
  j++;
} while (j < 3);          // 条件为 true 继续,false 结束

类型强制转换

// 类型转换:不同类型间自动或手动变换

// 自动转换(隐式)
console.log(1 + "2");   // "12" 数字转字符串
console.log("5" * 2);   // 10 字符串转数字

// 手动转换(显式)
console.log(Number("3")); // 3 转数字
console.log(String(4));   // "4" 转字符串
console.log(Boolean(0));  // false 转布尔

作用域

作用域:变量能被访问的范围。

  • 全局作用域:任何地方都能访问。
  • 函数作用域:函数内部定义的变量只在函数内有效。
  • 块级作用域:letconst{} 内有效。
  • 作用域链:访问变量时,逐层向外查找。
let a = 1; // 全局作用域

function f() {         // 函数作用域
  let b = 2;
  if (true) {
    let c = 3;         // 块级作用域
    console.log(a, b, c); // 1 2 3
  }
  // console.log(c); // 报错:c 未定义
}

f();
// 作用域链

let a = 1;

function f1() {
  let a = 2;     // 覆盖外层同名变量
  function f2() {
    let a = 3;   // 再次覆盖
    console.log(a); // 输出 3,优先使用最近的作用域
  }
  f2();
}

f1();
console.log(a); // 输出 1,全局的 a 不受影响

最后

作者困了,速通 #2 明天再写。若有笔误请海涵,熬夜脑子不太清醒。

此作者没有提供个人介绍。
最后更新于 2025-10-06