dojo类机制实现原理分析

Dojo框架1.8.1版本功能详解与实战应用 本文还有配套的精品资源,点击获取 简介:Dojo是一个功能强大的JavaScript工具库,提供DOM操作、事件处理、Ajax交互等多种Web开发功能。1.8.1版本是Dojo框架的一个稳定版本,包含了新特性和改进,如全面支持AMD规范、模块化组织代码、丰富的UI组件、数据存储和处理、动画效果等。Dojo的模块化和API的优化使它成为企业级Web应用开发的理想选择。 ... 阅读详情

前段时间曾经在InfoQ中文站上发表文章,介绍了dojo类机制的基本用法。有些朋友在读后希望能够更深入了解这部分的内容,本文将会介绍dojo类机制幕后的知识,其中会涉及到dojo类机制的实现原理并对一些关键方法进行源码分析,当然在此之前希望您能够对JavaScript和dojo的使用有些基本的了解。

dojo的类机制支持类声明、继承、调用父类方法等功能。dojo在底层实现上是通过操作原型链来实现其类机制的,而在实现继承时采用类式继承的方式。值得一提的是,dojo的类机制允许进行多继承(注意,只有父类列表中的第一个作为真正的父类,其它的都是将其属性以mixin的方法加入到子类的原型链中),为解决多重继承时类方法的顺序问题,dojo用JavaScript实现了Python和其它多继承语言所支持的C3父类线性化算法,以实现线性的继承关系,想了解更多该算法的知识,可参考这里,我们在后面的分析中将会简单讲解dojo对此算法的实现。

1.  dojo类声明概览

dojo类声明相关的代码位于“/dojo/_base/declare.js”文件中,定义类是通过dojo.declare方法来实现的。关于这个方法的基本用法,已经在dojo类机制简介这篇文章中进行了阐述,现在我们看一下它的实现原理(在这部分的代码分析中,会在整体上介绍dojo如何声明类,后文会对里面的重要细节内容进行介绍):

//此即为dojo.declare方法的定义
d.declare = function(className, superclass, props){
 
         //前面有格式化参数相关的操作,一般情况下定义类会把三个参数全传进来,分别为
//类名、父类(可以为null、某个类或多个类组成的数组)和要声明类的属性及方法
 
//定义一系列的变量供后面使用
              var proto, i, t, ctor, name, bases, chains, mixins = 1, parents = superclass;
 
              // 处理要声明类的父类
              if(opts.call(superclass) == "[object Array]"){
                     //如果父类参数传过来的是数组,那么这里就是多继承,要用C3算法处理父类的关系
             //得到的bases为数组,第一个元素能标识真正父类(即superclass参数中的第一个)//在数组中的索引,其余的数组元素是按顺序排好的继承链,后面还会介绍到C3算法
                     bases = c3mro(superclass, className);
                     t = bases[0];
                     mixins = bases.length - t;
                     superclass = bases[mixins];
              }else{
                     //此分支内是对没有父类或单个父类情况的处理,不再详述
              }
         //以下为构建类的原型属性和方法
              if(superclass){
                     for(i = mixins - 1;; --i){
               //此处遍历所有需要mixin的类
                //注意此处,为什么说多个父类的情况下,只有第一个父类是真正的父类呢,因//为在第一次循环的实例化了该父类,并记在了原型链中,而其它需要mixin的//父类在后面处理时会把superclass设为一个空的构造方法,合并父类原型链//后进行实例化
proto = forceNew(superclass);
                           if(!i){
                                  //此处在完成最后一个父类后跳出循环
                                  break;
                           }
                           // mix in properties
                           t = bases[i];//得到要mixin的一个父类
                           (t._meta ? mixOwn : mix)(proto, t.prototype);//合并原型链
                           // chain in new constructor
                           ctor = new Function;//声明一个新的Function
                           ctor.superclass = superclass;
                           ctor.prototype = proto;//设置原型链
//此时将superclass指向了这个新的Function,再次进入这个循环的时候,实例//化的是ctor,而不是mixin的父类
                           superclass = proto.constructor = ctor;
                     }
              }else{
                     proto = {};
              }
              //此处将上面得到的方法(及属性)与要声明类本身所拥有的方法(及属性)进行合并
              safeMixin(proto, props);
             
…………
              //此处收集链式调用相关的信息,后面会详述
              for(i = mixins - 1; i; --i){ // intentional assignment
                     t = bases[i]._meta;
                     if(t && t.chains){
                           chains = mix(chains || {}, t.chains);
                     }
              }
              if(proto["-chains-"]){
                     chains = mix(chains || {}, proto["-chains-"]);
              }
             
              //此处根据上面收集的链式调用信息和父类信息构建最终的构造方法,后文详述
              t = !chains || !chains.hasOwnProperty(cname);
              bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) :
                     (bases.length == 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t));
 
              //在这个构造方法中添加了许多的属性,在进行链式调用以及调用父类方法等处会用到
              ctor._meta  = {bases: bases, hidden: props, chains: chains,
                     parents: parents, ctor: props.constructor};
              ctor.superclass = superclass && superclass.prototype;
              ctor.extend = extend;
              ctor.prototype = proto;
              proto.constructor = ctor;
 
              // 对于dojo.declare方法声明类的实例均有以下的工具方法
              proto.getInherited = getInherited;
              proto.inherited = inherited;
              proto.isInstanceOf = isInstanceOf;
 
              // 此处要进行全局注册
              if(className){
                     proto.declaredClass = className;
                     d.setObject(className, ctor);
              }
 
              //对于链式调用父类的那些方法进行处理,实际上进行了重写,后文详述
              if(chains){
                     for(name in chains){
                           if(proto[name] && typeof chains[name] == "string" && name != cname){
                                  t = proto[name] = chain(name, bases, chains[name] === "after");
                                  t.nom = name;
                           }
                     }
              }
              return ctor;//Function
};


