SlideShare a Scribd company logo
1 of 49
Download to read offline
INSIDE JQUERY
                  市场开发部-前端 魏琪君




12年3月31⽇日星期六
⼤大纲
               1. 对象结构和构造成本
               2. Promise & Deferred
               3. jQuery.cache
               4. 事件
               5. Sizzle
               6. ⼀一些⽅方法和实现




12年3月31⽇日星期六
对象结构




12年3月31⽇日星期六
jQuery对象形成链表结构




12年3月31⽇日星期六
摘⾃自jQuery⽂文档
  $('ul.first').find('.foo').css('background-color', 'red')
    .end().find('.bar').css('background-color', 'green');

  1. 不推荐过分地使⽤用链式调⽤用
  2. 同类型操作可以使⽤用链式调⽤用,以简化代码




12年3月31⽇日星期六
构造流程
  1. $(element)
        this.context = this[0] = element
        this.length = 1
        return;
  2. $(‘body’)
  3. $(‘#id’)
        document.getElementById(
  4. $(selector)
        $(document).find(selector)
      $(selector, jq)
        jq.find(‘selector’)
      $(‘selector’, context)
        $(context).find(‘selector’)
  5. $(jquery)
  6. $(func)
        $(document).ready(func)

12年3月31⽇日星期六
7. $(html)
  1. simple tag $(‘<div>’), $(‘<div />’), $(‘<div></div>’)
     1.1 document.createElement(
     1.2? $(simpleTag, attrs)
               jQuery.fn.attr.call(elm, attrs)


  2. else
          jQuery.buildFragment




12年3月31⽇日星期六
应⽤用
  1. 构造jQuery对象的空间成本很低,正常情况下10000个也不会超过1M, 旺铺DIY后台打开
  渲染完毕⼤大约构造800个jQuery对象。⼀一般⻚页⾯面初始化时构造数量应该< 500

  2. 构造jQuery对象的时间成本通常也很低
     如从 dom节点,jquery节点,‘body’, id选择器 构造jQuery都不需要Sizzle参于

  3. 如果明确知道是jQuery对象,就不需要再次包装(⼀一般新⼿手会出现,喜欢任何变量都套个$)

  4. 使⽤用其他选择器,如果没有context, 相当于 $(document).find(...
     所以建议任何使⽤用css3选择器进⾏行构造jQuery对象,请带上相应模块的context节点进⾏行限
  制

  5. 从html构造jQuery对象时,优先采⽤用 simpleTag




12年3月31⽇日星期六
Promise接⼝口
  promise接⼝口为监异步操作定义了统⼀一的api
  它是commonjs规范中的内容⻅见 http://wiki.commonjs.org/wiki/Promises/A

  var Promise = {
     done: function(callback),
     fail: function(callback),
     always: function(callback),
     ....
  };




12年3月31⽇日星期六
$.ajax
  // 通常
  $.ajax(url, {
      dataType: ‘jsonp’
      success: function(o) {
         console.debug(o);
      }
  });




12年3月31⽇日星期六
通过promise接⼝口使⽤用ajax
  var o = $.ajax('mock.php', { dataType: 'jsonp' });
  o.done(function(o) { // 相当于success
      console.debug('success1', o);
  });

  o.fail(function(e) { // 相当于 error
      console.debug('error', e);
  });

  o.always(function() { // 相当于complete
      console.debug('complete');
  });

  var o = $.ajax('mock.php', { dataType: 'jsonp' });
  o.then(function() {
      console.debug('success');
  }, function() {
      console.debug('error');
  });



12年3月31⽇日星期六
$.when
  var url = 'mock.php'
     a1 = $.ajax(url, { dataType: 'jsonp' }),
     a2 = $.ajax(url, { dataType: 'jsonp' }),
     a3 = $.ajax(url, { dataType: 'jsonp' });

  $.when(a1, a2, a3).done(function() {
      console.debug('all complete');
  });


  额外阅读: promise.pipe()




12年3月31⽇日星期六
Promise接⼝口另外⼀一半
  var Promise = {
     // 下⾯面3个⽅方法相当于bind
     done: function() {},
     fail: function() {},
     always: function() {}

       // 下⾯面两个⽅方法相当于trigger
       resolve: function() {},
       reject: function() {}
  };




12年3月31⽇日星期六
Deferred对象
  ⽤用于⽅方便我们实现promise接⼝口




12年3月31⽇日星期六
⼀一个⽰示例




12年3月31⽇日星期六
注意依赖关系




12年3月31⽇日星期六
应⽤用
  1. 使⽤用 $.when, promise.pipe等完成⼀一些稍复杂的异步请求需求

  2. 组件开发者如有必要可以考虑提供promise接⼝口来统⼀一异步操作的使⽤用⽅方式

  3. 使⽤用Deferred对象简化promise接⼝口的实现

  4. 注意不能让Deferred侵⼊入业务代码(⻅见例⼦子)

  5. 使⽤用Callbacks来简化observer模式的实现(1.7提供)




12年3月31⽇日星期六
$.cache
  jQuery使⽤用$.cache这个对象保存 $.data, 事件, 动画操作所需要的数据结构

  所以了解它对于理解和调试代码有⾮非常⼤大的帮助




12年3月31⽇日星期六
12年3月31⽇日星期六
$.data
  1. 我们使⽤用$(elm).data(‘key’, value) 设置节点相关数据

  2. 有三种元素不能设置data: embed, ⾮非flash的object, applet. 查看jQuery.noData获得列表

  3. 设置时key字符串会被转成 camelCase
     如 $(elm).data(‘a-b’, ‘hello’) 相当于 $(elm).data(‘aB’, ‘hello’)

     我们应该全部使⽤用camelCase⽅方式进⾏行设置

  4. 获取时 使⽤用key尝试获取,如果获取不到,会把key转化成 camelCase再次获取。
     why? 因为允许 $(elm).data({ ‘a-b’: ‘hello’, ‘c-d’: ‘hello2’ }); 这种⽅方式进⾏行⼀一次设置多个data
     但是内部没做转换(估计后续会优化)

     我们应该全部使⽤用camelCase⽅方式进⾏行获取

  5. 获取data时,如果从cache中获取不到,则会尝试从data属性中获取
    <div data-config=’{ “requestUrl”: “url” }’>...
   可以使⽤用 $(elm).data(‘config’) 进⾏行读取配置信息
   ⼀一次读取后,这个值就会被cache,下次从cache中直接获取

  注:设置时,是不会更改节点属性的, 如果需要可以使⽤用 $#attr(...


12年3月31⽇日星期六
data类型识别
从data属性获取的值,会根据属性字符串的样式,⽽而进⾏行适当的类型转换

1. “true” --> true

2. “false” --> false

3. “null” --> null

4. $.isNumberic(data) --> parseFloat(data)

5. “{...}” 或 “[]” --> parseJSON, 如果parse失败,则返回字符串
   这⾥里如果要正常parse出json,必须是严格的json格式才可以。

6. 直接返回字符串

具体过程看:function dataAttr(




12年3月31⽇日星期六
data事件
  var elm = $('#mydiv1');

  elm.bind('getData', function(value, name) { // 取得数据前触发,可以拦截哦
  	

  console.debug('getData', name, value);
  	

  return 'new data';
  });

  elm.bind('setData', function(value, name) { // 设置数据前触发, 可惜这个⽅方法没有提供拦截
  	

  console.debug('setData', name, value);
  });

  elm.bind('changeData', function(value, name) { // 设置数据后触发
  	

  console.debug('changeData', name, value)
  });

  注: 这⼏几个⽅方法并未出现在⽂文档中(可能是我没发现),但这⼏几个事件出现在很早的
  jQuery版本中 1.4就有了




12年3月31⽇日星期六
事件




12年3月31⽇日星期六
12年3月31⽇日星期六
12年3月31⽇日星期六
12年3月31⽇日星期六
12年3月31⽇日星期六
事件源码导读
  1. 在理解上述结构后,看源码将会⾮非常⽅方便
  2. 源码中如果有结构和逻辑两部分, 要先弄懂数据结构。
  3. 数据结构是静⽌止的算法


  事件内部⼯工作流程:

  1. 当调⽤用elm.on/bind/delete时, 内部调⽤用 jQuery.event.add(elem, types, handler, data, selector)
     完成上述结构的构建

  2. 事件触发时,将会调⽤用相应节点 data events.handle ⽅方法

  3. events.handle 仅仅调⽤用 jQuery.event.dispatch(event) [备注,⽼老版本这个⽅方法叫
  jQuery.event.handle]

  4. 当调⽤用trigger⼈人为触发事件时, 内部调⽤用 jQuery.event.trigger(event, data, elem, onlyHandlers)
   后者调⽤用 节点data events.handle⽅方法



12年3月31⽇日星期六
事件使⽤用技巧
  0. 从原理上可以看出,⾃自定义事件和原⽣生事件使⽤用的是同⼀一套逻辑,和浏览器⽆无关

  1. jQuery 1.7开始使⽤用on/off 来代替原来的 bind, delegate, one 等事件,以规范化事件的使⽤用

  2. 如需要对很多相同性质的节点,或动态产⽣生的节点进⾏行事件绑定,请使⽤用 delegate

  3. delegate请尽量减少作⽤用域,如有可能,使⽤用 tag#id.cass来作为selecotr, 这样会直接使⽤用正
  则式进⾏行⽐比较

  4. 可以使⽤用事件数据来传递额外信息

  5. e.preventDefault/e.stopPropagation, return false相当于两者

  6. e.stopImmediatePropagation(), 节点事件数组中后⾯面的事件都不再执⾏行




12年3月31⽇日星期六
7. trigger/ triggerHandler区别

      trigger: function( type, data ) {
          return this.each(function() {
              jQuery.event.trigger( type, data, this );
 	

      });
     },

  triggerHandler: function( type, data ) {
      if ( this[0] ) {
           return jQuery.event.trigger( type, data, this[0], true );
      }
  }


 8. 在某些情况下可以使⽤用namespace事件来完成⼀一些要求
    如在组件中,对该组件所有的事件给个namespace, 以⽅方便清理
    我们绑定事件时,可以给⼀一个namespace, 以便在不保存事件引⽤用的情况下移除事件

 9. 注意api的⽅方便性, 如⼀一次可以添加多个事件哦, 还有事件函数可以直接为false
    link.on(‘click’, false);

 10. one / toggle 类型事件 在某些情况下可以帮助我们不要重新发明轮⼦子


12年3月31⽇日星期六
Sizzle
               jQuery中的CSS3选择器引擎




12年3月31⽇日星期六
querySelectorAll
  在⾼高级浏览器中,我们可能根本不需要js来处理选择器,因为有querySelectorAll




12年3月31⽇日星期六
⾼高效的选择器
  1. 即使有querySelectorAll, 也不⼀一定⽀支持任何复杂(⻅见jQuery⽂文档)的选择器

  2. 经过我的测试,以下选择器⼀一般都是⽀支持的,所以优先采⽤用(正常应⽤用中⾜足够了)
     - ⼦子代选择器,邻代选择器等都是⽀支持的 div ul, div>ul, div+ul, div~ul
     - 属性选择器 input[name], input[name=username], div[data-tracelog]
     - class选择器 div.mod
     - 不⽀支持伪类选择器, 只⽀支持hover, active等⼏几个伪类选择器, 如 :first, :last是不⽀支持
  的

  3. 以下⼏几种情况直接使⽤用dom api直接获取, ⽽而不需要 querySelectorAll
     ‘body‘ document.body
      #id    document.getElementById
      tag    getElementsByTagName
      .class getElementsByClass (如果⽀支持的话)




12年3月31⽇日星期六
其他可参考:http://caniuse.com/




12年3月31⽇日星期六
选择器使⽤用技巧
  1. 优先使⽤用简单选择器

  2. 选取元素时,请指定合适的context(推荐为模块⼦子模块)
     如 $(‘a.close’, panel)

  3. class选择器请限定tag, 如tag.class

  4. 适度使⽤用伪类

  5. 选择器层级不要太深,⼀一般指定context后, selector不会超过2层




12年3月31⽇日星期六
Sizzle流程
  0. jQuery.find = Sizzle

  1. 处理选择器 -》 数组
     jQuery.find('#doc div.mylist ul>li') -》 ["#doc", "div.mylist", "ul", ">", "li"]

  2. 根据类别采⽤用不同的处理顺序
     - 选择器中包含位置相关的伪类,如 :first, :last, :even, :odd
       ⻅见 Sizzle.selectors.match.POS 这个正则表达式

        如果存在, 则采⽤用⾃自左向右的顺序进⾏行处理
          $(‘div ul li:first’)-> $(‘div’).find(‘ul’).find(‘li:first’)

        如果不存在,则采⽤用⾃自右向左的顺序进⾏行处理[1]
          $(‘div ul li’) -> $(‘li’), 再过滤出所有符合条件的li, 处理是从右⾃自左的




  [1] 如果第⼀一个为id选择器,则先处理它,再使⽤用这个节点为context查找


12年3月31⽇日星期六
Sizzle(‘ul li’)
     - 分割选择器 [‘ul’, ‘li’]
     - Sizzle.find(‘li’)
        - Sizzle.selector.find.TAG ---> getElementsByTagName(li)
     - Sizzle.relative(lis, ‘ul’); -> 看看是否在ul下⾯面

  Sizzle(‘ul li:first’)
     - 分割选择器[‘ul’, ‘li:first’]
     - Sizzle.find(‘ul’)
        - 分析选择器 [‘ul’]
        - Sizzle.find(‘ul’)
            Sizzle.selector.find.TAG ---> getElementsByTagName
     - Sizzle(‘li’, ul)
     - Sizzle.filter(‘:first’, lis)




12年3月31⽇日星期六
Sizzle源码导读
  1. 正则式 chunker ⽤用于分解选择器

  2. 正则式Sizzle.selectors.match.POS ⽤用于判断是否有位置类型的选择器

  3. Sizzle.selectors.find ⽤用于查找节点(对应于原⽣生⽅方法, getElementById, getElementsByTagName
  等)

  4. Sizzle.selectors.relative 过滤出符合要求的⽗父节点

  5. Sizzle.selectors.filters 伪类过滤器 (我们可以扩展这个实现⾃自⼰己的伪类语法哦)

  6. Sizzle.selectors.filter 各种类型的过滤器逻辑




12年3月31⽇日星期六
创建⾃自⼰己的选择器
  jQuery.expr[‘:’][‘mod’] = function(elem) {
      return $(elem).hasClass(‘.mod’);
  };

  var mod = $(‘div:mod’);

  组件编写者可以根据需要编写⾃自⼰己的伪类选择器




12年3月31⽇日星期六
轻松⼀一下〜~




12年3月31⽇日星期六
$.noop




12年3月31⽇日星期六
$.proxy
  ⽅方便我们创建特定context和参数的closure


  var A = {
     init: function() {
         var div = $(‘div.mydiv’);
         $.ajax(url, {
             success: $.proxy(this, ‘success’, div);   // 代替 function(o) { self.success(div, o); }
         });
     },

       success: function(div, o) {
          this...
       }
  };




12年3月31⽇日星期六
elm.attr() / elm.prop()
  1. jQuery 1.6之后有了prop, 即把 attribute和 property分开

  2. 区分attribute 和 property
  前者相当于 elm.getAttribute 后者相当于 elm[key], 如 elm.checked, sel.selectedIndex

  3. checkbox.prop(‘checked’), select.prop(‘selectedIndex’)

  4. 尽管jQuery在1.64及以后的版本中做了兼容性,但是不要依赖于这个特性
     1. 存在jQuery.attrFn⾥里⾯面的,则直接调⽤用相应⽅方法
     2. 返回undefined的,尝试使⽤用prop获取
     3. 存在于jQuery.attrHook的字段,特殊处理
     4. 最后使⽤用getAttribute/setAttribute




12年3月31⽇日星期六
elm.remove() / elm.detach()




12年3月31⽇日星期六
elm.closest()




12年3月31⽇日星期六
remove event
   在ui/core.js中
     var _cleanData = $.cleanData;
      $.cleanData = function(elems){
         for (var i = 0, elem; (elem = elems[i]) != null; i++) {
            $(elem).triggerHandler('remove');
         }
         _cleanData(elems);
      };




12年3月31⽇日星期六
应⽤用
    所以在引⼊入ui/core的前提下:

    $(elm).bind(‘remove’, function() {
        // 清理⼯工作
        // 组件或框架设计的同学可以⽤用来做清理⼯工作
    });




12年3月31⽇日星期六
源码之前,了⽆无秘密!




12年3月31⽇日星期六
完




12年3月31⽇日星期六

More Related Content

What's hot

Script with engine
Script with engineScript with engine
Script with engineWebrebuild
 
不断归零的前端人生 - 2016 中国软件开发者大会
不断归零的前端人生 - 2016 中国软件开发者大会不断归零的前端人生 - 2016 中国软件开发者大会
不断归零的前端人生 - 2016 中国软件开发者大会Joseph Chiang
 
jQuery实践经验与技巧
jQuery实践经验与技巧jQuery实践经验与技巧
jQuery实践经验与技巧fangdeng
 
Yui3入门
Yui3入门Yui3入门
Yui3入门cly84920
 
GAE/J 簡介
GAE/J 簡介GAE/J 簡介
GAE/J 簡介Cloud Tu
 
Hibernate的高级操作
Hibernate的高级操作Hibernate的高级操作
Hibernate的高级操作yiditushe
 
Spry框架的简单使用小结
Spry框架的简单使用小结Spry框架的简单使用小结
Spry框架的简单使用小结sunnylqm
 

What's hot (11)

Script with engine
Script with engineScript with engine
Script with engine
 
不断归零的前端人生 - 2016 中国软件开发者大会
不断归零的前端人生 - 2016 中国软件开发者大会不断归零的前端人生 - 2016 中国软件开发者大会
不断归零的前端人生 - 2016 中国软件开发者大会
 
Drag & drop
Drag & dropDrag & drop
Drag & drop
 
jQuery实践经验与技巧
jQuery实践经验与技巧jQuery实践经验与技巧
jQuery实践经验与技巧
 
nodejs开发web站点
nodejs开发web站点nodejs开发web站点
nodejs开发web站点
 
Yui3入门
Yui3入门Yui3入门
Yui3入门
 
GAE/J 簡介
GAE/J 簡介GAE/J 簡介
GAE/J 簡介
 
Hibernate的高级操作
Hibernate的高级操作Hibernate的高级操作
Hibernate的高级操作
 
组合、备忘录、建造者模式、原型
组合、备忘录、建造者模式、原型组合、备忘录、建造者模式、原型
组合、备忘录、建造者模式、原型
 
Spry框架的简单使用小结
Spry框架的简单使用小结Spry框架的简单使用小结
Spry框架的简单使用小结
 
JS2
JS2JS2
JS2
 

Viewers also liked

Javascript正则
Javascript正则Javascript正则
Javascript正则fangdeng
 
前端开发之Js
前端开发之Js前端开发之Js
前端开发之Jsfangdeng
 
Html基础培训
Html基础培训Html基础培训
Html基础培训fangdeng
 
Javascript代码注释及文档生成
Javascript代码注释及文档生成Javascript代码注释及文档生成
Javascript代码注释及文档生成fangdeng
 
浅尝jQuery
浅尝jQuery浅尝jQuery
浅尝jQueryfangdeng
 
Teaching Students with Emojis, Emoticons, & Textspeak
Teaching Students with Emojis, Emoticons, & TextspeakTeaching Students with Emojis, Emoticons, & Textspeak
Teaching Students with Emojis, Emoticons, & TextspeakShelly Sanchez Terrell
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerLuminary Labs
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsLinkedIn
 

Viewers also liked (8)

Javascript正则
Javascript正则Javascript正则
Javascript正则
 
前端开发之Js
前端开发之Js前端开发之Js
前端开发之Js
 
Html基础培训
Html基础培训Html基础培训
Html基础培训
 
Javascript代码注释及文档生成
Javascript代码注释及文档生成Javascript代码注释及文档生成
Javascript代码注释及文档生成
 
浅尝jQuery
浅尝jQuery浅尝jQuery
浅尝jQuery
 
Teaching Students with Emojis, Emoticons, & Textspeak
Teaching Students with Emojis, Emoticons, & TextspeakTeaching Students with Emojis, Emoticons, & Textspeak
Teaching Students with Emojis, Emoticons, & Textspeak
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI Explainer
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving Cars
 

Similar to jQuery底层架构

用Jquery实现拖拽层
用Jquery实现拖拽层用Jquery实现拖拽层
用Jquery实现拖拽层yiditushe
 
JavaScript Advanced Skill
JavaScript Advanced SkillJavaScript Advanced Skill
JavaScript Advanced Skillfirestoke
 
Jquery指南
Jquery指南Jquery指南
Jquery指南yiditushe
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现hua qiu
 
jQuery源码学习
jQuery源码学习jQuery源码学习
jQuery源码学习fangdeng
 
KISSY for starter
KISSY for starterKISSY for starter
KISSY for starteryiming he
 
jQuery 選取器解析
jQuery 選取器解析jQuery 選取器解析
jQuery 選取器解析Kingsley Zheng
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejsChi-wen Sun
 
第三方内容开发最佳实践
第三方内容开发最佳实践第三方内容开发最佳实践
第三方内容开发最佳实践taobao.com
 
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用FLASH开发者交流会
 
面向开发的前端性能优化
面向开发的前端性能优化面向开发的前端性能优化
面向开发的前端性能优化li qiang
 
SeaJS - 前端模块化开发探索与网站性能优化实践
SeaJS - 前端模块化开发探索与网站性能优化实践SeaJS - 前端模块化开发探索与网站性能优化实践
SeaJS - 前端模块化开发探索与网站性能优化实践lifesinger
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程yiditushe
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程appollo0312
 
jQuery 教學 ( 搭配 EZoApp )
jQuery 教學 ( 搭配 EZoApp )jQuery 教學 ( 搭配 EZoApp )
jQuery 教學 ( 搭配 EZoApp )EZoApp
 

Similar to jQuery底层架构 (20)

用Jquery实现拖拽层
用Jquery实现拖拽层用Jquery实现拖拽层
用Jquery实现拖拽层
 
JavaScript Advanced Skill
JavaScript Advanced SkillJavaScript Advanced Skill
JavaScript Advanced Skill
 
Jquery指南
Jquery指南Jquery指南
Jquery指南
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现
 
jQuery源码学习
jQuery源码学习jQuery源码学习
jQuery源码学习
 
J Query Learn
J Query LearnJ Query Learn
J Query Learn
 
KISSY for starter
KISSY for starterKISSY for starter
KISSY for starter
 
jQuery 選取器解析
jQuery 選取器解析jQuery 選取器解析
jQuery 選取器解析
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
 
第三方内容开发最佳实践
第三方内容开发最佳实践第三方内容开发最佳实践
第三方内容开发最佳实践
 
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
详解AS3的内存管理机制,有效释放FLASH内存,减少资源占用
 
前端测试
前端测试前端测试
前端测试
 
前端测试
前端测试前端测试
前端测试
 
A
AA
A
 
A
AA
A
 
面向开发的前端性能优化
面向开发的前端性能优化面向开发的前端性能优化
面向开发的前端性能优化
 
SeaJS - 前端模块化开发探索与网站性能优化实践
SeaJS - 前端模块化开发探索与网站性能优化实践SeaJS - 前端模块化开发探索与网站性能优化实践
SeaJS - 前端模块化开发探索与网站性能优化实践
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
 
jQuery 教學 ( 搭配 EZoApp )
jQuery 教學 ( 搭配 EZoApp )jQuery 教學 ( 搭配 EZoApp )
jQuery 教學 ( 搭配 EZoApp )
 

More from fangdeng

J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架fangdeng
 
前端单元测试初体验
前端单元测试初体验前端单元测试初体验
前端单元测试初体验fangdeng
 
Java script测试之js unit ut
Java script测试之js unit utJava script测试之js unit ut
Java script测试之js unit utfangdeng
 
2011年方凳年度总结及颁奖
2011年方凳年度总结及颁奖2011年方凳年度总结及颁奖
2011年方凳年度总结及颁奖fangdeng
 
产品线中的思考
产品线中的思考产品线中的思考
产品线中的思考fangdeng
 
产品线中的思考
产品线中的思考产品线中的思考
产品线中的思考fangdeng
 
Postoffer前端架构设计
Postoffer前端架构设计Postoffer前端架构设计
Postoffer前端架构设计fangdeng
 
Varnish简介
Varnish简介Varnish简介
Varnish简介fangdeng
 
Let's talk about date in javascript
Let's talk about  date  in javascriptLet's talk about  date  in javascript
Let's talk about date in javascriptfangdeng
 
Test driven-frontend-develop
Test driven-frontend-developTest driven-frontend-develop
Test driven-frontend-developfangdeng
 
方凳良品1期
方凳良品1期方凳良品1期
方凳良品1期fangdeng
 
方凳良品2期
方凳良品2期方凳良品2期
方凳良品2期fangdeng
 
魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式fangdeng
 
Datalazyload
DatalazyloadDatalazyload
Datalazyloadfangdeng
 
Request animateframe初探
Request animateframe初探Request animateframe初探
Request animateframe初探fangdeng
 
简鲜侠Websocket
简鲜侠Websocket简鲜侠Websocket
简鲜侠Websocketfangdeng
 
魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式fangdeng
 
方凳激励体系(试行)
方凳激励体系(试行)方凳激励体系(试行)
方凳激励体系(试行)fangdeng
 
高性能Javascript
高性能Javascript高性能Javascript
高性能Javascriptfangdeng
 

More from fangdeng (20)

J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架
 
前端单元测试初体验
前端单元测试初体验前端单元测试初体验
前端单元测试初体验
 
Java script测试之js unit ut
Java script测试之js unit utJava script测试之js unit ut
Java script测试之js unit ut
 
2011年方凳年度总结及颁奖
2011年方凳年度总结及颁奖2011年方凳年度总结及颁奖
2011年方凳年度总结及颁奖
 
产品线中的思考
产品线中的思考产品线中的思考
产品线中的思考
 
产品线中的思考
产品线中的思考产品线中的思考
产品线中的思考
 
Postoffer前端架构设计
Postoffer前端架构设计Postoffer前端架构设计
Postoffer前端架构设计
 
Varnish简介
Varnish简介Varnish简介
Varnish简介
 
Websocket
WebsocketWebsocket
Websocket
 
Let's talk about date in javascript
Let's talk about  date  in javascriptLet's talk about  date  in javascript
Let's talk about date in javascript
 
Test driven-frontend-develop
Test driven-frontend-developTest driven-frontend-develop
Test driven-frontend-develop
 
方凳良品1期
方凳良品1期方凳良品1期
方凳良品1期
 
方凳良品2期
方凳良品2期方凳良品2期
方凳良品2期
 
魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式
 
Datalazyload
DatalazyloadDatalazyload
Datalazyload
 
Request animateframe初探
Request animateframe初探Request animateframe初探
Request animateframe初探
 
简鲜侠Websocket
简鲜侠Websocket简鲜侠Websocket
简鲜侠Websocket
 
魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式魏琪君-重构-关于可读性、原则和模式
魏琪君-重构-关于可读性、原则和模式
 
方凳激励体系(试行)
方凳激励体系(试行)方凳激励体系(试行)
方凳激励体系(试行)
 
高性能Javascript
高性能Javascript高性能Javascript
高性能Javascript
 

jQuery底层架构

  • 1. INSIDE JQUERY 市场开发部-前端 魏琪君 12年3月31⽇日星期六
  • 2. ⼤大纲 1. 对象结构和构造成本 2. Promise & Deferred 3. jQuery.cache 4. 事件 5. Sizzle 6. ⼀一些⽅方法和实现 12年3月31⽇日星期六
  • 5. 摘⾃自jQuery⽂文档 $('ul.first').find('.foo').css('background-color', 'red') .end().find('.bar').css('background-color', 'green'); 1. 不推荐过分地使⽤用链式调⽤用 2. 同类型操作可以使⽤用链式调⽤用,以简化代码 12年3月31⽇日星期六
  • 6. 构造流程 1. $(element) this.context = this[0] = element this.length = 1 return; 2. $(‘body’) 3. $(‘#id’) document.getElementById( 4. $(selector) $(document).find(selector) $(selector, jq) jq.find(‘selector’) $(‘selector’, context) $(context).find(‘selector’) 5. $(jquery) 6. $(func) $(document).ready(func) 12年3月31⽇日星期六
  • 7. 7. $(html) 1. simple tag $(‘<div>’), $(‘<div />’), $(‘<div></div>’) 1.1 document.createElement( 1.2? $(simpleTag, attrs) jQuery.fn.attr.call(elm, attrs) 2. else jQuery.buildFragment 12年3月31⽇日星期六
  • 8. 应⽤用 1. 构造jQuery对象的空间成本很低,正常情况下10000个也不会超过1M, 旺铺DIY后台打开 渲染完毕⼤大约构造800个jQuery对象。⼀一般⻚页⾯面初始化时构造数量应该< 500 2. 构造jQuery对象的时间成本通常也很低 如从 dom节点,jquery节点,‘body’, id选择器 构造jQuery都不需要Sizzle参于 3. 如果明确知道是jQuery对象,就不需要再次包装(⼀一般新⼿手会出现,喜欢任何变量都套个$) 4. 使⽤用其他选择器,如果没有context, 相当于 $(document).find(... 所以建议任何使⽤用css3选择器进⾏行构造jQuery对象,请带上相应模块的context节点进⾏行限 制 5. 从html构造jQuery对象时,优先采⽤用 simpleTag 12年3月31⽇日星期六
  • 9. Promise接⼝口 promise接⼝口为监异步操作定义了统⼀一的api 它是commonjs规范中的内容⻅见 http://wiki.commonjs.org/wiki/Promises/A var Promise = { done: function(callback), fail: function(callback), always: function(callback), .... }; 12年3月31⽇日星期六
  • 10. $.ajax // 通常 $.ajax(url, { dataType: ‘jsonp’ success: function(o) { console.debug(o); } }); 12年3月31⽇日星期六
  • 11. 通过promise接⼝口使⽤用ajax var o = $.ajax('mock.php', { dataType: 'jsonp' }); o.done(function(o) { // 相当于success console.debug('success1', o); }); o.fail(function(e) { // 相当于 error console.debug('error', e); }); o.always(function() { // 相当于complete console.debug('complete'); }); var o = $.ajax('mock.php', { dataType: 'jsonp' }); o.then(function() { console.debug('success'); }, function() { console.debug('error'); }); 12年3月31⽇日星期六
  • 12. $.when var url = 'mock.php' a1 = $.ajax(url, { dataType: 'jsonp' }), a2 = $.ajax(url, { dataType: 'jsonp' }), a3 = $.ajax(url, { dataType: 'jsonp' }); $.when(a1, a2, a3).done(function() { console.debug('all complete'); }); 额外阅读: promise.pipe() 12年3月31⽇日星期六
  • 13. Promise接⼝口另外⼀一半 var Promise = { // 下⾯面3个⽅方法相当于bind done: function() {}, fail: function() {}, always: function() {} // 下⾯面两个⽅方法相当于trigger resolve: function() {}, reject: function() {} }; 12年3月31⽇日星期六
  • 17. 应⽤用 1. 使⽤用 $.when, promise.pipe等完成⼀一些稍复杂的异步请求需求 2. 组件开发者如有必要可以考虑提供promise接⼝口来统⼀一异步操作的使⽤用⽅方式 3. 使⽤用Deferred对象简化promise接⼝口的实现 4. 注意不能让Deferred侵⼊入业务代码(⻅见例⼦子) 5. 使⽤用Callbacks来简化observer模式的实现(1.7提供) 12年3月31⽇日星期六
  • 18. $.cache jQuery使⽤用$.cache这个对象保存 $.data, 事件, 动画操作所需要的数据结构 所以了解它对于理解和调试代码有⾮非常⼤大的帮助 12年3月31⽇日星期六
  • 20. $.data 1. 我们使⽤用$(elm).data(‘key’, value) 设置节点相关数据 2. 有三种元素不能设置data: embed, ⾮非flash的object, applet. 查看jQuery.noData获得列表 3. 设置时key字符串会被转成 camelCase 如 $(elm).data(‘a-b’, ‘hello’) 相当于 $(elm).data(‘aB’, ‘hello’) 我们应该全部使⽤用camelCase⽅方式进⾏行设置 4. 获取时 使⽤用key尝试获取,如果获取不到,会把key转化成 camelCase再次获取。 why? 因为允许 $(elm).data({ ‘a-b’: ‘hello’, ‘c-d’: ‘hello2’ }); 这种⽅方式进⾏行⼀一次设置多个data 但是内部没做转换(估计后续会优化) 我们应该全部使⽤用camelCase⽅方式进⾏行获取 5. 获取data时,如果从cache中获取不到,则会尝试从data属性中获取 <div data-config=’{ “requestUrl”: “url” }’>... 可以使⽤用 $(elm).data(‘config’) 进⾏行读取配置信息 ⼀一次读取后,这个值就会被cache,下次从cache中直接获取 注:设置时,是不会更改节点属性的, 如果需要可以使⽤用 $#attr(... 12年3月31⽇日星期六
  • 21. data类型识别 从data属性获取的值,会根据属性字符串的样式,⽽而进⾏行适当的类型转换 1. “true” --> true 2. “false” --> false 3. “null” --> null 4. $.isNumberic(data) --> parseFloat(data) 5. “{...}” 或 “[]” --> parseJSON, 如果parse失败,则返回字符串 这⾥里如果要正常parse出json,必须是严格的json格式才可以。 6. 直接返回字符串 具体过程看:function dataAttr( 12年3月31⽇日星期六
  • 22. data事件 var elm = $('#mydiv1'); elm.bind('getData', function(value, name) { // 取得数据前触发,可以拦截哦 console.debug('getData', name, value); return 'new data'; }); elm.bind('setData', function(value, name) { // 设置数据前触发, 可惜这个⽅方法没有提供拦截 console.debug('setData', name, value); }); elm.bind('changeData', function(value, name) { // 设置数据后触发 console.debug('changeData', name, value) }); 注: 这⼏几个⽅方法并未出现在⽂文档中(可能是我没发现),但这⼏几个事件出现在很早的 jQuery版本中 1.4就有了 12年3月31⽇日星期六
  • 28. 事件源码导读 1. 在理解上述结构后,看源码将会⾮非常⽅方便 2. 源码中如果有结构和逻辑两部分, 要先弄懂数据结构。 3. 数据结构是静⽌止的算法 事件内部⼯工作流程: 1. 当调⽤用elm.on/bind/delete时, 内部调⽤用 jQuery.event.add(elem, types, handler, data, selector) 完成上述结构的构建 2. 事件触发时,将会调⽤用相应节点 data events.handle ⽅方法 3. events.handle 仅仅调⽤用 jQuery.event.dispatch(event) [备注,⽼老版本这个⽅方法叫 jQuery.event.handle] 4. 当调⽤用trigger⼈人为触发事件时, 内部调⽤用 jQuery.event.trigger(event, data, elem, onlyHandlers) 后者调⽤用 节点data events.handle⽅方法 12年3月31⽇日星期六
  • 29. 事件使⽤用技巧 0. 从原理上可以看出,⾃自定义事件和原⽣生事件使⽤用的是同⼀一套逻辑,和浏览器⽆无关 1. jQuery 1.7开始使⽤用on/off 来代替原来的 bind, delegate, one 等事件,以规范化事件的使⽤用 2. 如需要对很多相同性质的节点,或动态产⽣生的节点进⾏行事件绑定,请使⽤用 delegate 3. delegate请尽量减少作⽤用域,如有可能,使⽤用 tag#id.cass来作为selecotr, 这样会直接使⽤用正 则式进⾏行⽐比较 4. 可以使⽤用事件数据来传递额外信息 5. e.preventDefault/e.stopPropagation, return false相当于两者 6. e.stopImmediatePropagation(), 节点事件数组中后⾯面的事件都不再执⾏行 12年3月31⽇日星期六
  • 30. 7. trigger/ triggerHandler区别 trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { if ( this[0] ) { return jQuery.event.trigger( type, data, this[0], true ); } } 8. 在某些情况下可以使⽤用namespace事件来完成⼀一些要求 如在组件中,对该组件所有的事件给个namespace, 以⽅方便清理 我们绑定事件时,可以给⼀一个namespace, 以便在不保存事件引⽤用的情况下移除事件 9. 注意api的⽅方便性, 如⼀一次可以添加多个事件哦, 还有事件函数可以直接为false link.on(‘click’, false); 10. one / toggle 类型事件 在某些情况下可以帮助我们不要重新发明轮⼦子 12年3月31⽇日星期六
  • 31. Sizzle jQuery中的CSS3选择器引擎 12年3月31⽇日星期六
  • 33. ⾼高效的选择器 1. 即使有querySelectorAll, 也不⼀一定⽀支持任何复杂(⻅见jQuery⽂文档)的选择器 2. 经过我的测试,以下选择器⼀一般都是⽀支持的,所以优先采⽤用(正常应⽤用中⾜足够了) - ⼦子代选择器,邻代选择器等都是⽀支持的 div ul, div>ul, div+ul, div~ul - 属性选择器 input[name], input[name=username], div[data-tracelog] - class选择器 div.mod - 不⽀支持伪类选择器, 只⽀支持hover, active等⼏几个伪类选择器, 如 :first, :last是不⽀支持 的 3. 以下⼏几种情况直接使⽤用dom api直接获取, ⽽而不需要 querySelectorAll ‘body‘ document.body #id document.getElementById tag getElementsByTagName .class getElementsByClass (如果⽀支持的话) 12年3月31⽇日星期六
  • 35. 选择器使⽤用技巧 1. 优先使⽤用简单选择器 2. 选取元素时,请指定合适的context(推荐为模块⼦子模块) 如 $(‘a.close’, panel) 3. class选择器请限定tag, 如tag.class 4. 适度使⽤用伪类 5. 选择器层级不要太深,⼀一般指定context后, selector不会超过2层 12年3月31⽇日星期六
  • 36. Sizzle流程 0. jQuery.find = Sizzle 1. 处理选择器 -》 数组 jQuery.find('#doc div.mylist ul>li') -》 ["#doc", "div.mylist", "ul", ">", "li"] 2. 根据类别采⽤用不同的处理顺序 - 选择器中包含位置相关的伪类,如 :first, :last, :even, :odd ⻅见 Sizzle.selectors.match.POS 这个正则表达式 如果存在, 则采⽤用⾃自左向右的顺序进⾏行处理 $(‘div ul li:first’)-> $(‘div’).find(‘ul’).find(‘li:first’) 如果不存在,则采⽤用⾃自右向左的顺序进⾏行处理[1] $(‘div ul li’) -> $(‘li’), 再过滤出所有符合条件的li, 处理是从右⾃自左的 [1] 如果第⼀一个为id选择器,则先处理它,再使⽤用这个节点为context查找 12年3月31⽇日星期六
  • 37. Sizzle(‘ul li’) - 分割选择器 [‘ul’, ‘li’] - Sizzle.find(‘li’) - Sizzle.selector.find.TAG ---> getElementsByTagName(li) - Sizzle.relative(lis, ‘ul’); -> 看看是否在ul下⾯面 Sizzle(‘ul li:first’) - 分割选择器[‘ul’, ‘li:first’] - Sizzle.find(‘ul’) - 分析选择器 [‘ul’] - Sizzle.find(‘ul’) Sizzle.selector.find.TAG ---> getElementsByTagName - Sizzle(‘li’, ul) - Sizzle.filter(‘:first’, lis) 12年3月31⽇日星期六
  • 38. Sizzle源码导读 1. 正则式 chunker ⽤用于分解选择器 2. 正则式Sizzle.selectors.match.POS ⽤用于判断是否有位置类型的选择器 3. Sizzle.selectors.find ⽤用于查找节点(对应于原⽣生⽅方法, getElementById, getElementsByTagName 等) 4. Sizzle.selectors.relative 过滤出符合要求的⽗父节点 5. Sizzle.selectors.filters 伪类过滤器 (我们可以扩展这个实现⾃自⼰己的伪类语法哦) 6. Sizzle.selectors.filter 各种类型的过滤器逻辑 12年3月31⽇日星期六
  • 39. 创建⾃自⼰己的选择器 jQuery.expr[‘:’][‘mod’] = function(elem) { return $(elem).hasClass(‘.mod’); }; var mod = $(‘div:mod’); 组件编写者可以根据需要编写⾃自⼰己的伪类选择器 12年3月31⽇日星期六
  • 42. $.proxy ⽅方便我们创建特定context和参数的closure var A = { init: function() { var div = $(‘div.mydiv’); $.ajax(url, { success: $.proxy(this, ‘success’, div); // 代替 function(o) { self.success(div, o); } }); }, success: function(div, o) { this... } }; 12年3月31⽇日星期六
  • 43. elm.attr() / elm.prop() 1. jQuery 1.6之后有了prop, 即把 attribute和 property分开 2. 区分attribute 和 property 前者相当于 elm.getAttribute 后者相当于 elm[key], 如 elm.checked, sel.selectedIndex 3. checkbox.prop(‘checked’), select.prop(‘selectedIndex’) 4. 尽管jQuery在1.64及以后的版本中做了兼容性,但是不要依赖于这个特性 1. 存在jQuery.attrFn⾥里⾯面的,则直接调⽤用相应⽅方法 2. 返回undefined的,尝试使⽤用prop获取 3. 存在于jQuery.attrHook的字段,特殊处理 4. 最后使⽤用getAttribute/setAttribute 12年3月31⽇日星期六
  • 46. remove event 在ui/core.js中 var _cleanData = $.cleanData; $.cleanData = function(elems){ for (var i = 0, elem; (elem = elems[i]) != null; i++) { $(elem).triggerHandler('remove'); } _cleanData(elems); }; 12年3月31⽇日星期六
  • 47. 应⽤用 所以在引⼊入ui/core的前提下: $(elm).bind(‘remove’, function() { // 清理⼯工作 // 组件或框架设计的同学可以⽤用来做清理⼯工作 }); 12年3月31⽇日星期六