JavaScript 依赖管理
我目前正在维护大量的 JS 文件,依赖问题越来越严重。现在,我将每个函数放在一个单独的文件中,并手动维护一个数据库来计算函数之间的依赖关系。
我想将其自动化。例如,如果我有
Array.prototype.f = function() {};
另一个函数 g 引用的
MyObject.g = function() {
var a = new Array();
a.f();
};
函数 f,我希望能够检测到 g 正在引用 f。
我该怎么办?我从哪里开始?我是否需要实际编写一个编译器,或者我可以调整 Spidermonkey 吗?其他人已经这样做了吗?
非常感谢任何帮助我开始的指示,
谢谢 多克
I am currently maintaining a large number of JS files and the dependency issue is growing over my head. Right now I have each function in a separate file and I manually maintain a database to work out the dependencies between functions.
This I would like to automate. For instance if I have the function f
Array.prototype.f = function() {};
which is referenced in another function g
MyObject.g = function() {
var a = new Array();
a.f();
};
I want to be able to detect that g is referencing f.
How do I go about this? Where do I start? Do I need to actually write a compiler or can I tweak Spidermonkey for instance? Did anyone else already do this?
Any pointers to get me started is very much appreciated
Thanks
Dok
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
虽然理论上您可以编写一个静态分析工具来检测其他文件中定义的全局变量的使用,例如
MyObject
的使用,但您实际上无法跟踪prototype
扩展方法的使用情况。JavaScript 是一种动态类型语言,因此任何工具都没有实际的方法可以知道
a
(如果从g
函数传递出来)是一个Array,因此如果调用
f()
,就会存在依赖关系。它只会在运行时确定哪些变量保存哪些类型,因此要找出你需要一个解释器,并且你已经让自己成为了一个图灵完备的问题。更不用说 JavaScript 的其他动态方面完全无法静态分析,例如通过方括号表示法获取属性、可怕的
eval
或超时或事件处理程序属性中的字符串。我认为这确实有点无用。您可能最好手动跟踪依赖项,但通过将相关函数分组到模块中来简化它,这将是依赖项跟踪的基本单元。好的,您将添加更多技术上需要的功能,但希望不要太多。
为每个模块命名空间也是一个好主意,因此每个调用的去向都非常清楚,从而可以轻松地手动控制依赖项(例如,通过
// using: ThisModule, ThatModule
注释在顶部)。由于内置原型的扩展很难跟踪,因此请将它们减少到最低限度。扩展例如。
Array
在尚未具备 ECMAScript 第五版方法(如indexOf
)的浏览器上包含这些方法是一件好事,作为所有脚本都会使用的基本修复。向现有原型添加全新的任意功能是值得怀疑的。Whilst you could theoretically write a static analysis tool that detected use of globals defined in other files, such as use of
MyObject
, you couldn't realistically track usage ofprototype
extension methods.JavaScript is a dynamically-typed language so there's no practical way for any tool to know that
a
, if passed out of theg
function, is anArray
, and so iff()
is called on it there's a dependency. It only gets determined what variables hold what types at run-time, so to find out you'd need an interpreter and you've made yourself a Turing-complete problem.Not to mention the other dynamic aspects of JavaScript that completely defy static analysis, such as fetching properties by square bracket notation, the dreaded
eval
, or strings in timeouts or event handler attributes.I think it's a bit of a non-starter really. You're probably better of tracking dependencies manually, but simplifying it by grouping related functions into modules which will be your basic unit of dependency tracking. OK, you'll pull in a few more functions that you technically need, but hopefully not too much.
It's also a good idea to namespace each module, so it's very clear where each call is going, making it easy to keep the dependencies in control manually (eg. by a
// uses: ThisModule, ThatModule
comment at the top).Since extensions of the built-in prototypes are trickier to keep track of, keep them down to a bare minimum. Extending eg.
Array
to include the ECMAScript Fifth Edition methods (likeindexOf
) on browsers that don't already have them is a good thing to do as a basic fixup that all scripts will use. Adding completely new arbitrary functionality to existing prototypes is questionable.您是否尝试过使用依赖关系管理器,例如 RequireJS 或 LabJS?我注意到没有人在此线程中提到它们。
来自http://requirejs.org/docs/start.html:
您也可以嵌套这些依赖项,因此 helper/util 可以在其内部需要一些其他文件。
Have you tried using a dependency manager like RequireJS or LabJS? I noticed no one's mentioned them in this thread.
From http://requirejs.org/docs/start.html:
You can nest those dependencies as well, so helper/util can require some other files within itself.
正如 @bobince 已经建议的那样,对 JavaScript 程序进行静态分析几乎是一个不可能解决的问题。 Google Closure 编译器在一定程度上做到了这一点,但它也依赖于来自 JSDoc 评论。
我有一个 类似的问题,在以前的项目中查找 JS 文件应连接的顺序,并且由于JS文件太多,手动更新包含顺序似乎太繁琐了。相反,我坚持遵守构成我的目的的依赖项的某些约定,并在此基础上使用简单的 regexp :) 我能够生成正确的包含顺序。
该解决方案使用拓扑排序算法来生成依赖关系图,然后按照应包含的顺序列出文件以满足所有依赖关系。由于每个文件基本上都是使用 MooTools 语法的伪类,因此只有 3 种方法可以根据我的情况创建依赖项。
这是一个简单的,对于通用用途来说绝对是一个破碎的解决方案,但它对我很有帮助。如果您对该解决方案感兴趣,可以在此处查看代码 - 它是用 Ruby 编写的。
如果您的依赖项更复杂,那么也许您可以使用注释和一些自行开发的语法手动列出每个 JS 文件本身的依赖项,例如:
然后读取每个 JS 文件,解析出需要的注释,并构建一个依赖关系图,该图将为您提供您需要的包含顺序。
As @bobince already suggested, doing static analysis on a JavaScript program is a close to impossible problem to crack. Google Closure compiler does it to some extent but then it also relies on external help from JSDoc comments.
I had a similar problem of finding the order in which JS files should be concatenated in a previous project, and since there were loads of JS files, manually updating the inclusion order seemed too tedious. Instead, I stuck with certain conventions of what constitutes a dependency for my purposes, and based upon that and using simple regexp :) I was able to generated the correct inclusion order.
The solution used a topological sort algorithm to generate a dependency graph which then listed the files in the order in which they should be included to satisfy all dependencies. Since each file was basically a pseudo-class using MooTools syntax, there were only 3 ways dependencies could be created for my situation.
new
keyword.It was a simple, and definitely a broken solution for general purpose usage but it served me well. If you're interested in the solution, you can see the code here - it's in Ruby.
If your dependencies are more complex, then perhaps you could manually list the dependencies in each JS file itself using comments and some homegrown syntax such as:
Then read each JS file, parse out the requires comments, and construct a dependency graph which will give you the inclusion order you need.
如果有一个工具可以自动为您检测这些依赖项并选择它们的加载方式,那就太好了。不过,当今最好的解决方案有点粗糙。我为我的特殊需求创建了一个依赖管理器,我想将其添加到列表中(金字塔依赖管理器)。它具有一些关键功能,可以解决一些独特的用例。
一些示例代码展示了它在开发过程中如何工作。
文件:dependencyLoader.js
Html 文件
它确实需要您维护一个文件来管理依赖项。我正在考虑创建一个程序,可以根据标头中的包含内容自动生成加载程序文件,但由于它处理许多不同类型的依赖项,因此将它们维护在一个文件中实际上可能会更好。
It would be nice to have a tool that can automatically detect those dependencies for you and choose how they are loaded. The best solutions today are a bit cruder though. I created a dependency manager for my particular needs that I want to add to the list (Pyramid Dependency Manager). It has some key features which solve some unique use cases.
Some sample code to show how it works during development.
File: dependencyLoader.js
Html Files
It does require you to maintain a single file to manage dependencies. I am thinking about creating a program that can automatically generate the loader file for you based on includes in the header but since it handles many different types of dependencies, maintaining them in one file might actually be better.
JSAnalyse 使用静态代码分析来检测 javascript 文件之间的依赖关系:
http://jsanalysis.codeplex.com/
它还允许您定义允许的依赖项并确保在例如,构建。当然,它无法检测所有依赖项,因为 javascript 是动态解释语言,它不是类型安全的,就像已经提到的那样。但它至少让你意识到你的 javascript 依赖关系图并帮助你控制它。
JSAnalyse uses static code analysis to detect dependencies between javascript files:
http://jsanalyse.codeplex.com/
It also allows you to define the allowed dependencies and to ensure it during the build, for instance. Of course, it cannot detect all dependencies because javascript is dynamic interpretet language which is not type-safe, like already mentioned. But it at least makes you aware of your javascript dependency graph and helps you to keep it under control.
我写了一个工具来做这样的事情: http://github.com/damonsmith/js- class-loader
如果您有一个 java web 应用程序并且您以 java 风格构建 JS 代码,那么它是最有用的。如果您这样做,它可以检测所有代码依赖项并将它们捆绑在一起,并支持运行时和解析时依赖项。
I have written a tool to do something like this: http://github.com/damonsmith/js-class-loader
It's most useful if you have a java webapp and you structure your JS code in the java style. If you do that, it can detect all of your code dependencies and bundle them up, with support for both runtime and parse-time dependencies.