nodom源码阅读

nodom2.js中出现的所有类
  • Class
  • Complier
  • Directive
  • DirectiveManager
  • DirectiveFactory
  • Element
  • Error
  • Event
  • Expression
  • ExpressionFacory
  • ExternalEvent
  • Factory
  • Filter
  • FilterManager
  • Linker
  • MessageFactory
  • MethodFactory
  • Model
  • ModelFactory
  • Module
  • ModuleFactory
  • nodom
  • Renderer
  • Route
  • Router
  • RouterTree
  • Scheduler
    ---------------------------各个类的功能------------------------------

Class:模拟java的反射

补充知识点:java的反射知识:java中类与对象,在运行状态中,对于任意的一个类,都能知道其属性与方法,对于任意的对象,都能够调用它的任意方法和属性

Class.items = new Map(); 

静态方法,添加类名与类,

/**
     * 添加class
     * @param clsName   类名
     * @param cls       class
     */
static add(classsName,classt){
        this.items.set(classsName,classs);
    }

上面add函数的用处

Class.add('Directive',Directive);
Class.add('Filter',Filter);
Class.add('Expression',Expression);
Class.add('Element',Element);
Class.add('Module',Module);
Class.add('Model',Model);
Class.add('FilterFactory',FilterFactory);
Class.add('DirectiveFactory',DirectiveFactory);
Class.add('ExpressionFactory',ExpressionFactory);
Class.add('Route',Route);

Class.items存储了10个类,

/** 
     * 通过类名获取class
     * @param clsName   类名
     * @return          类
     */
    static getClass(clsName){
        return this.items.get(clsName);
    }

可以看见上面函数,通过类名,获取对应的类,也是一个静态的方法

/**
     * 创建实例(对象)
     * @param clsName   类名
     * @param 
     * @return          根据类创建的实例
     */
    static newInstance(clsName,config){
        let cls = this.items.get(clsName);
        if (cls === undefined) { }
        //Reflect.construct() 方法的行为有点像 new 操作符 构造函数 ,
        //相当于运行 new target(...args)
        return Reflect.construct(cls);
    }

参数config似乎没有用,不过很有可能是用于创建不同类的实例时,初始化需要的参数
以上代码中出现了Reflect.construct(cls),查MDN知,Reflect.construct(className:String,args:Array)
我们知道func.apply({},args:Array)与func.call(this,..args)是函数绑定this,并写参。Reflect.construct()有可能是调用封装apply对construct函数的调用。其最后返回的是一个对象,等同与new className(...args)
new 是call 对constrcut 的封装。,Refletc.construct是apply对constrcut的封装

Complier:编译器,负责模板的编译

  /**
     * 编译 
     * @param view      指定的view
     * @param module    模块
     * @return          view
     */
    static compile(view){
        return compileEl(view);
    }

上面这个函数,好像没有用,因为下面有同样的函数给覆盖了,js中不存在,java/c++那样函数名相同,参数不同,实际用的哪一个函数由编译器去判断这样的便捷方法

/**
     * 编译
     * @param elementStr   待编译element
     * @param module       模块
     * @return          虚拟element
     */
    static compile(module,elementStr){
        let dom = new Element();//元素实例
        const div = nodom.newEl('div');
        div.innerHTML = elementStr;
        dom.isRoot = true;
        //调用编译
        this.compileDom(module,div,dom);
        return dom;
    }

看看,这段代码的大意是:用一个div包裹要编译的elementStr,创建一个Element的实例,最后调一个函数去this.compileDom(module,div,dom);

 static compileDom(module,ele,parent){
...
 }

上面的函数比较长就不放出来了,这是一个编译Dom的函数
在函数中创建 Element的实例,将ele的属性,子节点,文本一个一个的,判断,取值,扩展,生成Element实例。

 static compileExpression(module,exprStr){
    ....
   }

