javascript框架设计之种子模块


Posted in Javascript onJune 23, 2015

种子模块也叫核心模块,是框架中最先执行的部分。即便像jQuery那样的单文件函数库,它的内部也分很多模块,必然有一些模块执行时在最前面立即执行,有一些模块只有用到才执行。有的模块可有可无,存在感比较弱,只有在特定的浏览器下才运行。

种子模块就是其中的先锋,它里边的方法不一定要求个个功能齐全,设计优良,但一定要极具扩展性,常用,稳定。

扩展性是指通过他们能给将其它模块包含进来;常用是指绝大多数的模块都能用到它们,防止做重复工作。稳定是指在版本迭代时不轻易被新方法替代。

参照许多框架和库的实现,我们认为种子模块包含如下功能:对象扩展,数组化,类型判定,简单的绑定与卸载,无冲突处理,模块的加载与domReady.本章学习的内容以mass Framework种子模块为范本。

https://github.com/RubyLouvre/mass-Framework

1.命名空间

种子模块作为一个框架最开始的部分,负责辅建全局的基础设施外。jQuery就有一个很好的开头,使用IIFE(立即调用函数表达式).

LIFE是现代javascript框架里最主要的基础设施,它就像细胞一样包裹自身,防止变量污染。就像一个立足点,这个就是命名空间,如prototype.js,mootools,它们让你感受不到框架的存在,它的意义深刻到javascript、DOM、BOM等整个执行环境的每个角落,对原生的对象原型就行扩展。由于道格拉斯(JSON作者)的极力反对,新的框架都在命名空间上构建了。

我们看怎么在javascript上模拟命名空间。javascript一切基于对象,但只有符合类型的对象才符合要求,比如function 、RegExp、Object,不过最常用的是object和function。我们往一个对象上添加一个属性,而这个属性又是一个对象,这个对象我们又可以为它添加一个对象,通过这种方法,我们就可以有条不紊的构建我们的框架。用户想调用某个方法,就以xxx.yyy.zzz()的形式调用。

if( typeof(Ten) === "undefined" ){
    Ten = {};
    Ten.Function = { /*略*/ }
    Ten.Array = { /*略*/ }
    Ten.Class = { /*略*/ }
    Ten.JSONP = new Ten.Class(/*略*/ )
    Ten.XHR = new Ten.Class(/*略*/ )
  }

纵观各大类库的实现,一开始基本都是定义一个全局变量作为命名空间,然后对它进行扩展,如Base2的Base,Ext的Ext,jQuery的jQuery,YUI的YUI,dojo的dojo,MochiKit的mochKit。从全局变量的污染程度来看,分为两类:

prototype.js和mootools与Base2归为一类,Prototype的哲学是对javascript的原生对象进行扩展。早些年,prototype差点称为事实的标准。因此没有考虑到与其它库共存的问题。基本Prototype,也发展出诸如script.aculo.us,rico,Plotr,protoChart,Script2等非常优秀的类库以一大类收费插件。而且,有些渊源的插件几乎都与Prototype有关,比如lightBox。mootools是prototype.js的升级版,更加OO,全面复制其API。Base2则是想修复IE的bug,让IE拥有标准浏览器的API,因此也把所有原生的对象污染一遍。

第二类是jQuery,YUI,EXT这些框架,YUI和Ext就是对象嵌套对象的方式构建的。jQuery则另辟蹊径,它是以选择器为导向的,因此它的命名空间是一个函数,方便用户将css表达器的字符串传进来。然后通过选择器进行查找,最后返回一个jQuery对象实例。

jQuery最开始也像Prototype使用$作为它的命名空间,因此,它实现了很多库的共存机制,在$和jQuery中任意切换,jQuery的多库共存原理很简单,因此后来也成为许多小库的标配。首先,把命名空间保存到一个临时变量中(注意,这时候这个对象并不是自己框架的东西,可能是prototype.js或者其他的),然后再使用个noConflict放回去。

//jQuery1.2
  var _jQuery = window.jQury , _$ = window.$; //把可能存在同名变量先保存起来
  jQury.extend({
    noConflict : function(deep) {
      window.$ = _$; //这时再放回去
      if (deep) //补充 if ( deep && window.jQuery === jQuery )
        window.jQury = _jQuery;
      return jQury;
    }
  })

