【问题标题】:How to iterate through all properties/functions of a global object(library) in v8?如何遍历 v8 中全局对象(库)的所有属性/函数?
【发布时间】:2021-11-19 09:21:25
【问题描述】:

Google 应用程序脚本提供了一个library feature,如果您在其中包含项目密钥,则会添加一个库作为全局对象。我正在寻找迭代添加库的所有功能。这在带有for...in 循环的 引擎中曾经是可能的。但我无法遍历 引擎中库的任何属性。

documentation

在 V8 运行时中,项目及其库在不同的执行上下文中运行,因此具有不同的全局和原型链。

谁能解释这个对象是如何创建的或如何访问它的所有属性?

项目A:

function testLib(prop = 'main') {
  const isEnumerable = MyLibrary.propertyIsEnumerable(prop);
  const isOwnProperty = MyLibrary.hasOwnProperty(prop);
  console.info({ isEnumerable, isOwnProperty }); // { isEnumerable: false, isOwnProperty: true }
  console.info(prop in MyLibrary);//true
  for (const property in MyLibrary) {
    //loop doesn't start
    console.info(property);
  }
  console.info(Object.getOwnPropertyDescriptor(MyLibrary, prop)); //logs expected  data:
  /*{ value: [Function: main],
  writable: true,
  enumerable: false,
  configurable: true }*/
  console.log(Object.getOwnPropertyDescriptors(MyLibrary)); //actual: {} Expected: array of all functions including `main`
  MyLibrary.a = 1;
  console.log(Object.getOwnPropertyDescriptors(MyLibrary)); //actual: {a:1} Expected: array of all functions including `main`
}

function testPropDescriptors() {
  const obj = { prop1: 1, b: 2 };
  console.log(Object.getOwnPropertyDescriptors(obj)); //logs expected data
  /*{prop1: { value: 1, writable: true, enumerable: true, configurable: true },
  b: { value: 2, writable: true, enumerable: true, configurable: true } }*/
}

我的图书馆(项目 B):

function main(){}
function onEdit(){}

为了重现,

  • 通过clicking here 创建一个新项目 - 比如项目 A
  • Create a another script project(比如项目 B):
    • 在项目 B 中添加一个名为 main 的函数并
    • Deploy it 点击右上角的部署。
    • add it's key 在项目 A 中并将其命名为 MyLibrary
  • 将以上脚本复制粘贴到项目A中,选择testLib函数点击运行

【问题讨论】:

  • 我怀疑 ProxyownKeys trap 实现已损坏
  • @Bergi 谢谢。无论如何要测试你的理论?显然,instanceof 不能用于测试MyLibrary 是否为proxy。即使是Proxy,你有没有看到任何迭代的方法?
  • 虽然我不确定我是否能正确理解您的问题,但我提出了 2 个解决方法,用于从客户端检索库端的属性和函数。你能确认一下吗?如果我误解了您的问题并且没有用,我深表歉意。
  • @TheMaster 你能得到脚本的调试连接吗?调试器应该能够判断它是否是代理(或者,在给定环境的情况下,更可能是具有在本机代码中实现的相同行为的本机对象)。
  • @Bergi 有一个调试器,它将MyLibrary 列为全局对象,例如:MyLibrary:{},带有“扩展”对象选项。但是“展开”按钮什么也不做(不像其他全局对象,如 Array:{}Proxy:{},它展开以显示对象属性)。

标签: rhino v8 javascript google-apps-script libraries v8 global-object


【解决方案1】:

问题和解决方法:

我一直在寻找在启用 V8 的情况下从客户端直接检索库端的属性和函数的方法。但不幸的是,我仍然找不到它。因此,就我而言,我使用了 2 种解决方法。

  1. 使用 Apps Script API 和/或 Drive API 检索所有脚本。

  2. 将属性和函数包装在一个对象中。

通过上述变通方法,可以从客户端检索库端的属性和函数。

解决方法 1:

在此解决方法中,库端的所有脚本都是使用 Apps Script API 和 Drive API 检索的。

示例脚本 1:

在此示例中,使用了 Apps Script API。因此,当您使用此脚本时,请将 Google Cloud Platform Project 链接到 Google Apps Script Project。 Ref 并且,请在 API 控制台启用 Apps Script API。

const projectIdOflibrary = "###"; // Please set the project ID of the library.

