Jquery 扩展方法实现原理

JSONP原理

  首先:JSON和JSONP是不一样的概念。

  JSON是一种数据交换格式,而JSONP是非正式传输协议。

  该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据。

  其实现细节是使用 Script标签携带一个Callback函数,动态的向服务端请求数据。

  如:   

<script type="text/javascript">
    var searchCallback = function (data) {
        console.log(data)
    }
    var url = "http://demo.com/jsonp/search?id=1&callback=searchCallback";
    // 创建script标签,设置其属性
    var script = document.createElement(‘script‘);
    script.setAttribute(‘src‘, url);
    // 把script标签加入head,此时调用开始
    document.getElementsByTagName(‘head‘)[0].appendChild(script);
</script>

  结论:

  在jquery 源码中, jsonp的实现方式是动态添加<script>标签来调用服务器提供的 js脚本。jquery 会在window对象中加载一个全局的函数,当 <script>代码插入时函数执行,执行完毕后就<script>会被移除。

Delegate原理

  早期版本中叫delegate, 后来有过live函数,再后来统一用on。下面的方法等效

    // jQuery 1.3
    $(selector).(events, data, handler);
    // jQuery 1.4.3+
    $(elements).delegate(selector, events, data, handler);
    // jQuery 1.7+ live过时 旧版本的jQuery中用户,应优先使用.delegate()来取代.live()
    $(elements).on(events, selector, data, handler);

  在3.0以后统一使用on.

  说到Delegate原理需要首先了解二个概念:事件捕获和事件冒泡

  

  

    • 事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。
    • 事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
    • 事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件冒泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。

  源码片段如下:  

jQuery.fn.extend( {
        ...
	delegate: function( selector, types, data, fn ) {
		return this.on( types, selector, data, fn );
	},
	...
} );

function on( elem, types, selector, data, fn, one ) {
        ...
        return elem.each( function() {
		jQuery.event.add( this, types, fn, data, selector );
	} );
}

jQuery.event = {
    ...
    add: function( elem, types, handler, data, selector ) {          ...
         // 若未指定特殊的事件处理,则使用addEventListener
	 if ( !special.setup ||
	   special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
             if ( elem.addEventListener ) {
		elem.addEventListener( type, eventHandle );
	     }
	 }         ...
    }
    ...
}

   以下是类似绑定的对比(后期绑定:元素已经加载后,进行事件的绑定。前期绑定:元素尚未存在,为动态元素添加事件):

    • bind是在dom树加载后,对元素的绑定,属于后期绑定。
    • one是在dom树加载后,对元素的绑定,和bind一样属于后期绑定,但是会在事件执行之后移除元素的绑定事件,事件只执行一次。
    • live 是先把事件绑定在document对象上面,通过事件冒泡,判断当前处于目标状态的元素是不是预绑定的那个元素对象,然后执行事件,属于前期绑定,元素可以是已存在的,也可以是动态添加的。
    • delegate绑定的元素可以是已经存在的,也可以是动态添加的。如果是已经存在的就会选定一个父元素,通过冒泡来触发指定子元素的事件。如果是动态添加的,就以document为父元素来触发冒泡事件。

  结论:

  Jquery使用事件冒泡(做了兼容性处理),通过addEventListener到父元素进行事件监听,通过e.target判断触发元素。

Data原理

  作用: Jquery.data() 方法向被选元素附加数据,或者从被选元素获取数据。

  Jquery在原型和构造上均有data方法,jQuery.extend 与 jQuery.fn.extend 的区别在于:

    • jQuery.extend 是在 jQuery 这个构造函数上扩展的方法,调用的时候写成 $.xxx 或者 jQuery.xxx
    • jQuery.fn.extend 是在 jQuery 原型上扩展的方法,原型上的方法被所有 jQuery 对象共享,调用的时候为 $().xxx

  Jquery通过全局对象 cache 来保存 dom 元素上绑定的数据,可以避免 dom 对象和 js 对象之间互相引用导致的循环引用问题。

  在内部实现上,Jquery通过使用一个随机数作为主键添加到元素上并附加一个index number。使用cache来进行查找。

  如:

$("#header").data("title", "hello world");
// 执行后打印 $.cache 结果为
{
    1: {
        data: {
            title : "hello world"
        }
    }
}
// 查看 header 元素,发现属性中多了一行
//"jQuery18102873769768048078: 1"
//其中 key 值那一长串为 jQuery 实例唯一标识,1 是 header 元素绑定的数据在 cache 对象中对应的属性名,该值每次增加 1,
$("#header").data("title");

  结论:

  Jquery.data在内部使用全局cache进行数据缓存,并未把实际数据添加到元素上。而是使用随机数作为属性名,自增数字作为值。在获取时根据以上匹配全局缓存。

Ajax原理

  Ajax:Asynchronous JavaScript and XML。在不重载整个网页的情况下,AJAX 通过后台加载数据,并在网页上进行显示。

  Jquery在实现Ajax时,对不同dataType的采用了不同的实现方式。dataType有:xml,html,script,json,jsonp,text。

  通过查看源码可以知道,Jquery中定义了二个全局变量:prefilters,预处理器,在ajax请求发出之前做预处理工作;transports,分发器,负责实际发送ajax请求。

  ajax会在每个请求发送前,根据datatype调用prefilters中的对应函数进行预处理,然后调用transports中的对应函数来发送请求。

  主要流程如下:

1,创建jqXHR对象,这个对象就是ajax的返回值
2,用deferred对象封装jqXHR对象,因此可以实现链式的异步操作:xhr.complete(x).success(x).errorl(x),这里的complete,success和error就是promise对象的add, done和fail的别名而已。
3,调用函数inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ),那些插件注册的prefilters函数就在这里被调用了。
4,后续处理,比如设置header参数,设置缓存cache
5,调用inspectPrefiltersOrTransports( transports, s, options, jqXHR )函数发送请求
6,定义了done函数,当请求发送结束之后做后续处理,包括调用convert转换结果、设置statusText,调用deferred.resolveWith触发异步回调等
7,最后返回了jqXHR对象

  结论:

    Jquery中的插件实现提供了二种方式:

    跨域/Script/Jsonp对应使用Script标签

    普通请求使用XMLHttpRequest对象  

Trigger原理

  Jquery源码事件流程图如下(引用自here):

  源码片段如下:

  

jQuery.extend( jQuery.event, {

    trigger: function( event, data, elem, onlyHandlers ) {
            ...
            // Fire handlers on the event path
        i = 0;
        while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
            lastElement = cur;
            event.type = i > 1 ?
                bubbleType :
                special.bindType || type;

            // jQuery handler
            handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
                dataPriv.get( cur, "handle" );
            if ( handle ) {
                handle.apply( cur, data );
            }

            // Native handler
            handle = ontype && cur[ ontype ];
            if ( handle && handle.apply && acceptData( cur ) ) {
                event.result = handle.apply( cur, data );
                if ( event.result === false ) {
                    event.preventDefault();
                }
            }
        }
        event.type = type;     

      // If nobody prevented the default action, do it now

        if ( !onlyHandlers && !event.isDefaultPrevented() ) {

      ...      

        if ( event.isPropagationStopped() ) {

  lastElement.addEventListener( type, stopPropagationCallback );

            }

elem[ type ]();//执行默认行为,比如:input[submit](),input元素的提交方法。点击类型为submint的input,会默认执行submit属性方法。但是这里不会调用dispatch方法。

if ( event.isPropagationStopped() ) {

lastElement.removeEventListener( type, stopPropagationCallback );

}

      ...

      }

        ...
    }
}

  结论:

  获取事件句柄,通过apply方法调用定义的回调函数。并执行默认行为。  

  

  triggerHandler

  类似于trigger,但是不会触发元素的默认行为,例如a标签的跳转行为,submit的提交行为等,且不会在DOM树中冒泡,因此事件不会传递给它的任何祖辈元素  

Animate原理

  源码片段如下:

jQuery.fn.extend({
    animate: function (prop, speed, easing, callback) {
        ...
        doAnimation = function () {
            // Operate on a copy of prop so per-property easing won‘t be lost
            var anim = Animation(this, jQuery.extend({}, prop), optall);

            // Empty animations, or finishing resolves immediately
            if (empty || dataPriv.get(this, "finish")) {
                anim.stop(true);
            }
        ...
        };
    }
)

function Animation( elem, properties, options ) {
    ...
    jQuery.fx.timer(
        jQuery.extend( tick, {
            elem: elem,
            anim: animation,
            queue: animation.opts.queue
        } )
    );
    ...
}

jQuery.fx.timer = function( timer ) {
    jQuery.timers.push( timer );
    jQuery.fx.start();
};

jQuery.fx.start = function() {
    if ( inProgress ) {
        return;
    }
    inProgress = true;
    schedule();
};

function schedule() {
    if ( inProgress ) {
        if ( document.hidden === false && window.requestAnimationFrame ) {
            window.requestAnimationFrame( schedule );
        } else {
            window.setTimeout( schedule, jQuery.fx.interval );
        }
        jQuery.fx.tick();
    }
}

  结论:

  根据以上片段,可以直击最终实现,在支持window.requestAnimationFrame的浏览器上,通过requestAnimationFrame调用。

  在不支持以上方式的浏览器上,通过定时器按interval频率触发元素改变。  

  优点:在不支持CSS3动画的浏览器上实现动画效果。

Ready原理

  源码片段如下:

// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
if ( document.readyState === "complete" ||
	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {

	// Handle it asynchronously to allow scripts the opportunity to delay ready
	window.setTimeout( jQuery.ready );

} else {

	// Use the handy event callback
	document.addEventListener( "DOMContentLoaded", completed );

	// A fallback to window.onload, that will always work
	window.addEventListener( "load", completed );
}

// The ready event handler and self cleanup method
function completed() {
	document.removeEventListener( "DOMContentLoaded", completed );
	window.removeEventListener( "load", completed );
	jQuery.ready();
}

  结论:

  二种实现方式: 1,标准浏览器采用监听 DOMContentLoaded事件,完成后调用定义的回调函数; 2,IE采用试图滚动页面,能滚动了就证明加载好了并进行回调。

Deferred原理

  Deferreds 可以理解为表示需要长时间才能完成的耗时操作的一种方式,相比于阻塞式函数它们是异步的,而不是阻塞应用程序等待其完成然后返回结果。 deferred对 象会立即返回,然后你可以把回调函数绑定到deferred对象上,它们会在异步处理完成后被调用。

jQuery.extend({

    Deferred: function (func) {
    ...
    promise = {
        state: function () {
            return state;
        },
        always: function () {
            deferred.done(arguments).fail(arguments);
            return this;
        },
        "catch": function (fn) {
            return promise.then(null, fn);
        },

        then: function (onFulfilled, onRejected, onProgress) {
        ...
			},
    deferred = {};
    // Make the deferred a promise
    promise.promise(deferred);
    // Call given func if any
    if (func) {
        func.call(deferred, deferred);
    }

    // All done!
    return deferred;
    }
}

  结论:

  总的来讲Deferred通过一组 API 来规范化异步操作,让异步操作的流程控制更加容易。

Lazyload原理

  核心:按需加载

  插件:jquery.lazyload.js

  源代码片段如下:

function update() {
    elements.each(function () {
       ...    //判断当前视窗与设置的关系
       if (!$.belowthefold(this, settings) &&
        !$.rightoffold(this, settings)) {
        $this.trigger("appear");
    }
        ...
});
}

/* 符合条件时,显示图片 */
$self.one("appear", function () {
    if (!this.loaded) {
        $("<img />")
            .bind("load", function () {

                var original = $self.attr("data-" + settings.data_attribute);
                $self.hide();
                if ($self.is("img")) {
                    $self.attr("src", original);
                } else {
                    $self.css("background-image", "url(‘" + original + "‘)");
                }
                ...
            })
    .attr("src", $self.attr("data-" + settings.data_attribute));
    }
});

  结论:

  根据配置,监听事件触发(Scroll等),判断图片是否在可见区域(接近),通过设置其src属性值为original中真实值,以加载图片。

refers:

https://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html

https://www.cnblogs.com/owenChen/archive/2013/02/18/2915521.html

https://www.cnblogs.com/yuanjun1/p/4001953.html

https://www.cnblogs.com/yaoyinglong/p/5738979.html

https://blog.csdn.net/lihongxun945/article/details/12029395

http://www.360doc.com/content/13/1128/09/10504424_332741972.shtml

https://www.cnblogs.com/aaronjs/p/3348569.html

原文地址:https://www.cnblogs.com/full-stack-engineer/p/8988362.html

时间: 2024-10-09 09:31:39

Jquery 扩展方法实现原理的相关文章

jquery ready方法实现原理 内部原理

今天闲来无事研究研究jquery.ready()的内部实现,看JQ的源码一头雾水,由于自己很菜了,于是翻了翻牛人的播客,讲述详细,收获颇多. 先普及一下jquery.ready()和window.onload,window.onload事件是在页面所有的资源都加载完毕后触发的. 如果页面上有大图片等资源响应缓慢, 会导致window.onload事件迟迟无法触发.所以出现了DOM Ready事件. 此事件在DOM文档结构准备完毕后触发, 即在资源加载前触发. 我的ready方法写了2版,借鉴了不

jquery源码解析:jQuery扩展方法extend的详解

jQuery中要扩展方法或者属性都是通过extend方法实现的.所谓的jQuery插件也是通过extend方法实现的. jQuery.extend扩展的是工具方法,也就是静态方法.jQuery.fn.extend扩展的是实例方法. 当只传入一个对象的时候,里面的方法和属性是扩展到this上的.比如: $.extend( { aaa:function(){}, bbb:function(){} } ) ,这里的this是$,所以用这种形式$.aaa()调用. $.fn.extend( { aaa:

Jquery 扩展方法

$.fn是指jquery的命名空间,加上fn上的方法及属性,会对jquery实例每一个有效. 如扩展$.fn.abc() 那么你可以这样子:$("#div").abc(); 通常使用extend方法扩展,详细请看API. $.fx是指jquery的特效. 如果使用显示.滑动.淡入淡出.动画等. $.fx.off可以关闭动画,其实是直接显示结果. -----------------------------------------------------------------------

jquery 扩展方法,自定义函数等一些写法

// 传参数 var aa = function(x){ //弹出对象 x 里的 aa 变量和 bb 变量 alert(x.aa + " 我成功啦 " + x.bb); } $.windowbox = aa; $.windowbox({ aa: "哈哈", bb: "啦啦" }); //方法定义 $.windowbox = { //定义一个方法aa aa: function(){ alert("aa"); }, //定义一个方

jQuery扩展方法

切换class类 1 (function($){ 2 $.fn.swapClass = function(class1,class2){ 3 this.each(function(){ 4 var $element = $(this); 5 if($element.hasClass(class1)){ 6 $element.removeClass(class1).addClass(class2); 7 }else if($element.hasClass(class2)){ 8 $element

jquery扩展方法(表单数据格式化为json对象)

<script type="text/javascript"> // 将表单数据序列化为一个json对象,例如 {"name":"zs", "age":10} // 使用:var jsonObj = $("#formId").serializeObject(); $.fn.serializeObject = function() { var o = {}; var a = this.serial

JQuery扩展方法实现Form表单与Json互相转换

1.把表单转换出json对象 //把表单转换出json对象 $.fn.toJson = function () { var self = this, json = {}, push_counters = {}, patterns = { "validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/, "key": /[a-zA-Z0-9_]+|(?=\[\])/g, "push&qu

Jquery自定义扩展方法

jquery是一款流行的JS框架,自定义JS方法,封装到Jquery中,调用起来也挺方便的,怎么写Jquery扩展方法那,网上翻阅了一部分代码,其实也挺简单的: 方式一: (jQuery.fn.setApDiv=function () { //apDiv浮动层显示位置居中控制 var wheight=$(window).height(); var wwidth=$(window).width(); var apHeight=wheight-$("#apDiv").height(); v

扩展方法用法及其原理和注意事项

前言 一直以来尤其像C#一些常见的语法,本人更愿意去探讨其内部实现的原理,为什么要这么做呢?只是为了当我真正在开发中运用语法的时候不会因为犯常识性错误或者说因为一些注意事项未曾注意到而耽误一些无谓的时间,同时也能理解的更深入而不是仅仅停留在表面(或许理解也不是太透).(当然本人能力有限,太高深的东西必定是研究不明白了,也只有这能力了). 概念 扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型. 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例