编译字符串如“{{name}}",首先使用正则得到一个迭代器,遍历。注意在遍历里面存在对在不在{{}}的字符串进行的不同的处理,在{{}}中的字符串作为表达式,之外的以此分割,压入数组。
<statement>:="<String>"
<String>:=<String>|{{<expression>}}|

Directive 指令类

/**
     * 构造方法
     * @param type      类型
     * @param value     指令值
     * @param vdom      虚拟dom
     * @param module    模块
     */
constructor(type,value,vdom,module){
        this.className = 'Directive';
        this.type = type;
        
        if(value !== undefined){
            this.value = value.trim();
        }

        if(type !== undefined){
            nodom.apply(DirectiveManager.init,DirectiveManager,[this,vdom,module]);
        }
        this.id = nodom.genId();
    }

构造函数,首先该对象的类名,与指令的类型,存在类型就调用,指令管理类的静态函数init,参数,改指令实例,指令管理类,虚拟dom,模块。
nodom.apply是对Reflect.apply(foo,obj,args);的封装,类似funcName.apply(obj,args:Array),不过是直接使用一个Reflect.apply函数,将需要调用的函数,函数需要的参数,作为其参,使用

/**
     * 执行
     * @param value     
     * @return          
     */
    exec(value){
        let args = [this.module,this.type,value].concat(this.params);
        return nodom.apply(DirectiveManager.exec,DirectiveManager,args);
    }

[this.module,this.type,value].concat(this.params); concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
类似上一个函数,,在参数与调用的函数上有些不同

DirectiveManager 指令管理类

/**
     * 创建指令类型
     * @param name      指令类型名
     * @param config    配置对象{order:优先级,init:初始化函数,handler:渲染处理函数}
     */
    static addType(name, config) {
        //重复,便抛出错误
        if(this.directiveTypes.has(name)){
            throw Error.handle('exist1',nodom.words.directiveType,name);
        }
        if(!nodom.isObject(config)){
            throw Error.handle('invoke','DirectiveManager.addType',1,'Function');   
        }
        //默认优先级10
        //没有初始化,就默认为10
        config.prio = config.prio || 10;
        //
        this.directiveTypes.set(name,config);
    }

一个指令名,对应一个配置对象config,config对象中,有,优先级,初始化函数,渲染处理函数。优先级默认为10,指令名+配置对象config使用map对象的实例存储。


    /**
     * 移除过滤器类型
     * @param name  过滤器类型名
     */
    static removeType(name) {
        //can not edit types
        if(this.cantEditTypes.indexOf(name) !== -1){//是不可编辑对象
            throw Error.handle('notupd',nodom.words.system + nodom.words.directiveType,name);
        }
        if(!this.directiveTypes.has(name)){//不存在改指令
            throw Error.handle('notexist1',nodom.words.directiveType,name);
        }
        delete this.directiveTypes.delete(name);//移除
    }

删操作

/**
     * 获取类型
     */
    static getType(name){
        return this.directiveTypes.get(name);
    }

查操作

 /**
     * 是否有某个指令类型
     * @param type      指令类型名
     * @return          true/false
     */
    static hasType(name){
        return this.directiveTypes.has(name);
    }

判断

/**
     * 指令初始化
     * @param diective      指令
     * @param dom                  虚拟dom
     * @param module             模块
     * @return                   
     */
    static init(directive,dom,module){
        let dt = this.directiveTypes.get(directive.type);
        if(dt === undefined){
            throw Error.handle('notexist1',nodom.words.directiveType,name);
        }
        return dt.init(directive,dom,module);//队列,父出子继
    }

指令初始化,是静态函数,在Map中查询到入参的指令,返回config对象,后调用config.init函数,去初始化指令。

/**
     * 执行指令
     * @param arguments 参数数组  0模块 1指令类型 2待处理值 3-n处理参数
     */
    //execute执行
    static exec(directive,ele,parent){
        if(!this.directiveTypes.has(directive.type)){//不存在
            throw Error.handle('notexist1',nodom.words.directiveType,type);
        }
        //调用
        return nodom.apply(this.directiveTypes.get(directive.type).handle,null,arguments);
    }

