js手写ajax 兼容IE浏览器

js手写ajax

最详细的手写ajax版本,兼容IE浏览器

request.js文件:

import { getBrowser } from './index'

//     Zepto.js
//     (c) 2010-2016 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.

const $ = {}

const document = window.document
let key
let name
const scriptTypeRE = /^(?:text|application)\/javascript/i
const xmlTypeRE = /^(?:text|application)\/xml/i
const jsonType = 'application/json'
const htmlType = 'text/html'
const blankRE = /^\s*$/
const originAnchor = document.createElement('a')
const class2type = {}
const toString = class2type.toString

const isArray = Array.isArray || function (object) {
  return object instanceof Array
}

function isFunction (value) {
  return type(value) === 'function'
}

function isWindow (obj) {
  return obj != null && obj === obj.window
}

function isObject (obj) {
  return type(obj) === 'object'
}

function type (obj) {
  return obj === null
    ? String(obj)
    : class2type[toString.call(obj)] || 'object'
}

function isPlainObject (obj) {
  return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) === Object.prototype
}

function likeArray (obj) {
  const length = !!obj && 'length' in obj && obj.length
  const type = $.type(obj)

  return type !== 'function' && !isWindow(obj) && (
    type === 'array' || length === 0 ||
    (typeof length === 'number' && length > 0 && (length - 1) in obj)
  )
}

$.isArray = isArray
$.isFunction = isFunction
$.type = type
$.isPlainObject = isPlainObject

originAnchor.href = window.location.href

function ajaxSuccess (data, xhr, settings, deferred) {
  const context = settings.context
  const status = 'success'
  settings.success.call(context, data, status, xhr)
  if (deferred) deferred.resolveWith(context, [data, status, xhr])
}

// type: "timeout", "error", "abort", "parsererror"
function ajaxError (error, type, xhr, settings, deferred) {
  const context = settings.context
  settings.error.call(context, xhr, type, error)
  if (deferred) deferred.rejectWith(context, [xhr, type, error])
}

function ajaxDataFilter (data, type, settings) {
  if (settings.dataFilter === empty) return data
  const context = settings.context
  return settings.dataFilter.call(context, data, type)
}

// Empty function, used as default callback
function empty () {
}

$.ajaxSettings = {
  // Default type of request
  type: 'GET',
  // Callback that is executed before request
  beforeSend: empty,
  // Callback that is executed if the request succeeds
  success: empty,
  // Callback that is executed the the server drops error
  error: empty,
  // Callback that is executed on request complete (both: error and success)
  complete: empty,
  // The context for the callbacks
  context: null,
  // Whether to trigger "global" Ajax events
  global: true,
  // Transport
  xhr: function () {
    return new window.XMLHttpRequest()
  },
  // MIME types mapping
  // IIS returns Javascript as "application/x-javascript"
  accepts: {
    script: 'text/javascript, application/javascript, application/x-javascript',
    json: jsonType,
    xml: 'application/xml, text/xml',
    html: htmlType,
    text: 'text/plain'
  },
  // Whether the request is to another domain
  crossDomain: false,
  // Default timeout
  timeout: 0,
  // Whether data should be serialized to string
  processData: true,
  // Whether the browser should be allowed to cache GET responses
  cache: true,
  // Used to handle the raw response data of XMLHttpRequest.
  // This is a pre-filtering function to sanitize the response.
  // The sanitized response should be returned
  dataFilter: empty
}

function mimeToDataType (mime) {
  if (mime) mime = mime.split(';', 2)[0]
  return (mime && (mime === htmlType
    ? 'html'
    : mime === jsonType
      ? 'json'
      : scriptTypeRE.test(mime)
        ? 'script'
        : xmlTypeRE.test(mime) && 'xml')) || 'text'
}

function appendQuery (url, query) {
  if (query === '') return url
  return (url + '&' + query).replace(/[&?]{1,2}/, '?')
}

// serialize payload and append it to the URL for GET requests
function serializeData (options) {
  if (options.processData && options.data && $.type(options.data) !== 'string') {
    options.data = $.param(options.data, options.traditional)
  }
  if (options.data && (!options.type || options.type.toUpperCase() === 'GET' || options.dataType === 'jsonp')) {
    options.url = appendQuery(options.url, options.data)
    options.data = undefined
  }
}

