• 围观STK


    先科谱一下,STK是新浪微博的前端javascript脚本库,这个介绍很风趣:

       STK起源于sina twitter`s kit。
       后来内部改成史塔克了,是钢铁侠的主角托尼·史塔克的意思,世界最大的军火商。
       但这史塔克这名字太晦涩难记,后来传着传着就成沙滩裤了,没啥具体含义,只是谐音好说。

    其实听到STK已经有很多次了,不过可能是因为新浪的知识产权保护策略,源代码一直没有公开,所以我也一直没有去仔细了解。
    最近又听CJ同学讲到了STK,于是决心再去看一下,就算是苦B的看压缩后的代码。
    压缩后的代码来自这个网址:http://js.t.sinajs.cn/t4/home/js/base.js?version=3c017d092cab8939
    注:可以将上面的代码复制到这个网址进行格式化:http://jsbeautifier.org/


    1. STK主干
    STK是一个命名空间,从主干代码可以看出是按YUI2的思路来整的一个静态方法与工具库。代码如下:

    View Code
    var STK = function () {
        var a = {}, b = [];
        a.inc = function (a, b) {
            return !0
        };
        a.register = function (c, d) {
            var e = c.split("."),
                f = a,
                g = null;
            while (g = e.shift()) if (e.length) {
                f[g] === undefined && (f[g] = {});
                f = f[g]
            } else if (f[g] === undefined) try {
                f[g] = d(a)
            } catch (h) {
                b.push(h)
            }
        };
        a.regShort = function (b, c) {
            if (a[b] !== undefined) throw "[" + b + "] : short : has been register";
            a[b] = c
        };
        a.IE = /msie/i.test(navigator.userAgent);
        a.E = function (a) {
            return typeof a == "string" ? document.getElementById(a) : a
        };
        a.C = function (a) {
            var b;
            a = a.toUpperCase();
            a == "TEXT" ? b = document.createTextNode("") : a == "BUFFER" ? b = document.createDocumentFragment() : b = document.createElement(a);
            return b
        };
        a.log = function (a) {
            b.push("[" + (new Date).getTime() % 1e5 + "]: " + a)
        };
        a.getErrorLogInformationList = function (a) {
            return b.splice(0, a || b.length)
        };
        return a
    }();

    其中最重要的是两个方法:inc与register。

    inc方法在发布时变成了一个空方法,应该是跟模块加载有关,具体就不妄测了。

    register方法,第一个参数是命名(包括命名空间与本身名字);第二个参数是一个生成器函数。
    JK小注:第二个参数只能是生成器,这样可以后面的代码看起来很统一,不过也会造成一些小小的不便,例如以下代码,使用生成器显得有点臃肿:

    STK.register("core.func.empty", function () {
        return function () {}
    });

    如果regShort的第一个参数也支持类似“core.func.empty”的带点字符串的话,这样使,上面的代码可以改成这一种的:

    STK.regShort("core.func.empty", function () {});

    2. STK堆砌
    主干之外,剩下的大部分内容就是利用STK.register进行堆砌了。
    这种堆砌也大略可以分成两种:方法粒度的堆砌、与命名空间或类粒度的堆砌。
    方法粒度的堆砌:例如register "core.func.empty"、"core.obj.parseParam",生成器会产生一个静态函数

    View Code
    STK.register("core.func.empty", function () {
        return function () {}
    });
    STK.register("core.obj.parseParam", function (a) {
        return function (a, b, c) {
            var d, e = {};
            b = b || {};
            for (d in a) {
                e[d] = a[d];
                b[d] != null && (c ? a.hasOwnProperty[d] && (e[d] = b[d]) : e[d] = b[d])
            }
            return e
        }
    });

    命名空间或类粒度的堆砌:例如register "core.util.cookie"生成器会产生一个子命名空间;例如register "core.util.drag"生成器会产生"类"

    View Code
    STK.register("core.util.cookie", function (a) {
        var b = {
            set: function (b, c, d) {
                var e = [],
                    f, g, h = a.core.obj.parseParam({
                        expire: null,
                        path: "/",
                        domain: null,
                        secure: null,
                        encode: !0
                    }, d);
                h.encode == !0 && (c = escape(c));
                e.push(b + "=" + c);
                h.path != null && e.push("path=" + h.path);
                h.domain != null && e.push("domain=" + h.domain);
                h.secure != null && e.push(h.secure);
                if (h.expire != null) {
                    f = new Date;
                    g = f.getTime() + h.expire * 36e5;
                    f.setTime(g);
                    e.push("expires=" + f.toGMTString())
                }
                document.cookie = e.join(";")
            },
            get: function (a) {
                a = a.replace(/([\.\[\]\$])/g, "\\$1");
                var b = new RegExp(a + "=([^;]*)?;", "i"),
                    c = document.cookie + ";",
                    d = c.match(b);
                return d ? d[1] || "" : ""
            },
            remove: function (a, c) {
                c = c || {};
                c.expire = -10;
                b.set(a, "", c)
            }
        };
        return b
    });
    STK.register("core.util.drag", function (a) {
        var b = function (a) {
            a.cancelBubble = !0;
            return !1
        }, c = function (b, c) {
            b.clientX = c.clientX;
            b.clientY = c.clientY;
            b.pageX = c.clientX + a.core.util.scrollPos().left;
            b.pageY = c.clientY + a.core.util.scrollPos().top;
            return b
        };
        return function (d, e) {
            if (!a.core.dom.isNode(d)) throw "core.util.drag need Element as first parameter";
            var f = a.core.obj.parseParam({
                actRect: [],
                actObj: {}
            }, e),
                g = {}, h = a.core.evt.custEvent.define(f.actObj, "dragStart"),
                i = a.core.evt.custEvent.define(f.actObj, "dragEnd"),
                j = a.core.evt.custEvent.define(f.actObj, "draging"),
                k = function (d) {
                    var e = c({}, d);
                    document.body.onselectstart = function () {
                        return !1
                    };
                    a.core.evt.addEvent(document, "mousemove", l);
                    a.core.evt.addEvent(document, "mouseup", m);
                    a.core.evt.addEvent(document, "click", b, !0);
                    if (!a.IE) {
                        d.preventDefault();
                        d.stopPropagation()
                    }
                    a.core.evt.custEvent.fire(h, "dragStart", e);
                    return !1
                }, l = function (b) {
                    var d = c({}, b);
                    b.cancelBubble = !0;
                    a.core.evt.custEvent.fire(h, "draging", d)
                }, m = function (d) {
                    var e = c({}, d);
                    document.body.onselectstart = function () {
                        return !0
                    };
                    a.core.evt.removeEvent(document, "mousemove", l);
                    a.core.evt.removeEvent(document, "mouseup", m);
                    a.core.evt.removeEvent(document, "click", b, !0);
                    a.core.evt.custEvent.fire(h, "dragEnd", e)
                };
            a.core.evt.addEvent(d, "mousedown", k);
            g.destroy = function () {
                a.core.evt.removeEvent(d, "mousedown", k);
                f = null
            };
            g.getActObj = function () {
                return f.actObj
            };
            return g
        }
    });

    JK小注:这里有一个很个性化的做法,例如cascadeNode,drag,函数运行后都返回一个{}对象,而不像别人使用的类。其中cascadeNode方法,每次调用就会产生很多function,产生一些不必要的副作用。
    JK小注2:register的拆分粒度,也体现了这种代码可定制的粒度。如果粒度精细到方法,会有一些副作用:让最后产出的代码里有很多冗余。
    例如这样一段代码:

    View Code
    STK.register("core.arr.isArray", function (a) {
        return function (a) {
            return Object.prototype.toString.call(a) === "[object Array]"
        }
    });
    STK.register("core.arr.foreach", function (a) {
        var b = function (a, b) {
            var c = [];
            for (var d = 0, e = a.length; d < e; d += 1) {
                var f = b(a[d], d);
                if (f === !1) break;
                f !== null && (c[d] = f)
            }
            return c
        }, c = function (a, b) {
            var c = {};
            for (var d in a) {
                var e = b(a[d], d);
                if (e === !1) break;
                e !== null && (c[d] = e)
            }
            return c
        };
        return function (d, e) {
            if (a.core.arr.isArray(d) || d.length && d[0] !== undefined) return b(d, e);
            if (typeof d == "object") return c(d, e);
            return null
        }
    });
    STK.register("core.arr.indexOf", function (a) {
        return function (a, b) {
            if (b.indexOf) return b.indexOf(a);
            for (var c = 0, d = b.length; c < d; c++) if (b[c] === a) return c;
            return -1
        }
    });
    STK.register("core.arr.inArray", function (a) {
        return function (b, c) {
            return a.core.arr.indexOf(b, c) > -1
        }
    });

    把拆分精度调成命名空间级别,代码变少了(673/807 约= 83%):

    View Code
    STK.register("core.arr", function (a) {
        var b = function (a, b) {
            var c = [];
            for (var d = 0, e = a.length; d < e; d += 1) {
                var f = b(a[d], d);
                if (f === !1) break;
                f !== null && (c[d] = f)
            }
            return c
        }, c = function (a, b) {
            var c = {};
            for (var d in a) {
                var e = b(a[d], d);
                if (e === !1) break;
                e !== null && (c[d] = e)
            }
            return c
        };
        return {
            isArray: function (a) {
                return Object.prototype.toString.call(a) === "[object Array]"
            },
            foreach: function (d, e) {
                if (a.core.arr.isArray(d) || d.length && d[0] !== undefined) return b(d, e);
                if (typeof d == "object") return c(d, e);
                return null
            },
            indexOf: function (a, b) {
                if (b.indexOf) return b.indexOf(a);
                for (var c = 0, d = b.length; c < d; c++) if (b[c] === a) return c;
                return -1
            },
            inArray: function (b, c) {
                return a.core.arr.indexOf(b, c) > -1
            }
        }
    });

    JK小注3:STK里都是用严格的静态调用方式,所以可以简单的直接用文本分析来抽取依赖,而不必进行依赖配置,就可以进行代码定制(类似于QWrap的codepicker)或代码抽取(类似于QWrap的solo)。----估计新浪同学已经实现了,只是我们外面的同学看不到。

    3. Retouch加工

    STK库里做了一些易用性的Retouch,例如为了链式调用而添加的cascadeNode、为了减少命名空间深度添加的STK.regShort(c, b[c])序列等

    4. 其它杂想:
    JK小注:脚本库以一个简单的主干开始,其后就是统一的用register来堆砌,代码风格简朴(很少用this、prototype等)也很一致,看代码比较轻松,很容易理解。
    JK小注2:带了很多组件级别(简单又常用的)的内容放在核心库,最后的总体大小也才89k,赞一下。
    JK小注3:最终的使用方式还是静态调用为主,在易用性上还有很大的提升空间。
    JK小注4:接口命名、参数等可以有更多的参考一下标准、或大家的约定俗成,例如foreach应为forEach、setXY与position命名不配、有stopEvent但是没有stopPropagation、insertHTML的参数与标准的insertAdjacentHTML参数顺序不一致、等等。
    JK小注5:代码还可以写得更简单一些的,例如str.trim的写法可以简化一下、Browser应该可以省一半(可以参考一下别的脚本库)、由于register的第二个参数只支持生成器导致臃肿、由于拆分粒度导致的代码臃肿、Retouch可以有一些技巧来省代码(用methodize变换可以省cascadeNode里的代码)。

    5. 一个小礼物
    听说STK的帮助文档还有些欠缺,这里恰好有QWrap的一个冒烟测试工具,可以临时简陋的替代一下,帮助用户了解STK提供哪些方法与功能,也可以快速的看到方法对应的代码。
    注:因为引用的是压缩后的STK,所以参数名都是abcd这样的无意义名字,如果改成引用压缩前的代码,情况会得到改善。
    注:引用的是压缩后的代码,所以请用firefox看。
    http://dev.qwrap.com/resource/js/_tools/smokingtest/_examples/SmokingTest_STK.html

    特别声明:因为STK还未开源,而本文未经新浪授权,若有版权问题,请通知我把此文撤下。非常感谢!

  • 相关阅读:
    对软件工程的理解及问题
    使用Junit等工具进行单元测试
    软件工程
    进销存管理系统——可行性分析
    使用Junit等工具进行单元测试
    两个人的分组
    物联网软件工程 认识与问题
    二人项目
    使用Junit等工具进行单元测试
    软件工程
  • 原文地址:https://www.cnblogs.com/jkisjk/p/about_stk.html
Copyright © 2020-2023  润新知