2025年3月23日 星期日 甲辰(龙)年 月廿二 夜 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > JavaScript

JS实现简单的jQuery框架(非常详细)

时间:03-07来源:作者:点击数:46

本节模拟 jquery 框架设计一个 Web 应用模型,训练 JavaScript 面向对象的编程技能。

定义类型

JavaScript 中,可以把构造函数理解为一个类型,这个类型是 JavaScript 面向对象编程的基础。定义一个函数就相当于构建了一个类型,然后借助这个类型类实例化对象。

示例

下面代码定义一个空类型,类名为 jQuery。

  • var jQuery = function () {
  • //函数体
  • }

下面为 jQuery 扩展原型。

  • var jQuery = function () {}
  • jQuery.prototype = {
  • //扩展的原型对象
  • }

为 jQuery 的原型起个别名:fn。如果直接命名为 fn,则表示它属于 window 对象,这样不安全。更安全的方法是为 jQuery 类型对象定义一个静态引用 jQuery.fn,然后把 jQuery 的原型对象传递给这个属性 jQuery.fn。实现代码如下:

  • jQuery.fn = jQuery.prototype = {
  • //扩展的原型对象
  • }

jQuery.fn 引用 jQuery.prototype,因此要访问 jQuery 的原型对象,可以使用 jQuery.fn,直接使用 jQuery.prototype 也是可以的。

下面为 jquery 类型起个别名:$。

  • var $ = jQuery = function () {}

模仿 jQuery 框架,给 jQuery 原型添加两个成员,一个是原型属性 version;另一个是原型方法 size(),分别定义 jQuery 框架的版本号和 jQuery 对象的长度。

  • var $ = jQuery = function () {}
  • jQuery.fn = jQuery.prototype = {
  • version : "3.2.1", //原型属性
  • size : function () { //原型方法
  • return this.length;
  • }
  • }

返回 jQuery 对象

下面介绍如何调用原型成员:version 属性和 size() 方法。

一般可以按以下方式调用。

  • var test = new $ (); //实例化
  • console.log(test.version); //读取属性,返回“3.2.1”
  • console.log(test.size()); //调用方法,返回undefined

但是,jQuery 框架按下面方法进行调用。

  • $().version;
  • $().size();

jQuery 没有使用 new 命令调用 jQuery 构造函数,而是直接使用小括号运算符调用 jQuery() 构造函数,然后在后面直接访问原型成员。

如何实现这样的操作呢?

示例1

可以使用 return 语句返回一个 jQuery 实例。

  • var $ = jQuery = function () {
  • return new jQuery(); //返回类的实例
  • }
  • jQuery.fn = jQuery.prototype = {
  • version : "3.2.1", //原型属性
  • size : function (){ //原型方法
  • return this.length;
  • }
  • }

执行下面的代码,则会如图出现内存溢出错误。

  • $().version;
  • $().size();

这说明在构造函数内部实例化对象是不允许的,因为这个引用会导致死循环。

示例2

下面尝试使用工厂模式进行设计:在 jQuery() 构造函数中返回 jQuery 的原型引用。

  • var $ = jQuery = function () {
  • return jQuery.prototype; //返回类的原型
  • }
  • jQuery.fn = jQuery.prototype = {
  • version : "3.2.1", //原型属性
  • size : function (){ //原型方法
  • return this.length;
  • }
  • }
  • console.log($().version); //读取属性,返回“3.2.1”
  • console.log($().size()); //调用方法,返回undefined

示例3

示例 2 基本实现了 $().size() 这种形式的用法,但是在构造函数中直接返回原型对象,设计思路过于狭窄,无法实现框架内部的管理和扩展。下面模拟其他面向对象语言的设计模式:在类型内部定义一个初始化构造函数 init(),当类型实例化后,直接执行初始化构造函数 init(),然后再返回 jQuery 的原型对象。

  • var $ = jQuery = function () {
  • return jQuery.fn.init(); //调用原型方法init(),模拟类的初始化构造函数
  • }
  • jQuery.fn = jQuery.prototype = {
  • init : function () { //在原型的初始化方法中返回原型对象
  • return this;
  • },
  • version : "3.2.1", //原型属性
  • size : function () { //原型方法
  • return this.length;
  • }
  • }
  • console.log($().version); //读取属性,返回“3.2.1”
  • console.log($().size()); //调用方法,返回undefined