$.ajax = function (options) {
  const settings = $.extend({}, options || {})
  const deferred = $.Deferred && $.Deferred()
  let urlAnchor
  let hashIndex
  for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]

  if (!settings.crossDomain) {
    urlAnchor = document.createElement('a')
    urlAnchor.href = settings.url
    // cleans up URL for .href (IE only), see https://github.com/madrobby/zepto/pull/1049
    settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host)
  }

  if (!settings.url) settings.url = window.location.toString()
  if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex)
  serializeData(settings)

  let dataType = settings.dataType
  const hasPlaceholder = /\?.+=\?/.test(settings.url)
  if (hasPlaceholder) dataType = 'jsonp'

  if (settings.cache === false || (
    (!options || options.cache !== true) &&
    (dataType === 'script' || dataType === 'jsonp')
  )) { settings.url = appendQuery(settings.url, '_=' + Date.now()) }

  let mime = settings.accepts[dataType]
  const headers = {}
  const setHeader = function (name, value) {
    headers[name.toLowerCase()] = [name, value]
  }
  const protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol
  const xhr = settings.xhr()
  const nativeSetHeader = xhr.setRequestHeader
  let abortTimeout

  if (deferred) deferred.promise(xhr)

  if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest')
  setHeader('Accept', mime || '*/*')
  mime = settings.mimeType || mime
  if (mime) {
    if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
    xhr.overrideMimeType && xhr.overrideMimeType(mime)
  }
  if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() !== 'GET')) {
    setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded')
  }

  if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name])
  xhr.setRequestHeader = setHeader

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      xhr.onreadystatechange = empty
      clearTimeout(abortTimeout)
      let result
      let error = false
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || (xhr.status === 0 && protocol === 'file:')) {
        dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))

        if (xhr.responseType === 'arraybuffer' || xhr.responseType === 'blob') { result = xhr.response } else {
          result = xhr.responseText

          try {
            // http://perfectionkills.com/global-eval-what-are-the-options/
            // sanitize response accordingly if data filter callback provided
            result = ajaxDataFilter(result, dataType, settings)
            // eslint-disable-next-line no-eval
            if (dataType === 'script') (1, eval)(result)
            else if (dataType === 'xml') result = xhr.responseXML
            else if (dataType === 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
          } catch (e) {
            error = e
          }

          if (error) return ajaxError(error, 'parsererror', xhr, settings, deferred)
        }

        ajaxSuccess(result, xhr, settings, deferred)
      } else {
        ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
      }
    }
  }

  const async = 'async' in settings ? settings.async : true
  xhr.open(settings.type, settings.url, async, settings.username, settings.password)

  if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name]

  for (name in headers) nativeSetHeader.apply(xhr, headers[name])

  if (settings.timeout > 0) {
    abortTimeout = setTimeout(function () {
      xhr.onreadystatechange = empty
      xhr.abort()
      ajaxError(null, 'timeout', xhr, settings, deferred)
    }, settings.timeout)
  }

  // avoid sending empty string (#319)
  xhr.send(settings.data ? settings.data : null)
  return xhr
}

// handle optional data/success arguments
function parseArguments (url, data, success, dataType) {
  if ($.isFunction(data)) {
    dataType = success
    success = data
    data = undefined
  }
  if (!$.isFunction(success)) {
    dataType = success
    success = undefined
  }
  return {
    url: url,
    data: data,
    success: success,
    dataType: dataType
  }
}

$.get = function (/* url, data, success, dataType */) {
  return $.ajax(parseArguments.apply(null, arguments))
}

$.post = function (/* url, data, success, dataType */) {
  const options = parseArguments.apply(null, arguments)
  options.type = 'POST'
  return $.ajax(options)
}

$.getJSON = function (/* url, data, success */) {
  const options = parseArguments.apply(null, arguments)
  options.dataType = 'json'
  return $.ajax(options)
}

const escape = encodeURIComponent

function serialize (params, obj, traditional, scope) {
  let type
  const array = $.isArray(obj)
  const hash = $.isPlainObject(obj)
  $.each(obj, function (key, value) {
    type = $.type(value)
    if (scope) {
      key = traditional
        ? scope
        : scope + '[' + (hash || type === 'object' || type === 'array' ? key : '') + ']'
    }
    // handle data in serializeArray() format
    if (!scope && array) params.add(value.name, value.value)
    // recurse into nested objects
    else if (type === 'array' || (!traditional && type === 'object')) { serialize(params, value, traditional, key) } else params.add(key, value)
  })
}

