将外部 Javascript 与在全局(窗口)范围内定义方法隔离开来
Posted
技术标签:
【中文标题】将外部 Javascript 与在全局(窗口)范围内定义方法隔离开来【英文标题】:Isolating External Javascript from defining methods in the global (window) scope 【发布时间】:2017-02-21 12:17:53 【问题描述】:我需要在我的网站上包含对第三方编写的 javascript 的引用。遗憾的是,编写此脚本的开发人员决定在全局范围内定义他们的所有功能。你知道,像这样:
function AwesomeStringHelper()
// ...
function MyGreatFunction()
// ...
当我使用<script>
标记引用此脚本时,这两个方法都将添加到window
对象中。
由于我更喜欢不污染全局范围,有没有办法可以更改外部脚本的范围?理想情况下,我希望能够参考这些类似于ExternalLibrary.MyGreatFunction()
等的方法。我无法修改第三方脚本,因为它在外部托管,并且经常更改。
【问题讨论】:
它必须在外部托管吗?您可以通过您的服务器代理它并使用代码即时修改它吗? @JamesThorpe 这是一个有趣的建议,我考虑这样做只是因为外部服务器不允许客户端缓存。 如果您想要这样做的唯一原因是纯粹的审美或偏好,并且不会造成任何问题,那么您最好忽略这个问题。 @torazaburo 目前没有引起问题,我只是想看看是否有更好的方法来处理它而不需要大量的努力。不过你是对的,这完全是优先且不必要的。 使用requirejs's shim 可能非常有益(请参阅here)。 【参考方案1】:不确定 jQuery 是否是一个选项,或者您是否喜欢它,但我不知道如何编写本机 JS AJAX 调用,所以请耐心等待:
$(document).ready(function()
$.ajax(
url: 'www.example.com/awesome_script.js', // get the contents of the external script
type: 'GET',
crossDomain: true,
dataType: 'html',
success: function(data)
// build our script tag and wrap the contents inside of a function call
var script = "<script>"
script+= "var callMe = function(call_func, var1, var2, var3)";
script+= data;
script+= "return typeof call_func === 'function' ? call_func(var1, var2, var3) : 'You trying to dynamically call a variable? idk how to do that.';";
script+= ";";
script+= "<\/script>";
// assuming this is legal then just append the custom script tag to the <body> :-)
$('body').append($(script)[0]);
// profit?
callMe('AwesomeStringHelper', 'some_var'); // this function accepts one parameter
callMe('MyGreatFunction'); // this function accepts no parameters
);
);
【讨论】:
不幸的是,CORS 在这里发挥了作用,阻止我使用这种方法加载 JavaScript。 @JohnD 我不熟悉 CORS 的所有问题,但值得一试***.com/a/20423411。祝你好运! @JohnD 另一个考虑可能是有一个计划的(每小时、每晚、每周)任务,它使用服务器端任务(例如 php 中的 CURL)下载 JS 文件并将其保存到本地服务器,然后然后你可以像我在我的例子中设置var script
一样,以编程方式在服务器端包装他们的JS代码。
是的,这是 James 在 OP 的 cmets 中提出的建议。【参考方案2】:
假设你知道具体定义的函数,那么在脚本加载之后,这会不会行不通?
const ThirdPartyLib = AwesomeStringHelper, MyGreatFunction;
delete window.AwesomeStringHelper;
delete window.MyGreatFunction;
ThirdPartyLib.AwesomeStringHelper(haveFun);
【讨论】:
这与我答案的后半部分相同,但需要更多的手动操作【参考方案3】:首先,尝试教育第三方开发人员如何正确编写他们的模块。
如果这不起作用,请执行:
var ExternalLibrary = ExternalLibrary || window;
在代码的顶部。
然后,您可以在整个过程中使用ExternalLibrary.MyGreatFunction()
来引用它们的功能(即使它们在全局window
范围内仍然可见),然后稍后一旦第三方开发人员修复了它们的范围问题,那么您最多需要一个更改一行以保持兼容性(或者根本不更改,如果它们碰巧使用与您相同的 ExternalLibrary
名称)。
或者,在<script>
标记两侧使用两个简单的sn-ps 代码来记住window
对象的键,然后将新出现的键移动到一个新对象中(同时将它们从window
):
预加载:
var ExternalLibrary = _current: Object.keys(window) ;
加载后:
Object.keys(window).forEach(function(key)
if (ExternalLibrary._current.indexOf(key) < 0)
ExternalLibrary[key] = window[key];
delete window[key];
);
delete ExternalLibrary._current;
我过去曾使用过类似的方法(在严格模式流行之前)来检查是否存在泄漏的全局变量。
【讨论】:
【参考方案4】:您可以将整个脚本包装在一个函数中,并返回一个带有您想要的“公共”函数的对象,这可能很乏味且难以维护。
var myLib = function()
//entire script
return
functionA : functionA,
functionB : functionB,
//rest of functions
或者像这样(立即调用的函数)
(function(global)
//entire script
myLib.functionA = functionA;
myLib.functionB = functionB;
//rest of fn
global.myLib = myLib;
)(window);
您可以使用 gulp 自动执行此操作,我不确定是否有一个好的插件。
【讨论】:
您可以使用 gulp 获取脚本,而不是更改它并将其包装在一个函数中。 抱歉,@Alnitak 是对的。脚本经常更改,并且将其作为 gulp 构建的一部分在这里不起作用。它需要被引用才能在页面加载时加载。 客户端直接从第三方加载脚本 - 如果 OP 可以直接下载、修改和提供脚本,我相信他会。 我不建议使用外部脚本,因为它们会破坏您的生产代码,除非它们使用版本。对不起,我的帖子,我没有得到外部的概念! 我认为你在正确的轨道上,但我不明白为什么这需要你“将整个脚本包装在一个函数中”,我不明白为什么很难维持。而且我看不出 gulp 在这里有什么作用。这不只是您在引入外部库的<script>
标记之后添加的代码吗?【参考方案5】:
如果您的第三方模块直接分配给window
对象(如window.myGlobal = someValue
),并且您可以手动下载源代码,您应该可以“包装”函数中的整个脚本,其中 window
对象已被重载:
function wrapModule(code)
// create a "fake" window object that inherits from the global object
var fakeWindow = Object.create(window);
// create a function wrapping the code
// note that "window" is a parameter name in this function, shadowing
// the global object
var func = Function("window", code);
// call function
func.call(fakeWindow, fakeWindow);
// return fake window object
return fakeWindow;
// run code
const fakeWindow = wrapModule(`
var x = 0; // local variable (will not be exported)
y = 1; // global variable (will still be leaked)
window.z = 2; // assignment to window
this.w = 3; // assignment to this
`);
// check what variables are exposed
console.log('window.x', typeof x); // window.x undefined
console.log('window.y', typeof y); // window.y number
console.log('window.z', typeof z); // window.z undefined
console.log('window.w', typeof w); // window.w undefined
// check what variables are exposed in fakeWindow
console.log('fakeWindow.x', typeof fakeWindow.x); // fakeWindow.x undefined
console.log('fakeWindow.y', typeof fakeWindow.y); // fakeWindow.y number
console.log('fakeWindow.z', typeof fakeWindow.z); // fakeWindow.z number
console.log('fakeWindow.w', typeof fakeWindow.w); // fakeWindow.w number
【讨论】:
是的,遗憾的是他们没有直接引用窗口,这都是隐含的:/ 不过我喜欢你的方法。以上是关于将外部 Javascript 与在全局(窗口)范围内定义方法隔离开来的主要内容,如果未能解决你的问题,请参考以下文章