const url = `https://script.googleapis.com/v1/projects/${projectIdOflibrary}/content`;
const res = UrlFetchApp.fetch(url, {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}});
const obj = JSON.parse(res.getContentText())
const functions = obj.files.reduce((ar, o) => {
  if (o.name != "appsscript") ar.push(o);
  return ar;
}, []);
console.log(functions)
// console.log(functions.map(({functionSet}) => functionSet.values)) // When you want to see the function names, you can use this line.
  • 当此脚本用于您的库脚本时,console.log(functions.flatMap(({functionSet}) => functionSet.values)) 返回[ { name: 'main' }, { name: 'onEdit' } ]

  • 在这种情况下,即使库是谷歌文档的容器绑定脚本,这个脚本也可以工作。

示例脚本 2:

在此示例中,使用了 Drive API。因此,当您使用此脚本时,请在 Google 高级服务中启用 Drive API。

const projectIdOflibrary = "###"; // Please set the project ID of the library.

const url = `https://www.googleapis.com/drive/v3/files/${projectIdOflibrary}/export?mimeType=application%2Fvnd.google-apps.script%2Bjson`;
const res = UrlFetchApp.fetch(url, {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}});
const obj = JSON.parse(res.getContentText())
const functions = obj.files.reduce((ar, o) => {
  if (o.name != "appsscript") ar.push(o.source);
  return ar;
}, []);
console.log(functions)
  • 当此脚本用于您的库脚本时,console.log(functions) 返回[ 'function main(){}\nfunction onEdit(){}\n' ]

  • 在这种情况下,不会自动解析函数名称。但 Google Apps Script Project 不需要与 Google Cloud Platform Project 链接。但是,当库是 Google Docs 的容器绑定脚本时,不能使用该脚本。在这种情况下,当库仅为独立类型时,可以使用此脚本。请注意这一点。

解决方法 2:

在此解决方法中,库端的属性和函数用对象包装。

示例脚本:库端

var sample1 = {
  method1: function() {},
  method2: function() {}
};


var sample2 = class sample2 {
  constructor() {
    this.sample = "sample";
  }

  method1() {
    return this.sample;
  }
}

示例脚本:客户端

function myFunction() {
  const res1 = MyLibrary.sample1;
  console.log(res1)

  const res2 = Object.getOwnPropertyNames(MyLibrary.sample2.prototype);
  console.log(res2)
}
  • 在这种情况下,console.log(res1)console.log(res2) 分别返回 { method1: [Function: method1], method2: [Function: method2] }[ 'constructor', 'method1' ]

参考资料:

【讨论】:

  • 谢谢。我已经考虑过解决方法#1。但我希望有一种更直接的方式或一些关于这些对象本身的信息,或者它为什么会这样。
  • @TheMaster 感谢您的回复。关于解决方法 1,不需要更改库端。使用 Apps Script API 时,会自动解析函数。但是,我认为设置有点复杂。另一方面,当使用 Drive API 时,设置比 Apps Script API 更容易。但函数不会自动解析。所以我认为有利有弊。
  • @TheMaster 所以一开始,我建议使用包装函数。因为我认为在现阶段,不幸的是,需要修改库端才能直接实现这个问题。我不确定哪个是更好的解决方案。对此我深表歉意。
  • 不用担心。只是希望有一个直接的方法:像Object.getOwnPropertyDescriptors() 或类似的东西。不过感谢您的回答
  • @TheMaster 是的。我认同。现在,我找不到它。但是当我找到它时,我想更新我的答案。
【解决方案2】:

Google Apps 脚本是 V8 的自定义嵌入,因此它使用 V8 C++ API 创建“魔术”对象。最终结果类似于代理:如果您知道属性的名称,则可以检索它;但是没有内置的方法来枚举库中的可用函数。 (我不知道为什么它是这样设计的。)

如果您控制有问题的库,一种可能的解决方法是从那里导出函数列表:

// MyLibrary:
Object.defineProperty(this, "global", {value: this});
function getExports() {
  let result = [];
  let descriptors = Object.getOwnPropertyDescriptors(global);
  for (let p in descriptors) {
    if (descriptors[p].enumerable) result.push(p);
  }
  return result;
}

// main project:
console.log(MyLibrary.getExports());

(如果你不控制图书馆,@Tanaike 的回答提供了一些建议。)

【讨论】:

  • 你有关于这些“魔法物品”的参考资料吗?
  • 我查看了源代码,但该源不是公开的,所以我无法链接到它。它归结为具有“Getter”但没有“Enumerator”回调的NamedPropertyHandlerConfiguration可能只是因为没有人认为这可能有用;您可以尝试提交feature request
  • 这是特定于库的吗,因为testPropDescriptors() 正确记录了所有内容?
  • 是的,我所说的适用于表示导入库的对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-23
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
相关资源
最近更新 更多