$.param = function (obj, traditional) {
  const params = []
  params.add = function (key, value) {
    if ($.isFunction(value)) value = value()
    if (value === null) value = ''
    this.push(escape(key) + '=' + escape(value))
  }
  serialize(params, obj, traditional)
  return params.join('&').replace(/%20/g, '+')
}

$.each = function (elements, callback) {
  let i, key
  if (likeArray(elements)) {
    for (i = 0; i < elements.length; i++) { if (callback.call(elements[i], i, elements[i]) === false) return elements }
  } else {
    for (key in elements) { if (callback.call(elements[key], key, elements[key]) === false) return elements }
  }

  return elements
}

function extend (target, source, deep) {
  for (key in source) {
    if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
      if (isPlainObject(source[key]) && !isPlainObject(target[key])) { target[key] = {} }
      if (isArray(source[key]) && !isArray(target[key])) { target[key] = [] }
      extend(target[key], source[key], deep)
    } else if (source[key] !== undefined) target[key] = source[key]
  }
}

// Copy all but undefined properties from one or more
// objects to the `target` object.
$.extend = function (target) {
  let deep
  const args = [].slice.call(arguments, 1)
  if (typeof target === 'boolean') {
    deep = target
    target = args.shift()
  }
  args.forEach(function (arg) {
    extend(target, arg, deep)
  })
  return target
}

$.each('Boolean Number String Function Array Date RegExp Object Error'.split(' '), function (i, name) {
  class2type['[object ' + name + ']'] = name.toLowerCase()
})

/**
 * @description 发送ajax请求,兼容IE9及以下版本,IE9及一下版本用form模拟(代码来源于主站base.core.dev.js)
 * @param handleOption
 * @param option
 * @private
 */
export function adPostJSONLite (handleOption) {
  const browser = getBrowser()
  if (browser.msie && parseInt(browser.version) <= 9) {
    adPostJSON(handleOption)
  } else {
    $.ajax(handleOption)
  }
}

/**
 * @description IE9及以下浏览器,使用form POST数据(代码来源于主站base.core.dev.js)
 * @param options
 * @private
 */
function adPostJSON (options) {
  const locHref = window.location.href || ''
  const setDocMain = function (interfacedomain, ndomain) {
    // window.location.href;
    window.Bilibili && (window.Bilibili.interface_domain = interfacedomain)
    document.domain = ndomain
  }
  if (locHref.match(/\.bilibili\.tv/)) {
    setDocMain('interface.bilibili.tv', 'bilibili.tv')
  } else if (locHref.match(/\.bilibili\.com/)) {
    setDocMain('interface.bilibili.com', 'bilibili.com')
  } else {
    setDocMain('interface.bilibili.cn', 'bilibili.cn')
  }

  const callback = options.callback || '_jsonpCallback_' + (new Date().getTime())

  const objIFrame = document.createElement('iframe')
  objIFrame.name = 'frm' + callback
  objIFrame.id = 'frm' + callback
  objIFrame.style = 'visibility:hidden; width:1px; height:1px'
  document.body.appendChild(objIFrame)

  const objForm = document.createElement('form')

  objForm.action = options.url
  objForm.enctype = 'application/x-www-form-urlencoded'
  objForm.method = 'POST'
  objForm.target = 'frm' + callback
  document.body.appendChild(objForm)

  if (typeof (options.data) === 'undefined') options.data = {}
  if (typeof (options.data) === 'string') {
    try {
      options.data = JSON.parse(options.data)
    } catch (e) {
    }
  }

  options.data[options.jsonpCallback || 'callback'] = callback
  for (const i in options.data) {
    const input = document.createElement('input')
    input.type = 'hidden'
    input.name = i
    input.value = options.data[i]
    objForm.appendChild(input)
  }
  window[callback] = function (data) {
    if (typeof options.success === 'function') {
      options.success(data)
    }
    if (typeof options.complete === 'function') {
      options.complete(data)
    }
    objIFrame.remove()
    objForm.remove()
  }
  objForm.submit()
}

export default {
  $,
  adPostJSONLite,
  adPostJSON
}

index.js 文件:

let browserIns = null
const userAgent = window.navigator.userAgent
const toString = Object.prototype.toString

function typeOf (obj) {
  const maps = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  }
  return maps[toString.call(obj)]
}

