永远不要跟别人比幸运,我从来没想过我比别人幸运,我也许比他们更有毅力,在最困难的时候,他们熬不住了,我可以多熬一秒钟、两秒钟,甚至更久。

详解jQuery对象,以及如何扩展jQuery插件

JQuery 新民 966℃ 已收录 0评论

早几年学习前端,大家都非常热衷于研究jQuery源码。我还记得当初从jQuery源码中学到一星半点应用技巧的时候常会有一种发自内心的惊叹,“原来JavaScript居然可以这样用!”

虽然随着前端的发展,另外几种前端框架的崛起,jQuery慢慢变得不再是必须。因此大家对于jQuery的热情低了很多。但是许多从jQuery中学到的技巧用在实际开发中仍然非常好用。简单的了解它也有助于我们更加深入的理解JavaScript。

这篇文章的主要目的就是跟大家分享一下,jquery对象是如何封装的。算是对于大家进一步学习jQuery源码的一个抛砖引玉。

使用jQuery对象时,我们这样写:

	// 声明一个jQuery对象
	$('.target')

	// 获取元素的css属性
	$('.target').css('width')

	// 获取元素的位置信息
	$('.target').offset()

在使用之初可能会有许多疑问,比如$是怎么回事?为什么不用new就可以直接声明一个对象等等。后来了解之后,才知道原来这正是jQuery对象创建的巧妙之处。

先直接用代码展示出来,再用图跟大家解释是怎么回事。

	;
(function(ROOT) {

    // 构造函数
    var jQuery = function(selector) {

        // 在jQuery中直接返回new过的实例,这里的init是jQuery的真正构造函数
        return new jQuery.fn.init(selector)
    }

    jQuery.fn = jQuery.prototype = {
        constructor: jQuery,

        version: '1.0.0',

        init: function(selector) {
            // 在jquery中这里有一个复杂的判断,但是这里我做了简化
            var elem, selector;
             elem = document.querySelector(selector);
            this[0] = elem;

            // 在jquery中返回一个由所有原型属性方法组成的数组,我们这里简化,直接返回this即可
            // return jQuery.makeArray(selector, this);
            return this;
        },

        // 在原型上添加一堆方法
        toArray: function() {},
        get: function() {},
        each: function() {},
        ready: function() {},
        first: function() {},
        slice: function() {}
        // ... ...
    }

    jQuery.fn.init.prototype = jQuery.fn;

    // 实现jQuery的两种扩展方式
    jQuery.extend = jQuery.fn.extend = function(options) {

        // 在jquery源码中会根据参数不同进行很多判断,我们这里就直接走一种方式,所以就不用判断了
        var target = this;
        var copy;

        for(name in options) {
            copy = options[name];
            target[name] = copy;
        }
        return target;
    }

    // jQuery中利用上面实现的扩展机制,添加了许多方法,其中

    // 直接添加在构造函数上,被称为工具方法
    jQuery.extend({
        isFunction: function() {},
        type: function() {},
        parseHTML: function() {},
        parseJSON: function() {},
        ajax: function() {}
        // ...
    })

    // 添加到原型上
    jQuery.fn.extend({
        queue: function() {},
        promise: function() {},
        attr: function() {},
        prop: function() {},
        addClass: function() {},
        removeClass: function() {},
        val: function() {},
        css: function() {}
        // ...
    })

    // $符号的由来,实际上它就是jQuery,一个简化的写法,在这里我们还可以替换成其他可用字符
    ROOT.jQuery = ROOT.$ = jQuery;

})(window);

在上面的代码中,我封装了一个简化版的jQuery对象。它向大家简单展示了jQuery的整体框架情况。如果了解了整体框架,那么大家去读jQuery源码,就会变得非常轻松。

我们在代码中可以看到,jQuery自身对于原型的处理使用了一些巧妙的语法,比如jQuery.fn = jQuery.prototypejQuery.fn.init.prototype = jQuery.fn;等,这几句正式jQuery对象的关键所在,下面我用图给大家展示一下这中间的逻辑是怎么回事。

对象封装分析

在上面的实现中,代码首先在jQuery构造函数中声明了一个fn属性,并将其指向了原型jQuery.prototype。并在原型中添加了init方法。

jQuery.fn = jQuery.prototype = {
    init: {}
}

之后又将init的原型,指向了jQuery.prototype。

jQuery.fn.init.prototype = jQuery.fn;

而在构造函数jQuery中,返回了init的实例对象。

var jQuery = function(selector) {

    // 在jQuery中直接返回new过的实例,这里的init是jQuery的真正构造函数
    return new jQuery.fn.init(selector)
}

最后对外暴露入口时,将字符$jQuery对等起来。

ROOT.jQuery = ROOT.$ = jQuery;

因此当我们直接使用$('#test')创建一个对象时,实际上是创建了一个init的实例,这里的正真构造函数是原型中的init方法。

注意:许多对jQuery内部实现不太了解的盆友,在使用jQuery时常常会毫无节制使用$(),比如对于同一个元素的不同操作:

var width = parseInt($('#test').css('width'));
if(width > 20) {
    $('#test').css('backgroundColor', 'red');
}

通过我们上面的一系列分析,我们知道每当我们执行$()时,就会重新生成一个init的实例对象,因此当我们这样没有节制的使用jQuery时是非常不正确的,虽然看上去方便了一些,但是对于内存的消耗是非常大的。正确的做法是既然是同一个对象,那么就用一个变量保存起来后续使用即可。

var $test = $('#test');
var width = parseInt($test.css('width'));
if(width > 20) {
    $test.css('backgroundColor', 'red');
}

扩展方法分析

在上面的代码实现中,我还简单实现了两个扩展方法。

jQuery.extend = jQuery.fn.extend = function(options) {

    // 在jquery源码中会根据参数不同进行很多判断,我们这里就直接走一种方式,所以就不用判断了
    var target = this;
    var copy;

    for(name in options) {
        copy = options[name];
        target[name] = copy;
    }
    return target;
}

要理解它的实现,我们首先要明确的知道this的指向。如果你搞不清楚,可以回头去看看我们之前关于this指向的讲解。传入的参数options对象为一个key: value模式的对象,我通过for in遍历options,将key作为jQuery的新属性,value作为该新属性所对应的新方法,分别添加到jQuery方法和jQuery.fn中。

也就是说,当我们通过jQuery.extend扩展jQuery时,方法被添加到了jQuery构造函数中,而当我们通过jQuery.fn.extend扩展jQuery时,方法被添加到了jQuery原型中。

上面的例子中,我也简单展示了在jQuery内部,许多方法的实现都是通过这两个扩展方法来完成的。

当我们通过上面的知识了解了jQuery的大体框架之后,那么我们对于jQuery的学习就可以具体到诸如css/val/attr等方法是如何实现这样的程度,那么源码学习起来就会轻松很多,也会节约更多的时间。也给一些对于源码敬而远之的朋友提供了一个学习的可能。

 

本站文章如未注明,均为原创丨本网站采用BY-NC-SA协议进行授权,转载请注明转自:http://www.snowruin.com/?p=1657
喜欢 (5)or分享 (0)
发表我的评论
取消评论
表情 代码 贴图 加粗 链接 私信 删除线 签到

Hi,请填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址