设计作用域

上面模拟了 jQuery 的用法,让 jQuery() 返回 jQuery 类型的原型。实现方法:定义初始化函数 init() 返回 this,而 this 引用的是 jQuery 原型 jQuery.prototype。但是,在使用过程中也会发现一个问题:作用域混乱,给后期的扩展带来隐患。下面结合一个示例进行说明。

示例1

定义 jQuery 原型中包含一个 length 属性,同时初始化函数 init() 内部也包含一个 length 属性和一个 _size() 方法。

  • var $ = jQuery = function () {
  • return jQuery.fn.init();
  • }
  • jQuery.fn = jQuery.prototype = {
  • init : function () {
  • this.length = 0; //原型属性
  • this._size = function () { //原型方法
  • return this.length;
  • }
  • return this;
  • },
  • length : 1,
  • version : "3.2.1", //原型属性
  • size : function () { //原型方法
  • return this.length;
  • }
  • }
  • console.log($().version); //返回“3.2.1”
  • console.log($()._size()); //返回0
  • console.log($().size()); //返回0

运行示例 1 可以看到 init() 函数内的 this 与外面的 this 均引用同一个对象:jQuery.prototype 原型对象。因此,会出现 init() 函数内部的 this.length 会覆盖掉外部的 this.length。

简单概括:初始化函数 init() 的内、外作用域缺乏独立性,对于 jQuery 这样的框架来说,很有可能造成消极影响。而 jQuery 框架是通过下面方式调用 init() 初始化函数。

  • var $ = jQuery = function (selector, context) {
  • return new jQuery.fn.init (selector, context); //实例化init(),分隔作用域
  • }

使用 new 命令调用初始化函数 init(),创建一个独立的实例对象,这样就分隔了 init() 函数内外的作用域,确保内外 this 引用不同。

示例2

修改示例 1 中的 jQuery(),使用 return 返回新创建的实例。

  • var $ = jQuery = function () {
  • return new jQuery.fn.init ();
  • }
  • jQuery.fn = jQuery.prototype = {
  • init : function () {
  • this.length = 0; //本地属性
  • this._size = function () { //本地方法
  • return this.length;
  • }
  • return this;
  • },
  • length : 1,
  • version : "3.2.1", //原型属性
  • size : function (){ //原型方法
  • return this.length;
  • }
  • }
  • console.log($().version); //返回undefined
  • console.log($()._size()); //返回0
  • console.log($().size()); //抛出异常

运行示例 2 会发现:由于作用域被阻断,导致无法访问 jQuery.fn 对象的属性或方法。

跨域访问

下面来探索如何越过作用域的限制,实现跨域访问外部的 jQuery.prototype。

分析 jQuery 框架源码,发现它是通过原型传递解决这个问题。实现方法:把 jQuery.fn 传递给 jQuery.fn.init.prototype,用 jQuery 的原型对象覆盖掉 init 的原型对象,从而实现跨域访问。

示例

下面代码具体演示了跨域访问的过程。

  • var $ = jQuery = function () {
  • return new jQuery.fn.init();
  • }
  • jQuery.fn = jQuery.prototype = {
  • init : function () {
  • this.length = 0; //本地属性
  • this._size = function () { //本地方法
  • return this.length;
  • }
  • return this;
  • },
  • length : 1,
  • version : "3.2.1", //原型属性
  • size : function () { //原型方法
  • return this.length;
  • }
  • }
  • jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型对象覆盖init的原型对象
  • console.log($().version); //返回“3.2.1”
  • console.log($()._size()); //返回0
  • console.log($().size()); //返回0

设计选择器