以上简单介绍了dojo声明类的整体流程,但是一些关键的细节如C3算法、链式调用在后面会继续进行介绍。

2.  C3算法的实现

通过以前的文章和上面的分析,我们知道dojo的类声明支持多继承。在处理多继承时,不得不面对的就是继承链如何构造,比较现实的问题是如果多个父类都拥有同名的方法,那么在调用父类方法时,要按照什么规则确定调用哪个父类的呢?在解决这个问题上dojo实现了C3父类线性化的方法,对多个父类进行合理的排序,从而完美解决了这个问题。

为了了解继承链的相关知识,我们看一个简单的例子:

dojo.declare("A",null);
dojo.declare("B",null);
dojo.declare("C",null);
dojo.declare("D",[A, B]);
dojo.declare("E",[B, C]);
 dojo.declare("F",[A, C]);
 dojo.declare("G",[D, E]);

以上的代码中,声明了几个类,通过C3算法得到G的继承顺序应该是这样G->E->C->D->B->A的,只有按照这样的顺序才能保证类定义和依赖是正确的。那我们看一下这个C3算法是如何实现的呢:

function c3mro(bases, className){
        //定义一系列的变量
              var result = [], roots = [{cls: 0, refs: []}], nameMap = {}, clsCount = 1,
                     l = bases.length, i = 0, j, lin, base, top, proto, rec, name, refs;
 
              //在这个循环中,构建出了父类各自的依赖关系(即父类可能会依赖其它的类)
              for(; i < l; ++i){
                     base = bases[i];//得到父类
             …………
             //在dojo声明的类中都有一个_meta属性,记录父类信息,此处能够得到包含本身在//内的继承链
                     lin = base._meta ? base._meta.bases : [base];
                     top = 0;
                     for(j = lin.length - 1; j >= 0; --j){
                 //遍历继承链中的元素,注意,这里的处理是反向的,即从最底层的开始,一直到链的顶端
                           proto = lin[j].prototype;
                           if(!proto.hasOwnProperty("declaredClass")){
                                  proto.declaredClass = "uniqName_" + (counter++);
                           }
                           name = proto.declaredClass;
                  // nameMap以map的方式记录了用到的类,不会重复
                           if(!nameMap.hasOwnProperty(name)){
                      //每个类都会有这样一个结构,其中refs特别重要,记录了引用了依赖类
                                  nameMap[name] = {count: 0, refs: [], cls: lin[j]};
                                  ++clsCount;
                           }
                           rec = nameMap[name];
                           if(top && top !== rec){
                      //满足条件时,意味着当前的类依赖此时top引用的类,即链的前一元素
                                  rec.refs.push(top);
                                  ++top.count;
                           }
                           top = rec;//top指向当前的类,开始下一循环
                     }
                     ++top.count;
                     roots[0].refs.push(top);//在一个父类处理完成后就将它放在根的引用中
              }
//到此为止,我们建立了父类元素的依赖关系,以下要正确处理这些关系
              while(roots.length){
top = roots.pop();
//将依赖的类放入结果集中
                     result.push(top.cls);
                     --clsCount;
                     // optimization: follow a single-linked chain
                     while(refs = top.refs, refs.length == 1){
                  //若当前类依赖的是一个父类,那处理这个依赖链
                           top = refs[0];
                           if(!top || --top.count){
                     //特别注意此时有一个top.count变量,是用来记录这个类被引用的次数,//如果减一之后,值还大于零,说明后面还有引用,此时不做处理,这也就是//在前面的例子中为什么不会出现G->E->C->B的原因
                                  top = 0;
                                  break;
                           }
                           result.push(top.cls);
                           --clsCount;
                     }
                     if(top){
                 //若依赖多个分支,则将依赖的类分别放到roots中,这段代码只有在多继承,//第一次进入时才会执行
                           for(i = 0, l = refs.length; i < l; ++i){
                                  top = refs[i];
                                  if(!--top.count){
                                         roots.push(top);
                                  }
                           }
                     }
              }
              if(clsCount){//如果上面处理完成后,clsCount的值还大于1,那说明出错了
                     err("can't build consistent linearization", className);
              }
 
              //构建完继承链后,要标识出真正父类在链的什么位置,就是通过返回数组的第一个元素
              base = bases[0];
              result[0] = base ?
                     base._meta && base === result[result.length - base._meta.bases.length] ?
                           base._meta.bases.length : 1 : 0;
 
              return result;
       }

