zoukankan      html  css  js  c++  java
  • 我的MVVM框架 v2发布

    此版本仍然有许多knouckoutjs的影子,其中最下方那个normalizeJSON直接抄自knouckoutjs,我深感内疚。

    knouckoutjs的声明式绑定的值部分是非常复杂,它允许用户直接在里面使用函数,表达式什么,然后再往它两边花括号一包就是一个JSON的字面量。但直接转换为JSON是不行的,IE对键名为关键字的对象肯定报错,必须要用双引号括起。于是才有了normalizeJSON这东西。另一方面,knouckoutjs为VM的每个字段建立关联的方式非常复杂冗长,大量使用闭包。这个我的MVVM框架avalon做了改进。我非常讨厌knouckoutjs的事件绑定方式,那还不如直接用onclick=xxx绑好了,或结合jQuery, mass Framework能得到更大的回报。不过在v2中,我还没有想到更好的方法,既不跟风照搬,也没有搞出相应方案,因此事件绑定部分是一片空白。

    说说avalon v2的优点吧。只有730行,非常精简,虽然建立在功能残缺的基础上,没有knouckoutjs的虚拟结点,没有事件绑定,没有options绑定。

    双向绑定链的许多概念已经成形。我们做一件事,要做好,就要知道怎么去做,什么分步做。这些步骤,其中细节,全部都要抽象成特定的术语才能指导我们行动。knouckoutjs之所以能在前端MVC拥有一席之地,是因为它确定了许多以后被大家抄袭的东西。knouckout的三大理论基石:

    • 位于VM中的监控属性, 监控数组, 依赖监控属性。
    • 位于视图中的声明式绑定,以data-bind的形式写在标签中。
    • 位于视图中的动态模板,就是那些被声明式绑定圈起来的区域。

    avalon也建立对应的概念来指导自己的发展:

    • 位于VM中的原子监控者,依赖监控者,监控数组与绑定监控者。原子监控者对应ko的监控属性,它是由M中字段抽象而成,是一个函数。依赖监控者对应ko的依赖监控属性。在VM中,有些字段是建立在M的两个或多个字段的基础上。比如fullName 对应为 firstName+" "+lastName,但在M中是找不到它的跟影,但它在视图中却又是一个独立的个体,有专门的文本域给它。绑定监控者是我独创的,它是根据视图中的数据绑定转化而为,在ko中这些依然称之为依赖监控属性。但这一类监控函数分明是真正用于连结DOM树与VM的。它通过框架提供的默认绑定器将自己中数据渲染到DOM中。它的结构比一般的依赖监控属性复杂多了。依赖监控属性,knouckoutjs称之为Dependent Observables,在另一个著名的emberjs框架中,它叫做computed。在绑定监控者这类东西显然不需要返回值,它一般将比它低一级的原子监控者,依赖监控者当作事件回调来监控用户行为就行了。当用户改变了文本域的值,就相当于对这个原子监控者进行写入,从而引发整条依赖的更新。绑定监控者是与DOM树某个节点或某一些节点交道,当底层涌上来的变动要求它也刷新时,它就调用框架自带的绑定器进行干活。因此你看到绑定器都有一个叫update的函数,就是干这事。
    • 位于视图中的声明式绑定,我使用bind,但也可以配置你喜欢的属性名。我个人喜欢称之为数据绑定,其实它还兼容流程控制,事件绑定的活儿。
    • 位于视图中的动态模板,就是那些被声明式绑定圈起来的区域。动态模板与流程绑定最密切,它的实现好坏,牵及到最小化刷新的实现与内存的占有率。这个v2有实现比knouckout要高明多了。

    avalon在数据绑定上做了许多友好的改进,虽然与knouckoutjs一样支持表达式,是没有花括号的JSON。但它要求用户不用写双引号,这辛苦活由框架去实现了。

    avalon的自带绑定器有text绑定,html绑定,value绑定,class绑定,style绑定,attr绑定,display绑定,checked绑定,with绑定,if绑定,unless绑定,foreach绑定

    有关它们的使用,可以查看这里

    不过大家也急着看它们是怎么用的,因为写这文章时,我已经完成非常先进的v3了。这世界就是这样,当日本把小灵通卖给中国时,人家已经普及下下一代的方案。另一更让人心痛的比喻时,当中国在炫耀那垃圾的J10时,美国早在几十年前把飞碟,水上飞机,鸭嘴机玩一遍了。这就是差距。比差距更可怕的是知识的断层。面试时有人问我既然Sizzle这么快了,为什么还要自己写个?这说得那啥的,人家兜里有一百万跟我屁事啊!反正,我是搞出选择器,加载器,动画引擎,异步列队,操作流等东西出来。知识体系,至少是在前端的应用层是完整的。MVVM是前端究极解决方案。从 v1到v2,起码我算是逐渐吃透相关概念,形成自己的解决方案。这个对于后来想进入这领域的国人来说,也留下一点带路石。

    define("avalon",["$attr","$event"], function(){
        $.log("已加载avalon v2", 7);
        var BINDING = $.config.bindname || "bind", bridge = {}, uuid = 0, expando = new Date - 0;
        //将一个普通的对象转换为ViewModel,ViewModel里面每个对象都是监控者(监控函数或监控数组)
        $.ViewModel = function(data, model){
            model = model || {};
            if(Array.isArray(data)){
                return listWatch(data);
            }
            for(var p in data) {
                if(data.hasOwnProperty(p)) {
                    addWatchs(p, data[p], model);
                }
            }
            return model;
        }
        //监控数组,listWatch,它实质上就是一个数组,不过它的许多方法都被覆写了,
        //以便与DOM实现同步在您添加、删除、移动、刷新或替换数组中的项目时触发对应元素的局部刷新
        //我们可以在页面通过foreach绑定此对象
        function listWatch(array, models){
            models = models || [];
            for(var index = 0; index < array.length; index++){
                var f =  addWatchs(index, array[index], models);
                f.$value = f.$value || f;
            }
            String("push,pop,shift,unshift,splice,sort,reverse").replace($.rword, function(method){
                var nativeMethod = models[ method ];
                models[ method ] = function(){
                    nativeMethod.apply( models, arguments)
                    var Watchs = models["$"+expando];
                    for(var i = 0, Watch; Watch = Watchs[i++];){
                        Watch(method, arguments);
                    }
                }
            });
            models.removeAt = function(index){//移除指定索引上的元素
                models.splice(index, 1);
            }
            models.remove = function(item){//移除第一个等于给定值的元素
                var array = models.map(function(el){
                    return el();
                })
                var index = array.indexOf(item);
                models.removeAt(index);
            }
            //模拟监控函数的行为,监控函数在foreach都会生成一个$value函数
            models.$value = function(){
                return models
            }
            models.$value.$uuid = ++uuid;
            return models;
        }
        //将ViewMode绑定到元素节点上,没有指定默认是绑在body上
        $.View = function(model, node){
            node = node || document.body;
            //开始在其自身与孩子中绑定
            return setBindingsToElementAndChildren.call( node, model );
        }
        //我们根据用户提供的最初普通对象的键值,选用不同的方式转换成各种监控函数或监控数组
        var err = new Error("只能是字符串,数值,布尔,Null,Undefined,函数以及纯净的对象")
        function addWatchs( key, val, model ){
            switch( $.type( val )){
                case "Null":
                case "Undefined":
                case "String":
                case "Number":
                case "Boolean":
                    return atomWatch( key, val, model );
                case "Function":
                    return depsWatch( key, val, model, "get");
                case "Array":
                    var models = model[key] || (model[key] = []);
                    return listWatch( val, models );
                case "Object":
                    if($.isPlainObject( val )){
                        if( $.isFunction( val.setter ) && $.isFunction( val.getter )){
                            return  depsWatch( key, val, model, "setget");
                        }else{
                            var object = model[key] || (model[key] = {});
                            $.ViewModel( val,object );
                            object.$value = function(){
                                return object
                            }
                            return object
                        }
                    }else{
                        throw err
                    }
                    break;
                default:
                    throw err
            }
        }
    
        //atomWatch,原子监控者,它是最简单的监控函数,是指在ViewModel定义时,键值为基础类型的个体
        //它们是位于双向依赖链的最底层。不需要依赖于其他Watch!
        function atomWatch( key, val, host ){
            function Watch( neo ){
                if( bridge[ expando ] ){ //收集依赖于它的depsWatch,以便它的值改变时,通它们更新自身
                    $.Array.ensure( Watch.$deps, bridge[ expando ] );
                }
                if( arguments.length ){//在传参不等于已有值时,才更新自已,并通知其的依赖
                    if( Watch.$val !== neo ){
                        Watch.$val = neo;
                        updateDeps( Watch );
                    }
                }
                return Watch.$val;
            }
            Watch.$val = val;
            Watch.$uuid = ++uuid
            return addWatch( key, Watch, host );
        }
    
        //depsWatch,依赖监控者,是指在ViewModel定义时,值为类型为函数,或为一个拥有setter、getter函数的对象。
        //它们是位于双向绑定链的中间层,需要依赖于其他atomWatch或depsWatch的返回值计算自己的value。
        //当顶层的VM改变了,通知底层的改变
        //当底层的VM改变了,通知顶层的改变
        //当中间层的VM改变,通知两端的改变
        function depsWatch( key, val,host, type){
            var getter, setter//构建一个至少拥有getter,scope属性的对象
            if(type == "get"){//getter必然存在
                getter = val;
            }else if(type == "setget"){
                getter = val.getter;
                setter = val.setter;
                host = val.scope || host;
            }
            function Watch( neo ){
                if( bridge[ expando ] ){
                    //收集依赖于它的depsWatch与bindWatch,以便它的值改变时,通知它们更新自身
                    $.Array.ensure( Watch.$deps, bridge[ expando ] );
                }
                var change = false;
                if( arguments.length ){//写入新值
                    if( setter ){
                        setter.apply( host, arguments );
                    }
                }else{
                    if( !("$val" in Watch) ){
                        if( !Watch.$uuid ){
                            bridge[ expando ] = Watch;
                            Watch.$uuid = ++uuid;
                        }
                        neo = getter.call( host );
                        change = true;
                        delete bridge[ expando ];
                    }
                }
                //放到这里是为了当是最底层的域的值发出改变后,当前域跟着改变,然后再触发更高层的域
                if( change && (Watch.$val !== neo) ){
                    Watch.$val = neo;
                    //通知此域的所有直接依赖者更新自身
                    updateDeps( Watch );
                }
                return Watch.$val;
            }
            return addWatch( key, Watch, host );
        }
        //bindWatch,绑定监控者,用于DOM树或节点打交道的Watch,它们仅在用户调用了$.View(viewmodel, node ),
        //把写在元素节点上的bind属性的分解出来之时生成的。
        //names values 包含上一级的键名与值
        function bindWatch (node, names, values, key, str, binding, model ){
            function Watch( neo ){
                if( !Watch.$uuid ){ //只有在第一次执行它时才进入此分支
                    if( key == "foreach" ){
                        var arr = model[str]
                        var p = arr["$"+expando] || ( arr[ "$"+ expando] =  [] );
                        $.Array.ensure( p ,Watch);
                        arguments = ["start"];
                    }
                    bridge[ expando ] = Watch;
                }
                var callback, val;
                try{
                    val = Function(names, "return "+ str).apply(null, values );
                }catch(e){
                    return  $.log(e, 3)
                }
                if(typeof val == "function" ){ //&& isFinite( val.$uuid ) 如果返回值也是个域
                    callback = val; //这里的域为它所依赖的域
                    val = callback();//如果是监控体
                }
                if( !Watch.$uuid ){
                    delete bridge[ expando ];
                    Watch.$uuid = ++uuid;
                    //第四个参数供流程绑定使用
                    binding.init && binding.init(node, val, callback, Watch);
                }
                var method = arguments[0], args = arguments[1]
                if( typeof binding[method] == "function" ){
                    //处理foreach.start, sort, reserve, unshift, shift, pop, push....
                    var ret = binding[method]( Watch, val, Watch.fragments, method, args );
                    if(ret){
                        val = ret;
                    }
                }
                //只有执行到这里才知道要不要中断往下渲染
                binding.update(node, val, Watch, model, names, values);
                return Watch.$val = val;
            }
            return addWatch( "interacted" ,Watch, node);
        }
        //执行绑定在元素标签内的各种指令
        //MVVM不代表什么很炫的视觉效果之类的,它只是组织你代码的一种方式。有方便后期维护,松耦合等等优点而已
        var inputOne = $.oneObject("text,password,textarea,tel,url,search,number,month,email,datetime,week,datetime-local")
        $.ViewBindings = {
            text: {
                update:  function( node, val ){
                    val = val == null ? "" : val+""
                    if(node.childNodes.length === 1 && node.firstChild.nodeType == 3){
                        node.firstChild.data = val;
                    }else{
                        $( node ).text( val );
                    }
                }
            },
            value:{
                init: function(node, val, Watch){
                    if(/input|textarea/i.test(node.nodeName) && inputOne[node.type]){
                        $(node).on("input",function(){
                            Watch(node.value)
                        });
                    }
                },
                update:  function( node, val ){
                    node.value = val;
                }
            },
            html: {
                update:  function( node, val ){
                    $( node ).html( val );
                },
                stopBindings: true
            },
            //通过display样式控制显隐
            display: {
                update:  function( node, val ){
                    node.style.display = val ? "" : "none";
                }
            },
            enable: {
                update:  function( node, val ){
                    if (val && node.disabled)
                        node.removeAttribute("disabled");
                    else if ((!val) && (!node.disabled))
                        node.disabled = true;
                }
            },
            style: {
                update:  function( node, val ){
                    var style = node.style, styleName;
                    for (var name in val) {
                        styleName = $.cssName(name, style) || name;
                        style[styleName] = val[ name ] || "";
                    }
                }
            },
            "class": {
                update:  function( node, val ){
                    if (typeof val == "object") {
                        for (var className in val) {
                            var shouldHaveClass = val[className];
                            toggleClass(node, className, shouldHaveClass);
                        }
                    } else {
                        val = String(val || '');
                        toggleClass(node, val, true);
                    }
                }
            } ,
            attr: {
                update:  function( node, val ){
                    for (var name in val) {
                        $.attr(node, name, val[ name ] );
                    }
                }
            },
            checked: {
                init:  function( node, val, Watch ){
                    if(typeof Watch !== "function"){
                        throw new Error("check的值必须是一个Feild")
                    }
                    $(node).bind("change",function(){
                        Watch(node.checked);
                    });
                },
                update:function( node, val ){
                    if ( node.type == "checkbox" ) {
                        if (Array.isArray( val )) {
                            node.checked = val.indexOf(node.value) >= 0;
                        } else {
                            node.checked = val;
                        }
                    } else if (node.type == "radio") {
                        node.checked = ( node.value == val );
                    }
                }
            },
            template: {
                //它暂时只供内部使用
                update: function( node, val, callback, model, names, values){
                    var transfer = callback(), code = transfer[0], Watch = transfer[1];
                    var fragment = Watch.fragments[0];         //取得原始模板
                    if( code > 0 ){                            //处理with if 绑定
                        fragment.recover();                    //将Watch所引用着的节点移出DOM树
                        var elems = getChildren( fragment );   //取得它们当中的元素节点
                        node.appendChild( fragment );          //将Watch所引用着的节点放回DOM树
                        if( elems.length ){
                            if( code == 2 ){                    //处理with 绑定
                                model = transfer[2]
                            }
                            return setBindingsToChildren.call( elems, model, true, names, values )
                        }
                    }else if( code === 0 ){                    //处理unless 绑定
                        fragment.recover();
                    }
                    if( code < 0  && val ){                    //处理foreach 绑定
                        var fragments = Watch.fragments, models = val;
                        if(!models.length){
                             fragments[0].recover();
                             return
                        }
                        for( var i = 0, el ; el = fragments[i]; i++){
                            el.recover();                      //先回收,以防在unshift时,新添加的节点就插入在后面
                            elems = getChildren( el );
                            node.appendChild( el );            //继续往元素的子节点绑定数据
                            setBindingsToChildren.call( elems, models[i], true, names, values );
                        }
                    }
                },
                stopBindings: true
            }
        }
        $.ViewBindings.disable = {
            update: function( node, val ){
                $.ViewBindings.enable.update(node, !val);
            }
        }
        //if unless with foreach四种bindings都是基于template bindings
        "if,unless,with,foreach,case".replace($.rword, function( type ){
            $.ViewBindings[ type ] = {
                init: function(node, _, _, Watch){
                    node.normalize();                  //合并文本节点数
                    var fragment = node.ownerDocument.createDocumentFragment(), el
                    while((el = node.firstChild)){
                        fragment.appendChild(el);     //将node中的所有节点移出DOM树
                    }
                    Watch.fragments = [];             //添加一个数组属性,用于储存经过改造的文档碎片
                    Watch.fragment = fragment;         //最初的文档碎片,用于克隆
                    Watch.cloneFragment = function( dom, unshift ){ //改造文档碎片并放入数组
                        dom = dom || Watch.fragment.cloneNode(true);
                        var add = unshift == true ? "unshift" : "push"
                        Watch.fragments[add]( patchFragment(dom) );
                        return dom;
                    }
                    var clone = Watch.cloneFragment();  //先改造一翻,方便在update时调用recover方法
                    node.appendChild( clone );          //将文档碎片中的节点放回DOM树
                },
                update : function(node, val, Watch, model, names, values){
                    $.ViewBindings['template']['update'](node, val, function(){
                        switch(type){//返回结果可能为 -1 0 1 2
                            case "if":
                                return [ !!val - 0, Watch];//1 if
                            case "unless":
                                return [!val - 0, Watch]; //0  unless
                            case "with":
                                return [2, Watch, val];   //2  with
                            default:
                                return [-1, Watch];       //-1 foreach
                        }
                    }, model, names, values);
                },
                stopBindings: true
            }
        });
        //foreach绑定拥有大量的子方法,用于同步数据的增删改查与排序
        var foreach = $.ViewBindings.foreach;
        foreach.start = function( Watch, models, fragments, method, args ){
            if(!Array.isArray(models)){
                var array = []
                for(var key in models){
                    //通过这里模拟数组行为
                    if(models.hasOwnProperty(key) && (key !== "$value") && (key != "$"+expando)){
                        var value = models[key];
                        value.$value = value;
                        array.push( value );
                    }
                }
                models = array
            }
            for(var i = 1; i < models.length; i++ ){
                Watch.cloneFragment();
            }
            return models
        };
        //push ok
        foreach.push = function( Watch, models, fragments, method, args ){
            var l = fragments.length
            for(var index = 0; index < args.length; index++ ){
                var n = index + l;
                var f =  addWatchs(n, models[n], models);
                f.$value = f;
                Watch.cloneFragment()
            }
        }
        //unshift ok
        foreach.unshift = function( Watch, models, fragments, method, args ){
            for(var index = 0; index < args.length; index++ ){
                var f =  addWatchs(index, models[index], models);
                f.$value = f;
                Watch.cloneFragment(0, true)
            }
            for( index = 0; index < models.length; index++ ){
                models[index].$key = index
            }
        }
        // shift pop ok
        foreach.shift = function( Watch, models, fragments, method, args ){
            var fragment = fragments[method]()
            fragment.recover();
            for(var index = 0; index < models.length; index++ ){
                models[index].$key = index
            }
        }
        foreach.pop = foreach.shift;
        foreach.splice = function( Watch, models, fragments, method, args ){
            var start = args[0], n = args.length - 2;
            var removes = fragments.splice(start, args[1]);
            //移除对应的文档碎片
            for(var i = 0; i < removes.length; i++){
                removes[i].recover();
            }
            for(i = 0; i < n; i++ ){
                //将新数据封装成域
                var index = start + i
                var f =  addWatchs(index, models[ index ], models);
                f.$value = f;
                //为这些新数据创建对应的文档碎片
                var dom = Watch.fragment.cloneNode(true);
                Watch.fragments.splice(index, 0, patchFragment(dom) );
            }
            for( index = start+n; index < models.length; index++ ){
                models[index].$key = index
            }
        }
        //对文档碎片进行改造,通过nodes属性取得所有子节点的引用,以方便把它们一并移出DOM树或插入DOM树
        function patchFragment( fragment ){
            fragment.nodes = $.slice( fragment.childNodes );
            fragment.recover = function(){
                this.nodes.forEach(function( el ){
                    this.appendChild(el)
                },this);
            }
            return fragment;
        }
        //$.ViewBindings.class的辅助方法
        var toggleClass = function (node, className, shouldHaveClass) {
            var classes = (node.className || "").split(/\s+/);
            var hasClass = classes.indexOf( className) >= 0;//原className是否有这东西
            if (shouldHaveClass && !hasClass) {
                node.className += (classes[0] ? " " : "") + className;
            } else if (hasClass && !shouldHaveClass) {
                var newClassName = "";
                for (var i = 0; i < classes.length; i++)
                    if (classes[i] != className)
                        newClassName += classes[i] + " ";
                node.className = newClassName.trim();
            }
        }
    
    
        //为当前元素把数据隐藏与视图模块绑定在一块
        //参数分别为model, pnames, pvalues
        $.fn.model = function(){
            return $._data(this[0], "$model")
        }
        $.fn.$value = function(){
            var watch = $(this).model()
            if(typeof watch == "function"){
                return watch();
            }
        }
        //取得标签内的属性绑定,然后构建成bindWatch,并与ViewModel关联在一块
        function setBindingsToElement( model, pnames, pvalues ){
            var node = this;
            pnames = pnames || [];
            pvalues = pvalues || [];
            var attr = node.getAttribute( BINDING ), names = [], values = [], continueBindings = true,
            key, val, binding;
            $._data(node,"$model",model);
            for(var name in model){
                if(model.hasOwnProperty(name)){
                    names.push( name );
                    values.push( model[ name ] );
                }
            }
            if(pnames.length){
                pnames.forEach(function(name, i){
                    if(names.indexOf(name) === -1){
                        names.push(name);
                        values.push(pvalues[i])
                    }
                })
            }
            var array = normalizeJSON("{"+ attr+"}",true);
            for(var i = 0; i < array.length; i += 2){
                key = array[i]
                val = array[i+1];
                binding = $.ViewBindings[ key ];
                if( binding ){
                    if( binding.stopBindings ){
                        continueBindings = false;
                    }
                    if(key == "foreach" && Array.isArray(model[key]) && !model[key].length ){
                        continueBindings = false;
                      //  continue
                    }
                    bindWatch(node, names, values, key, val, binding, model);
                }
            }
            return continueBindings;
        }
        //在元素及其后代中将数据隐藏与viewModel关联在一起
        //参数分别为model, pnames, pvalues
        function setBindingsToElementAndChildren(){
            if ( this.nodeType === 1  ){
                var continueBindings = true;
                if( hasBindings( this ) ){
                    continueBindings = setBindingsToElement.apply(this, arguments);
                }
                if( continueBindings ){
                    var elems = getChildren( this );
                    elems.length && setBindingsToChildren.apply(elems, arguments);
                }
            }
        }
        //参数分别为model, pnames, pvalues
        function setBindingsToChildren( ){
            for(var i = 0, n = this.length; i < n ; i++){
                setBindingsToElementAndChildren.apply( this[i], arguments );
            }
        }
        //通知此监控函数或数组的所有直接依赖者更新自身
        function updateDeps(Watch){
            var list = Watch.$deps || [] ;
            if( list.length ){
                var safelist = list.concat();
                for(var i = 0, el; el = safelist[i++];){
                    delete el.$val;
                    el()
                }
            }
        }
        //判定是否设置数据绑定的标记
        function hasBindings( node ){
            var str = node.getAttribute( BINDING );
            return typeof str === "string" && str.indexOf(":") > 1
        }
        //取得元素的所有子元素节点
        function getChildren( node ){
            var elems = [] ,ri = 0;
            for (node = node.firstChild; node; node = node.nextSibling){
                if (node.nodeType === 1){
                    elems[ri++] = node;
                }
            }
            return elems;
        }
        //为监控函数添加更多必须的方法或属性
        function addWatch( key, Watch, host ){
            //收集依赖于它的depsWatch与bindWatch,以便它的值改变时,通知它们更新自身
            Watch.toString = Watch.valueOf = function(){
                if( bridge[ expando ] ){
                    $.Array.ensure( Watch.$deps, bridge[ expando ] );
                }
                return Watch.$val
            }
            if(!host.nodeType){
                Watch.$key = key;
                host[ key ] = Watch;
            }
            Watch.$deps = [];
            Watch();
            return Watch;
        }
        //============================================================
        // 将bindings变成一个对象或一个数组 by 司徒正美
        //============================================================
        function normalizeJSON(json, array){
            var keyValueArray = parseObjectLiteral(json),resultStrings = [],keyValueEntry;
            for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
                if (resultStrings.length > 0 && !array)
                    resultStrings.push(",");
                if (keyValueEntry['key']) {
                    var key = keyValueEntry['key'].trim();
                    var quotedKey = ensureQuoted(key, array), val = keyValueEntry['value'].trim();
                    resultStrings.push(quotedKey);
                    if(!array)
                        resultStrings.push(":");
                    if(val.charAt(0) == "{" && val.charAt(val.length - 1) == "}"){
                        val = normalizeJSON( val );//逐层加引号
                    }
                    resultStrings.push(val);
                } else if (keyValueEntry['unknown']) {
                    resultStrings.push(keyValueEntry['unknown']);//基于跑到这里就是出错了
                }
            }
            if(array){
                return resultStrings
            }
            resultStrings = resultStrings.join("");
            return "{" +resultStrings +"}";
        };
        //============================================================
        // normalizeJSON的辅助函数 by 司徒正美
        //============================================================
        var restoreCapturedTokensRegex = /\@mass_token_(\d+)\@/g;
        function restoreTokens(string, tokens) {
            var prevValue = null;
            while (string != prevValue) { //把原字符串放回占位符的位置之上
                prevValue = string;
                string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
                    return tokens[tokenIndex];
                });
            }
            return string;
        }
        function parseObjectLiteral(objectLiteralString) {
            var str = objectLiteralString.trim();
            if (str.length < 3)
                return [];
            if (str.charAt(0) === "{")// 去掉最开始{与最后的}
                str = str.substring(1, str.length - 1);
            // 首先用占位符把字段中的字符串与正则处理掉
            var tokens = [];
            var tokenStart = null, tokenEndChar;
            for (var position = 0; position < str.length; position++) {
                var c = str.charAt(position);//IE6字符串不支持[],开始一个个字符分析
                if (tokenStart === null) {
                    switch (c) {
                        case '"':
                        case "'":
                        case "/":
                            tokenStart = position;//索引
                            tokenEndChar = c;//值
                            break;
                    }//如果再次找到一个与tokenEndChar相同的字符,并且此字符前面不是转义符
                } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
                    var token = str.substring(tokenStart, position + 1);
                    tokens.push(token);
                    var replacement = "@mass_token_" + (tokens.length - 1) + "@";//对应的占位符
                    str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
                    position -= (token.length - replacement.length);
                    tokenStart = null;
                }
            }
            // 将{},[],()等括起来的部分全部用占位符代替
            tokenEndChar = tokenStart = null;
            var tokenDepth = 0, tokenStartChar = null;
            for (position = 0; position < str.length; position++) {
                var c = str.charAt(position);
                if (tokenStart === null) {
                    switch (c) {
                        case "{":
                            tokenStart = position;
                            tokenStartChar = c;
                            tokenEndChar = "}";
                            break;
                        case "(":
                            tokenStart = position;
                            tokenStartChar = c;
                            tokenEndChar = ")";
                            break;
                        case "[":
                            tokenStart = position;
                            tokenStartChar = c;
                            tokenEndChar = "]";
                            break;
                    }
                }
                if (c === tokenStartChar)
                    tokenDepth++;
                else if (c === tokenEndChar) {
                    tokenDepth--;
                    if (tokenDepth === 0) {
                        var token = str.substring(tokenStart, position + 1);
                        tokens.push(token);
                        replacement = "@mass_token_" + (tokens.length - 1) + "@";
                        str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
                        position -= (token.length - replacement.length);
                        tokenStart = null;
                    }
                }
            }
            //拆解字段,还原占位符的部分
            var result = [];
            var keyValuePairs = str.split(",");
            for (var i = 0, j = keyValuePairs.length; i < j; i++) {
                var pair = keyValuePairs[i];
                var colonPos = pair.indexOf(":");
                if ((colonPos > 0) && (colonPos < pair.length - 1)) {
                    var key = pair.substring(0, colonPos);
                    var value = pair.substring(colonPos + 1);
                    result.push({
                        'key': restoreTokens(key, tokens),
                        'value': restoreTokens(value, tokens)
                    });
                } else {//到这里应该抛错吧
                    result.push({
                        'unknown': restoreTokens(pair, tokens)
                    });
                }
            }
            return result;
        }
        function ensureQuoted(key, array) {
            var trimmedKey = key.trim()
            if(array){
                return trimmedKey;
            }
            switch (trimmedKey.length && trimmedKey.charAt(0)) {
                case "'":
                case '"':
                    return key;
                default:
                    return "'" + trimmedKey + "'";
            }
        }
    })
    

    相关链接:avalon v1

    机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
  • 相关阅读:
    Linux如何通过命令查看日志文件的某几行(中间几行或最后几行)
    将生成200 个激活码(或者优惠券)保存到 oracle关系型数据库中
    将你的 QQ 头像(或者微博头像)右上角加上红色的数字,类似于微信未读信息数量那种提示效果
    面试笔试题:多表关联的update语句、将in中的查询条件按顺序输出和SQL语句增加列、修改列、删除列
    sql中级到高级
    Linux常用命令
    类的特殊成员方法
    正则表达式的方法匹配规则
    启动ecilpse 报错an error has occurred. see the log file
    访问修饰符
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2813735.html
Copyright © 2011-2022 走看看