下面探索 jQuery 内部的核心功能:选择器。使用过 jQuery 的用户应该熟悉,jQuery 返回的是 jQuery 对象,jQuery 对象实际上就是伪类数组。

示例

下面示例尝试为 jQuery() 函数传递一个参数,并让它返回一个 jQuery 对象。

jQuery() 构造函数包含两个参数:selector 和 context。其中 selector 表示选择器,context 表示匹配的上下文,即可选择的范围,它表示一个 DOM 元素。为了简化操作,本例假设选择器的类型仅为标签选择器。实现的代码如下:

  • <script>
  • var $ = jQuery = function (selector, context) { //jQuery构造函数
  • return new jQuery.fn.init(selector, context); //jQuery实例对象
  • }
  • jQuery.fn = jQuery.prototype = { //jQuery原型对象
  • init : function (selector, context) { //初始化构造函数
  • selector = selector || document; //初始化选择器,默认值为document
  • context = context || document; //初始化上下文对象,默认值为document
  • if (selector.nodeType) { //如果是DOM元素
  • this[0] = selector; //直接把该DOM元素传递给实例对象的伪数组
  • this.length = 1; //设置实例对象的length属性,表示包含1个元素
  • this.context = selector; //重新设置上下文为DOM元素
  • return this; //返回当前实例
  • }
  • if (typeof selector === "string") { //如果是选择符类型的字符串
  • var e = context.getElementsByTagName(selector); //获取指定名称的元素
  • for (var i = 0; i < e.length; i ++ ) { //使用for把所有元素传入到当前实例数组中
  • this[i] = e[i];
  • }
  • this.length = e.length; //设置实例的length属性,定义包含元素的个数
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • } else {
  • this.length = 0; //设置实例的length属性值为0,表示不包含元素
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • }
  • }
  • }
  • jQuery.fn.init.prototype = jQuery.fn;
  • window.onload = function () {
  • console.log($("div").length); //返回3
  • }
  • </script>
  • <div></div>
  • <div></div>
  • <div></div>

在上面示例中,$("div") 基本拥有了 jQuery 框架中 $("div") 选择器的功能,使用它可以选取页面中的指定范围的 div 元素。同时,读取 length 属性可以返回 jQuery 对象的长度。

设计迭代器

下面探索如何操作 jQuery 对象。

在 jQuery 框架中,jQuery 对象是一个普通的 JavaScript 对象,但是它以索引数组的形式包含了一组数据,这组数据就是使用选择器匹配的所有 DOM 元素。

操作 jQuery 对象实际上就是操作这些 DOM 元素,但是无法直接使用 JavaScript 方法操作 jQuery 对象。只有逐一读取它包含的每一个 DOM 元素,才能够实现各种操作,如插入、删除、嵌套、赋值、读写属性等。

在实际使用 jQuery 过程中,我们可以看到类似下面的 jQuery 用法。

$("div").html()

也就是直接在 jQuery 对象上调用 html() 方法来操作 jQuery 包含的所有 DOM 元素。那么这个功能是怎么实现的呢?

jQuery 定义了一个工具函数 each(),利用这个工具可以遍历 jQuery 对象中所有的 DOM 元素,并把操作 jQuery 对象的行为封装到一个回调函数中,然后通过在每个 DOM 元素上调用这个回调函数来实现逐一操作每个 DOM 元素。