通过以上的分析,我们可以看到,这个算法实现起来相当复杂,如果朋友们对其感兴趣,建议按照上文的例子,自己加断点进行调试分析。dojo的作者使用了不到100行的代码实现了这样强大的功能,里面有很多值得借鉴的设计思想。


 

3.  链式构造器的实现

在第一部分代码分析中我们曾经看到过定义构造函数的代码,如下:

bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) :
                     (bases.length == 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t));

这个方法对于理解dojo类机制很重要。从前一篇文章的介绍中,我们了解到默认情况下,如果dojo声明的类存在继承关系,那么就会自动调用父类的构造方法,且是按照继承链的顺序先调用父类的构造方法,但是从1.4版本开始,dojo提供了手动设置构造方法调用的选项。在以上的代码中涉及到dojo声明类的三个方法,如果该类没有父类,那么调用的就是singleConstructor,如果有父类的话,那么默认调用的是chainedConstructor,如果手动设置了构造方法,那么调用的就是simpleConstructor,要启动这个选项只需在声明该类的时候添加chains的constructor声明即可。


比方说,我们在定义继承自com.levinzhang.Person的com.levinzhang.Employee类时,可以这样做:

dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{
       "-chains-": {
              constructor:"manual"
       },
…………
}

添加以上代码后,在构造com.levinzhang.Employee实例时,就不会再调用所有父类的构造方法了,但是此时我们可以使用inherited方法显式的调用父类方法。


限于篇幅,以上的三个方法不全部介绍,只介绍chainedConstructor的核心实现:

 

function chainedConstructor(bases, ctorSpecial){
              return function(){
                     //在此之前有一些准备工作,不详述了
             //找到所有的父类,分别调用其构造方法
                     for(i = l - 1; i >= 0; --i){
                           f = bases[i];
                           m = f._meta;
                           f = m ? m.ctor : f;//得到父类的构造方法
                           if(f){
                      //通过apply调用父类的方法
                                  f.apply(this, preArgs ? preArgs[i] : a);
                           }
                     }
             // 请注意在构造方法执行完毕后,会执行名为postscript的方法,而这个方法是//dojo的dijit组件实现的关键生命周期方法
                     f = this.postscript;
                     if(f){
                           f.apply(this, args);
                     }
              };
       }

 

4.  调用父类方法的实现

在声明dojo类的时候,如果想调用父类的方法一般都是通过使用inherited方法来实现,但从1.4版本开始,dojo支持链式调用所有父类的方法,并引入了一些AOP的概念。我们将会分别介绍这两种方式。

1)  通过inherited方式调用父类方法