参考:http://zhidao.baidu.com/question/1239712776390687219.html

但jQuery的noConflict只是对单文件的类库框架有用,像Ext就不能复制了。因此把命名空间改名后,将Ext置为null,然后通过动态加载的方法引入新的javascript文件中,该文件会以Ext调用,会导致报错。

2.对象扩展

我们需要一种机制,将新功能添加到我们的命名空间上来。这方法在javascript通常称作extend或mixin。javascript对象在属性描述符(Property Descriptor)没有诞生之前,是可以随意添加、更改、删除其成员的,因此,扩展一个对象非常便捷。一个简单的扩展方法实现是这样的。

function extend (destination,source){
    for (var property in source)
      destination[property] = source[property];
    return destination;
  }

不过,旧版本IE在这里有个问题,它认为像Object的原型方法就是不应该被遍历出来,因此for in循环是无法遍历valueOf、toString的属性名。这导致,模拟Object.keys方法是现实时也遇到了这个问题。

Object.keys = Object.keys || function(obj){
    var a = [];
    for(a[a.length] in obj);
    return a;
  }

在不同的框架,这个方法还有不同的实现,如Ext分别为apply与applyIf两个方法,前者会覆盖目标对象的同名属性,而后者不会。dojo允许多个对象合并在一起。jQuery还支持深拷贝。下面是mass Farmework的mix方法。支持多对象合并与选择是否覆写。

function mix(target,source){ //如果最后参数是布尔,判定是否覆盖同名属性
    var args = [].slice.call(arguments), i = 1, key,
      ride = typeof args[args.length - 1] == "boolean" ? args.pop() : true;
    if (args.length === 1){ //处理$.mix(hash)的情形
      target = !this.window ? this : {};
      i = 0;
    }  
    while ((source = args[i++])) {
      for (key in source){ //允许对象糅杂,用户保证都是对象
        if (ride || !(key in target)) {
          target[key] = source[key];
        }
      }
    }
    return target;
  }

3.数组化

浏览器下存在很多类数组对象,如function内的arguments,通过document.forms、form.elements,document.links、select.options、document.getElementsByName,document.getElementsByTagName、childNodes、children等方式获取的节点的结合(HTMLCollection 、NodeList)或按照某些特殊的写法自定义对象。

类数组对象是一个很好的存储结构。不过功能太弱了,为了能使用纯数组的那些便捷的方法,我们会在处理它们前都会做一下转换。

通常来说,使用[].slice.call就能转换了 ,不过功能不够用,但在旧版本的HTMLCollection、NodeList不是Object的子类,采用如上的方法会导致IE执行异常。我们看一下

jQuery:makeArray

  var makeArray = function(array) {
    var ret = [] ;
    if(array != null){
      var i = array.length;
      if(i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval)
        ret[0] = array;
      else
        while (i)
          ret(--i) = array[i];
    }
    return ret;
  }

mass的实现,一开始就进行区分,直接[].slice.call,IE的话自己动手实现一个slice方法

$.slice = window.dispatchEvent ? function(nodes,start,end){
    return [].slice.call(nodes,start,end);
  } : function (nodes,start,end){
    var ret = [],
       n = nodes.length;
    if (end === void 0 || typeof end === "number" && isFinite(end)){
      start = parseInt (start,0) || 0;
      end = end == void 0 ? n:parseInt (end,10);
      if(start < 0){
        start += n;
      }
      if (end > n) {
        end =n
      };
      if (end < 0) {
        end += n
      };
      for (var i = start; i < end; ++i){
        ret[i-start] = nodes[i];
      }
    }
    return ret;
  }

4.类型的判定

javascript存在两套类型系统,一套是基本的数据类型,另一套是对象类型系统。基本数据类型包括6中 。分别是undefined、string、null、boolean、function、object。基本数据类型是通过typeof来检测的。对象类型系统是以基础类型系统为基础的,通过instanceof来检测的。然而,javascript自带的这两套识别机制非常不靠谱,于是就催生了isXXX系列。就拿typeof来说,它只能粗略识别出string、number、boolearn、function、undefined、object这6种数据类型,无法识别null,RegExpArgument等细分的对象类型。