实现代码如下:

  • var $ = jQuery = function (selector, context) { //jQuery构造函数
  • return new jQuery.fn.init(selector,context); //jQuery实例对象
  • }
  • jQuery.fn = jQuery.prototype = { //jQuery原型对象
  • init : function (selector,context){ //初始化构造函数
  • selector = selector || document; //初始化选择器,默认值为document
  • context = context || document; //初始化上下文对象,默认值document
  • if (selector.nodeType) { //如果是DOM元素
  • this[0] = selector; //直接把该DOM元素传递给实例对象的伪数组
  • this.length = 1; //设置实例对象的length属性,表示1个元素
  • this.context = selector; //重新设置上下文为DOM元素
  • return this; //返回当前实例
  • }
  • if (typeof selector === "string") { //如果是选择符字符串
  • var e = context.getElementsByTagName(selector); //获取指定名称的元素
  • for (var i = 0; i < e.length;i ++){ //使用for把所有元素传入当前实例数组中
  • this[i] = e[i];
  • }
  • this.length = e.length; //设置实例的length属性,定义包含元素的个数
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • } else {
  • this.length = 0; //设置实例的length属性值为0,表示不包含元素
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • }
  • },
  • html : function(val){ //模仿jQuery的html()方法,为匹配DOM元素插入html字符串
  • jQuery.each(this,function(val){ //为每一个DOM元素执行回调函数
  • this.innerHTML = val;
  • },val);
  • }
  • }
  • jQuery.fn.init.prototype = jQuery.fn;
  • //扩展方法:jQuery迭代函数
  • jQuery.each = function(object,callback,args){
  • for(var i = 0;i<object.length;i ++){ //使用for迭代jQuery对象中每个DOM元素
  • callback.call(object[i],args); //在每个DOM元素上调用回调函数
  • }
  • return object; //返回jQuery对象
  • }

在上面代码中,为 jQuery 对象绑定 html() 方法,然后利用 jQuery() 选择器获取页面中所有的 div 元素,调用 html() 方法,为所有匹配的元素插入 HTML 字符串。

each() 的当前作用对象是 jQuery 对象,故 this 指向当前 jQuery 对象;而在 html() 内部,由于是在指定 DOM 元素上执行操作,则 this 指向的是当前 DOM 元素,不再是 jQuery 对象。

最后,在页面中进行测试,预览效果如图所示。 

  • <script>
  • window.onload = function () {
  • $("div").html("<h1>你好</h1>");
  • }
  • </script>
  • <div></div>
  • <div></div>
  • <div></div>

当然,上面示例所定义的 each() 函数和 html() 方法的功能比较有限。在 jQuery 框架中,它封装的 each() 函数功能就很强大。

设计扩展

jQuery 提供了良好的扩展接口,方便用户自定义 jQuery 方法。根据设计习惯,如果为 jQuery 或者 jQuery.prototype 新增方法时,可以直接通过点语法,或者在 jQuery.prototype 对象结构内增加。但是,如果分析 jQuery 源码,会发现它是通过 extend() 函数来实现功能扩展的。

示例1

下面代码是 jQuery 框架通过 extend() 函数扩展的功能。

  • jQuery.extend ({ //扩展工具函数
  • noConflict : function (deep) {},
  • isFunction : function (obj) {},
  • isArray : function (obj) {},
  • isXMLDoc : function (elem) {},
  • globalEval : function (date) {}
  • });

或者

  • jQuery.fn.extend ({ //扩展jQuery对象方法
  • show : function (speed, callback) {},
  • hide : function (speed, callback) {},
  • toggle : function (fn, fn2) {},
  • fadeTo : function (speed, to, callback) {},
  • animate : function (prop, speed, easing, callback) {},
  • stop : function (clearQueue, gotoEnd) {}
  • });

这样做有什么好处呢?方便用户快速扩展 jQuery 功能,但不会破坏 jQuery 框架的结构。如果直接在 jQuery 源码中添加方法,这样容易破坏 jQuery 框架的纯洁性,也不方便后期代码维护。如果不需要某个插件,使用 jQuery 提供的扩展工具添加,只需要简单地删除即可,而不需要在 jQuery 源码中去寻找要删除的代码段。

extend() 函数的功能很简单,它只是把指定对象的方法复制给 jQuery 对象或者 jQuery.prototype。

示例2

