将外部Javascript与定义全局(窗口)范围中的方法隔离开来

Isolating External Javascript from defining methods in the global (window) scope

我需要在我的网站上包含一个由第三方编写的对javascript的引用。遗憾的是,编写此脚本的开发人员决定全局定义其所有函数。你知道,就像这样:

1
2
3
4
5
6
7
function AwesomeStringHelper() {
  // ...
}

function MyGreatFunction() {
  // ...
}

当我使用标记引用此脚本时,这两个方法都将添加到window对象中。

由于我更喜欢不污染全局范围,有没有方法可以更改外部脚本的范围?理想情况下,我希望能够引用类似于ExternalLibrary.MyGreatFunction()等的方法。我无法修改外部托管的第三方脚本,而且它经常更改。


首先,尝试教育第三方开发人员如何正确编写模块。

如果不起作用,请执行以下操作:

1
var ExternalLibrary = ExternalLibrary || window;

在代码的顶部。

然后,您可以通过使用ExternalLibrary.MyGreatFunction()来引用它们的功能(即使它们在全局window范围内仍然可见),然后,在第三方dev解决了它们的范围问题之后,最多需要一行更改来保持兼容性(或者根本不更改,如果它们恰好使用相同的ExternalLibrary名称a)是的。

或者,在标记的任一侧使用两个简单的代码片段来记住window对象的键,然后将新出现的键移动到新对象中(同时从window中删除它们):

预加载:

1
var ExternalLibrary = { _current: Object.keys(window) };

后负荷:

1
2
3
4
5
6
7
Object.keys(window).forEach(function(key) {
    if (ExternalLibrary._current.indexOf(key) < 0) {
        ExternalLibrary[key] = window[key];
        delete window[key];
    }
});
delete ExternalLibrary._current;

我在过去使用过类似的方法(在使用严格模式之前)来检查泄漏的全局变量。


如果您的第三方模块直接分配给window对象(如window.myGlobal = someValue对象),并且您能够手动下载源代码,那么您应该能够将整个脚本"包装"到一个函数中,其中window对象已过载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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


假设您知道正在定义的特定函数,那么在加载脚本之后,这将不起作用吗?

1
2
3
4
5
const ThirdPartyLib = {AwesomeStringHelper, MyGreatFunction};
delete window.AwesomeStringHelper;
delete window.MyGreatFunction;

ThirdPartyLib.AwesomeStringHelper(haveFun);


不确定jquery是否是一个选项,或者您是否关心它,但我不知道如何编写本机JS Ajax调用,请放心:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$(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+="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
        }
    });
});


您可以将整个脚本包装在一个函数中,并返回一个具有您想要的"public"函数的对象,这可能很繁琐,并且很难维护。

1
2
3
4
5
6
7
8
var myLib = function() {
   //entire script
   return {
       functionA : functionA,
       functionB : functionB,
       //rest of functions
   }
}

或者像这样(中间调用的函数)

1
2
3
4
5
6
7
8
(function(global) {
    //entire script
    myLib.functionA = functionA;
    myLib.functionB = functionB;
    //rest of fn
    global.myLib = myLib;

})(window);

你可以使用Gulp自动完成这个,我不确定是否有一个好的插件。