typeof null // => "object"
  typeof document.childNodes //=> safari: "function"
  typeof document.creatElement('embed') //=> ff3-10 "function"
  typeof document.creatElement('object') //=> ff3-10 "function"
  typeof document.creatElement('object') //=> ff3-10 "function"
  typeof /\d/i //在实现了ecma262v4的浏览器返回"function"
  typeof window.alert //ie678 "object"
  var iframe = document.creatElement("iframe")
  document.body.appendChild(iframe)
  xArray = window.frames[window.frames.length - 1].Array;
  var arr = new xArray(1,2,3) //=>[1,2,3]
  arr instanceof Array ;// false
  isNaN("aaa") //=> true

另外,以前人们总是以document.all来判断是否为ie,这其实是很危险的,因为,用document.all来取得页面中所有的元素是不错的注意,这个方法FF,chrome打算使用很久了,不过人们都这样判断,就是在chrome下有这样的闹剧。

typeof document.all //undefined
  document.all //HTMLAllCollection [728] (728为元素总数)

在判定undefined、null、string、number、boolean、function这六个还算简单,前面两个可以分别与void(0)、null比较,后面4个的typeof也可以满足90%的情形。这样说是因为string、number、boolean可以包装成伪对象。

typeof new Boolean(1); //=>"object"
  typeof new Number(1); //=>"object"
  typeof new String("aa"); //=> "object"

这些还不是最难的,难点在于RegExp与Array.判定RegExp的情况很少。Array则不一样。有关isArray的实现不下二十种。都是因为鸭式辨型被打破了。直到prototype.js把Object.prototype.toString发掘出来。此方法是直接输出内部的[[Class]],绝对精准。有了它,95%的陷阱被跳过了。

function isArray(arr){
    return arr instanceof Array;
  }

  function isArray(arr){
    return !!arr && arr.constructor === Array;
  }

  function isArray(arr) { //prototype.js 1.6
    return arr != null && typeof arr === "object" && 'splice' in arr && 'join' in arr;
  }

  function isArray(arr){// Douglas Crockford(JSON作者,反对原型污染)
    return typeof arr.sort == "function"
  }

  function isArray(array){ //kriszyp
    var result = false;
    try{
      new array.constructor (Math.pow(2,32))
    } catch (e){
      result = /Array/.test(e.message)
    }
    return result;
  };

  function isArray(o){//kangax
    try{
      Array.prototype.toString.call(o);
      return true;
    } catch (e) {
    }
    return false;
  }

  function isArray(o){ //kangax
    if(o && typeof o == 'object' && typeof o.length == 'number' && isFinite(o.length))
    {
      var _origLength = o.length;
      o[o.length] = '__test__';
      var _newLength = o.length;
      o.length = _origLength;
      return _newLength == _origLength + 1;
    }
    return false
  }

至于null 、 undefined 、NaN直接这样写

function isNaN(obj) {
    return obj !== obj
  }

  function isNull(obj) {
    return obj === null;
  }

  function isUndefined(obj){
    return obj === void 0;
  }

最后要判定的是对象是window,由于ECMA是不规范的Host对象,window对象属于host.所以也就没有被约定。就算Object.prototype.toString也对它没办法

[object Object] IE6 
  [object Object] IE7 
  [object Object] IE8
  [object Window] IE9
  [object Window] ff3.6
  [object Window] opera10
  [object DOMWindow] safari 4.04
  [object global] chrome5.0

不过根据window.window和window.setInterval去判定更加不靠谱,用一个技巧就可以完美识别ie6 ie7 ie8的window对象,其它还用toString,这个神奇的hack就是,window与document互相比较,如果顺序不一样,结果也是不一样的!

剩下的就是一些经典方法:

在prototype.js中,拥有isElement,isArray,isHash,isFunctoion,isString,isNumber,isDate,isUndefined。

mootools有一个typeOf判定基本类型,instanceOf判定自定义“类”

RightJS 有isFunction , isHash , isString , isNumber , isArray ,isElement, isNode.