在下面示例中,为 jQuery 类型和 jQuery 对象定义了一个扩展函数 extend(),设计把参数对象包含的所有属性复制给 jQuery 或者 jQuery.prototype,这样就可以实现动态扩展 jQuery 方法。

  • var $ = jQuery = function (selector, context) { //jQuery构造函数
  • return new jQuery.fn.init(selector, context); //jQuery实例对象
  • }
  • jQuery.fn = jQuery.prototype = { //jQuery原型对象
  • init : function (selector, context) { //初始化构造函数
  • selector = selector || document; //初始化选择器,默认值为document
  • context = context || document; //初始化上下文对象,默认值为document
  • if (selector.nodeType) { //如果是DOM元素
  • this[0] = selector; //直接把该DOM元素传递给实例对象的伪数组
  • this.length = 1; //设置实例对象的length属性,表示包含1个元素
  • this.context = selector; //重新设置上下文为DOM元素
  • return this; //返回当前实例
  • }
  • if (typeof selector === "string") { //如果是选择符字符串
  • var e = context.getElementsByTagName(selector); //获取指定名称的元素
  • for (var i = 0; i < e.length; i ++) { //使用for把所有元素传入当前实例数组中
  • this[i] = e[i];
  • }
  • this.length = e.length; //设置实例的length属性,定义包含元素的个数
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • } else {
  • this.length = 0; //设置实例的length属性值为0,表示不包含元素
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • }
  • }
  • }
  • jQuery.fn.init.prototype = jQuery.fn;
  • //扩展方法:jQuery迭代函数
  • jQuery.each = function (object, callback, args) {
  • for (var i = 0; i < object.length; i ++) { //使用for迭代jQuery对象中每个DOM元素
  • callback.call(object[i], args); //在每个DOM元素上调用回调函数
  • }
  • return object; //返回jQuery对象
  • }
  • //jQuery扩展函数
  • jQuery.extend = jQuery.fn.extend = function (obj) {
  • for (var prop in obj) {
  • this[prop] = obj[prop];
  • }
  • return this;
  • }
  • //jQuery对象扩展方法
  • jQuery.fn.extend ({
  • html : function (val) { //模仿jQuery的html()方法,为匹配DOM元素插入html字符串
  • jQuery.each (this, function (val) { //为每一个DOM元素执行回调函数
  • this.innerHTML = val;
  • }, val);
  • }
  • })
  • window.onload = function () {
  • $("div").html("<h1>你好</h1>");
  • }

在上面示例中,先定义一个 jQuery 扩展函数 extend(),然后为 jQuery.fn 原型方法调用 extend() 函数,为其添加一个 jQuery 方法 html()。

传递参数

很多 jQuery 方法如果包含参数,一般都要求传递参数对象。例如:

  • $.ajax ({
  • type : "GET",
  • url : "test.js",
  • dataType : "script"
  • });

使用对象直接量作为参数进行传递,方便参数管理。当方法或者函数的参数长度不固定时,使用对象直接量作为参数进行传递有以下优势:

  • 参数个数不受限制
  • 参数顺序可以随意

这体现了 jQuery 用法的灵活性。

如果 ajax() 函数的参数长度和位置是固定的,如 $.ajax("GET", "test.js", "script")。这种用法本身没有问题,但是很多 jQuery 方法包含大量的可选参数,参数位置没有必要限制,再使用传统方式来设计参数,就比较麻烦。所以使用对象直接量作为参数进行传递,是最佳的解决方法。

使用对象直接量作为参数进行传递,就涉及参数处理问题,如何解决并提取参数,如何处理默认值问题,我们可以通过下面的方式来实现。

【操作步骤】

