exportFunction()

此函数提供了一种安全的方式,将特权范围内的函数暴露给低特权范围。这使得特权代码(例如扩展)能够与低特权代码(例如标准网页脚本)共享代码。从特权代码导出到低特权代码的函数可以在低特权代码的上下文中被调用。

该函数可以像在特权上下文中调用一样访问其周围的闭包。

导出的函数不需要添加到低特权代码的全局 window 对象中;它可以导出到目标作用域中的任何对象。

请参阅 导出接受参数的函数,了解您导出的函数是否接受参数时会发生什么。

语法

js
let exportedFunction = exportFunction(
  func,              // function
  targetScope,       // object
  options            // optional object
);

参数

func

function. 要导出的函数。

targetScope

object. 要将函数附加到的对象。这不一定是全局 window 对象;它可以是目标窗口中的一个对象,或者由调用者创建的对象。

options 可选

object. 函数的选项。

defineAs 可选

string. 在 targetScope 中函数的名称。如果省略,则需要将 exportFunction() 的返回值分配给目标作用域中的一个对象。

allowCrossOriginArguments 可选

boolean. 是否检查导出函数的参数是否被调用者 包含。这允许调用者将具有不同源的对象传递给导出函数,然后导出函数可以利用其特权身份使用该对象进行跨域请求。默认为 false

返回值

在目标上下文中创建的占位符函数。

导出接受参数的函数

传入函数的任何参数都不会被克隆。相反,它们会作为 Xrays 传递到特权范围。

修改参数

对象的 Xray 指的是原始对象。在导出函数中对参数所做的任何更改都会影响传入的原始对象。例如:

js
// privileged scope: for example, a content script
function changeMyName(user) {
  user.name = "Bill";
}
exportFunction(changeMyName, window, {
  defineAs: "changeMyName",
});
js
// less-privileged scope: for example, a page script
const user = { name: "Jim" };
const test = document.getElementById("test");
test.addEventListener("click", () => {
  console.log(user.name); // "Jim"
  window.changeMyName(user);
  console.log(user.name); // "Bill"
});

此行为受 Xrays 的常规规则约束。例如,添加到 DOM 节点的 expando 属性在原始对象中是不可见的。

Xray 过滤和豁免

Xrays 提供对原始对象的过滤视图。例如,在 JavaScript Object 类型的 Xrays 中,函数是不可见的。如果您需要对原始对象的无过滤访问,可以 豁免 Xrays

js
// privileged scope: for example, a content script
function logUser(user) {
  // console.log(user.getUser());                 // error
  console.log(user.wrappedJSObject.getUser()); // "Bill"
}
exportFunction(logUser, window, {
  defineAs: "logUser",
});
js
// less-privileged scope: for example, a page script
const user = {
  getUser() {
    return "Bill";
  },
};
const test = document.getElementById("test");
test.addEventListener("click", () => {
  window.logUser(user);
});

有关更多信息,请参阅 Firefox Source Tree 文档中的 Xray vision

传递函数作为参数

如果将函数作为参数传递,这些函数也会作为 Xrays 传递。由于您可以像普通函数一样调用 Function Xrays,这意味着将回调传递给导出函数是可行的。

js
// privileged scope: for example, a content script
function logUser(getUser) {
  console.log(getUser()); // "Bill"
}
exportFunction(logUser, unsafeWindow, {
  defineAs: "logUser",
});
js
// less-privileged scope: for example, a page script
function getUser() {
  return "Bill";
}
const test = document.getElementById("test");
test.addEventListener("click", () => {
  window.logUser(getUser);
});

跨域检查

调用导出函数时,将检查每个参数(包括 this),以确保调用者 包含 该参数。这可以防止将跨域对象(例如 WindowLocation)传递给特权函数,因为特权代码可以完全访问这些对象,并可能无意中执行危险操作。通过将 { allowCrossOriginArguments: true } 传递给 exportFunction 可以覆盖此规定。

示例

导出到全局作用域

此脚本定义了一个函数,然后将其导出到内容窗口。

js
// extension-script.js
const salutation = "hello ";
function greetMe(user) {
  return salutation + user;
}
exportFunction(greetMe, window, { defineAs: "foo" });

脚本可以使用 `defineAs` 以外的方式,将 `exportFunction` 的结果分配给目标作用域中的一个对象。

js
// extension-script.js
const salutation = "hello ";
function greetMe(user) {
  return salutation + user;
}
window.foo = exportFunction(greetMe, window);

无论哪种方式,在内容窗口作用域中运行的代码都可以调用该函数。

js
// page-script.js
const greeting = foo("alice");
console.log(greeting);
// "hello alice"

导出到现有的本地对象

调用者可以将函数附加到目标上下文中的任何其他对象,而不是将其附加到目标的全局 window 对象。假设内容窗口定义了一个局部变量 bar

js
// page-script.js
const bar = {};

现在,扩展脚本可以将函数附加到 bar

js
// extension-script.js
exportFunction(greetMe, window.bar, {
  defineAs: "greetMe",
});
js
// page-script.js
const value = bar.greetMe("bob");
console.log(value);
// "hello bob"

浏览器兼容性