Ext有比较全面的判断,isEmpty,isArray,isDate,isObject,isSimpleObject,isPrimitive,isFunction,isNumber,isMumeric,isString,isBoolean,isElement,isTextNode,isDefined,isIterable,应有尽有。最后,还有typeOf判断基本类型。

Underscore.js有isElement,isEmpty,isArray,isArgument,isObject,isFunction,isString,isNumber,isFinite,isNaN,isBoolean,isDate,isRegExp,isNull,isUndefined.

jQuery就不与其它框架一样了,在jQuery1.4中只有isFunction,isArray,isPlainObject,isEmptyObject。isFunction,isArray用户肯定用的较多,isPlainObject是用来判断是否是纯净的js对象。既不是DOM,BOM对象,也不是自定义的“类”的实例对象,制造它的目的最初是为了深拷贝,避开像window那样自己引用自己的对象。isEmptyObject是用于数据缓存的系统,当此对象为空时,就可以删除它。

//jQuery2.0纯净数组的判定思路
  jQuery.isPlainObject = function(obj){
    //首先排除基础类型不为Object的类型,然后是DOM节点与window对象
    if(jQuery.type(obj) !== "object" || object.nodeType || jQuery.isWindow(obj)){
      return false;
    }

    //然后回溯它的最近的原型对象是否有isPrototypeOf.
    //旧版本的IE一些原生的对象没有暴露constructor、prototype。因此在这里过滤掉
    try{
      if (obj.constructor && !hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){
        return false;
      }
    } case(e) {
      return false;
    }
    return true;
  }

avalon.mobile中有一个更精简的版本,只支持最新的浏览器,可以大胆的使用ecma262v5新API

avalon.isPlainObject = function(obj){
    return obj && typeof obj === "object" && Object.getPrototypeOf(obj) === Object.prototype
  }

isArrayLike也是一个常用的方法,但是判定一个类数组太难了,唯一的识别方法就是判断有一个大于0或等于0的length属性,此外,还有一些共识,如window与函数和元素节点,如(form元素),不算类数组,虽然它们都满足前面的条件。因此,至今jQuery都没有把它暴露出来。

//jQuery2.0
  function isArrayLike(obj){
    var length = obj.length , type = jQuery.type(obj);
    if (jQuery.isWindow(obj)){
      return false;
    }
    if (obj.nodeType === 1 && length){
      return true
    }
    return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length -1) in obj);
  }

  // avalonjs
  function isArrayLike (obj) {
    if (obj && typeof obj === "object"){
      var n = obj.length
      if (+n === n && !( n % 1) && n >= 0){//检测length是否为非负整数
        try{
          if ({}.prototypeIsEnumerable.call(obj,'length') === false){
          return Array.isArray(obj) || /^\s?function/.test(obj.item || obj.callee)
         }
        return true;
       } catch (e) { //IE的NodeList直接报错
        return true;
       }
      }
    }
  return false
  }

在Prototype.js1.3版本中的研究成果(Object.prototype.toString.call)就应用于jQuery,在jQuery1.2中,判断一个变量是否为函数非常复杂。

    isFunction : function(fn){

        return !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test(fn + ""); 

    }

jQuery1.43中引入isWindow来处理makeArray中对window的判定,引入isNaN用于确保样式赋值的安全。同时引入type代替typeof关键字,用于获取基本数据的基本类型。

class2type = {};
  jQuery.each("Boolean Number String Function Array Date RegExpObject".split(" "),function( i , name ){
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
  });

  jQuery.type = function(obj){
    return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
  }

jQuery1.7中添加isNumeric代替isNaN。这是个不同于其它框架的isNumber,它可以是个字符串,只要外观上像数字就可以了。但jQuery1.7还做了一个违背之前提到稳定性的事情。冒然去掉jQuery.isNaN,因此,基于旧版jQuery的有大批插件失效。

//jQuery.1.43-1.64
  jQuery.isNaN = function ( obj ) {
    return obj == null || !rdigit.test( obj ) || isNaN( obj );
  }

  //jQuery1.7就是isNaN的去反版
  jQuery.isNumeric = function ( obj ) {
    return obj != null && rdigit.test( obj ) && !isNaN( obj );
  }

  //jQuery.1.71 - 1.72
  jQuery.isNumeric = function ( obj ) {
    return !isNaN( parseFloat(obj) ) && isFinite( obj );
  }

  //jQuery2.1
  jQuery.isMumeric = function( obj ) {
    return obj - parseFloat(obj) >= 0;
  }