执行函数,与上一个函数的思想相同,不过这里有个特殊的对象,arguments对象那个,这个对象是参数对象,具有以参数为数组元素的类数组,同时有一个代替本身函数的函数。arguments.callee

DirectiveManager.directiveTypes = new Map();//Map对像
//不可编辑类型
DirectiveManager.cantEditTypes = ['model','repeat','if','else','show','class','field'];

一个是指令的存储结构,一个是禁止修改项(框架保留字)

Element类 虚拟dom element

constructor(){
        const me = this;
        me.className = 'Element';
        me.directives = [];
        me.props = {};              //属性集合
        me.events = {};             //事件集合
        me.exprProps = {};          //含表达式的属性集合
        me.changeProps = [];        //修改后的属性
        me.removeProps = [];        //待删除属性
        me.children = [];           //子element
        me.parentKey = undefined;   //父对象key
        me.tagName = undefined;     //元素名
        me.dontRender = false;      //不渲染标志,不渲染到html
        me.key = nodom.genId();
    }

赋予创建的dom元素,指令,属性,事件,表达式属性集合,修改后的属性,待删除的属性,子元素,父级的key,元素名,不渲染的标志,元素的key

render(module,parent){
        const me = this;
        // 设置父对象
        if (parent) {
            //模块的父key,这个element实例的父的key值
            me.parentKey = parent.key;
            // 设置modelId
            if(!me.modelId){
                me.modelId = parent.modelId;
            }
        }
        //根据元素名便能判断?
        if(me.tagName !== undefined){ //element//元素名存在
            me.handleProps(module);//元素的属性处理,参数是模块
            //某些指令可能会终止渲染,如果返回false,则不继续渲染
            me.handleDirectives(module,parent);
        }else{ //textContent
            me.handleTextContent(module);
        }

        //dontrender 为false才渲染子节点
        /*
          下面if语句的作用:
          当前元素需要渲染,便执行
          当前元素的子元素,同样要被渲染
          但是子元素有些不希望被渲染,就从当前元素移除子元素
          这里可以改改
        */
        if(!me.dontRender){
            //子节点渲染
            for(let i=0;i<me.children.length;i++){
                let item = me.children[i];
                item.render(module,me);
                //dontRender 删除
                if(item.dontRender){
                    me.removeChild(item);
                    i--;
                }
            }   
        }
        
        return true;
    }

元素的渲染,核心在根据tagName是否有意义,进行不同的处理,对子元素的渲染处理

/**
    * 渲染到html element
    * @param module    模块
    * @param el        对应的element
    * @param type      类型
    * @param parent    父虚拟dom
    */
   renderToHtml(module,params){
............
}

模块是渲染的位置的抽象化,me是虚拟dom,在该函数中通过,模块定位el,使用newEl或newText生成dom节点。其中有三个重要函数

function newEl(vdom,parent,parentEl){
            //创建element

            let el = document.createElement(vdom.tagName);
            //设置属性
            nodom.getOwnProps(vdom.props).forEach((p)=>{
                el.setAttribute(p,vdom.props[p]);
            });
            el.setAttribute('key', vdom.key);
            //以上生成了dom元素
            vdom.handleEvents(module,el,parent,parentEl);
            return el;
        }

        /**
         * 新建文本节点
         * el1 = newText(me.textContent,me);
         */
        function newText(text,dom){
            if('html' === dom.type){ //html fragment 或 element
                let div = nodom.newEl('div');
                div.setAttribute('key',dom.key);
                div.appendChild(text);
                return div;
            }else{
                return document.createTextNode(text);   
            }
        }

        /**
         * 生成子节点
         * @param pEl   父节点
         * @param vNode 虚拟dom父节点    
         */
        function genSub(pEl,vNode){
            if(vNode.children && vNode.children.length>0){
                vNode.children.forEach((item)=>{
                    let el1;
                    if(item.tagName){
                        el1 = newEl(item,vNode,pEl);
                        genSub(el1,item);
                    }else{
                        el1 = newText(item.textContent,item);
                    }
                    pEl.appendChild(el1);
                });
            }
        }

