首页 > 代码库 > AMD and CMD are dead之KMDjs内核之依赖分析

AMD and CMD are dead之KMDjs内核之依赖分析

有人说js中有三座大三:this、原型链和scope tree,搞懂了他们就算是js成人礼。当然还有其他不同看法的js成人礼,如熟悉js的:OOP、AP、FP、DOP、AOP。当然还听说一种最牛B的js成人礼:熟悉jQuery……=   =!因为$里面可以放下全世界,比如$(“全世界”)…

这篇文章主要讲KMDjs利用Uglify2去分析出一个函数的所有依赖,之后才能正确地加载相关的js文件。该文涉及到js中三座大山中的scope tree….先看下面这段程序:

function test() {    var user = new User();}

很显然,该函数依赖User。我一定要去加载User.js才能正确执行该函数。那么我是不是可以写一段非常牛B的正则找到new  和()之间的User。当然,这样一定是不对的,因为js里创建对象的实例可以省略括号,比如:

function test() {    var user = new User;}

那么,是不是可以写一段非常牛B的正则,去查找new与分号之间的东西?相对于这个函数,是可以的!但是如果,这个函数长成这个样子:

function test(User) {    var user = new User;}

<style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>

可以看得到,该函数不依赖任何对象,User是test的参数传递进来…,那么我是不是可以写一段非常牛B的正则,先找到test(User)中的User参数,然后再通过正则找到new后面的所有对象,最后把第二次查找到的结果过滤掉第一次查找到的参数。好问题来了,如果程序长这样子:

function test(User) {    var xxx = "(User)";    var user = new User;}
<style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>可能有人会说了,只能说明你的正则不牛B。牛B的正则是可以确认是字符串中的,还是非字符串中的。那么再看下一段程序:
function test() {    var User = function (name) {        this.name = name;    }    var u = new User();}
<style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>

可以看得出,User根本不是参数,而是直接在test内部定义!那么这个函数其实是不依赖User。那么,刚刚憋出的两段牛B的正则就彻底废掉了。再比如:

function test() {    var vp = Bom.getViewport();}

可以看到,直接访问Bom的静态方法,都不需要new,就能访问Bom下的getViewport方法。所以,该函数依赖Bom。那么,刚刚憋出的两段牛B的正则再次彻底废掉了。再比如:

function test() {    var vp = new Array();    var el = document.getElementById("xx");}
<style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>

可以发现,window下的Array和document还需要过滤掉…,再比如

function test() {    var h = 1;    //这里 h a 都是可以找到,不能判定为赖    function a(e) {        //这里面h a e i都是可以找到,不能判定为赖        var i = 2;        function b(f) {            //这里面h a e i b f c都是可以找到,不能判定为赖            function c(g) {                //这里面h a e b f都是可以找到,不能判定为赖            }        }    }}
<style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>

如上面注释所描述,会有一串scope tree。那么…怎么办?uglify2有强大无比的ast.walk和UglifyJS.TreeWalker!如:

http://lisperator.net/blog/using-uglifyjs-for-code-refactoring/

这是官方重构代码的简单例子。受此文启发,我便为kmdjs写了个完美的依赖分析:

function getRef(fn) {    var U2 = UglifyJS;    var ast = U2.parse(fn.toString());             ast.figure_out_scope();    var result = [];    ast.walk(new U2.TreeWalker(function (node) {        if (node instanceof U2.AST_New) {            var ex = node.expression;            var name = ex.name;            if (!isInScopeChainVariables(ex.scope, name)) {                result.push(name);            }        }        if (node instanceof U2.AST_Dot) {            var ex = node.expression;            var name = ex.name;            if (!isInScopeChainVariables(ex.scope, name)) {                result.push(name);            }        }    }));    return result;}function isInScopeChainVariables(scope, name) {    var vars = scope.variables._values;    if (Object.prototype.hasOwnProperty.call(vars, "$" + name)) {        return true;    }    if (scope.parent_scope) {        return isInScopeChainVariables(scope.parent_scope, name);    }    return false;}

意外惊喜,在kmdjs加入lazy(kmdjs.get)之后,lazy内部的依赖不会加载!且看下面这段代码

function test(DDDDD) {    kmdjs.get("HelloKMD.Ball", function (Ball) {        //因为Ball是参数,属于该scope tree中的对象,所以不依赖        var ball = new Ball(100, 100, 28, 1, 2, "KMD.js");    });}<style type="text/css">.csharpcode, .csharpcode pre{	font-size: small;	color: black;	font-family: consolas, "Courier New", courier, monospace;	background-color: #ffffff;	/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {	background-color: #f4f4f4;	width: 100%;	margin: 0em;}.csharpcode .lnum { color: #606060; }</style>

完!

github:https://github.com/kmdjs/kmdjs<style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>