在上一篇文章中,我们曾经介绍过,通过在类中使用inherited就可以调用到。这里我们要深入inherited的内部,看一下其实现原理。因为inherited支持调用父类的一般方法和构造方法,两者略有不同,我们关注调用一般方法的过程。

   function inherited(args, a, f){
              …………
         //在此之前有一些参数的处理
              if(name != cname){
                     // 不是构造方法
                     if(cache.c !== caller){
                           //在此之间的一些代码解决了确定调用者的问题,即确定从什么位置开始找父类
                     }
                     //按照顺序找父类的同名方法
                     base = bases[++pos];
                     if(base){
                           proto = base.prototype;
                           if(base._meta && proto.hasOwnProperty(name)){
                                  f = proto[name];//找到此方法了
                           }else{
                     //如果没有找到对应的方法将按照继承链依次往前找
                                  opf = op[name];
                                  do{
                                         proto = base.prototype;
                                         f = proto[name];
                                         if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){
                                                break;
                                         }
                                  }while(base = bases[++pos]); // intentional assignment
                           }
                     }
                     f = base && f || op[name];
              }else{
              //此处是处理调用父类的构造方法
              }
              if(f){
             //方法找到后,执行
                     return a === true ? f : f.apply(this, a || args);
              }
}


2)  链式调用父类方法

这是从dojo 1.4版本新加入的功能。如果在执行某个方法时,也想按照一定的顺序执行父类的方法,只需在定义类时,在-chains-属性中加以声明即可。

dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{
"-chains-": {
     sayMyself:    "before"
       },
……
}

添加了以上声明后,意味着Employee及其所有的子类,在调用sayMyself方法时,都会先调用本身的同名方法,然后再按照继承链依次调用所有父类的同名方法,我们还可以将值“before”替换为“after”,其执行顺序将会相反。在-chains-属性中声明的方法,在类定义时,会进行特殊处理,正如我们在第一章中看到的那样:

 if(chains){
                     for(name in chains){
                           if(proto[name] && typeof chains[name] == "string" && name != cname){
                                  t = proto[name] = chain(name, bases, chains[name] === "after");
                                  t.nom = name;
                           }
                     }
              }

我们可以看到在-chains-中声明的方法都进行了替换,换成了chain方法的返回值,而这个方法也比较简单,源码如下:

       function chain(name, bases, reversed){
              return function(){
                     var b, m, f, i = 0, step = 1;
                     if(reversed){
                  //判定顺序,即“after”还是“before”,分别对应于循环的不同起点和方向
                           i = bases.length - 1;
                           step = -1;
                     }
                     for(; b = bases[i]; i += step){
                //按照顺序依次查找父类
                           m = b._meta;
                  //找到父类中同名的方法
                           f = (m ? m.hidden : b.prototype)[name];
                           if(f){
                     //依次执行
                                  f.apply(this, arguments);
                           }
                     }
              };
       }
 
 

5.  工具方法和属性如isInstanceOf、declaredClass的实现

除了上面提到的inherited方法以外,dojo在实现类功能的时候,还实现了一些工具方法和属性,这里介绍一个方法isInstanceOf和一个属性declaredClass。从功能上来说isInstanceOf方法用来判断一个对象是否为某个类的实例,而declaredClass属性得到的是某个对象所对应声明类的名字。

       function isInstanceOf(cls){
        //得到实例对象继承链上的所有类
              var bases = this.constructor._meta.bases;
         //遍历所有的类,看是否与传进来的类相等
              for(var i = 0, l = bases.length; i < l; ++i){
                     if(bases[i] === cls){
                           return true;
                     }
              }
              return this instanceof cls;
       }


而declaredClass属性的实现比较简单,只是在声明类的原型上添加了一个属性而已,类的实例对象就可以访问这个属性得到其声明类的名字了。这段代码在dojo.declare方法中:

if(className){
                     proto.declaredClass = className;
                     d.setObject(className, ctor);
              }


  在dojo实现类机制的过程中,有一些内部的方法,是很值得借鉴的如forceNew、safeMixin等,这些方法在实现功能的同时,保证了代码的高效执行,感兴趣的朋友可以进一步的研究。

6.  总结与思考

1)  dojo在实现类机制方面支持多继承方式,其它JavaScript类库中很少能做到,而利用JavaScript原生语法实现多继承也较为困难。在这一点上dojo的类机制的功能确实足够强大。但是多继承会增加编码的难度,对开发人员如何组织类也有更高的要求;

2)  链式调用父类方法时,我们可以看到dojo引入了许多AOP的理念,在1.7的版本中,将会有单独的模块提供AOP相关的支持,我们将会持续关注类似的功能;

