我很难理解通过AJAX动态获取代码并通过执行代码时如何处理异常eval。使用客户端javascript,如果我有一段这样的代码,这相当简单
eval
var j = 'some string'; j.propA.x++;
这将引发异常,因为propA类型为的undefined没有x。此外,引发的异常非常容易理解。
propA
undefined
x
现在,将上面的代码放入一个文本文件中,进行调用test.js,并将其存储在服务器上。现在让我们使用Ajax动态加载它。我正在使用以下代码动态加载它
test.js
dojo.xhrGet({ url: 'load.php', handleAs: "javascript", content : { fileName : 'test.js' }, load: function(returnValue) { /*Do Something*/ }, error: function(errorMessage) { /*Report Error*/ } });
这是一个非常基本的php脚本,用于加载文件并将其作为javascript代码返回
<?php $fileName = $_GET['fileName']; $handle = fopen($fileName , 'r'); $script = fread($handle, filesize($fileName)); fclose($handle); echo $script; ?>
在上面的dojo.xhrGet调用中,error可以将属性设置为显示错误消息的函数,这是可以完成此操作的一些示例。
dojo.xhrGet
error
error: function(errorMessage) { console.error(errorMessage); console.error(errorMessage.arguments); console.error(errorMessage.message); console.error(errorMessage.stack); console.error(errorMessage.type); }
以下是输出示例。尽管此输出是针对另一个问题的,但它突出了它的不可理解性:
Cannot read property 'x' of undefined TypeError: Cannot read property 'x' of undefined at eval at <anonymous> (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:3088) at Object.load (http://192.168.1.8/easel.js:166:6) at http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:89998 at _144 (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:36518) at _142 (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:36328) at [object Object].<anonymous> (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:36994) at _144 (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:36780) at _142 (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:36328) at [object Object].<anonymous> (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:36994) at Object.resHandle (http://o.aolcdn.com/dojo/1.6/dojo/dojo.xd.js:14:92730) non_object_property_load
我假设这dojo.xd.js:14是eval语句所在的行。
dojo.xd.js:14
如果知道他们在寻找什么,上面的内容就足够了。但是,是否存在一种更简单或更有效的方式来处理出现的异常eval?
这是一个有点类似的问题。
菲金在下面为这个问题提供了很好的解决方案,所以我给了他赏金。使用他的解决方案,我得到的输出看起来像这样(我将其削减了一点)
ReferenceError in JS Code detected: (url: module.require.php?module=MainMenu.Bg_S) easel.js:211Error Message: ReferenceError: apple is not defined easel.js:213(function(){ return function(args){ dojo.require("Shape"); Module.assert('MainMenu_V'); /** * The rectangular background of the Main View * @property MainMenuBg_S * @type Shape **/ new Shape({ /** * Unique descriptive name used when later accessing this shape via '$$()' * @param name * @type String **/ name : 'MainMenu.Bg_S' , /** * Left side of this rectangle * @param x * @type Number **/ x : $$('MainMenu_V').x , /** * Top of this rectangle * @param y * @type Number **/ y : $$('MainMenu_V').y , /** * Width of this rectangle * @param w * @type Number **/ w : $$('MainMenu_V').w , /** * Height of this rectangle * @param h * @type Number **/ h : $$('MainMenu_V').h , /** * Type of this Shape * @param h * @type Number **/ type : shapeType.RECTANGLE , /** * Generate function which contains all the graphics instructions, as well as the contexts * to preload and initialize. This is currently under development. Backgrounds should NEVER * have mouse events associated with them as a redraw of a background implies a redraw of * every single displayObject infront of the background. * @param generate * @type method **/ generate : function (){ var x = this.x << 0 , y = this.y << 0 , h = this.h << 0 , w = this.w << 0 , a = this.a; this.graphics(contextID.LEAVE).lf([hsl(180,100,60,0.9),hsl(180,100,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.ENTER).lf([hsl(135,100,40,0.9),hsl(135,100,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.CLICK).lf([hsl(90,100,40,0.9),hsl(90,50,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.RCLICK).lf([hsl(90,110,40,0.9),hsl(80,60,20,0.45)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.DBLCLICK).lf([hsl(45,100,40,0.9),hsl(45,100,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.DBLRCLICK).lf([hsl(10,100,40,0.9),hsl(10,100,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.LPRESS).lf([hsl(110,25,40,0.9),hsl(110,25,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.RPRESS).lf([hsl(110,50,40,0.9),hsl(110,50,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); this.graphics(contextID.SCROLL).lf([hsl(110,50,40,0.9),hsl(110,50,20,0.75)],[0,1],0,h/2,w,h/2).dr(x,y,w,h).ef(); if (debugFlags.BOUNDINGBOX()){ this.graphics(contextID.ENTER).ss(2).s(rgba(0,255,0,a)).dr(this.boundingBox.softBounds.L +4<<0, this.boundingBox.softBounds.T +4<<0, this.boundingBox.softBounds.w-8<<0 , this.boundingBox.softBounds.h-8<<0).es(); this.graphics(contextID.ENTER).ss(2).s(rgba(255,0,0,a)).dr(this.boundingBox.bounds.L +4<<0, this.boundingBox.bounds.T +4<<0, this.boundingBox.bounds.w-8<<0 , this.boundingBox.bounds.h-8<<0).es(); this.graphics(contextID.ENTER).f(rgba(0,0,255,a)).dc(this.boundingBox.points[0].x+4 , this.boundingBox.points[0].y+4 , 4).ef(); this.graphics(contextID.ENTER).f(rgba(0,0,255,a)).dc(this.boundingBox.points[1].x-8 , this.boundingBox.points[1].y+4 , 4).ef(); this.graphics(contextID.ENTER).f(rgba(0,0,255,a)).dc(this.boundingBox.points[2].x-8 , this.boundingBox.points[2].y-8 , 4).ef(); this.graphics(contextID.ENTER).f(rgba(0,0,255,a)).dc(this.boundingBox.points[3].x+4 , this.boundingBox.points[3].y-8 , 4).ef(); } }, /** * Arguments to pass to the mouse initialization function. These will get mixed in (via * dojo.mixin) to the mouse object. To increase performance, the signalOrderIn has been set to * NOHIT. This will limit the number of redraws (remember background redraws are extremely * expensive as they require redrawing everything in the container). The signalOrderOut is * then set to BLOCK to prvent anything behind the background from receiving mouse signals * (this is actually unecessary as the only think behind the background is, and always should * be, the container, which itself has signalOrderIn and signalOrderOut set to NOHIT and BLOCK * respectively). * @param mouse * @type Object **/ mouse : { _signalOrderIN : signalFlags.NOHIT , _signalOrderOUT : signalFlags.BLOCK } , /** * All views are initially loaded via Ajax. Generally, views do not have any preconditions, beyond * that the stage be present. They can, however, and generally do, have modules they require. These * are called after this view has been created and loaded (load() function call). They are called * in the order of the sub arrays. In the example below: * [[A , B , C , D , E , F , G]] * The 7 modules are requested in that order, but, due to Ajax, they can be loaded in any order. * In the below example, on the other hand: * [[A] , [B , C , D , E , F , G]] * Modules B-G depend on module A, therefore, module A is ordered to be loaded first. * @property providedModules * @type Array[Array[String]] * @protected **/ providedModules : [[]] , /** * Carries out all the initializations when loading the module * @method load * @protected **/ load : function (){ 0/apple; $$('MainMenu_V').addChild(this); } , /** * Carries out all memory deallocation when leaving the module (generally only necessary if modules * were loaded but not added to stage as in the case with cached bitmaps) * @method leave * @protected **/ leave : function (){ } }); $$('MainMenu.Bg_S')._code="dojo.require(\"Shape\");..."; }; }()); easel.js:217Error triggered by: function (_2bd){return err.call(args,_2bd,_2b7);} easel.js:220XHR Object: easel.js:221 Object args: Object handleAs: "javascript" query: null url: "module.require.php?module=MainMenu.Bg_S" xhr: XMLHttpRequest __proto__: Object easel.js:222Error Object: easel.js:223 ReferenceError arguments: Array[1] message: "—" stack: "—" type: "not_defined" __proto__: Error dojo.xd.js:14 ReferenceError arguments: Array[1] message: "—" stack: "—" type: "not_defined" __proto__: Error dojo.xd.js:14 ReferenceError arguments: Array[1] message: "—" stack: "—" type: "not_defined" __proto__: Error
我唯一需要缺少的是能够指出问题发生在哪一行。
这是一个片段,可从xhr-get请求中检测与网络无关的错误,并在控制台中输出有关此错误的一些信息。
还有一个额外的isEvalError()函数可以处理所有eval- error类型……我并不为此感到骄傲。更好的方法是获取errorMessage子类的父对象。我认为您通常可以放弃isEvalError(),因为在此块中不应有任何其他错误。
function isEvalError(errorMessage){ return errorMessage.name == "RangeError" || errorMessage.name == "ReferenceError" || errorMessage.name == "SyntaxError" || errorMessage.name == "URIError" || errorMessage.name == "TypeError"; } var foo = dojo.xhrGet({ url: 'stacko.js', handleAs: "javascript", load: function(returnValue) { console.log("load: "+returnValue); }, error: function(errorMessage,ioargs) { //request worked fine, this must be a non-network related error if(ioargs.xhr.readyState == 4 && ioargs.xhr.status == 200) { if(isEvalError(errorMessage)){ //show eval-error, url request & the JS code that causes the exception //eval-error types: RangeError,ReferenceError,SyntaxError, URIError, TypeError console.error(errorMessage.name+" in JS Code detected: (url: "+ioargs.url+")") console.error("Error Message: "+ errorMessage); console.error(ioargs.xhr.responseText); } //a little reflection - if u want to know who triggered this error //(although in this case the output is not very helpful ) console.error("Error triggered by: "+arguments.callee.caller.toString()); //last but not least log the error & the xhr-request object for more information console.error("XHR Object:"); console.error(ioargs); console.error("Error Object:"); console.error(errorMessage); } } });