资源共享
如你所知,图书馆 https://developers.google.com/apps-script/guides/libraries#resource_scoping有共享和非共享资源。PropertiesService
列在下面非共享资源,意味着图书馆拥有its own引用服务时所访问的服务实例在库代码中.
const getStore = () => PropertiesService.getScriptProperties();
如果上面的函数在库中声明,它将使用库的资源(如果在调用脚本中) - 它自己的实例。
V8运行时解决方案
V8运行时不创建特殊的上下文 https://developers.google.com/apps-script/guides/v8-runtime/migration#adjust_handling_of_global_this用于您的代码,并让您可以直接访问内置服务。因此,在使用运行时时,可以通过简单地定义或替换全局上的属性来注入服务this
:
//in the library;
var getProperty = ((ctxt) => (key) => {
var service = ctxt.injectedService;
var store = service.getScriptProperties();
return store.getProperty(key);
})(this);
var setProperty = ((ctxt) => (key, val) => {
var service = ctxt.injectedService;
var store = service.getScriptProperties();
return store.setProperty(key, val);
})(this);
var inject = ((ctxt) => (service) => ctxt.injectedService = service)(this);
var greet = ((ctxt) => () => {
var store = ctxt.injectedService.getScriptProperties();
return store.getProperty("greeting") || "Ola!";
})(this);
//in the calling script;
function testSharedResources() {
PropertiesService.getScriptProperties().setProperty("greeting", "Hello, lib!");
$.inject(PropertiesService);
Logger.log($.greet()); //Hello, lib!
$.setProperty("greeting", "Hello, world!");
Logger.log($.greet()); //Hello, world!
}
在某些全球范围内this
将undefined
(我在将库添加到绑定脚本时遇到了这个问题)。在这种情况下,只需定义一个私有全局命名空间(以避免泄漏到调用者脚本):
//in the library;
var Dependencies_ = {
properties : PropertiesService
};
var use = (service) => {
if ("getScriptProperties" in service) {
Dependencies_.properties = service;
}
};
//in the calling script;
$.use(PropertiesService);
Rhino运行时解决方案
另一方面,较旧的 Rhino 运行时会创建一个特殊的隐式上下文。这意味着您有无法访问到内置服务或全局this
。您唯一的选择是绕过调用库中的服务(您的方法 #3 非常适合这样做)。
问题
- 为什么前两种方法不行,第三种方法却行呢?
您的方法的所有问题都归结为:
- 资源共享(图书馆有自己的服务实例)
- 特殊的隐式上下文(Rhino 中无法从外部访问内置库)
但有一个问题:所有 3 种方法do work按设计。
一、方法一确实有效如果您特别引用PropertiesService
on $
。这是有道理的,因为库作为名称空间包含在内,其中的成员映射到库中的全局声明。例如:
//in the caller script
PropertiesService.getScriptProperties().setProperty("test", "test");
$.PropertiesService = PropertiesService;
Logger.log( $.PropertiesService.getScriptProperties().getProperty("test") ); // "test"
Logger.log( $.getProperty("test") ); // "null"
//in the library
function getProperty(key) {
var store = PropertiesService.getScriptProperties();
return store.getProperty(key);
}
方法二也有效。调用者脚本中的函数绑定不会改变调用时的事实在图书馆它接收库上下文,但如果您直接在调用脚本中调用绑定副本,它会起作用:
//in the caller script
PropertiesService.getScriptProperties().setProperty("test", "test");
var bound = $.PropertiesService.getScriptProperties.bind(PropertiesService);
var obj = { getScriptProperties : bound };
$.PropertiesService = obj;
Logger.log( bound().getProperty("test") ); // "test"
Logger.log( $.getProperty("test") ); // "null"
现在,为什么第三种方法可以开箱即用?由于封装函数捕获了闭包PropertiesService
of the 调用脚本并应用getScriptProperties
方法。为了显示:
//in the caller script
var appl = {
getScriptProperties : (function(val) {
return function() {
return val.apply(PropertiesService);
};
})(PropertiesService.getScriptProperties)
};
$.PropertiesService = appl;
Logger.log( $.getProperty("test") ); // "test"
- 我可以期望它继续发挥作用吗?
是和不是。是的,因为你的_mock
函数行为在所有情况下都表现出预期的行为。没有为什么apply
依赖于getScriptProperties
没有被实现为箭头函数,其中this
覆盖将被忽略。
- 有没有更好的方法让库访问主脚本的属性?
对于 Rhino 运行时 - 不这么认为。对于 V8 - 直接注入服务就足够了。