3)  在dojo的代码中,多处都会出现方法替换,如链式方法调用、事件绑定等,这种设计思想值得我们关注和学习;

4)  使用了许多的内部属性,如_meta、bases等,这些元数据在实现复杂的类机制中起到了至关重要的作用,在进行源码分析的时候,我们可以给予关注,如果要实现类似功能也可以进行借鉴。

 

                探究类库的实现原理是提高自己编码水平的好办法,类似于dojo这样类库的核心代码基本上每一行都有其设计思想在里面(当然也不可以盲目崇拜),每次阅读和探索都会有所发现和心得,当然里面肯定也会有自以为是或谬误之处,在此很乐意和读到这篇文章的朋友们一起研究,欢迎批评指正。

 

 

参考资料:

http://docs.dojocampus.org/

http://blog.youkuaiyun.com/dojotoolkit/

http://dojotoolkit.org/

 

作者信息:张卫滨,关注企业级Java开发和RIA技术,个人博客:http://lengyun3566.iteye.com,微博:http://weibo.com/zhangweibin1981

 

声明: 
  本文已经首发于InfoQ中文站,版权所有,原文为《dojo类机制实现原理分析》,如需转载,请务必附带本声明,谢谢。 
  InfoQ中文站是一个面向中高端技术人员的在线独立社区,为Java、.NET、Ruby、SOA、敏捷、架构等领域提供及时而有深度的资讯、高端技术大会如QCon 、线下技术交流活动QClub、免费迷你书下载如《架构师》等。

 

知识见闻 - Salesforce公司发展史 不过,通过将自己开发的产品与公司收购的其它云产品进行结合,Salesforce可以为用户提供更多的产品种类,Salesforce可以继续强化自己的平台和产品套件属性,这一平台和套件的发展规模已经远远超出了一个单一的CRM的规模。他们围绕一个相对简单的产品向用户兜售云的概念,然后,他们开始开发其它产品,并寻找培育一个不断增长的生态系统的方法,最理想的情况是能驱动Salesforce的营收增长。现在,Salesforce通过打造更多的用例并改进自己产品的方式来直接应对竞争对手的竞争,从而捍卫自己现在的地位。 阅读详情

相关推荐

【深圳】IBM V3500存储维修 更换控制器故障

今天给大家分享的是深圳.福田区一个电商客户公司一台IBM Storwize v3500存储控制器故障从远程诊断 收集日志 分析结果的实际案例,如图所示通过存储管理口登陆进去后直接看到少了一个控制器,经过工程师分析日志后判定为该控制器损坏,接下来更换控制器配件,标准套件的控制器不带子卡和电池,所以需要把故障控制器上的子卡和电池拆下来装到新控制器上,这个电池拆还需要点技巧,接着把新的控制器插回V3500主机,等待大约十分钟后,新控制器指示灯全绿,管理界面看到节点已联机;冠峰科技-爱科技爱分享。

OuYangYuXi的博客 4579

Dojo中define和declare

Define 先看define。作用是定义一个模块(module)。这个模块可以被require引用,引用了之后就可以使用define里面的东西。一个模块想当然,里面干什么事情,不一定全部自己实现。就像人要coding,除了脑子,也不能没有电脑、键盘。因此,define的第一个参数就是将要用到的其他模块引进来。第二个参数描述这个模块具体干什么,并且给第一个参数中的模块分别起一个朗朗上口的名字。就像...

挣钱花388的博客 346

BERTopic模型入门教程(附代码+参数调节)

BERTopic 是一种用于从文本数据中提取主题模型的新型方法。通过结合 BERT嵌入和传统的主题模型来生成语义丰富的主题。传统的主题模型(如LDA)主要依赖于词频矩阵,而 BERTopic 通过预训练语言模型生成高维语义嵌入来捕捉词语间的复杂关系,因此可以更好地处理上下文信息。BERTopic模型可以看作是创建其主题表示的一系列步骤。核心原理如下:就本质而言,BERTopic就是一个拼装框架,可以把每部分都替换成其他模型,只要完成任务即可。

m0_52069102的博客 1万+

dojo--inherited

与java类一样,在dojo里也可以定义constructor构造函数,在创建一个实例时可以对需要的属性进行初始化。 //定义一个类mqsy_yj varmqsy_yj = declare(null, { // the default username username:"yanjun", // the constructor c...

wendy_dw的博客 759

ArcGIS遥感影像分类技术在植被覆盖度计算中的应用