massFarmeWork的思路与jQuery一致,尽量减少isXXX系列的代码,把is Window,isNaN,nodeName等方法做了整合。代码较长,既可以获取类型,也可以传入第二参数进行类型比较。

var class2type = {
    "[objectHTMLDocument]" : "Document",
    "[objectHTMLCollection]" : "NodeList",
    "[objectStaticNodeList]" : "NodeList",
    "[objectIXMLDOMNodeList]" : "NodeList",
    "[objectDOMWindow]" : "window",
    "[object global]" : "window",
    "Null" : "Null",
    "undefined" : "undefined"
  },
  toString = class2type.toString;
  "Boolean,Number,String,Function,Array,Date,RegExp,Window,Document,Arguments,NodeList".replace($.rword,function( name ) {
    class2type[ "[object" + name + "]" ] = name;
  });

  //class2type这个映射几乎把常用的判定对象一网打尽
  mass.type = function( obj , str ){
    var result = class2type[ (obj == null || obj !== obj) ? obj : toString.call(obj) ] || obj.nodeName || "#";
    if(result.charAt(0) === "#") { //兼容旧版浏览器的个别情况,如window.opera
      //利用IE678 window === document为true,document === window为false
      if( obj == obj.document && obj.document != obj ) {
        result = "window"; //返回构造器名称
      } else if ( obj.nodeType === 9 ) {
        result = "Document";
      } else if ( obj.callee) {
        result = "Arguments";
      } else if ( isFinite(obj.length) && obj.item ) {
        result = "NodeList" //处理节点集合
      } else {
        result = toString.call(obj).slice(8,-1);
      }
    }
    if(str){
      result str === result;
    }
    return result;
  }

然后type方法就十分轻松了,用toSring.call(obj)得出值的左键,直接从映射中取得。IE678,我们才费一些周折处理window,document,argument,nodeList等对象。

百度的七巧板基于实用主义,判定也十分严谨。与EXT一样,能想到的写上,并且判定十分严谨。

目前版本2.0.2.5 http://tangram.baidu.com/api#baidu.type()

baidu.isDate = function( unknow ) {
  return baidu.type(unknow) == "date" && unknow.toString() != 'Invalid Date' && !isNaN(unknow);
  };
  baidu.isNumber = function( unknow ) {
  return baidu.type(unknow) == "number" && isFinite( unknow );
  };

 5.主流框架的引入机制-domReady

domReady其实是一种名为"DOMContentLoaded"事件的别称,不过由于框架的需要,它与真正的DOMContentLoaded有一点区别,在很多新手和旧的书中,很多人将其写在window.onload回调中,防止dom树还没有建完就开始对节点操作。而对于框架来说,越早越介入dom就越好,如要进行特征侦测之类的。domready还可以满足用户提前绑定事件需求,因为有时页面图片过多等,window.onload事件迟迟不能触发,这时用户操作都没有效果,因此,主流的框架都引入了domReady机制,并且费了很大周折才兼容所有浏览器。具体的策略如下:

对于支持DOMContentLoaded事件使用DOMcontentLoaded事件
旧版本IE使用Diego perini发现的著名Hack

//by Diego Perini 2007.10.5
   function IEContentLoaded (w, fn) {
   var d = w.document, done = false,
   // 只执行一次用户的回调函数init()
   init = function () {
    if (!done) {
      done = true;
      fn();
    }
   };
  (function () {
    try {
      // DOM树未创建完之前调用doScroll会抛出错误
      d.documentElement.doScroll('left');
    } catch (e) {
      //延迟再试一次~
      setTimeout(arguments.callee, 50);
      return;
    }
    // 没有错误就表示DOM树创建完毕,然后立马执行用户回调
    init();
         })();
  //监听document的加载状态
  d.onreadystatechange = function() {
    // 如果用户是在domReady之后绑定的函数,就立马执行
    if (d.readyState == 'complete') {
      d.onreadystatechange = null;
      init();
    }
       };
  }

此外,IE还可以通过script defer hack进行判定

