Javascript Uglify JS-压缩未使用的变量
Uglify有一个“压缩”选项,可以删除未使用的变量 但是,如果我在这样的对象中存储了一些函数Javascript Uglify JS-压缩未使用的变量,javascript,gruntjs,uglifyjs,uglifyjs2,Javascript,Gruntjs,Uglifyjs,Uglifyjs2,Uglify有一个“压缩”选项,可以删除未使用的变量 但是,如果我在这样的对象中存储了一些函数 helpers = { doSomething: function () { ... }, doSomethingElese: function () { ... } } 。。。如果helpers.doSomething()从未被访问,是否有方法删除它 我想我应该允许压缩机改变我的对象 如果可能的话,有什么想法吗?或者任何其他有帮助的工具?使用Uglify2或Esprima之类的静态
helpers = {
doSomething: function () { ... },
doSomethingElese: function () { ... }
}
。。。如果helpers.doSomething()从未被访问,是否有方法删除它
我想我应该允许压缩机改变我的对象
如果可能的话,有什么想法吗?或者任何其他有帮助的工具?使用Uglify2或Esprima之类的静态分析器来完成这项任务有些不寻常,因为有很多情况会调用难以确定的函数。为了显示复杂性,有以下网站: 它尝试至少识别未使用的函数。但是,该网站上提供的代码不会对您的示例起作用,因为它查找的是函数声明,而不是函数表达式。它还寻找CallExpression作为标识符,同时忽略您的示例使用的作为MemberExpression的CallExpression。这里还有一个作用域的问题,它没有考虑不同作用域中同名的函数——完全合法的Javascript,但是使用该代码会失去逼真度,因为它会忽略一些未使用的函数,认为它们是在未调用时调用的 为了处理作用域问题,您可能可以使用ESTR(),以帮助确定变量的作用域以及未使用的函数。然后,您需要使用escodegen之类的工具来删除未使用的函数 作为您的起点,我已经修改了该网站上的代码,以适应您提供的非常具体的情况,但请注意,它将有范围问题 这是为Node.js编写的,因此您需要使用npm获得esprima以使用提供的示例,当然,还需要使用Node执行它
var fs = require('fs');
var esprima = require('esprima');
if (process.argv.length < 3) {
console.log('Usage: node ' + process.argv[1] + ' <filename>');
process.exit(1);
}
notifydeadcode = function(data){
function traverse(node, func) {
func(node);
for (var key in node) {
if (node.hasOwnProperty(key)) {
var child = node[key];
if (typeof child === 'object' && child !== null) {
if (Array.isArray(child)) {
child.forEach(function(node) {
traverse(node, func);
});
} else {
traverse(child, func);
}
}
}
}
}
function analyzeCode(code) {
var ast = esprima.parse(code);
var functionsStats = {};
var addStatsEntry = function(funcName) {
if (!functionsStats[funcName]) {
functionsStats[funcName] = {calls: 0, declarations:0};
}
};
var pnode = null;
traverse(ast, function(node) {
if (node.type === 'FunctionExpression') {
if(pnode.type == 'Identifier'){
var expr = pnode.name;
addStatsEntry(expr);
functionsStats[expr].declarations++;
}
} else if (node.type === 'FunctionDeclaration') {
addStatsEntry(node.id.name);
functionsStats[node.id.name].declarations++;
} else if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
addStatsEntry(node.callee.name);
functionsStats[node.callee.name].calls++;
}else if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression'){
var lexpr = node.callee.property.name;
addStatsEntry(lexpr);
functionsStats[lexpr].calls++;
}
pnode = node;
});
processResults(functionsStats);
}
function processResults(results) {
//console.log(JSON.stringify(results));
for (var name in results) {
if (results.hasOwnProperty(name)) {
var stats = results[name];
if (stats.declarations === 0) {
console.log('Function', name, 'undeclared');
} else if (stats.declarations > 1) {
console.log('Function', name, 'decalred multiple times');
} else if (stats.calls === 0) {
console.log('Function', name, 'declared but not called');
}
}
}
}
analyzeCode(data);
}
// Read the file and print its contents.
var filename = process.argv[2];
fs.readFile(filename, 'utf8', function(err, data) {
if (err) throw err;
console.log('OK: ' + filename);
notifydeadcode(data);
});
其中test.js包含:
helpers = {
doSomething:function(){ },
doSomethingElse:function(){ }
};
helpers.doSomethingElse();
您将获得以下输出:
OK: test.js
Function doSomething declared but not called
最后要注意的一点是:试图查找未使用的变量和函数可能是一个兔子洞,因为您遇到了诸如eval和从字符串创建的函数之类的情况。您还必须考虑应用和调用等。这就是为什么,我认为,我们今天的静态分析器中没有这种功能。我正要建议google closure编译器,但我做了一个小测试,使用高级优化(导出helpers对象),它不会删除对象中的函数,即使没有使用。你可能需要重写你的代码,如果你想让这些工具中的任何一个摆脱未使用的代码(直接声明函数而不是使用helpers对象),Google closure Compiler会按照你的要求去做——但这意味着你需要用JSDoc注释正确地记录你的代码。谢谢你有趣的回答,我很高兴,我希望你能以某种方式满足你的需求。但是,我不知道为什么我没有想到这一点,但是你可能会考虑像伊斯坦布尔这样的代码覆盖工具,那么也许有一种方法可以用ESCODEGEN使伊斯坦布尔自动化,从而消除不符合任何覆盖的死区功能。这可能是一个很好的项目,但可能需要几天的修补。
OK: test.js
Function doSomething declared but not called