Skip to content

函数

HS 具有一定的函数式特征,支持定义匿名函数、立即调用函数、高阶函数等。

具名函数(函数对象的声明)

你可以用以下语句定义和调用一个具名函数:

js
// 定义
function hello(msg) {
  msg = "Hello " + msg
  console.log(msg)
}

// 调用
hello("World!")

这个语句实质上是在当前上下文对象上定义了一个名为 hello、值为函数的成员。

INFO

HS 不支持如下的函数定义表达式:

js
function() {...}

它在 HS 中可以被箭头函数完全替代。

箭头函数(函数对象)

你可以用以下表达式定义和调用一个匿名函数:

js
// 定义
const hello = (msg) => {
  msg = "Hello " + msg
  console.log(msg)
}

// 调用
hello("World!")

INFO

与 JS 不同,HS 中用箭头函数语法和具名函数语法定义的函数的特性没有任何区别,这意味着它们都:

  • 在调用上下文拥有自己的 thissuper
  • 可以被 new 调用
  • 会捕获自己被定义时的上下文对象的引用

因此,你可以将箭头函数视作一种对象字面量,而 function 语句则是将一个函数对象字面量声明入上下文的语法糖。

不定形参

可以在函数的参数声明中用以下方式声明一个不定形参:

js
function add(a, ...b) {
  return a + b.sum()
}

// 输出 45
console.log(add(1, 2, 3, 4, 5, 6, 7, 8, 9))

不定形参 c 在函数体内部将被声明为一个数组,其中包含了所有不在普通参数列表中的"剩余参数"。

也就是说,以下方式其实与上方的写法效果相同:

js
function add(a, b) {
  return a + b.sum()
}

// 输出 45
console.log(add(1, [2, 3, 4, 5, 6, 7, 8, 9]))

不定形参只能作为函数参数列表的最后一个参数出现,类似以下的写法是非法的:

js
// 解析时报错
function add(a, ...b, c) {
}

参数字典调用法

假设有以下函数:

js
function add(a, b, c) {
  console.log("a = " + a)
  console.log("b = " + b)
  console.log("c = " + c)
}

除了传统的函数调用法:

js
add(1, 2, 3)

你还可以使用以下方式调用函数:

js
// 以下两种调用方式
// 输出的的结果完全相同
add{a=1, b=2, c=3}
add{b=2, a=1, c=3}

以上三个函数调用输出的结果都是:

js
a = 1
b = 2
c = 3

对于没有编程基础用户来说,这种调用法更加易于理解,同时在缺乏 IDE 支持的情况下更具可读性。

这种调用法也是在模仿 MythicMobs 等插件所用的脚本语言的函数调用风格。

使用参数字典调用法时,若函数包含 不定形参,则多余的参数也会被填入不定形参的数组中,如:

js
function test(a, b, ...c) {
  console.log("a = " + a)
  console.log("b = " + b)
  console.log("c = " + c)
}

test{a=1, b=2, c=3, d=4, e=5, f=6}

以上代码的输出为:

js
a = 1
b = 2
c = [3, 4, 5, 6]

参数默认值

定义函数对象时,可以在参数列表中为参数指定默认值,例子如下:

js
function test(arg1 = 1) {
  console.log(arg1)
}

// 输出 1
test()
// 输出 Hi
test("Hi")

函数默认值是在函数被调用时动态、从左到右地计算并被注入上下文的,这意味着你可以让参数的默认值参与它之后的参数的默认值的计算:

js
const test = (a, b, c = a) => {
  console.log("a = " + a)
  console.log("b = " + b)
  console.log("c = " + c)
}

test(1, 2)

以上函数的输出是:

js
a = 1
b = 2
c = 1

不完整的参数

HS 的函数签名只包含自己被引用的变量名,而不包含其参数列表,也就是说,无论提供多少参数,一个函数都能成功进入调用阶段。对于未提供(且未指定默认值)的参数,默认赋值为 null。一个例子如下:

js
const func = (a, b, c) => {
  console.log("a = " + a)
  console.log("b = " + b)
  console.log("c = " + c)
}

func{a=1, b=2}

以上函数的输出是:

js
a = 1
b = 2
c = null

因此,最佳实践是在函数开头检查每个无默认值的参数是否为 null 并预先处理。

高阶函数(柯里化)

返回一个函数的函数或接受函数作为参数的函数都可以被称为高阶函数,而柯里化则是"返回一个函数的函数"的具体应用。

以下是一些例子:

js
// 返回一个函数的函数
// 也可以被称为柯里化函数
const div = (a) => {
  return (b) => {
    return a / b
  }
}

// 调用,输出 2
console.log(div(10)(5))
js
// 接受函数作为参数的函数
const task = (callback) => {
  const result = 8
  callback(8)
}

// 输出 8
task((result) => console.log(result))

INFO

这种机制也被称为可以捕获上下文中变量的闭包。

立即调用的函数

你可以在声明后立即调用一个函数对象:

js
(() => console.log("test"))()

带标签的模板字符串

可以用一个模板字符串字面量作为操作符调用一个函数:

js
const sum = (strings, ...values) => {
  return values.sum()
}
sum`Hello ${-2} ${8 ** 2} ${3 - 1}` == 64

上述 sum 函数收到的参数实际上是两个数组(其中一个是不定参数),第一个数组储存了这个模板字符串中所有字符串常量的部分的顺序排列,第二个数组储存了所有插值表达式 ${} 对应的值的顺序排列。