开发者社区> 问答> 正文

jquery里面ajax自动补全jsonp回调函数名如何实现的以及jq使用了哪些http方法?

小旋风柴进 2016-05-31 11:44:52 2067

大神们好!小弟在最近学习jquery里面关于ajax部分,有几个问题不太明白,希望能和大神们讨论一下:

1.jq中使用jsonp,callback=?能自动补全函数名,这是什么原理,原生js需要什么方法才能实现自动补全函数名呢?或者原生下如何才能知道应该补全什么样?
2.对于jsonp跨域也是通过瞬间添加script标签调用回调函数然后瞬间删除实现的吗?
3.看到ajax里面判断了mimeType和调用了xhr.getResponseHeader()方法,这样做的目的是什么?还调用了其他什么关于http方面的方法?作用?
4.在学习jq中,对于jq实现原理,对应原生的方法,该如何拆解和分析?该从哪开始?

前端开发 JavaScript
分享到
取消 提交回答
全部回答(1)
  • 小旋风柴进
    2019-07-17 19:21:44

    1、jsonp必须遵循一个固定的格式。即请求的URL的search中必须存在一个jsonpcallback=functionName;响应的格式为functionName(/ json data /); 原理就是利用script不受同源策略限制的特点。
    2、在server返回数据之后,尽量删掉这个script。因为页面中有n多个jsonp,就会有n多个生成的script标签。造成dom臃肿。并且别人也可以通过观察html来指导你jsonp的请求细节。
    3、是看重写的mimeType是不是和server返回的一样。如果一样就没必要重写mimeType了。除此之外还用了setRequestHeader、overriderMimeType、abort等方法。
    4、看代码流程即可。我写了一个简单点的例子

    (function (global) {
        // 防止低版本ie里,undefined被重写
        var undefined = void(0);
        // 定义命名空间
        var namespace = {};
    
        // 默认的参数列表
        var defaultOptions = {
            // ajax请求的路径是什么
            url: '',
            // 往服务器发送的数据
            data: '',
            // 使用什么http方法
            type: 'get',
            // ajax请求方式,同步还是异步。默认为异步
            async: true,
            // 成功时执行的函数
            success: function (data) {
            },
            // 失败时执行的函数
            error: function (errInfo) {
            },
            // 自定义请求首部列表
            header: {},
            // 重写的mimeType
            overrideMimeType: '',
            // 是否走缓存
            cache: false,
            // 超时毫秒数。默认为0 表示不执行超时逻辑
            timeout: 0,
            // 是否格式化参数为uri string
            processData: true,
            // 请求的mime类型 默认为表单提交
            contentType: 'application/x-www-form-urlencoded',
            // 返回的数据格式 text|json
            dataType: 'text'
        };
        /**
         * CORE
         * @param {Object} options 用户输入的参数
         * @throw TypeError
         */
        var ajax = function (options) {
            // 判断参数是否为对象,如果不是则抛出类型错误
            if (!tool.isObject(options)) {
                throw new TypeError('参数类型错误');
            }
            // 合并用户输入的参数列表和默认的参数列表 返回一个全新的参数列表对象
            var userOptions = tool.extend(defaultOptions, options);
    
            // ajax第一步:获取ajax对象
            var xhr = tool.getXHR();
    
            // 1、如果是get系 需要把data拼接到url后面
            if (/^(get|delete|head)$/img.test(userOptions.type)) {
                var data = tool.encodeToURIString(userOptions.data);
                userOptions.url = tool.hasSearch(userOptions.url, data);
                // 因为get系不需要传send参数,所以设置为null
                userOptions.data = null;
            }
            // 2、是否走缓存,如果不走缓存则在url后面加一个随机数来防止缓存
            if (userOptions.cache === false) {
                // 因为search是有固定格式的 key=value 如果只写一个value是不合法的,所以必须构造一个key,而且这个key不能和已有的key重复
                var random = '_=' + (Math.random() * 0xffffff).toFixed(0);
                userOptions.url = tool.hasSearch(userOptions.url, random);
            }
            // ajax操作第二步
            xhr.open(userOptions.type, userOptions.url, userOptions.async);
    
            // 2.1 设置自定义请求首部信息
            if (userOptions.header && tool.isObject(userOptions.header)) {
                tool.eachObject(userOptions.header, function (key, value) {
                    xhr.setRequestHeader(key, value);
                })
            }
            // 2.2 设置content-type http里表现mimeType的字段就是content-type
            // 设置请求的mimeType
            if (userOptions.contentType && tool.isString(userOptions.contentType)) {
                xhr.setRequestHeader('content-type', userOptions.contentType);
            }
            // 2.3 设置重写的mime类型
            // 设置响应的mimeType
            if (userOptions.overrideMimeType && tool.isString(userOptions.overrideMimeType)) {
                xhr.overrideMimeType(userOptions.overrideMimeType);
            }
            // 2.4 判断是否执行超时逻辑
            if (tool.isNumber(userOptions.timeout) && userOptions.timeout > 0) {
                xhr.timeout = userOptions.timeout;
                // 标准浏览器
                if ('ontimeout' in xhr) {
                    xhr.ontimeout = function () {
                        userOptions.error('timeout');
                    }
                } else {
                    // 低版本ie
                    setTimeout(function () {
                        // http的事务是否还没有完成
                        if (xhr.readyState !== 4) {
                            // 强制终止http事务
                            xhr.abort();
                        }
                    }, xhr.timeout);
                }
            }
    
            // 2.5 是否需要处理给服务器发送的数据,判断processData是否为true
            // 当给服务器发送的数据为二进制或者formData的时候,不需要处理这个数据
            // 要把processData设置为false
            if (/^(post|put)$/igm.test(userOptions.type) && userOptions.processData === true) {
                userOptions.data = tool.encodeToURIString(userOptions.data);
            }
            // ajax第三步:接收响应
            xhr.onreadystatechange = function () {
                // http的事务是否完成
                if (xhr.readyState === 4) {
                    // 获取响应主体
                    var responseText = xhr.responseText;
                    // 判断状态码是否成功
                    if (/^2\d{2}$/.test(xhr.status)) {
                        // 判断是否需要把响应主体格式化为json对象
                        if (userOptions.dataType === 'json') {
                            // 因为不合法的json字符串无法转换为json对象,会出异常
                            try {
                                responseText = tool.JSONParse(responseText);
                            } catch (ex) {
                                userOptions.error(ex);
                                return;
                            }
                        }
                        userOptions.success(responseText);
                        // R如果响应码是错误的类型
                    } else if (/^(4|5)\d{2}$/.test(xhr.status)) {
                        // 直接执行error
                        userOptions.error(xhr.status);
                    }
                }
            };
            // ajax第四步:发送
            xhr.send(userOptions.data);
        };
    
        /**
         * 利用闭包,实现获取数据类型
         * @param {string} type 数据类型
         * @returns {Function}
         */
        var getType = function (type) {
            return function (obj) {
                // 为什么要用Object.prototype.toString来判断类型?
                return Object.prototype.toString.call(obj) === '[object ' + type + ']';
            }
        };
    
        var tool = {
            /**
             * 利用惰性函数,实现获取ajax对象的方法
             */
            getXHR: (function () {
                var list = [function () {
                    return new XMLHttpRequest;
                }, function () {
                    return new ActiveXObject('Microsoft.XMLHTTP');
                }, function () {
                    return new ActiveXObject("Msxml2.XMLHTTP");
                }, function () {
                    return new ActiveXObject("Msxml3.XMLHTTP");
                }];
                var len = list.length;
                var xhr = null;
                while (len--) {
                    try {
                        list[len]();
                        xhr = list[len];
                        break;
                    } catch (ex) {
                        continue;
                    }
                }
                if (xhr !== null) {
                    return xhr;
                }
                throw new Error('当前浏览器不支持此方法');
            })(),
            /**
             * 合并多个对象
             * @returns {{}} 合并后的对象
             */
            extend: function () {
                // 因为参数长度不固定,所以把参数列表转成数组
                // var params = [].slice.call(arguments, 0);
                var voidObj = {};
                this.each(arguments, function (item) {
                    // item为每一个参数对象
                    tool.eachObject(item, function (key, value) {
                        voidObj[key] = value;
                    });
                });
                return voidObj;
            },
            /**
             * 循环帮助函数,利用惰性函数
             */
            each: (function () {
                if ([].forEach) {
                    return function (list, callback, context) {
                        [].forEach.call(list, callback, context);
                    }
                }
                return function (list, callback, context) {
                    for (var i = 0, j = list.length; i < j; i++) {
                        callback.call(context, list[i], i, list);
                    }
                }
            })(),
            /**
             * 循环对象
             * @param {Object} obj 要循环的对象
             * @param {Function} callback 回调函数
             * @param {Object|undefined} context 回调函数里头的上下文对象
             */
            eachObject: function (obj, callback, context) {
                for (var n in obj) {
                    if (!obj.hasOwnProperty(n)) continue;
                    callback.call(context, n, obj[n]);
                }
            },
            /**
             * 给tool动态添加判断数据类型的方法
             */
            init: function () {
                this.each(['Object', 'Function', 'Array', 'String', 'Number'], function (item) {
                    tool['is' + item] = getType(item);
                })
            },
            /**
             * 把一个对象格式化为uri string
             * @param {*} data 需要格式化的数据
             * @return {string} 格式化之后得到的uri string
             */
            encodeToURIString: function (data) {
                if (this.isString(data))  return data;
                if (!this.isObject(data)) return '';
                var arr = [];
                this.eachObject(data, function (key, value) {
                    arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
                });
                return arr.join('&');
            },
            /**
             * 往url后面拼接参数的方法
             * @param {string} url url
             * @param {string} padString 要拼接的参数
             * @returns {string} 拼接之后的url
             */
            hasSearch: function (url, padString) {
                if (!padString) return url;
                // 如果有问号,说明url里已经有参数了,因为参数和参数之间用&来分隔
                /*if (/\?/.test(url)) {
                 return url + '&' + padString;
                 } else {
                 return url + '?' + padString;
                 }*/
                return url + (/\?/.test(url) ? '&' : '?') + padString;
            },
            /**
             * 把json字符串格式化为json对象
             * @param {string} jsonString json字符串
             * @return {Object} json对象
             */
            JSONParse: function (jsonString) {
                if (window.JSON) {
                    return JSON.parse(jsonString)
                }
                return eval('(' + jsonString + ')');
            }
        };
        tool.init();
    
    
        // 把ajax方法放入命名空间中
        namespace.ajax = ajax;
    
        tool.each(['get', 'post'], function (item) {
            /**
             * 动态添加get和post方法
             * @param {string} url 请求的url
             * @param {Object} data 往服务器发送的数据
             * @param {Function} callback 成功的回调函数
             * @param {string} dataType 数据格式
             */
            namespace[item] = function (url, data, callback, dataType) {
                ajax({
                    url: url,
                    type: item,
                    data: data,
                    success: callback,
                    dataType: dataType
                });
            }
        });
    
        // 先把全局里已经存在的x先放到一边
        var globalX = global.x;
        /**
         * 解决全局变量名冲突
         * @param {string|undefined} symbol 更改的全局变量名
         * @returns {Object}
         */
        namespace.noConflict = function (symbol) {
            if (symbol && tool.isString(symbol)) {
                window[symbol] = namespace;
            }
            global!==undefined&&(window.x = globalX);
            return namespace;
        };
        // 暴露到全局环境中
        global.x = namespace;
    
    })(this);

    用法和jquery的一样。不过我暴露的是x变量,不是$.

    0 0

集结各类场景实战经验,助你开发运维畅行无忧

推荐文章
相似问题
推荐课程