当使用 new Function(params,body) 构造函数从 JavaScript 代码创建新函数时,在 body 中传递无效字符串会产生 SyntaxError。虽然此异常包含错误消息(即:Unexpected token =),但似乎不包含上下文(即发现错误的行/列或字符)。
fiddle 示例:https://jsfiddle.net/gheh1m8p/
var testWithSyntaxError = "{\n\n\n=2;}";
try {
var f=new Function('',testWithSyntaxError);
} catch(e) {
console.log(e instanceof SyntaxError);
console.log(e.message);
console.log(e.name);
console.log(e.fileName);
console.log(e.lineNumber);
console.log(e.columnNumber);
console.log(e.stack);
}
true
(index):54 Unexpected token =
(index):55 SyntaxError
(index):56 undefined
(index):57 undefined
(index):58 undefined
(index):59 SyntaxError: Unexpected token =
at Function (native)
at window.onload (https://fiddle.jshell.net/_display/:51:8)
最佳答案
如您所见,在基于 Chromium 的浏览器中,将 try/catch在 V8 解析代码时(在实际运行之前)抛出 SyntaxError 的东西不会产生任何有用的东西;它将描述导致在堆栈跟踪中评估有问题的脚本的行,但没有关于问题在所述脚本中的位置的详细信息。
但是,有一个跨浏览器的解决方法。而不是使用 try/catch ,您可以添加 error window 的听众,并且提供给回调的第一个参数将是 ErrorEvent有用的lineno和 colno特性:
window.addEventListener('error', (errorEvent) => {
const { lineno, colno } = errorEvent;
console.log(`Error thrown at: ${lineno}:${colno}`);
// Don't pollute the console with additional info:
errorEvent.preventDefault();
});
const checkSyntax = (str) => {
// Using setTimeout because when an error is thrown without a catch,
// even if the error listener calls preventDefault(),
// the current thread will stop
setTimeout(() => {
eval(str);
});
};
checkSyntax(`console.log('foo') bar baz`);
checkSyntax(`foo bar baz`); Look in your browser console to see this in action, not in the snippet consoleError thrown at: 1:20
Error thrown at: 1:5
console.log('foo') bar baz
^
foo bar baz
^
error 中确定。监听是运行时抛出的错误checkSyntax .另外,try/catch可用于解释器将脚本文本解析为 AST 后的运行时错误(包括语法错误)。所以,你可能有 checkSyntax只检查 Javascript 最初是可解析的,没有别的,然后使用 try/catch (如果您想真正运行代码)以捕获运行时错误。您可以通过插入 throw new Error 来执行此操作到文本的顶部 eval编。// Use an IIFE to keep from polluting the global scope
(async () => {
let stringToEval;
let checkSyntaxResolve;
const cleanup = () => {
stringToEval = null;
checkSyntaxResolve = null; // not necessary, but makes things clearer
};
window.addEventListener('error', (errorEvent) => {
if (!stringToEval) {
// The error was caused by something other than the checkSyntax function below; ignore it
return;
}
const stringToEvalToPrint = stringToEval.split('\n').slice(1).join('\n');
// Don't pollute the console with additional info:
errorEvent.preventDefault();
if (errorEvent.message === 'Uncaught Error: Parsing successful!') {
console.log(`Parsing successful for: ${stringToEvalToPrint}`);
checkSyntaxResolve();
cleanup();
return;
}
const { lineno, colno } = errorEvent;
console.log(`Syntax error thrown at: ${lineno - 1}:${colno}`);
console.log(describeError(stringToEval, lineno, colno));
// checkSyntaxResolve should *always* be defined at this point - checkSyntax's eval was just called (synchronously)
checkSyntaxResolve();
cleanup();
});
const checkSyntax = (str) => {
console.log('----------------------------------------');
return new Promise((resolve) => {
checkSyntaxResolve = resolve;
// Using setTimeout because when an error is thrown without a catch,
// even if the 'error' listener calls preventDefault(),
// the current thread will stop
setTimeout(() => {
// If we only want to check the syntax for initial parsing validity,
// but not run the code for real, throw an error at the top:
stringToEval = `throw new Error('Parsing successful!');\n${str}`;
eval(stringToEval);
});
});
};
const describeError = (stringToEval, lineno, colno) => {
const lines = stringToEval.split('\n');
const line = lines[lineno - 1];
return `${line}\n${' '.repeat(colno - 1) + '^'}`;
};
await checkSyntax(`console.log('I will throw') bar baz`);
await checkSyntax(`foo bar baz will throw too`);
await checkSyntax(`console.log('A snippet without compile errors'); const foo = bar;`);
await checkSyntax(`console.log('A multi line snippet');
With a syntax error on the second line`);
})(); Look in your browser console to see this in action, not in the snippet consoleawait checkSyntax(`console.log('I will throw') bar baz`);
await checkSyntax(`foo bar baz will throw too`);
await checkSyntax(`console.log('A snippet without compile errors'); const foo = bar;`);
await checkSyntax(`console.log('A multi line snippet');
With a syntax error on the second line`);
----------------------------------------
Syntax error thrown at: 1:29
console.log('I will throw') bar baz
^
----------------------------------------
Syntax error thrown at: 1:5
foo bar baz will throw too
^
----------------------------------------
Parsing successful for: console.log('A snippet without compile errors'); const foo = bar;
----------------------------------------
Syntax error thrown at: 2:6
With a syntax error on the second line
^
window 处引发错误的事实是一个问题(例如,如果其他东西已经在监听窗口错误,您不想打扰,并且您不能先附加您的监听器并在事件上调用 stopImmediatePropagation()),另一种选择是使用而是一个 web worker,它有自己的执行上下文,与原始的 window 完全分离:// Worker:
const getErrorEvent = (() => {
const workerFn = () => {
const doEvalAndReply = (jsText) => {
self.addEventListener(
'error',
(errorEvent) => {
// Don't pollute the browser console:
errorEvent.preventDefault();
// The properties we want are actually getters on the prototype;
// they won't be retrieved when just stringifying
// so, extract them manually, and put them into a new object:
const { lineno, colno, message } = errorEvent;
const plainErrorEventObj = { lineno, colno, message };
self.postMessage(JSON.stringify(plainErrorEventObj));
},
{ once: true }
);
eval(jsText);
};
self.addEventListener('message', (e) => {
doEvalAndReply(e.data);
});
};
const blob = new Blob(
[ `(${workerFn})();`],
{ type: "text/javascript" }
);
const worker = new Worker(window.URL.createObjectURL(blob));
// Use a queue to ensure processNext only calls the worker once the worker is idle
const processingQueue = [];
let processing = false;
const processNext = () => {
processing = true;
const { resolve, jsText } = processingQueue.shift();
worker.addEventListener(
'message',
({ data }) => {
resolve(JSON.parse(data));
if (processingQueue.length) {
processNext();
} else {
processing = false;
}
},
{ once: true }
);
worker.postMessage(jsText);
};
return (jsText) => new Promise((resolve) => {
processingQueue.push({ resolve, jsText });
if (!processing) {
processNext();
}
});
})();
// Calls worker:
(async () => {
const checkSyntax = async (str) => {
console.log('----------------------------------------');
const stringToEval = `throw new Error('Parsing successful!');\n${str}`;
const { lineno, colno, message } = await getErrorEvent(stringToEval);
if (message === 'Uncaught Error: Parsing successful!') {
console.log(`Parsing successful for: ${str}`);
return;
}
console.log(`Syntax error thrown at: ${lineno - 1}:${colno}`);
console.log(describeError(stringToEval, lineno, colno));
};
const describeError = (stringToEval, lineno, colno) => {
const lines = stringToEval.split('\n');
const line = lines[lineno - 1];
return `${line}\n${' '.repeat(colno - 1) + '^'}`;
};
await checkSyntax(`console.log('I will throw') bar baz`);
await checkSyntax(`foo bar baz will throw too`);
await checkSyntax(`console.log('A snippet without compile errors'); const foo = bar;`);
await checkSyntax(`console.log('A multi line snippet');
With a syntax error on the second line`);
})(); Look in your browser console to see this in action, not in the snippet consolecheckSyntax正在检查是否可以将提供的代码解析为 Abstract Syntax Tree由当前的解释器。您还可以使用 @babel/parser 之类的软件包或 acorn尝试解析字符串,尽管您必须将其配置为当前环境中允许的语法(这将随着新语法添加到语言中而改变)。const checkSyntax = (str) => {
try {
acorn.Parser.parse(str);
console.log('Parsing successful');
} catch(e){
console.error(e.message);
}
};
checkSyntax(`console.log('I will throw') bar baz`);
checkSyntax(`foo bar baz will throw too`);
checkSyntax(`console.log('A snippet without compile errors'); const foo = bar;`);
checkSyntax(`console.log('A multi line snippet');
With a syntax error on the second line`); <script src="https://cdn.jsdelivr.net/npm/acorn@6.1.1/dist/acorn.min.js"></script>console.log('I will throw') bar baz
evalmachine.<anonymous>:1
console.log('I will throw') bar baz
^^^
SyntaxError: Unexpected identifier
at createScript (vm.js:80:10)
at Object.runInNewContext (vm.js:135:10)
<etc>
^ 之前的空格数。获取列号。使用与之前类似的技术,如果解析成功,则在第一行抛出错误:const vm = require('vm');
const checkSyntax = (code) => {
console.log('---------------------------');
try {
vm.runInNewContext(`throw new Error();\n${code}`);
}
catch (e) {
describeError(e.stack);
}
};
const describeError = (stack) => {
const match = stack
.match(/^\D+(\d+)\n(.+\n( *)\^+)\n\n(SyntaxError.+)/);
if (!match) {
console.log('Parse successful!');
return;
}
const [, linenoPlusOne, caretString, colSpaces, message] = match;
const lineno = linenoPlusOne - 1;
const colno = colSpaces.length + 1;
console.log(`${lineno}:${colno}: ${message}\n${caretString}`);
};
checkSyntax(`console.log('I will throw') bar baz`);
checkSyntax(`foo bar baz will throw too`);
checkSyntax(`console.log('A snippet without compile errors'); const foo = bar;`);
checkSyntax(`console.log('A multi line snippet');
With a syntax error on the second line`);
---------------------------
1:29: SyntaxError: Unexpected identifier
console.log('I will throw') bar baz
^^^
---------------------------
1:5: SyntaxError: Unexpected identifier
foo bar baz will throw too
^^^
---------------------------
Parse successful!
---------------------------
2:6: SyntaxError: Unexpected identifier
With a syntax error on the second line
^
How can I, without using external dependencies, pinpoint SyntaxError location withinn passed string? I require solution both for browser and nodejs.
关于javascript - 查找 javascript new Function() 构造函数抛出的 SyntaxError 的详细信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35252731/
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s
我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt