Bootstrap 源码解析
1、Bootstrap的作用域
2、Bootstrap的类定义
3、Bootstrap的插件定义
4、Bootstrap的事件代理
5、Bootstrap的对象数据缓存
6、Bootstrap的防冲突
7、作用域外如何使用Button类
8、Bootstrap的单元测试
Bootstrap的作用域
Bootstrap每个插件都定义在下面这段作用域代码中:
+function ($) { ... }(window.jQuery)请看《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')