Google 表格提供数百种内置函数,例如 AVERAGE
、SUM
和 VLOOKUP
。当这些功能都无法满足您的需求时,您可以使用 Google Apps 脚本编写自定义函数(例如,将米换算为英里或从互联网提取实时内容),然后像内置函数一样在 Google 表格中使用这些函数。
使用入门
自定义函数是使用标准 JavaScript 创建的。如果您是 JavaScript 新手,Codecademy 为初学者提供了绝佳的课程。(注意:本课程并非由 Google 开发,与 Google 无关。)
以下是一个名为 DOUBLE
的简单自定义函数,该函数将输入值乘以 2:
/**
* Multiplies an input value by 2.
* @param {number} input The number to double.
* @return The input multiplied by 2.
* @customfunction
*/
function DOUBLE(input) {
return input * 2;
}
如果您不知道如何编写 JavaScript,也没有时间学习,请查看插件商店,看看其他人是否已构建了您所需的自定义函数。
创建自定义函数
如需编写自定义函数,请执行以下操作:
- 在 Google 表格中创建或打开电子表格。
- 依次选择扩展程序 > Apps 脚本菜单项。
- 删除脚本编辑器中的所有代码。对于上面的
DOUBLE
函数,只需将代码复制并粘贴到脚本编辑器中即可。 - 点击顶部的“保存”图标 。
现在,您可以使用自定义函数了。
从 Google Workspace Marketplace获取自定义函数
Google Workspace Marketplace 以 Google 表格插件的形式提供了多个自定义函数。如需使用或探索这些插件,请执行以下操作:
- 在 Google 表格中创建或打开电子表格。
- 点击顶部的插件 > 获取插件。
- Google Workspace Marketplace 打开后,点击右上角的搜索框。
- 输入“自定义函数”,然后按 Enter 键。
- 如果您找到了感兴趣的自定义函数插件,请点击安装进行安装。
- 系统会显示一个对话框,告诉您该插件需要授权。在这种情况下,请仔细阅读通知,然后点击允许。
- 您可以在电子表格中使用插件。如需在其他电子表格中使用该插件,请打开另一个电子表格,然后点击顶部的插件 > 管理插件。找到要使用的插件,然后依次点击“选项”图标 > 在本文档中使用。
使用自定义函数
在您编写自定义函数或从Google Workspace Marketplace安装自定义函数后,它可以像内置函数一样易用:
- 点击您想使用该函数的单元格。
- 输入等号 (
=
),后跟函数名称和任意输入值(例如=DOUBLE(A1)
),然后按 Enter 键。 - 该单元格将暂时显示
Loading...
,然后返回结果。
自定义函数指南
在编写自己的自定义函数之前,需要了解一些准则。
命名
除了为 JavaScript 函数命名的标准惯例外,请注意以下事项:
- 自定义函数的名称必须与内置函数的名称不同,如
SUM()
。 - 自定义函数的名称不能以下划线 (
_
) 结尾,后者表示 Apps 脚本中的私有函数。 - 自定义函数的名称必须使用语法
function myFunction()
(而不是var myFunction = new Function()
)声明。 - 大小写形式无关紧要,尽管电子表格函数的名称通常都是大写的。
参数
与内置函数一样,自定义函数也可以接受参数作为输入值:
- 如果您通过将单个单元格的引用作为参数(如
=DOUBLE(A1)
)调用函数,该参数将是该单元格的值。 如果您通过将一系列单元格的引用作为参数(如
=DOUBLE(A1:B10)
)调用函数,则该参数将是单元格值的二维数组。例如,在下面的屏幕截图中,Apps 脚本会将=DOUBLE(A1:B2)
中的参数解读为double([[1,3],[2,4]])
。请注意,上述的DOUBLE
示例代码需要修改为接受数组作为输入。自定义函数参数必须是确定性的。也就是说,不得将每次计算时返回不同结果的内置电子表格函数(例如
NOW()
或RAND()
)作为自定义函数的参数。如果自定义函数尝试根据其中一个易失性内置函数返回值,它将无限期显示Loading...
。
返回值
每个自定义函数都必须返回一个要显示的值,例如:
- 如果自定义函数返回一个值,该值将显示在调用该函数的单元格中。
- 如果自定义函数返回一个二维值数组,则只要相邻单元格为空,值就会溢出到相邻单元格中。如果这会导致数组覆盖现有的单元格内容,则自定义函数会改为抛出错误。如需查看示例,请参阅优化自定义函数部分。
- 自定义函数不能影响除了为其返回值的单元格。换言之,自定义函数无法修改任意单元格,只能修改从中调用它的单元格及其相邻单元格。如需修改任意单元格,请改用自定义菜单运行函数。
- 自定义函数调用必须在 30 秒内返回。否则,单元格将显示错误:
Internal error executing the custom function.
数据类型
Google 表格会根据数据的性质以不同的格式存储数据。当这些值用于自定义函数时,Apps 脚本会将其视为 JavaScript 中的相应数据类型。以下是最常见的混淆领域:
- Google 表格中的时间和日期在 Apps 脚本中变为 Date 对象。如果电子表格和脚本使用不同的时区(这种情况很少出现),则需要使用自定义函数进行补偿。
- Google 表格中的时长值也会变为
Date
对象,但处理时长值可能会很复杂。 - Google 表格中的百分比值在 Apps 脚本中会变为十进制数。例如,在 Apps 脚本中,值为
10%
的单元格会变为0.1
。
自动补全
与内置函数类似,Google 表格支持自定义函数的自动补全功能。当您在单元格中输入函数名称时,您会看到与您输入的内容匹配的内置和自定义函数列表。
如果自定义函数的脚本包含 JsDoc
@customfunction
标记(如下面的 DOUBLE()
示例所示),则自定义函数会显示在此列表中。
/**
* Multiplies the input value by 2.
*
* @param {number} input The value to multiply.
* @return The input multiplied by 2.
* @customfunction
*/
function DOUBLE(input) {
return input * 2;
}
高级
使用 Google Apps 脚本服务
自定义函数可以调用某些 Google Apps 脚本服务来执行更复杂的任务。例如,自定义函数可以调用 Language 服务来将英语短语翻译成西班牙语。
与大多数其他类型的 Apps 脚本不同,自定义函数绝不会要求用户授权访问个人数据。因此,它们只能调用无权访问个人数据的服务,具体而言:
支持的服务 | 备注 |
---|---|
缓存 | 适用于自定义函数,但不是特别有用 |
HTML | 可以生成 HTML,但无法显示它(很少有用) |
JDBC | |
语言 | |
锁定 | 适用于自定义函数,但不是特别有用 |
地图 | 可以计算路线,但无法显示地图 |
媒体资源 | getUserProperties() 只能获取电子表格所有者的属性。电子表格编辑者无法在自定义函数中设置用户属性。 |
电子表格 | 只读(可以使用大多数 get*() 方法,但不能使用 set*() )。无法打开其他电子表格( SpreadsheetApp.openById() 或 SpreadsheetApp.openByUrl() )。 |
网址提取 | |
实用程序 | |
XML |
如果您的自定义函数抛出错误消息 You do not have permission to
call X service.
,则表示该服务需要用户授权,因此无法在自定义函数中使用。
如需使用上文未列出的服务,请创建运行 Apps 脚本函数的自定义菜单,而不是编写自定义函数。从菜单触发的函数会在必要时要求用户授权,以便可以使用所有 Apps 脚本服务。
分享
自定义函数最初会绑定到创建它们时使用的电子表格。这意味着,除非您使用以下方法之一,否则在一个电子表格中编写的自定义函数将无法在其他电子表格中使用:
- 点击扩展程序 > Apps 脚本打开脚本编辑器,然后从原始电子表格中复制脚本文本,并将其粘贴到另一个电子表格的脚本编辑器中。
- 依次点击文件 > 复制,创建包含自定义函数的电子表格的副本。复制电子表格时,系统会同时复制该电子表格附带的所有脚本。有权访问电子表格的任何人都可以复制脚本。(仅拥有查看权限的协作者无法在原始电子表格中打开脚本编辑器。但是,当他们创建副本后,他们便会成为副本的所有者,并可以看到此脚本。)
- 将脚本发布为 Google 表格编辑器插件。
优化
每当在电子表格中使用自定义函数时,Google 表格都会单独调用 Apps 脚本服务器。如果您的电子表格包含数十(或者数百或数千!)自定义函数调用,此过程可能会很慢。
因此,如果您计划对大量数据多次使用自定义函数,请考虑修改该函数,使其以二维数组的形式接受范围作为输入,然后返回可以溢出到相应单元格的二维数组。
例如,可以将上面显示的 DOUBLE()
函数重写为接受单个单元格或单元格范围,如下所示:
/**
* Multiplies the input value by 2.
*
* @param {number|Array<Array<number>>} input The value or range of cells
* to multiply.
* @return The input multiplied by 2.
* @customfunction
*/
function DOUBLE(input) {
return Array.isArray(input) ?
input.map(row => row.map(cell => cell * 2)) :
input * 2;
}
上述方法使用 JavaScript 的 Array
对象的 map 方法,以递归方式对二维单元格数组中的每个值调用 DOUBLE
。它会返回一个包含结果的二维数组。通过这种方式,您只需调用 DOUBLE
一次,但让其一次计算大量单元格,如以下屏幕截图所示。(您可以使用嵌套的 if
语句而不是 map
调用来实现相同的目的。)
同样,以下自定义函数可高效从互联网提取实时内容,并且只需一次函数调用即可使用二维数组显示两列结果。如果每个单元格都需要自己的函数调用,那么相应操作所需的时间会大大增加,因为 Apps 脚本服务器每次都必须下载并解析 XML Feed。
/**
* Show the title and date for the first page of posts on the
* Developer blog.
*
* @return Two columns of data representing posts on the
* Developer blog.
* @customfunction
*/
function getBlogPosts() {
var array = [];
var url = 'https://gsuite-developers.googleblog.com/atom.xml';
var xml = UrlFetchApp.fetch(url).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement();
var atom = XmlService.getNamespace('http://www.w3.org/2005/Atom');
var entries = document.getRootElement().getChildren('entry', atom);
for (var i = 0; i < entries.length; i++) {
var title = entries[i].getChild('title', atom).getText();
var date = entries[i].getChild('published', atom).getValue();
array.push([title, date]);
}
return array;
}
虽然实现细节因函数的行为而异,但这些技巧几乎可以应用于整个电子表格中重复使用的任何自定义函数。