1) 在前面示例基础上重新编写 jQuery.extend() 工具函数。

  • var $ = jQuery = function (selector, context) { //jQuery构造函数
  • return new jQuery.fn.init(selector, context); //jQuery实例对象
  • }
  • jQuery.fn = jQuery.prototype = { //jQuery原型对象
  • init : function (selector, context) { //初始化构造函数
  • selector = selector || document; //初始化选择器,默认值为document
  • context = context || document; //初始化上下文对象,默认值为document
  • if (selector.nodeType) { //如果是DOM元素
  • this[0] = selector; //直接把该DOM元素传递给实例对象的伪数组
  • this.length = 1; //设置实例对象的length属性,表示包含1个元素
  • this.context = selector; //重新设置上下文为DOM元素
  • return this; //返回当前实例
  • }
  • if (typeof selector === "string") { //如果是选择符字符串
  • var e = context.getElementsByTagName(selector); //获取指定名称的元素
  • for (var i = 0; i < e.length; i ++) { //使用for把所有元素传入当前实例数组中
  • this[i] = e[i];
  • }
  • this.length = e.length; //设置实例的length属性,定义包含元素的个数
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • } else {
  • this.length = 0; //设置实例的length属性值为0,表示不包含元素
  • this.context = context; //保存上下文对象
  • return this; //返回当前实例
  • }
  • }
  • }
  • jQuery.fn.init.prototype = jQuery.fn;
  • //扩展方法:jQuery迭代函数
  • jQuery.each = function (object, callback, args) {
  • for (var i = 0; i < object.length; i ++){ //使用for迭代jQuery对象中每个DOM元素
  • callback.call(object[i], args); //在每个DOM元素调用回调函数
  • }
  • return object; //返回jQuery对象
  • }
  • /*重新定义jQuery扩展函数 */
  • jQuery.extend = jQuery.fn.extend = function () {
  • var destination = arguments[0], source = arguments[1]; //获取第2个和第2个参数
  • //如果两个参数都存在,且都为对象
  • if (typeof destination == "object" && typeof source == "object") {
  • //把第2个参数对合并到第1个参数对象中,并返回合并后的对象
  • for (var property in source) {
  • destination[property] = source[property];
  • }
  • return destination;
  • } else { //如果包含一个参数,则为jQuery扩展功能,把插件复制到jQuery原型对象上
  • for (var prop in destination) {
  • this[prop] = destination[prop];
  • }
  • return this;
  • }
  • }

在上面代码中重写了 jQuery.extend() 工具函数,让它实现了两个功能:合并对象、为 jQuery 扩展插件。

为此,在工具函数中使用 if 条件语句检测参数对象 arguments 所包含的参数个数,以及参数类型,来决定是合并对象还是扩展插件。

如果用户给了两个参数,且都为对象,则把第 2 个对象合并到第 1 个对象中,并返回第 1 个对象;如果用户给了一个参数,则继续沿用前面的设计方法,把参数对象复制到 jQuery 原型对象上,实现插件扩展。

2) 利用 jQuery.extend() 工具函数为 jQuery 扩展了一个插件 fontStyle(),使用这个插件可以定义网页字体样式。

  • //jQuery对象扩展方法
  • jQuery.fn.extend ({
  • fontStyle : function (obj) { //设置字体样式
  • var defaults = { //设置默认值,可以扩展
  • color : "#000",
  • bgcolor : "#fff",
  • size : "14px",
  • style : "normal"
  • };
  • defaults = jQuery.extend (defaults, obj || {}); //如果传递参数,则覆盖原默认参数
  • jQuery.each (this, function () { //为每一个DOM元素执行回调函数
  • this.style.color = defaults.color;
  • this.style.backgroundColor = defaults.bgcolor;
  • this.style.fontSize = defaults.size;
  • this.style.fintStyle = defaults.style;
  • });
  • }
  • })

在上面的插件函数 fontStyle() 中,首先定义一个默认配置对象 defaults。初始化字体样式:字体颜色为黑色,背景色为白色,大小为 14 像素,样式为正常。

再使用 jQuery.extend() 工具函数把用户传递的参数对象 obj 合并到默认配置参数对象 defaults,返回并覆盖掉 defaults 对象。为了避免用户没有传递参数,可以使用 obj || {} 检测用户是否传递参数对象,如果没有,则使用空对象参数合并操作。

最后,使用迭代函数 jQuery.each(),逐个访问 jQuery 对象中包含的 DOM 元素,然后分别为它设置字体样式。

3) 在页面中调用 jQuery 查找所有段落文本 p,然后调用 fontStyle() 方法,设置字体颜色为白色,背景色为黑色,大小为 24 像素,样式保持默认值。

  • window.onload = function () {
  • $("p").fontStyle ({
  • color : "#fff" ,
  • bgcolor : "#000" ,
  • size : "24px"
  • });
  • }