以vdom,为模板,使用存储的prop与tagName生成对应的html标签对象,没有tagName便使用一个div包裹虚拟dom的textcontent内容

    clone(module){
        const me = this;
        let dst = new Element();
        //简单属性
        nodom.getOwnProps(me).forEach((p)=>{
            if(typeof me[p] !== 'object'){
                dst[p] = me[p];
            }
        });

        me.directives.forEach((d)=>{
            dst.directives.push(d);
        });

        //普通
        nodom.getOwnProps(me.props).forEach((d)=>{
            dst.props[d]=me.props[d];
        });

        //表达式属性
        nodom.getOwnProps(me.exprProps).forEach((d)=>{
            dst.exprProps[d]=me.exprProps[d];
        });

        //事件
        nodom.getOwnProps(me.events).forEach((d)=>{
            dst.events[d]=me.events[d].clone();
        });

        //表达式
        dst.expressions = me.expressions;
    
        me.children.forEach((d)=>{
            dst.children.push(d.clone(module));
        });
        return dst;
    }

克隆从普通属性,表达式属性,事件,表达式,都遍历一遍后赋值,这是一个深拷贝

handleDirectives(module,parent){
        const me = this;
        if(me.dontRender){//没有被渲染,代表不会处理指令
            return false;
        }
        let dirs = me.directives;
        for(let i=0;i<dirs.length && !me.dontRender;i++){
            DirectiveManager.exec(dirs[i],me,module,parent);//执行指令
        }
        return true;
    }

使用指令管理类处理指令

/**
     * 表达式预处理,添加到expression计算队列
     * 
     */
    handleExpression(exprArr,module){
        const me = this;
        if(me.dontRender){
            return;
        }
        let value = '';
        let model = module.modelFactory.get(me.modelId);
        exprArr.forEach((v)=>{
            if(typeof v === 'number'){  //处理表达式
                // 统一添加到表达式计算队列
                let v1 = module.expressionFactory.get(v).val(model);
                //html或 fragment
                if(v1 instanceof DocumentFragment || nodom.isEl(v1)){
                    // 设置类型
                    me.type = 'html';
                    return v1;
                }
                value += v1;
            }else{
                value += v;
            }
        });
        return value;
    }

似乎是,将字符,使用model数据替换

/**
     * 处理属性(带表达式)
     * me.handleProps(module);
     */
    handleProps(module){
        const me = this;
        if(me.dontRender){
            return;
        }
        nodom.getOwnProps(me.exprProps).forEach((item)=>{
            //属性值为数组,则为表达式
            if(nodom.isArray(me.exprProps[item])){
                me.props[item] = me.handleExpression(me.exprProps[item],module);
            }else if(me.exprProps[item] instanceof Expression){ //单个表达式
                me.props[item] = me.exprProps[item].val(module.modelFactory.get(me.modelId));
            }
        });
    }

将表达式中的元素处理后,加入props中成为普通属性

/**
     * 处理文本(表达式)
     *  me.handleTextContent(module);
     */
    handleTextContent(module){
        const me = this;
        if(me.dontRender){
            return;
        }//这里的expressions属性,在定义的时候并不存在
        //在complieDom时,会赋值给expression
        if(me.expressions !== undefined){
            me.textContent =  me.handleExpression(me.expressions,module);
        }
    }

处理文本中的表达式

/**
     * 处理事件
     * @param module 
     * @param model
     * @param el
     * @param parent
     *  vdom.handleEvents(module,el,parent,parentEl);
     */
    handleEvents(module,el,parent,parentEl){
        const me = this;
        
        if(me.events.length === 0){
            return;
        }
        
        nodom.getOwnProps(me.events).forEach((en)=>{
            let ev = me.events[en];
            if(ev.delg && parent){  //代理到父对象
                ev.delegateTo(module,me,el,parent,parentEl);
            }else{
                ev.bind(module,me,el);
            }
        });
    }

