我正在尝试编写一个可重用的插件模块,以异步方式加载Google Maps
并返回一个承诺。
Here is the code I came up with, using AngularJS
然而,在场景后面创建全局回调函数存在可重用性缺陷"。如果任何其他库碰巧使用相同的命名空间,则可能导致错误的副作用。
我的问题 - 有没有办法在不创建全局变量的情况下实现这种效果。
以下是创建" evil"全球回调:
// Google async initializer needs global function, so we use $window
angular.module('GoogleMapsInitializer')
.factory('Initializer', function($window, $q){
// maps loader deferred object
var mapsDefer = $q.defer();
// Google's url for async maps initialization accepting callback function
var asyncUrl = 'https://maps.googleapis.com/maps/api/js?callback=';
// async loader
var asyncLoad = function(asyncUrl, callbackName) {
var script = document.createElement('script');
//script.type = 'text/javascript';
script.src = asyncUrl + callbackName;
document.body.appendChild(script);
};
// Here is the bad guy:
// callback function - resolving promise after maps successfully loaded
$window.googleMapsInitialized = function () {
mapsDefer.resolve();
};
// loading google maps
asyncLoad(asyncUrl, 'googleMapsInitialized');
return {
// usage: Initializer.mapsInitialized.then(callback)
mapsInitialized : mapsDefer.promise
};
})
答案 0 :(得分:3)
用于从您引用的跨源域上的服务器获取数据的脚本加载技术是JSONP。您可以阅读更多相关信息here。根据定义,JSONP只能通过调用全局范围的函数来工作。
所以,直接回答你的问题:没有全局功能,你不能使用JSONP跨域技术 - 这就是机制的运作方式。脚本在全局命名空间中执行,它必须调用它可以从全局命名空间访问的函数。甚至jQuery和YUI都是这样做的,用于实现JSONP。
而且,由于您使用的是Angular,因此它已经内置了JSONP功能。请参阅the doc here,这样您就不必创建自己的机制来执行此操作。
但是,那就是说,如果你自己制作,你可以通过采取一些预防措施,使你的全局功能与其他人的代码或甚至与你的图书馆的另一个实例发生冲突的可能性大大降低您创建的全局名称更随机。
以下是一个如何使任何类型的命名冲突的几率非常小的例子。这使用了三种技术:
以下是您实施这些方面的代码。
// Google async initializer needs global function, so we use $window
angular.module('GoogleMapsInitializer')
.factory('Initializer', function($window, $q){
// maps loader deferred object
var mapsDefer = $q.defer();
// Google's url for async maps initialization accepting callback function
var asyncUrl = 'https://maps.googleapis.com/maps/api/js?callback=';
// async loader
var asyncLoad = function(asyncUrl, callbackName) {
var script = document.createElement('script');
//script.type = 'text/javascript';
script.src = asyncUrl + callbackName;
document.body.appendChild(script);
};
// generate a unique function name
// includes prefix, current time and random number
var fname = "__googleMapsInitialized_" +
(new Date().getTime()) + "_" +
(Math.random() + "").replace(".", "");
// callback function - resolving promise after maps successfully loaded
$window[fname] = function () {
mapsDefer.resolve();
// remove the global now that we're done with it
delete $window[fname];
};
// loading google maps
asyncLoad(asyncUrl, fname);
return {
// usage: Initializer.mapsInitialized.then(callback)
mapsInitialized : mapsDefer.promise
};
})
演示唯一函数名称生成器:http://jsfiddle.net/jfriend00/oms7vc6o/
P.S。我自己并不了解Angular,但似乎Angular已经知道如何自己完成JSONP调用,所以你不必在这里制定自己的解决方案。有关详细信息,请参阅this Angular doc page和this other question以及this article。