function uaMatch (ua) {
  const rwebkit = /(webkit)[\\/]([\w.]+)/
  const ropera = /(opera)(?:.*version)?[\\/]([\w.]+)/
  const rmsie = /(msie) ([\w.]+)/
  const rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/
  // Matches dashed string for camelizing
  ua = (ua || window.navigator.userAgent).toLowerCase()
  const match = rwebkit.exec(ua) ||
    ropera.exec(ua) ||
    rmsie.exec(ua) ||
    (ua.indexOf('compatible') < 0 && rmozilla.exec(ua)) ||
    []
  return {
    browser: match[1] || '',
    version: match[2] || '0'
  }
}

/**
 //  * @description 检测函数
 * @param obj
 * @returns {boolean}
 */
export function isFunction (obj) {
  return typeOf(obj) === 'function'
}

/**
 //  * @description 检测数组
 * @param obj
 * @returns {boolean}
 */
export function isArray (obj) {
  return typeOf(obj) === 'array'
}

/**
 //  * @description 检测数字
 * @param obj
 * @returns {boolean}
 */
export function isNumber (obj) {
  return typeOf(obj) === 'number'
}

export function isString (obj) {
  return typeOf(obj) === 'string'
}

/**
 //  * @description 检测
 * @param obj
 * @returns {boolean}
 */
export function isPlainObject (obj) {
  if (typeof obj !== 'object' || obj === null) return false
  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  return Object.getPrototypeOf(obj) === proto
}

/**
 //  * @description 扩展数据
 * @returns {any | Object | {} | extend}
 */
export function extend () {
  let options; let name; let src; let copy; let copyIsArray; let clone
  let target = arguments[0] || {}
  let i = 1
  const length = arguments.length
  let deep = false
  if (typeof target === 'boolean') {
    deep = target
    target = arguments[1] || {}
    i = 2
  }
  if (typeof target !== 'object' && !this.isFunction(target)) {
    target = {}
  }
  if (length === i) {
    target = this
    --i
  }
  for (; i < length; i++) {
    if ((options = arguments[i]) != null) {
      for (name in options) {
        src = target[name]
        copy = options[name]
        if (target === copy) {
          continue
        }
        if (deep && copy && (this.isPlainObject(copy) || (copyIsArray = this.isArray(copy)))) {
          if (copyIsArray) {
            copyIsArray = false
            clone = src && this.isArray(src) ? src : []
          } else {
            clone = src && this.isPlainObject(src) ? src : {}
          }
          target[name] = this.extend(deep, clone, copy)
        } else if (copy !== undefined) {
          target[name] = copy
        }
      }
    }
  }
  return target
}

export function getNowTs () {
  return (new Date()).getTime()
}

export function getBrowser () {
  if (!browserIns) {
    browserIns = uaMatch(userAgent)
  }
  return browserIns
}

/**
 * @description
 * @param obj
 * @returns {string}
 */
export function stringifyQuery (obj) {
  let res = null
  if (obj) {
    res = Object.keys(obj).map(function (key) {
      const encode = window.encodeURIComponent
      const val = obj[key]
      if (val === undefined) {
        return ''
      }
      if (val === null) {
        return encode(key)
      }
      if (Array.isArray(val)) {
        const result = []
        val.slice().forEach(function (val2) {
          if (val2 === undefined) {
            return
          }
          if (val2 === null) {
            result.push(encode(key))
          } else {
            result.push(encode(key) + '=' + encode(val2))
          }
        })
        return result.join('&')
      }
      return encode(key) + '=' + encode(val)
    }).filter(function (x) { return x.length > 0 }).join('&')
  }
  return (res || '')
}

/**
 * @description 解析URL参数
 * @param url
 * @returns {string}
 */
export function parseQuery (url) {
  url = url || window.location.href
  const querys = url.split('?')
  const params = querys[1] ? querys[1].split('&') : []
  const obj = {}
  for (let i = 0; i < params.length; i++) {
    const param = params[i] || ''
    const index = param.indexOf('=')
    obj[param.substr(0, index)] = param.substr(index + 1)
    // var param = params[i].split("=");
    // obj[param[0]] = param[1];
  }
  return obj
}

export default {
  isFunction,
  isArray,
  isNumber,
  isPlainObject,
  extend,
  getNowTs,
  getBrowser,
  stringifyQuery,
  parseQuery
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值