目录
jQuery.Deferred对象
jQuery.when方法
后续
首页 web前端 js教程 jQuery源码解析Deferred异步对象的方法

jQuery源码解析Deferred异步对象的方法

Oct 26, 2017 am 10:21 AM
jquery 异步

在工作中我们可能会把jQuery选择做自己项目的基础库,因为其提供了简便的DOM选择器以及封装了很多实用的方法,比如$.ajax(),它使得我们不用操作xhrxdr对象,直接书写我们的代码逻辑即可。更为丰富的是它在ES6没有原生支持的那段时间,提供了Deferred对象,类似于Promise对象,支持done/fail/progress/always方法和when批处理方法,这可能在项目上帮助过你。

ES6提供了Promise对象,但由于它是内置C++实现的,所以你也没法看它的设计。不如我们通过jQuery的源码来探究其设计思路,并比较一下两者的区别。本文采用jquey-3.1.2.js版本,其中英文注释为原版,中文注释为我添加。

jQueryajax总体设计

jQuery在内部设置了全局的ajax参数,在每一个ajax请求初始化时,用传递的参数与默认的全局参数进行混合,并构建一个jqXHR对象(提供比原生XHR更为丰富的方法,同时实现其原生方法),通过传递的参数,来判断其是否跨域、传递的参数类型等,设置好相关头部信息。同时其被初始化为一个内置Deferred对象用于异步操作(后面讲到),添加done/fail方法作为回调。同时我们也封装了$.get/$.post方法来快捷调用$.ajax方法。

上面提到的Deferred对象,与ES6的Promise对象类似,用于更为方便的异步操作,多种回调以及更好的书写方式。提供progress/fail/done方法,并分别用该对象的notify/reject/resolve方法触发,可以使用then方法快速设置三个方法,使用always添加都会执行的回调,并且提供when方法支持多个异步操作合并回调。可以追加不同的回调列表,其回调列表是使用内部Callbacks对象,更方便的按照队列的方式来进行执行。

Callbacks回调队列对象,用于构建易于操作的回调函数集合,在操作完成后进行执行。支持四种初始化的方式once/unique/memory/stopOnFalse,分别代表只执行依次、去重、缓存结果、链式调用支持终止。提供fired/locked/disabled状态值,代表是否执行过、上锁、禁用。提供add/remove/empty/fire/lock/disable方法操作回调函数队列。