本文详细介绍了如何利用ArcGIS遥感影像分类技术高效计算植被覆盖度。通过数据准备、监督与非监督分类方法详解,并结合制表分析与栅格计算器进行面积统计,最终实现从分类图到覆盖度百分比的全流程自动化与可视化,为生态评估与规划提供精准数据支持。

weixin_29091445的博客 421

解锁SPWM电压利用率:三次谐波注入幅值的黄金分割点

本文深入探讨了SPWM电压利用率的提升方法,重点介绍了三次谐波注入技术及其黄金分割点(1/6比例)。通过数学推导和工程实践,揭示了如何通过特定幅值的三次谐波注入将电压利用率从86.6%提升至115%,与SVPWM相当。文章还提供了实验室调试流程和DSP实现代码,帮助工程师优化逆变器设计。

weixin_30729609的博客 916

dojo中 this.inherited(arguments);

在dojo中,你会经常遇到这样的一句代码 this.inherited(arguments);啥意思啊?如果你是一个习惯性的java编程员的话,你会觉得像一个方法调用,其实不然,这个是一个dojo的内部方法,用来向基类查找该代码所在的方法,然后调用它,直到知道为止。我才知道,dojo不能以学习java的方式来学习它,oh,oh...

hanshuai123的博客 723

贝塞尔曲线在机器人路径平滑中的实战应用与优化

本文深入探讨了贝塞尔曲线算法在机器人路径平滑中的核心应用。通过对比G1CBS与G2CBS两种方法,详细阐述了如何实现从切线连续到曲率连续的平滑优化,以满足不同机器人对运动平稳性和效率的需求,并提供了完整的Python代码实现与关键参数调优指南。

weixin_29051811的博客 307

(3)Dojo学习之Class

引言 dojo中的类 1创建一个简单的类 2类和模块结合使用 3关于类的继承 需求 1需求一 2需求二很重要 21代码实现 22程序出错 23程序运行 3 需求三 再次介绍require加载模块 总结 1本博客代码的下载地址dojo_class1.引言      说到面向对象,我们就不得不提一下类,在原生的js中我们是不可以创建类的,没有class这个关键字,但是在dojo中,dojo自定义了一个模

王培晓Carpenter 4158

S32K344 Bootloader实战:一个按键实现从Boot到App的无缝跳转(附完整代码)

本文详细介绍了基于NXP S32K344微控制器的Bootloader开发实战,重点解析如何通过一个硬件按键实现从Boot到App的可靠跳转。内容涵盖内存分区规划、中断向量表重映射、外设初始状态管理等核心要素,并提供了完整的代码示例和调试技巧,适用于汽车电子和工业控制等高可靠性应用场景。

weixin_29192365的博客 307

二次规划问题(qp)和序列二次规划问题(sqp)的简单理解

当二次规划的约束为非线性约束时,通常会采用sqp进行求解,用连续求解qp的方法来得到非线性约束条件下的最优解,上述的qpoases和osqp均无法直接求解非线性约束问题,所以如果使用这两个库的话,注意,sqp是结果,而不是原因,只有在非线性约束的情况下才会考虑sqp求解,如果问题本身就是线性约束,则直接用qp解就行。对于工程师而言,我们在编写代码的时候,并不关心二次规划问题的求解细节,所以一般是把二次规划问题建立好后,直接调用三方库进行求解。因为约束为非线性约束,所以先将约束进行线性化,约束原方程为。

zmhzmhzm的博客 1万+

Dojo

dojo的类机制支持类声明、继承、调用父类方法等功能。dojo在底层实现上是通过操作原型链来实现其类机制的,而在实现继承时采用类式继承的方式。值得一提的是,dojo的类机制允许进行多重继承(注意,只有父类列表中的第一个作为真正的父类,其它的都是将其属性以mixin的方法加入到子类的原型链中),为解决多重继承时类方法的顺序问题,dojo用JavaScript实现了Python和其它多继承语...

dianxuanfan4560的博客 337

YOLOv11改进 | 特殊场景检测篇 | 利用图像去雾网络UnfogNet改进yolov11图像雾天检测能力(全网独家首发改进)

本文给大家带来的改进机制是利用UnfogNet超轻量化图像去雾网络,我将该网络结合YOLOv11针对图像进行去雾检测(也适用于一些模糊场景),我将该网络结构和YOLOv11的网络进行结合同时该网络的结构的参数量非常的小,我们将其添加到模型里增加的计算量和参数量基本可以忽略不计这是非常难得的,因为其也算是一种图像增强算法,同时本文的内容不影响其它的模块改进可以作为工作量凑近大家的论文里,非常的适用,图像去雾检测为群友最近提出的需要的改进。欢迎大家订阅我的专栏一起学习YOLO!