document.write("<script id=__ie_onload defer src=//0 mce_src=http://0></scr"+"ipt>"); 
    script = document.getElementById("__ie_onload"); 
    script.onreadystatechange = function() { //IE即使是死链也能触发事件 
          if (this.readyState == "complete") 
    init(); // 指定了defer的script在dom树建完才触发
  };

不过还有个问题,如果我们的种子模块是动态加载的,在它插入dom树时,DOM树是否已经建完呢?这该怎么触发ready回调?jQuery的方案是,连onload也监听了 ,但如果连onload也没赶上,就判定document.readyState等于complete。(完美)可惜ff3.6之前没有这属性,看mass的方案

var readyList = [];
  mess.ready = function( fn ) {
    if ( readyList ) {
      fn.push( fn );
    } else {
      fn();
    }
  }
  var readyFn ,ready = W3C ? "DOMContentLoaded" : "readyStatechange";
  function fireReady() {
    for (var i = 0 , fn; fn = readyList[i++]) {
      fn();
    }
    readyList = null;
    fireReady = $.noop; //惰性函数,防止IE9调用_checkDeps
  }
  function doScrollCheck() {
    try { //IE下通过doScrollCheck检测DOM树是否建设完
      html.doScroll("left");
      fireReady();
    } catch (e){
      setTimeout(doScrollCheck);
    }
  }

  //FF3.6前,没有readyState属性
  if (!document.readyState){
    var readyState = document.readyState = document.body ? "complete" : "loading";
    if (document.readyState === "complete") {
      fireReady(); //如果在domReay之外加载
    } else {
      $.bind(document,ready,readyFn = function(){
        if(W3C || document.readyState === "complete") {
          fireReady();
          if(readyState){ //IE下不能该项document.readyState
            document.readyState = "complete";
          }
        }
      });
      if (html.doScroll){
        try {//如果跨域就报错,证明有两个窗口
          if (self.eval === parent.eval) {
            doScrollCheck();
          }
        } catch (e) {
          doScrollCheck();
        }
      }
    }

  }

6.无冲突处理

无冲突处理也叫多库共存,$是这个重要的函数名,以至于大家都爱拿它来做自己的命名空间,当jQuery开始发展时,Prototype是主流,jQuery发明了noConflict函数

var window = this,
  undefined,
  _jQuery = window.jQuery,
  _$ = window.$,
  //将window存入闭包中的同名变量,方便内部函数调用windows不用太麻烦查找它。
  //_jQuery与_$用于以后重写
  jQuery = window.jQuery = window.$ = function(selector, context){
    //用于返回一个jQuery对象
    return new jQuery.fn.init(selector,context);
  }

  jQuery.extend({
    noConflict : function(deep) {
      //引入jQuery类库后,闭包外边的window.$与window.jQuery都储存着一个函数
      //它是用来生成jQuery对象或在domReady后执行里面的函数
      //回顾最上面的代码,在还没有把function赋值给它们时,_jQuery和_$已经被赋值了,因此,它们两的值必然是undefined
      //因此,这种放弃控制权的技术很简单,就是用undefined把window.$里边jQuery函数清除掉。
      //这时,prototype或mootools的$就被拿走了
      window.$ = _$; //相当于window.$ = undefined,如果你有一个叫jQuery的库,也能大方的过渡出去。
      //这时,需要给noConflict添加一个布尔值,true
    
    if (deep)
      //但我们必须使用一个东西接纳jQuery与jQuey的入口函数
      //闭包里边的东西除非被window等宿主引用,否则是不可见的
      //因此,我们把闭包里的jQuery return出去,外面用一个变量接纳就可以
      window.jQuery = _jQuery; //相当window.jQuery = undefined
    return jQuery;
    }
  })

使用时,先引入别人的库,然后引入jQuery,使用调用$.noConflict()进行改名,这样就不影响别人的$运行了。

mass的操作方式是在script标签上定义一个nick属性,那么释放出来的命名空间就是你的那个属性值。里面实现了类似jQuery的机制。

<script nike="aaa" src="mass.js"></script>
<script>
  aaa.log("xxxxxx")
