美文网首页我爱编程
jQuery的无new创建方法

jQuery的无new创建方法

作者: Michael_bdb5 | 来源:发表于2017-09-13 22:34 被阅读117次

    一般我们去写一个框架,会采用什么样的设计呢?比如设计一个jQuery框架,一般我们会创建一个函数对象

    functionjQuery(params){  //构造函数}

    jQuery.prototype.init = function(params){

    //初始化操作

    }jQuery.prototype.html=function(html){

    //实现类似设置innerHTML的操作

    }varjq =newjQuery("#id");

    jq.init();

    jq.html("test");

    我想这是我们最常见的使用方式,这种方式需要new一个新的实例,然后再进行初始化操作,然后才能继续操作,很显然这样会比较繁琐而且代码较多,而jQuery中的使用方法却非常简洁:

    获取指定元素:$("#id")/$(".class")/$("body").html("test");

    创建html片段$("

    ").html("test");

    设置文档加载完成后的方法$(function(){XXX});

    等等,为何jQuery的使用方法如此简单,这是因为jQuery设计的是一种无new的创建方法,这是jQuery独特的设计思路之一。

    我们打开jQuery初始化的这块代码,发现基本结构是这样子的(提取主要框架部分源码):

    (function( window, undefined ) {varjQuery =function( selector, context ) {returnnewjQuery.fn.init( selector, context, rootjQuery );

    };

    jQuery.fn= jQuery.prototype ={

    constructor: jQuery,

    init:function( selector, context, rootjQuery ) {

    if ( !selector ) {

    return this;

    }

    if ( typeof selector === "string" ) {

    if ( match && (match[1] || !context) ) {

    return this;

    } else if ( !context || context.jquery ) {

    return ( context || rootjQuery ).find( selector );

    } else {

    return this.constructor( context ).find( selector );

    }

    } else if ( selector.nodeType ) {

    return this;

    } else if ( jQuery.isFunction( selector ) ) {

    return rootjQuery.ready( selector );

    }

    if ( selector.selector !== undefined ) {

    this.selector = selector.selector;

    this.context = selector.context;

    }

    return jQuery.makeArray( selector, this );

    },

    html:function(value){

    //实现类似innerHTML操作

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

    })( window );

    1. 首先把代码封装在一个匿名函数里面,防止污染全局环境,然后创建一个对象jQuery,这个对象接受两个参数selector,context,其中selector就是我们通常传入的 #id,.class等字符串,这个对象函数里面并没有做什么逻辑而是直接返回了一个原型上的init方法,然后把参数传了进去。

    2. init方法接受了3个参数,然后在内部进行了各种判断,根据不同的场景,返回一个不同的实例对象,类似一个工厂方法。

    3. 将jQuery.fn.init的原型挂载到jquery的原型上。

    4. 将jQuery的constructor还原为jQuery

    init方法的内部逻辑我们后面再看,首先看下为何jQuery要这样做?

    首先jQuery在构造函数里面就执行了new方法并执行了init方法,将new和init两步直接合并成一步,而且自带new操作,不需要用户再去进行new的操作,但是为何要使用一个原型上的init方法,而不是在自身自己new出一个对象呢?

    想象一下如果jQuery这样写

    varjQuery =function( selector, context ) {

    //做上面的各种场景判断returnnewjQuery( selector, context, rootjQuery );

    }

    这样写会存在一个问题,jQuery函数内部new一个新的jQuery,然后会一直这样new下去无限循环,陷入死循环。为了防止这种情况的出行,jQuery将这部分逻辑放在了原型中的一个init方法,由init方法来进行工厂方法的处理,return new jQuery.fn.init( selector, context, rootjQuery ),这样就避免了死循环的问题,而且实现了一步到位的初始化。

    但是这样写还存在一个问题,init方法里面的return this是指向的jQuery的实例对象,那他将无法访问jQuery的原型对象,比如说jQuery.prototype.html方法,因为html的方法是jQuery.prototype上的所以init方法无法访问这个函数,为了解决这个问题,我们看到之前的源码有最后一行:

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

    其中jQuery.fn就是jQuery.prototpye,也就等价于

    jQuery.prototpye.init.prototype = jQuery.prototpye;

    这句话的意思是,把init的方法的原型改为指向jQuery的原型,这样new init的出来的实例对象也就等价于new jQuery的实例对象,所以也就可以访问jQuery的原型方法了。

    jQuery.prototype.init和jQuery的原型指向的是同一个对象,这样不管是init还是jQuery去修改原型对象都是等价的,这样就实现了上面提到的问题。

    最后,jQuery再把constructor还愿回来到jQuery,防止构造函数指向错误,引起调用问题,为何要这样处理呢?因为我们是把jQuery的prototype直接覆盖成了一个新的实例对象,所以jQuerty的constructor就变成了{}的构造函数Object,所以需要将constructor还原为真正的jQuery。

    对比下两种原型改写方式:

    //方式1

    jQuery.prototype.html =function(value){}

    //方式2jQuery.prototype={

    html:function(value){}

    }

    两种方式看起来好像没啥区别,但其实区别很大,第一种是给jQuery的prototype新增一个方法,这种方式是不会影响jQuery的constructor,还是function jQuery(),

    而第二种方式是直接覆盖了jQuery的prototype,这种情况下的constructor会被直接改写为{html:function(value)}这个实例对象的构造函数,也就是function Object()

    所以需要重置一下jQuery的constructor

    相关文章

      网友评论

        本文标题:jQuery的无new创建方法

        本文链接:https://www.haomeiwen.com/subject/calfattx.html