• bootstrap 源码解析


    目录

    1、Bootstrap的作用域

    2、Bootstrap的类定义

    3、Bootstrap的插件定义

    4、Bootstrap的事件代理

    5、Bootstrap的对象数据缓存

    6、Bootstrap的防冲突

    7、作用域外如何使用Button类

    8、Bootstrap的单元测试

    Bootstrap的作用域

    Bootstrap每个插件都定义在下面这段作用域代码中: 

     请看《IIFE》和《严格模式》编译环境。

    在插件的作用域之外,全局范围执行代码的第一行,检测了jQuery是否定义。在Grunt的concat任务中,合并所有插件时,检测代码添加在目标文件的banner说明后面。Grunt.js的相关代码: 

    jqueryCheck: 'if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }\n\n',
    concat: {
          options: {
            banner: '<%= banner %><%= jqueryCheck %>',
            stripBanners: false
          },
          bootstrap: {
            src: [
              'js/transition.js',
              'js/alert.js',
              'js/button.js',
              'js/carousel.js',
              'js/collapse.js',
              'js/dropdown.js',
              'js/modal.js',
              'js/tooltip.js',
              'js/popover.js',
              'js/scrollspy.js',
              'js/tab.js',
              'js/affix.js'
            ],
            dest: 'dist/js/<%= pkg.name %>.js'
          }
        }

    Bootstrap的类定义

    var Button = function (element, options) {
        this.$element = $(element)
        this.options  = $.extend({}, Button.DEFAULTS, options)
      }
    
      Button.DEFAULTS = {
        loadingText: 'loading...'
      }
    
      Button.prototype.setState = function (state) {
        ...
      }
    
      Button.prototype.toggle = function () {
        ...  
      }

    Bootstrap采用这种类定义方式的好处,以及Javascript其他几种类定义的方式,请参照《Javascript面向对象编程(一):封装》 

    Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。在Button函数体内部定义的属性和方法可以看做是类的私有属性和方法, 为Button.prototype对象定义的属性和方法都可以看做是类的公共属性和方法。这个类封装了插件对象初始化所需的方法和属性。

    Bootstrap的插件定义

    请参看《jQuery插件开发快速入门》,注意两个this指向的是不同对象 

    $.fn.button = function (option) {
        return this.each(function () {
          var $this   = $(this)
          ...
        })
      }

    Bootstrap的事件代理

    Bootstrap Button插件定义最后一部分,事件绑定是这么写的 

    $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
        ...    
    })

    这段JavaScript代码将click委托事件监听器绑定在document元素上,并给click事件赋予命名空间click.bs.button.data-api,选择器匹配的是属性data-toggle的值为"button"开头的标签。

    关于jQuery将事件绑定在document文档对象上的好处,就是js事件代理的优点,性能上做了一个测试比较

    关于jQuery命名空间的好处,请参看《jQuery .on() and .off() 命名空间

    Bootstrap的防止冲突

    jQuery是全局对象,所以jQuery的插件定义$.fn.button并不受作用域限制。如果在别的插件中同样定义了button插件,后加载的button插件将会覆盖先加载的button插件,jsbin示例: 

    // Old button
    +function($){
      $.fn.button = function() {
        alert('Old button')
      }
    }(window.jQuery)  
    
    // Bootstrap button
    +function($){
      $.fn.button = function() {
        alert('Bootstrap button')
      }
    }(window.jQuery)
    
    $('a').button() // alert('Bootstrap button')
    Bootstrap做了插件冲突处理,jsbin示例: 
    // Old button
    +function($) {
      $.fn.button = function() {
        alert('Old button')
      } 
    }(window.jQuery)
    
    // Bootstrap button
    +function($){
      // 将原先的button插件对象赋值给一个临时变量old
      var old =  $.fn.button
    
      $.fn.button = function() {
          alert('Bootstrap button')
      }
    
      // 执行该函数,恢复原先的button定义,并返回Bootstrap定义的button插件
      $.fn.button.noConflict = function () {
        $.fn.button = old
        return this
      }
    
    }(window.jQuery)
    
    // 作用域外我们可以灵活使用两个button插件
    $.fn.button = $.fn.button.noConflict()
    $('a').button() // alert('Bootstrap button')
    
    $.fn.button.noConflict()
    $('a').button() // alert('Old button')

    Bootstrap作用域外如何使用Button类 

    $.fn.button.Constructor = Button
    在Bootstrap的button插件中还有上面者句代码,去掉它不影响插件的正确执行。 

    它很像javascript中类构造器: 

    var Cat = function(name) {
       this.name = name
    }
    var cat1 = new Cat('Hello Kitty')
    var cat2 = new Cat('Doramon')
    
    cat1.constructor == Cat.prototype.constructor
     

    但是Javascript是区分大小写的,也就是 这里大写开头的的Constructor 和 Javascript小写开头的constructor 没有任何关系。

    查找jQuery源码中也没有对于大写开头的Constructor的定义。所以这里的Constructor只是一个普通属性,我们也可以写成其他名字 $.fn.button.Something = Button,Bootstrap为了指明这个属性的意义而命名为构造器“Constructor”更合理。

    这样一来,这段代码就很好理解了:$.fn.button.Constructor = Button 通过将作用域内的Button类赋值给jQuery的button对象的Constructor属性,在IIFE作用域外也可以使用Button类。调用方式:

    +function($){
      // 类定义
      var Button = function() {}
      // 插件定义
      $.fn.button = function() {
          alert('Bootstrap button')
      }
      // 类赋值到jQuery button对象的Constructor属性
      $.fn.button.Constructor = Button
    
    }(window.jQuery) 
    
    var Button = $.fn.button.Constructor

    Bootstrap的对象数据缓存

    // 获取存储的Button对象,如果是第一次执行变量data的值为undefined
    var data    = $this.data('bs.button')
    var options = typeof option == 'object' && option
    
    // 创建Button对象: new Button(this, options),
    // 并赋值给变量data: data = new Button(this, options)
    // 存储在元素的jQuery对象上的‘bs.button’数据字段 $this.data('bs.button', data)
    if (!data) $this.data('bs.button', (data = new Button(this, options)))
    
    // data是一个Button对象,可以调用Button的原生方法
    if (option == 'toggle') data.toggle()
    else if (option) data.setState(option)

    利用jQuery的 .data(key, value)存储Button对象

  • 相关阅读:
    mac与phy怎样实现网络自适应
    POJ 3304 Segments(计算几何:直线与线段相交)
    iOS类目
    MFC中改变控件的大小和位置(zz)
    Delphi中根据分类数据生成树形结构的最优方法
    Delphi下EasyGrid使用体会
    Delphi中Messagedlg用法
    Delphi获取其它进程窗口句柄的3种方法
    delphi获取一个窗口的所有子窗口(包括嵌套)
    关于获取其它程序窗口编辑框内容解决思路
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/5783267.html
Copyright © 2020-2023  润新知