存在代理情况,便将事件代理给父级,否则调用Event对象的绑定方法,有两种方法,第一种是扩展事件中的触摸事件,在扩展事件中注册,第二种是,在根据虚拟dom创建的dom上添加事件监听器

/**
     * 移除指令
     * @param directives    待删除的指令集
     */
    removeDirectives(delDirectives){
        const me = this;
        for(let i=me.directives.length-1;i>=0;i--){
            let d = me.directives[i];
            for(let j=0;j<delDirectives.length;j++){
                if(d.type===delDirectives[j]){
                    me.directives.splice(i,1);
                }
            }
        }
    }

    /**
     * 是否有某个类型的指令
     * 
     * @param directiveType     指令类型名
     * @return true/false
     */
    hasDirective(directiveType){
        const me = this;
        for(let i=0;i<me.directives.length;i++){
            if(me.directives[i].type === directiveType){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取某个类型的指令
     * @param directiveType     指令类型名
     * @return directive
     */
    getDirective(directiveType){
        const me = this;
        for(let i=0;i<me.directives.length;i++){
            if(me.directives[i].type === directiveType){
                return me.directives[i];
            }
        }
    }

删,查,操作

/**
     * 从虚拟dom树和html dom树删除自己
     * @param module    模块
     * @param html      删除html中的
     */
    remove(module,html){
        const me = this;
        // 从父树中移除
        if(me.parentKey !== undefined){
            let p = module.renderTree.query(me.parentKey);
            if(p){
                p.removeChild(me);
            }
        }
        
        // 删除html dom节点
        if(html && module && module.container){
            let el = module.container.querySelector("[key='"+ me.key +"']");
            if(el !== null){
                nodom.remove(el);
            }
        }
        me.free();
    }

删除一个Element,要从虚拟dom删除,也要从html文档中删除,最后,回收本对象分配的内存

/**
     * 从html删除
     */
    removeFromHtml(module){
        const me = this;
        let el = module.container.querySelector("[key='"+ me.key +"']");
        if(el !== null){
            nodom.remove(el);
        }
    }

是上面的一部分,抽出来做为一个函数存在

/**
     * 移除子节点
     */
    removeChild(dom){
        const me = this;
        let ind = -1;
        // 移除
        if(nodom.isArray(me.children) && (ind = me.children.indexOf(dom)) !== -1) {
            me.children.splice(ind,1);
        }
    }

删除子节点,主要是splice函数的使用

/**
     * 替换目标节点
     * @param dst   目标节点 
     */
    replace(dst){
        const me = this;
        if(!dst.parent){
            return false;
        }
        let ind = dst.parent.children.indexOf(dst);
        if(ind === -1){
            return false;
        }
        //替换
        dst.parent.children.splice(ind,1,me);
        return true;
    }

替换的思路是,找到目标节点的父级节点,父级节点,删去目标节点,然后加上本节点

/**
     * 是否包含节点
     * @param dom   包含的节点 
     */
    contains(dom){
        const me = this;
        /*
        从节点,沿父级向上查询
        me包含dom则dom不会为undefined
        如果不包含dom会被赋值为undefined
        因为,根结点没有父级

        */
        for(;dom!==undefined && dom!==me;dom=dom.parent);
        return dom !== undefined;
    }

这个函数的思路有点不一样,一般是从本节查找孩子节点,但是本节点不同是从查找的孩子节点,顺着父级查找,这样的好处是,不会访问其他兄弟节点。查找速度会更快。

/**
     * 查找子孙节点
     * @param key   element key
     * @return vdom element/undefined
         * dom1.compare(dom2,retArr,me);
     */
    query(key){
        const me = this;
        if(me.key === key){
            return me;
        }
        for(let i=0;i<me.children.length;i++){
            let dom = me.children[i].query(key);
            if(dom){
                return dom;
            }
        }
    }

查找子孙节点递归法深度优先搜素

compare(dst,retArr,parentNode){
........
}

对当前节点与子节点有着这样的处理,当前节点,比较类型,类型相同,分为文本与元素,文本比较textcontent,元素比较属性,属性有两种,改变的与不存在的(删除的)在,改变与删除的操作不再此函数中,此函数是一个相当于回收站,存储需要的改变的元素属性,同时遍历子节点,比较key值,比较属性,最后都会归于一个回收站对象。

Error

/**
    * 按照消息编号进行处理并返回消息内容
    * @param 异常名
    * @param args1,args2,args3,... 待替换的参数
    * @return 转换后的消息
    */
   
   static handle(errname){//{}中的字符
      var reg = new RegExp(/\{.+?\}/);
      
      var msg = nodom.ErrorMsgs[errname];
      if(msg === undefined){
        return "未知错误";
      }
      var args = [msg];
      for(var i=1;i<arguments.length;i++){
        args.push(arguments[i]);
      }
      //处理字符串
      return nodom.compileStr.apply(null,args);
   }

可以看见,Error本身没对错误进行处理,只是使用nodom中的函数,Error只是提供一个使用的时机

Event

 /**
     * @param eventName     事件名
     * @param eventStr      事件串(可空) 方法名[:delg(代理到父对象):nopopo(禁止冒泡):once(只执行一次):useCapture]
     */
    constructor(eventName,eventStr){
        const me = this;
        me.events = undefined;  //子事件,存储代理事件集合,结构为{'click':[ev1,ev2],'swipe':[],...}
        me.name = eventName;

        //如果事件串不为空,则不需要处理
        if(eventStr){
            eventStr.split(':').forEach((item,i)=>{
                item = item.trim();
                if(i===0){    //事件方法
                    me.handler = item;
                }else{              //事件附加参数
                    me[item] = true;
                }
            });    
        }
        //触屏事件根据设备类型进行处理
        if(nodom.config.deviceType === 1){
            switch(me.name){
                case 'click':
                    me.name = 'tap';
                    break;
                case 'mousedown':
                    me.name = 'touchstart';
                    break;
                case 'mouseup':
                    me.name = 'touchend';
                    break;
                case 'mousemove':
                    me.name = 'touchmove';
                    break;
            }
        }

    }

构造event,提取事件字符串中的关键字如:delg,使用switch语句连接PC端与手机端的鼠标事件(触摸)。

 / * 
   *@param     e         事件
   */
fire(e){
.....
}

触发事件,根据本类的capture选择不同的处理事件的顺序,

 /**
     * 绑定事件
     * @param el      element
     * @param vdom     view模型
     * @param module    模块
     * ev.bind(module,me,el);
     */
    bind(module,vdom,el){
        const me = this;
        me.virtualDom = vdom;
        me.el = el;
        me.module = module;
        //触屏事件
        if(ExternalEvent.TouchEvents[me.name]){
            ExternalEvent.regist(me);//注册当前事件
        }else{//处理事件函数是fire函数的封装
            me.handleEvent = function(e){
                me.fire(e);
            }
            el.addEventListener(me.name,me.handleEvent,me.capture);
        }
    }

事件绑定模块,虚拟dom,element.,根据扩展事件的有无,注册/监听事件

/**
     * 
     * 事件代理到父对象
     * @param parent    父虚拟dom
     * @param el        事件作用的html element 
     * @param vdom       view模型
     * @param parentEl   父对象
     * @param module   模块
     */
    delegateTo(module,vdom,el,parent,parentEl){
        const me = this;
        me.virtualDom = vdom;
        me.el = el;
        me.module = module;

        //如果不存在父对象,则用body
        if(!parentEl){
            parentEl = document.body;
        }

        //父节点如果没有这个事件,则新建,否则直接指向父节点相应事件
        if(!parent.events[me.name]){
            let ev = new Event(me.name);
            ev.bind(module,parent,parentEl);
            parent.events[me.name] = ev;
        }

        parent.events[me.name].addSubEvt(me);
    }

代理给父级,存入父虚拟dom的事件数组

 /**
     * 添加子事件
     * @param ev    事件
     */
    addSubEvt(ev){
        const me = this;
        if(!me.events){
            me.events = Object.create(null);
        }
        
        //事件类型对应的数组
        if(!me.events[me.name]){
            me.events[me.name] = new Array();
        }
        me.events[me.name].push(ev);
    }

 removeSubEvt(ev){
        const me = this;
        if(me.events === undefined || me.events[ev.name] === undefined){
            return;
        }
        let ind = me.events[ev.name].indexOf(ev);
        if(ind !== -1){
            me.events[ev.name].splice(ind,1);
            if(me.events[ev.name].length === 0){
                me.events.delete(ev.name);
            }
        }
    }
 clone(){
        const me = this;
        let evt = new Event(me.name);
        let arr = ['delg','once','nopopo','useCapture','handler','handleEvent','module'];
        arr.forEach((item)=>{
            evt[item] = me[item];
        });
        return evt;
    }

let arr = ['delg','once','nopopo','useCapture','handler','handleEvent','module'];一个event对象包含的所用属性的名称

Expression

/**
     * @param exprStr   表达式串
     */
    constructor(exprStr,module){
        const me = this;
        //旧值
        me.fields = [];                 // 字段数组
        me.modelMap = {};               //一个expression可能被多次使用,以modelid进行区分,针对不同的模型id构建对象{modelId:{fieldValue:,value:}
        me.id = nodom.genId();
        if(module){
            me.moduleName = module.name;
            module.expressionFactory.add(me.id,me);
        }

        if (exprStr) {
            //堆
            me.stack = this.init(exprStr);
        }
    }

表达式与存储表达式的结构,表达是中的栈

init(exprStr)(
....
}

从字符串中,提取变量,函数

/**
     * 表达式计算
     * @param model     模型 或 fieldObj对象 
     * @param modelId   模型id(model为fieldObj时不能为空)
     */
    val(model,modelId){
        const me = this;
        if(!model){return '';}
        if(me.stack === null){
            return '';
        }

        let fieldObj;
        // 模型
        if(model instanceof Model){
            modelId = model.id;
            fieldObj = Object.create(null);
            //字段值
            me.fields.forEach((field)=>{
                fieldObj[field] = me.getFieldValue(model,field);
            });
            
        }else{
            fieldObj = model;
        }
        let newFieldValue = '';
        me.fields.forEach((field)=>{
            newFieldValue += fieldObj[field];
        });


        //如果对应模型的值对象不存在,需要新建
        if(me.modelMap[modelId]===undefined){
            me.modelMap[modelId] = Object.create(null);
        }
        //field值不一样,需要重新计算
        if(me.modelMap[modelId].fieldValue !== newFieldValue){
            me.modelMap[modelId].value = me.cacStack(me.stack,fieldObj,modelId);
        }
        
        me.modelMap[modelId].fieldValue = newFieldValue;
        return me.modelMap[modelId].value;
    }

....

/**
     * 添加变量
     */
    addVar(field,stack){
        const me = this;
        let values = ['null','undefined','true','false','NaN'];
        //判断是否为值表达式 null undefined true false
        let addFlag = values.indexOf(field) === -1 ? false:true;
        addFlag = addFlag || nodom.isNumberString(field);

        //作为字符串处理   
        if(addFlag){
            this.addStr(field,stack);
        }else{
            stack.push({
                val:field.trim(),
                type:'field'
            });
            me.addField(field);
        }
    }

变量的有无定义,分出两种处理,添加字符串还是入栈

  • addFilter(filterArr,stack)
  • cacStack(stack,fieldObj,modelId)

以后看

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342