4) 在 <body> 内设计两段文本。

  • <p>少年不知愁滋味,爱上层楼。爱上层楼,为赋新词强说愁</p>
  • <p>而今识尽愁滋味,欲说还休。欲说还休,却道天凉好个秋</p>

在 jQuery 框架中,extend() 函数功能很强大,它既能为 jQuery 扩展方法,也能处理参数对象并覆盖默认值。

设计独立空间

当在页面中引入多个 JavaScript 框架,或者编写了大量 JavaScript 时,很难确保这些代码不发生冲突。如果希望 jQuery 框架与其他代码完全隔离开,闭包体是一种最佳的方式。

示例

在下面示例中,把前面设计的 jQuery 框架模型放入匿名函数中,然后自调用,并传入 Window 对象。

  • (function(window) {
  • var $ = jQuery = function(selector,context){ //jQuery构造函数
  • return new jQuery.fn.init(selector,context); //jQuery实例对象
  • }
  • jQuery.fn = jQuery.prototype = { //jQuery原型对象
  • init : function(selector,context){ //初始化构造函数
  • //省略代码,参考上面示例代码
  • }
  • }
  • jQuery.fn.init.prototype = jQuery.fn;
  • //扩展方法:jQuery迭代函数
  • jQuery.each = function(object,callback,args){
  • for(var i=0;i<object.length;i++){ //使用for迭代jQuery对象中每个DOM元素
  • callback.call(object[i],args); //在每个DOM元素上调用函数
  • }
  • return object; //返回jQuery对象
  • }
  • //jQuery扩展函数
  • jQuery.extend = jQuery.fn.extend = function(){
  • var destination = arguments[0], source = arguments[1]; //获取第1个和第2个参数
  • //如果两个参数都存在,且都为对象
  • if(typeof destination == "object" && typeof source == "object"){
  • //把第2个参数对合并到第1个参数对象中,并返回合并后的对象
  • for(var property in source){
  • destination[property] = source[property];
  • }
  • return destination;
  • } else { //如果包含一个参数,则把插件复制到jQuery原型对象上
  • for(var prop in destination){
  • this[prop] = destination[prop];
  • }
  • return this;
  • }
  • }
  • //开放jQuery接口
  • window.jQuery = window.$ = jQuery;
  • }) (window)

其中倒数第二行代码:

  • window.jQuery = window.$ = jQuery;

其主要作用是:把闭包体内的私有变量 jQuery 传递给参数对象 window 的 jQuery 属性,而参数对象 window 引用外部传入的 window 变量,window 变量引用全局对象 window。所以,在全局作用域中就可以通过 jQuery 变量来访问闭包体内的 jQuery 框架,通过这种方式向外界暴露自己,允许外界使用 jQuery 框架。但是,外界只能访问 jQuery,不能访问闭包体内其他私有变量。

至此,jQuery 框架的设计模型就初见端倪了,后面的工作就是根据需要使用 extend() 函数扩展 jQuery 功能。例如,在闭包体外,直接引用 jQuery.fn.extend() 函数为 jQuery 扩展 fontStyle 插件。

  • //jQuery对象扩展方法
  • jQuery.fn.extend ({
  • fontStyle : function (obj) { //设置字体样式
  • var defaults = { //设置默认值,可以扩展
  • color : "#000",
  • bgcolor : "#fff",
  • size : "14px",
  • style : "normal"
  • };
  • defaults = jQuery.extend (defaults, obj || {});
  • jQuery.each (this, function () {
  • this.style.color = defaults.color;
  • this.style.backgroundColor = defaults.bgcolor;
  • this.style.fontSize = defaults.size;
  • this.style.fontStyle = defaults.style;
  • });
  • }
  • })

最后,就可以在页面中使用这个插件了。

  • window.onload = function () {
  • $("p").fontStyle ({
  • color : "#fff",
  • bgcolor : "#000",
  • size : "24px"
  • });
  • }
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门