</script>

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
简短几句 通俗解释javascript的闭包
Jan 17 Javascript
基于jquery的图片幻灯展示源码
Jul 15 Javascript
Javascript this 的一些学习总结
Aug 02 Javascript
ztree获取选中节点时不能进入可视区域出现BUG如何解决
Dec 03 Javascript
使用堆实现Top K算法(JS实现)
Dec 25 Javascript
jQuery基于扩展简单实现倒计时功能的方法
May 14 Javascript
基于vue的短信验证码倒计时demo
Sep 13 Javascript
jQuery实现仿京东防抖动菜单效果示例
Jul 06 jQuery
微信js-sdk 录音功能的示例代码
Nov 01 Javascript
JS数组及对象遍历方法代码汇总
Jun 16 Javascript
js实现简单的随机点名器
Sep 17 Javascript
全面解析Vue中的$nextTick
Dec 24 Vue.js
在JavaScript的AngularJS库中进行单元测试的方法
Jun 23 #Javascript
javascript框架设计之框架分类及主要功能
Jun 23 #Javascript
js的flv视频播放器插件使用方法
Jun 23 #Javascript
使用Raygun来自动追踪AngularJS中的异常
Jun 23 #Javascript
使用JavaScript的AngularJS库编写hello world的方法
Jun 23 #Javascript
浅谈setTimeout 与 setInterval
Jun 23 #Javascript
简介可以自动完成UI的AngularJS工具angular-smarty
Jun 23 #Javascript
You might like
PHP5.0对象模型探索之抽象方法和抽象类
2006/09/05 PHP
php4的session功能评述(二)
2006/10/09 PHP
PHP获取文件扩展名的4种方法
2015/11/24 PHP
PHP MSSQL 分页实例
2016/04/13 PHP
yii通过小物件生成view的方法
2016/10/08 PHP
php常用字符函数实例小结
2016/12/29 PHP
jBox 2.3基于jquery的最新多功能对话框插件 常见使用问题解答
2011/11/10 Javascript
javascript实现playfair和hill密码算法
2014/12/07 Javascript
JS实现鼠标滑过链接改变网页背景颜色的方法
2015/10/20 Javascript
七个不允许错过的jQuery小技巧
2015/12/21 Javascript
JS未跨域操作iframe里的DOM
2016/06/01 Javascript
seajs学习教程之基础篇
2016/10/20 Javascript
基于BootStrap multiselect.js实现的下拉框联动效果
2017/07/28 Javascript
详解Vue组件插槽的使用以及调用组件内的方法
2018/11/13 Javascript
JavaScript监听一个DOM元素大小变化
2020/04/26 Javascript
Vue性能优化的方法
2020/07/30 Javascript
Python中如何获取类属性的列表
2016/12/26 Python
Python多线程threading和multiprocessing模块实例解析
2018/01/29 Python
基于python3 OpenCV3实现静态图片人脸识别
2018/05/25 Python
解决pycharm运行出错,代码正确结果不显示的问题
2018/11/30 Python
Python Web框架之Django框架文件上传功能详解
2019/08/16 Python
如何基于python操作json文件获取内容
2019/12/24 Python
Tensorflow实现部分参数梯度更新操作
2020/01/23 Python
new_zeros() pytorch版本的转换方式
2020/02/18 Python
如何配置关联Python 解释器 Anaconda的教程(图解)
2020/04/30 Python
python爬虫使用正则爬取网站的实现
2020/08/03 Python
详解Pytorch显存动态分配规律探索
2020/11/17 Python
Origins悦木之源英国官网:雅诗兰黛集团高端植物护肤品牌
2017/11/06 全球购物
旧时光糖果:Old Time Candy
2018/02/05 全球购物
马来西亚最热门的在线时尚商店:FashionValet
2018/11/11 全球购物
Doyoueven官网:澳大利亚健身服饰和配饰品牌
2019/03/24 全球购物
网站出售协议书范文
2014/10/10 职场文书
开展批评与自我批评发言材料
2014/10/17 职场文书
详细分析PHP7与PHP5区别
2021/06/26 PHP
MybatisPlus EntityWrapper如何自定义SQL
2022/03/22 Java/Android
Java 超详细讲解IO操作字节流与字符流
2022/03/25 Java/Android