jQuery源码分析(二):无new构建原理

前端语法/样式/布局 2016-10-24

起步

jQuery里面,你既可以用$.isArray()调用jq的静态方法,也可以通过$().css()获取对象执行对象方法。

在js中,要实现一个变量既能被new,又能调用它的方法,其实不难实现:

var a = function () {};
a.func = function () {alert("静态方法");};
a.prototype.t = function () {alert("对象方法");};

var t = new a();
t.t();
a.func();

原型 prototype

能实现这种效果得益于js的prototype特性。来自W3C的解释:

定义和用法 prototype 属性使您有能力向对象添加属性和方法。 语法 object.prototype.name=value

也因为如此,很多人将它当做继承使用:

A.prototype = new B();

A的prototype为B的一个实例,可以理解A将B中的方法和属性全部克隆了一遍。在此,我觉得不该用继承,而是用克隆会比较合适。因为有一种情况是允许的:A的prototype是B的实例,同时B的prototype也是A的实例。而在继承的世界是不会出现这种“乱伦”的。

无new构建

说无new是因为我们创建jq对象不通过new关键字创建,其实是jq为我们封装了new:

var jQuery = function( selector, context ) {
    return new jQuery.fn.init( selector, context );
};

你可以看到,每次调用$()方法等于是创建了一个新的实例,所以说为此为了减少多余空间,以后最好用var a = $();的方式让下文使用提供方便,避免多余的封装和空间的浪费。现在,也可以把我们的代码调整一下,也让它看起来像jq:

(function () {
    var a = function(){
      return new a.prototype.init();
    };
    a.func = function () {
        alert("静态方法");
    };
    a.prototype = {
        init:function () {},
        t:function () {
            alert("对象方法");
        }
    };
    a.prototype.init.prototype = a.prototype;
    window.a = a;
})();

var t =a();
t.t();
a.func();

再看看jq源码中是怎么实现的。

var jQuery = function(selector, context) {
    return new jQuery.fn.init(selector, context);
};
jQuery.fn = jQuery.prototype = {};
//...
init = jQuery.fn.init = function(selector, context){};
init.prototype = jQuery.fn;

妙笔就在init.prototype = jQuery.fn;让所有jq对象都有jQuery.fn的属性和方法。拆解一下会更好理解:jQuery.prototype.init.prototype = jQuery.prototype。所以在jq中,如果你看到jQuery.fn.xxx = function(){};,意思就是jq对象有xxx方法,而该方法不是静态方法。

总结

jQuery的无new构建原理中在$()内部中首先保证是通过new创建,使得我们在函数调用中可以使用this来代表该jq实例,通过原型的共享而实现了静态方法与实例方法的共存。手段惊艳,不得不赞叹作者的过人之处。


本文由 hongweipeng 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

如果对您有用,您的支持将鼓励我继续创作!