Snu77的博客 1629

Dojo面向对象机制深入剖析

Dojo是一个强大的基于javascript语言的web控件库,他提供了一种基于javascript的面向对象开发编码模式,使得善于于运用面向对象思维coding的我们在写比较复杂的前端web2.0应用的时候,也可以避开javascript语言的不足,写出漂亮的面向对象风格的前端代码,dojo的这个特性使得web工程师们开发web2.0应用显得更加得心应手,尤其是开发大型的web2.0应用,可以极大的提高代码的可读性和可维护性。本文主要和大家探讨一下dojo的这种面向对象的机制。

Dojo中文博客 6976

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

dojo1.7功能介绍:面向方面编程(AOP)功能与原理

声明:本文为笔者原创,但首发于InfoQ中文站,详见文末声明     日前发布的dojo 1.7版本对其源码进行了很大的变更。在迈向2.0版本之际,dojo提供了许多新的功能,也对许多已有的功能进行了修改,具体来说新版本更好地支持AMD规范、提供了新的事件处理系统(计划在2.0版本中替换dojo.connect API)和DOM查询模块、更新对象存储相关接口(ObjectStore)等。在本...

张卫滨的技术记录 239

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

dojo 加载器源文件讲解

之前写的都是Dojo的基本使用教程,整体的讲解了Dojo  AMD加载器使用及原理, DOM 操作, 事件,动画,这些都是平常工作中会经常用到的, 但对于真正的提升还是要分析源代码,了解整个的框架设计,同时针对每个函数或每行代码,看看大师们是怎么写的,有助于自己提写自己的编码水平。  我们先从 dojo 包中 dojo.js 开始讲解。 概述  dojo.js 这个文件是 dojo 的加载器

robin xiong的专栏 3473

dojo 中对象扩展与原型继承的实现

dojo 是一款优秀的javascript框架,通常我们可以在复杂的企业级应用中看到它的身影。dojo 提供了两个方法来实现继承 dojo.mixin 、dojo.extenddojo.mixin 作用在对象上,它负责扩展一个对象 dojo.extend 作用在实例上,他负责扩展实例(本质上是扩展产生实例的类)而这两个方法的底层实现是dojo._mixin。因此,我将这一部分抽出,并进行分析。(function(){ // 创建一个命名空间 var dojo = {}; // 初始化一些变量 v

似水流年 529

Dojo.gfx包分析

 Dojo.gfx包分析2006-12-22 修订:2007-01-08:更正对attach的描述,改“拷贝”为“引用”1.     背景由于目前在主流浏览器之间没有一个标准的WEB2D矢量图形技术,特别是在IE没有内置支持SVG之前,WEB开发人员一直以来都在寻找一个能够跨平台的解决方案。在INTERNET上不难发现很多人都在这方面进行着不懈的努力。其主要的方向是

firefight的专栏 6417

面向对象的 JavaScript 编程:dojo.declare 详解

引言 JavaScript 早在发明初期时,仅用来进行简单的表单数据验证,但随着 Web 2.0 技术的蓬勃发展,尤其是近年来 Ajax 的异军突起,JavaScript 如今已成为 Internet 上最流行的脚本语言,用其开发的 web 应用也因高互动性极大的丰富了用户体验。而与此同时,当前的富英特网应用程序的代码量也产生了爆炸式的增长,因此利用面向对象的 JavaScript 编程

987

dojo的connect方法实现及源码剖析(实现JS上的类似AOP效果)

dojo是功能非常强大的JavaScript类库,论类库本身的功能,丝毫不逊色于jQuery,但是jQuery的轻量级和丰富的插件使其在互联网领域风生水起,知名度要高得多。dojo也提供了自己的组件库,名为dijit,它们拥有统一的生命周期。dojo这几年也得到了很多大公司的支持,并应用与很多领域,如struts2就使用了它,通过研究dojo的使用方式和源码对提升前台的开发能力大有益处。 d...

张卫滨的技术记录 200
上一篇: dojo类机制简介
下一篇: Dojo中文博客征稿启事
dojotoolkit
博客等级 码龄16年 1205粉丝 61原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值