主要涉及到的概念就是这三个,不再做延伸,三个对象的设计代码行数在1200行左右,断断续续看了我一周 (´ཀ`」 ∠) 。我们从这三个倒序开始入手剖析其设计。

jQuery.Callbacks对象

Callbacks对象,用于管理回调函数的多用途列表。它提供了六个主要方法:

  1. add: 向列表中添加回调函数

  2. remove: 移除列表中的回调函数

  3. empty: 清空列表中的回调函数

  4. fire: 依次执行列表中的回调函数

  5. lock: 对列表上锁,禁止一切操作,清除数据,但保留缓存的环境变量(只在memory参数时有用)

  6. disable: 禁用该回调列表,所有数据清空

在初始化时,支持四个参数,用空格分割:

  1. once: 该回调列表只执行依次

  2. memory: 缓存执行环境,在添加新回调时执行先执行一次

  3. unique: 去重,每一个函数均不同(指的是引用地址)

  4. stopOnFalse: 在调用中,如果前一个函数返回false,中断列表的后续执行

我们来看下其实例使用:

let cl = $.Callbacks('once memory unique stopOnFalse');
fn1 = function (data) {
    console.log(data);
};
fn2 = function (data) {
    console.log('fn2 say:', data);
    return false;
};
cl.add(fn1);
cl.fire('Nicholas');    // Nicholas
// 由于我们使用memory参数,保存了执行环境,在添加新的函数时自动执行一次
cl.add(fn2);    // fn2 say: Nicholas
// 由于我们使用once参数,所以只能执行(fire)一次,此处无任何输出
cl.fire('Lee');

// 后面我们假设这里没有传入once参数,每次fire都可以执行

cl.fire('Lee');    // Lee    fn2 say: Lee
// 清空列表
cl.empty();
cl.add(fn2, fn1);
// 由于我们设置了stopOnFalse,而fn2返回了false,则后添加的fn1不会执行
cl.fire('Nicholas');    // fn2 say: Nicholas
// 上锁cl,禁用其操作,清除数据,但是我们添加了memory参数,它依然会对后续添加的执行一次
cl.lock();
// 无响应
cl.fire();
cl.add(fn2);    // fn2 say: Nicholas
// 禁用cl,禁止一切操作,清除数据
cl.disable();
登录后复制

除了上面所说的主要功能,还提供has/locked/disabled/fireWith/fired等辅助函数。

其所有源码实现及注释为:

jQuery.Callbacks = function( options ) {
    options = typeof options === "string" ?
        // 将字符串中空格分割的子串,转换为值全为true的对象属性
        createOptions( options ) :
        jQuery.extend( {}, options );

    var // Flag to know if list is currently firing
        firing,

        // Last fire value for non-forgettable lists
        memory,

        // Flag to know if list was already fired
        fired,

        // Flag to prevent firing
        locked,

        // Actual callback list
        list = [],

        // Queue of execution data for repeatable lists
        queue = [],

        // Index of currently firing callback (modified by add/remove as needed)
        firingIndex = -1,

        // Fire callbacks
        fire = function() {

            // Enforce single-firing
            locked = locked || options.once;

            // Execute callbacks for all pending executions,
            // respecting firingIndex overrides and runtime changes
            fired = firing = true;
            // 为quene队列中不同的[context, args]执行list回调列表,执行过程中会判断stopOnFalse中间中断
            for ( ; queue.length; firingIndex = -1 ) {
                memory = queue.shift();
                while ( ++firingIndex < list.length ) {

                    // Run callback and check for early termination
                    if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
                        options.stopOnFalse ) {

                        // Jump to end and forget the data so .add doesn&#39;t re-fire
                        firingIndex = list.length;
                        memory = false;
                    }
                }
            }

            // Forget the data if we&#39;re done with it
            if ( !options.memory ) {
                memory = false;
            }

            firing = false;

            // Clean up if we&#39;re done firing for good
            // 如果不再执行了,就将保存回调的list清空,对内存更好
            if ( locked ) {

                // Keep an empty list if we have data for future add calls
                if ( memory ) {
                    list = [];

                // Otherwise, this object is spent
                } else {
                    list = "";
                }
            }
        },

        // Actual Callbacks object
        self = {

            // Add a callback or a collection of callbacks to the list
            add: function() {
                if ( list ) {

                    // If we have memory from a past run, we should fire after adding
                    // 如果我们选择缓存执行环境,会在新添加回调时执行一次保存的环境
                    if ( memory && !firing ) {
                        firingIndex = list.length - 1;
                        queue.push( memory );
                    }

                    ( function add( args ) {
                        jQuery.each( args, function( _, arg ) {
                            // 如果是函数,则判断是否去重,如果为类数组,则递归执行该内部函数
                            if ( jQuery.isFunction( arg ) ) {
                                if ( !options.unique || !self.has( arg ) ) {
                                    list.push( arg );
                                }
                            } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {

                                // Inspect recursively
                                add( arg );
                            }
                        } );
                    } )( arguments );

                    if ( memory && !firing ) {
                        fire();
                    }
                }
                return this;
            },

            // Remove a callback from the list
            // 移除所有的相同回调,并同步将firingIndex-1
            remove: function() {
                jQuery.each( arguments, function( _, arg ) {
                    var index;
                    while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                        list.splice( index, 1 );

                        // Handle firing indexes
                        if ( index <= firingIndex ) {
                            firingIndex--;
                        }
                    }
                } );
                return this;
            },

            // Check if a given callback is in the list.
            // If no argument is given, return whether or not list has callbacks attached.
            // 检查是否存在该函数,如果不传递参数,则返回是否有回调函数
            has: function( fn ) {
                return fn ?
                    jQuery.inArray( fn, list ) > -1 :
                    list.length > 0;
            },

            // Remove all callbacks from the list
            empty: function() {
                if ( list ) {
                    list = [];
                }
                return this;
            },

            // Disable .fire and .add
            // Abort any current/pending executions
            // Clear all callbacks and values
            // 置locked为[],即!![] === true,同时将队列和列表都清空,即禁用了该回调集合
            disable: function() {
                locked = queue = [];
                list = memory = "";
                return this;
            },
            disabled: function() {
                return !list;
            },

            // Disable .fire
            // Also disable .add unless we have memory (since it would have no effect)
            // Abort any pending executions
            // 不允许执行,但如果有缓存,则我们允许添加后在缓存的环境下执行新添加的回调
            lock: function() {
                locked = queue = [];
                if ( !memory && !firing ) {
                    list = memory = "";
                }
                return this;
            },
            locked: function() {
                return !!locked;
            },

            // Call all callbacks with the given context and arguments
            // 为fire附带了一个上下文来调用fire函数,
            fireWith: function( context, args ) {
                if ( !locked ) {
                    args = args || [];
                    args = [ context, args.slice ? args.slice() : args ];
                    queue.push( args );
                    if ( !firing ) {
                        fire();
                    }
                }
                return this;
            },

            // Call all the callbacks with the given arguments
            fire: function() {
                self.fireWith( this, arguments );
                return this;
            },

            // To know if the callbacks have already been called at least once
            fired: function() {
                return !!fired;
            }
        };

    return self;
};
登录后复制

jQuery.Deferred对象

jQuery.Deferred对象是一个工厂函数,返回一个用于异步或同步调用的deferred对象,支持链式调用、回调函数队列,并且能针对返回的状态不同执行不同的回调。它类似于ES6提供的Promise对象,提供9个主要的方法:

  1. done: 操作成功响应时的回调函数(同步或异步,以下相同)

  2. fail: 操作失败响应时的回调函数

  3. progress: 操作处理过程中的回调函数

  4. resolve: 通过该方法解析该操作为成功状态,调用done

  5. reject: 通过该方法解析该操作为失败状态,调用fail

  6. notify: 通过该方法解析该操作为执行过程中,调用progress

  7. then: 设置回调的简写,接收三个参数,分别是done/fail/progress

  8. always: 设置必须执行的回调,无论是done还是fail

  9. promise: 返回一个受限制的Deferred对象,不允许外部直接改变完成状态

它的实现思想是创建一个对象,包含不同状态下回调函数的队列,并在状态为失败或成功后不允许再次改变。通过返回的Deferred对象进行手动调用resolve/reject/notify方法来控制流程。

看一个实例(纯属胡扯,不要当真)。我们需要从间谍卫星返回的数据用不同的算法来进行解析,如果解析结果信号强度大于90%,则证明该数据有效,可以被解析;如果强度小于10%,则证明只是宇宙噪音;否则,证明数据可能有效,换一种算法解析:

// 我们封装Deferred产生一个promise对象,其不能被外部手动解析,只能内部确定最终状态
asynPromise = function () {
    let d = $.Deferred();
    (function timer() {
        setTimeout(function () {
            // 产生随机数,代替解析结果,来确定本次的状态
            let num = Math.random();
            if (num > 0.9) {
                d.resolve();    // 解析成功
            } else if (num < 0.1) {
                d.reject();    // 解析失败
            } else {
                d.notify();    // 解析过程中
            }
            setTimeout(timer, 1000);    // 持续不断的解析数据
        }, 1000);
    })();
    // 如果不返回promise对象,则可以被外部手动调整解析状态
    return d.promise();
};

// then方法的三个参数分别代表完成、失败、过程中的回调函数
asynPromise().then(function () {
    console.log(&#39;resolve success&#39;);
}, function () {
    console.log(&#39;reject fail&#39;);
}, function () {
    console.log(&#39;notify progress&#39;);
});

// 本地执行结果(每个人的不一样,随机分布,但最后一个一定是success或fail)
notify progress
notify progress
notify progress
notify progress
notify progress
reject fail    // 后面不会再有输出,因为一旦解析状态为success或fail,则不会再改变
登录后复制

除了上面的主要功能,还提供了notifyWith/resolveWith/rejectWith/state辅助方法。

其所有的源码实现和注释为:

Deferred: function( func ) {
        var tuples = [
                // action, add listener, callbacks,
                // ... .then handlers, argument index, [final state]
                // 用于后面进行第一个参数绑定调用第二个参数,第三个和第四个参数分别是其不同的回调函数队列
                [ "notify", "progress", jQuery.Callbacks( "memory" ),
                    jQuery.Callbacks( "memory" ), 2 ],
                [ "resolve", "done", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), 0, "resolved" ],
                [ "reject", "fail", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), 1, "rejected" ]
            ],
            state = "pending",
            promise = {
                state: function() {
                    return state;
                },
                // 同时添加done和fail句柄
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                "catch": function( fn ) {
                    return promise.then( null, fn );
                },
                then: function( onFulfilled, onRejected, onProgress ) {
                    var maxDepth = 0;
                    function resolve( depth, deferred, handler, special ) {
                        return function() {
                            var that = this,
                                args = arguments,
                                mightThrow = function() {
                                    var returned, then;

                                    // Support: Promises/A+ section 2.3.3.3.3
                                    // https://promisesaplus.com/#point-59
                                    // Ignore double-resolution attempts
                                    if ( depth < maxDepth ) {
                                        return;
                                    }

                                    returned = handler.apply( that, args );

                                    // Support: Promises/A+ section 2.3.1
                                    // https://promisesaplus.com/#point-48
                                    if ( returned === deferred.promise() ) {
                                        throw new TypeError( "Thenable self-resolution" );
                                    }

                                    // Support: Promises/A+ sections 2.3.3.1, 3.5
                                    // https://promisesaplus.com/#point-54
                                    // https://promisesaplus.com/#point-75
                                    // Retrieve `then` only once
                                    then = returned &&

                                        // Support: Promises/A+ section 2.3.4
                                        // https://promisesaplus.com/#point-64
                                        // Only check objects and functions for thenability
                                        ( typeof returned === "object" ||
                                            typeof returned === "function" ) &&
                                        returned.then;

                                    // Handle a returned thenable
                                    if ( jQuery.isFunction( then ) ) {

                                        // Special processors (notify) just wait for resolution
                                        if ( special ) {
                                            then.call(
                                                returned,
                                                resolve( maxDepth, deferred, Identity, special ),
                                                resolve( maxDepth, deferred, Thrower, special )
                                            );

                                        // Normal processors (resolve) also hook into progress
                                        } else {

                                            // ...and disregard older resolution values
                                            maxDepth++;

                                            then.call(
                                                returned,
                                                resolve( maxDepth, deferred, Identity, special ),
                                                resolve( maxDepth, deferred, Thrower, special ),
                                                resolve( maxDepth, deferred, Identity,
                                                    deferred.notifyWith )
                                            );
                                        }

                                    // Handle all other returned values
                                    } else {

                                        // Only substitute handlers pass on context
                                        // and multiple values (non-spec behavior)
                                        if ( handler !== Identity ) {
                                            that = undefined;
                                            args = [ returned ];
                                        }

                                        // Process the value(s)
                                        // Default process is resolve
                                        ( special || deferred.resolveWith )( that, args );
                                    }
                                },

                                // Only normal processors (resolve) catch and reject exceptions
                                // 只有普通的process能处理异常,其余的要进行捕获,这里不是特别明白,应该是因为没有改最终的状态吧
                                process = special ?
                                    mightThrow :
                                    function() {
                                        try {
                                            mightThrow();
                                        } catch ( e ) {

                                            if ( jQuery.Deferred.exceptionHook ) {
                                                jQuery.Deferred.exceptionHook( e,
                                                    process.stackTrace );
                                            }

                                            // Support: Promises/A+ section 2.3.3.3.4.1
                                            // https://promisesaplus.com/#point-61
                                            // Ignore post-resolution exceptions
                                            if ( depth + 1 >= maxDepth ) {

                                                // Only substitute handlers pass on context
                                                // and multiple values (non-spec behavior)
                                                if ( handler !== Thrower ) {
                                                    that = undefined;
                                                    args = [ e ];
                                                }

                                                deferred.rejectWith( that, args );
                                            }
                                        }
                                    };

                            // Support: Promises/A+ section 2.3.3.3.1
                            // https://promisesaplus.com/#point-57
                            // Re-resolve promises immediately to dodge false rejection from
                            // subsequent errors
                            if ( depth ) {
                                process();
                            } else {

                                // Call an optional hook to record the stack, in case of exception
                                // since it&#39;s otherwise lost when execution goes async
                                if ( jQuery.Deferred.getStackHook ) {
                                    process.stackTrace = jQuery.Deferred.getStackHook();
                                }
                                window.setTimeout( process );
                            }
                        };
                    }

                    return jQuery.Deferred( function( newDefer ) {

                        // progress_handlers.add( ... )
                        tuples[ 0 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onProgress ) ?
                                    onProgress :
                                    Identity,
                                newDefer.notifyWith
                            )
                        );

                        // fulfilled_handlers.add( ... )
                        tuples[ 1 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onFulfilled ) ?
                                    onFulfilled :
                                    Identity
                            )
                        );

                        // rejected_handlers.add( ... )
                        tuples[ 2 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onRejected ) ?
                                    onRejected :
                                    Thrower
                            )
                        );
                    } ).promise();
                },

                // Get a promise for this deferred
                // If obj is provided, the promise aspect is added to the object
                // 通过该promise对象返回一个新的扩展promise对象或自身
                promise: function( obj ) {
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
            },
            deferred = {};

        // Add list-specific methods
        // 给promise添加done/fail/progress事件,并添加互相的影响关系,并为deferred对象添加3个事件函数notify/resolve/reject
        jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ],
                stateString = tuple[ 5 ];

            // promise.progress = list.add
            // promise.done = list.add
            // promise.fail = list.add
            promise[ tuple[ 1 ] ] = list.add;

            // Handle state
            // 只有done和fail有resolved和rejected状态字段,给两个事件添加回调,禁止再次done或者fail,锁住progress不允许执行回调
            if ( stateString ) {
                list.add(
                    function() {

                        // state = "resolved" (i.e., fulfilled)
                        // state = "rejected"
                        state = stateString;
                    },

                    // rejected_callbacks.disable
                    // fulfilled_callbacks.disable
                    tuples[ 3 - i ][ 2 ].disable,

                    // progress_callbacks.lock
                    tuples[ 0 ][ 2 ].lock
                );
            }

            // progress_handlers.fire
            // fulfilled_handlers.fire
            // rejected_handlers.fire
            // 执行第二个回调列表
            list.add( tuple[ 3 ].fire );

            // deferred.notify = function() { deferred.notifyWith(...) }
            // deferred.resolve = function() { deferred.resolveWith(...) }
            // deferred.reject = function() { deferred.rejectWith(...) }
            // 绑定notify/resolve/reject的事件,实际执行的函数体为加入上下文的With函数
            deferred[ tuple[ 0 ] ] = function() {
                deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                return this;
            };

            // deferred.notifyWith = list.fireWith
            // deferred.resolveWith = list.fireWith
            // deferred.rejectWith = list.fireWith
            deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
        } );

        // Make the deferred a promise
        // 将deferred扩展为一个promise对象
        promise.promise( deferred );

        // Call given func if any
        // 在创建前执行传入的回调函数进行修改
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;
    },
登录后复制

jQuery.when方法

$.when()提供一种方法执行一个或多个函数的回调函数。如果传入一个延迟对象,则返回该对象的Promise对象,可以继续绑定其余回调,在执行结束状态之后也同时调用其when回调函数。如果传入多个延迟对象,则返回一个新的master延迟对象,跟踪所有的聚集状态,如果都成功解析完成,才调用其when回调函数;如果有一个失败,则全部失败,执行错误回调。

其使用方法:

$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
  .then(myFunc, myFailure);
登录后复制

其所有源码实现和注释为(能力有限,有些地方实在不能准确理解执行流程):

// 给when传递的对象绑定master.resolve和master.reject,用于聚集多异步对象的状态
function adoptValue( value, resolve, reject, noValue ) {
    var method;
    try {
        // Check for promise aspect first to privilege synchronous behavior
        // 如果when传入的参数promise方法可用,则封装promise并添加done和fail方法调用resolve和reject
        if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
            method.call( value ).done( resolve ).fail( reject );

        // Other thenables
        // 否则,就判断传入参数的then方法是否可用,如果可用就传入resolve和reject方法
        } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
            method.call( value, resolve, reject );

        // Other non-thenables
        // 如果均不可用,则为非异步对象,直接resolve解析原值
        } else {

            // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
            // * false: [ value ].slice( 0 ) => resolve( value )
            // * true: [ value ].slice( 1 ) => resolve()
            resolve.apply( undefined, [ value ].slice( noValue ) );
        }

    // For Promises/A+, convert exceptions into rejections
    // Since jQuery.when doesn&#39;t unwrap thenables, we can skip the extra checks appearing in
    // Deferred#then to conditionally suppress rejection.
    } catch ( value ) {

        // Support: Android 4.0 only
        // Strict mode functions invoked without .call/.apply get global-object context
        // 一个安卓4.0的bug,这里不做阐释
        reject.apply( undefined, [ value ] );
    }
}

// Deferred helper
    when: function( singleValue ) {
        var
            // count of uncompleted subordinates
            remaining = arguments.length,

            // count of unprocessed arguments
            i = remaining,

            // subordinate fulfillment data
            resolveContexts = Array( i ),
            resolveValues = slice.call( arguments ),

            // the master Deferred
            master = jQuery.Deferred(),

            // subordinate callback factory
            // 将每一个响应的环境和值都保存到列表里,在全部完成后统一传给主Promise用于执行
            updateFunc = function( i ) {
                return function( value ) {
                    resolveContexts[ i ] = this;
                    resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                    if ( !( --remaining ) ) {
                        master.resolveWith( resolveContexts, resolveValues );
                    }
                };
            };

        // Single- and empty arguments are adopted like Promise.resolve
        // 如果只有一个参数,则直接将其作为master的回调
        if ( remaining <= 1 ) {
            adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
                !remaining );

            // Use .then() to unwrap secondary thenables (cf. gh-3000)
            if ( master.state() === "pending" ||
                jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {

                return master.then();
            }
        }

        // Multiple arguments are aggregated like Promise.all array elements
        // 多参数时,进行所有参数的解析状态聚合到master上
        while ( i-- ) {
            adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
        }

        return master.promise();
    }
登录后复制

后续

本来想把jQuery.DeferredjQuery.ajax以及ES6Promise对象给统一讲一下,结果发现牵涉的东西太多,每一个都可以单独写一篇文章,怕大家说太长不看,这里先写第一部分jQuery.Deferred吧,后续再补充另外两篇。

jQuery的文档很容易,使用也很方便,但其实真正想要讲好很复杂,更不要说写篇源码分析文章了。真的是努力理解设计者的思路,争取每行都能理解边界条件,但踩坑太少,应用场景太少,确实有很大的疏漏,希望大家能够理解,不要偏听一面之词。


以上是jQuery源码解析Deferred异步对象的方法的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

jQuery中如何使用PUT请求方式? jQuery中如何使用PUT请求方式? Feb 28, 2024 pm 03:12 PM

jQuery中如何使用PUT请求方式?在jQuery中,发送PUT请求的方法与发送其他类型的请求类似,但需要注意一些细节和参数设置。PUT请求通常用于更新资源,例如更新数据库中的数据或更新服务器上的文件。以下是在jQuery中使用PUT请求方式的具体代码示例。首先,确保引入了jQuery库文件,然后可以通过以下方式发送PUT请求:$.ajax({u

jQuery如何移除元素的height属性? jQuery如何移除元素的height属性? Feb 28, 2024 am 08:39 AM

jQuery如何移除元素的height属性?在前端开发中,经常会遇到需要操作元素的高度属性的需求。有时候,我们可能需要动态改变元素的高度,而有时候又需要移除元素的高度属性。本文将介绍如何使用jQuery来移除元素的高度属性,并提供具体的代码示例。在使用jQuery操作高度属性之前,我们首先需要了解CSS中的height属性。height属性用于设置元素的高度

jQuery小技巧:快速修改页面所有a标签的文本 jQuery小技巧:快速修改页面所有a标签的文本 Feb 28, 2024 pm 09:06 PM

标题:jQuery小技巧:快速修改页面所有a标签的文本在网页开发中,我们经常需要对页面中的元素进行修改和操作。在使用jQuery时,有时候需要一次性修改页面中所有a标签的文本内容,这样可以节省时间和精力。下面将介绍如何使用jQuery快速修改页面所有a标签的文本,同时给出具体的代码示例。首先,我们需要引入jQuery库文件,确保在页面中引入了以下代码:&lt

使用jQuery修改所有a标签的文本内容 使用jQuery修改所有a标签的文本内容 Feb 28, 2024 pm 05:42 PM

标题:使用jQuery修改所有a标签的文本内容jQuery是一款流行的JavaScript库,被广泛用于处理DOM操作。在网页开发中,经常会遇到需要修改页面上链接标签(a标签)的文本内容的需求。本文将介绍如何使用jQuery来实现这个目标,并提供具体的代码示例。首先,我们需要在页面中引入jQuery库。在HTML文件中添加以下代码:

Python asyncio 进阶指南:从初学者到专家 Python asyncio 进阶指南:从初学者到专家 Mar 04, 2024 am 09:43 AM

并发和异步编程并发编程处理同时执行的多个任务,异步编程是一种并发编程,其中任务不会阻塞线程。asyncio是python中用于异步编程的库,它允许程序在不阻塞主线程的情况下执行I/O操作。事件循环asyncio的核心是事件循环,它监控I/O事件并调度相应的任务。当一个协程准备就绪时,事件循环会执行它,直到它等待I/O操作。然后,它会暂停协程并继续执行其他协程。协程协程是可暂停和恢复执行的函数。asyncdef关键字用于创建协程。协程使用await关键字等待I/O操作完成。asyncio的基础以下

如何判断jQuery元素是否具有特定属性? 如何判断jQuery元素是否具有特定属性? Feb 29, 2024 am 09:03 AM

如何判断jQuery元素是否具有特定属性?在使用jQuery操作DOM元素时,经常会遇到需要判断元素是否具有某个特定属性的情况。这种情况下,我们可以借助jQuery提供的方法来轻松实现这一功能。下面将介绍两种常用的方法来判断一个jQuery元素是否具有特定属性,并附上具体的代码示例。方法一:使用attr()方法和typeof操作符//判断元素是否具有特定属

了解jQuery中eq的作用及应用场景 了解jQuery中eq的作用及应用场景 Feb 28, 2024 pm 01:15 PM

jQuery是一种流行的JavaScript库,被广泛用于处理网页中的DOM操作和事件处理。在jQuery中,eq()方法是用来选择指定索引位置的元素的方法,具体使用方法和应用场景如下。在jQuery中,eq()方法选择指定索引位置的元素。索引位置从0开始计数,即第一个元素的索引是0,第二个元素的索引是1,依此类推。eq()方法的语法如下:$("s

Java异常处理中的异步和非阻塞技术 Java异常处理中的异步和非阻塞技术 May 01, 2024 pm 05:42 PM

异步和非阻塞技术可用于补充传统异常处理,允许创建更具响应性和高效的Java应用程序:异步异常处理:在另一个线程或进程中处理异常,允许主线程继续执行,避免阻塞。非阻塞异常处理:涉及在I/O操作出错时事件驱动的异常处理,避免阻塞线程,由事件循环处理异常。

See all articles