源码阅读系列2:axios 工具函数

93 阅读5分钟

参加若川源码共读活动

源码地址:github.com/axios/axios

1.什么是Axios

Axios 是一个基于 Promise 的 HTTP 客户端,拥有以下特性:

  • 支持 Promise API;
  • 能够拦截请求和响应;
  • 能够转换请求和响应数据;
  • 客户端支持防御 CSRF 攻击;
  • 同时支持浏览器和 Node.js 环境;
  • 能够取消请求及自动转换 JSON 数据。

2.工具函数

下面所说的工具函数,都是位于utils.js中的

var toString = Object.prototype.toString;
/**
* 确定值是否为数组
* @param {Object} val 要测试的值
* @returns {boolean} 如果值是数组,则为 true,否则为 false
*/
function isArray(val) {
  return Array.isArray(val);
}
/**\
* 确定值是否未定义\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值未定义,则为 true,否则为 false\
*/
function isUndefined(val) {
  return typeof val === 'undefined';
}
/**\
* 确定值是否为缓冲区\
*\
* @param {Object} val 要测试的值\
* @returns {布尔} 如果值是缓冲区,则为 true,否则为 false\
*/
function isBuffer(val) {
  return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
    && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
}
/**\
* 确定某个值是否为阵列缓冲区\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} True,如果值是 ArrayBuffer,则为 false\
*/
function isArrayBuffer(val) {
  return toString.call(val) === '[object ArrayBuffer]';
}
/**\
* 确定值是否为阵列缓冲区上的视图\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} True,如果值是 ArrayBuffer 上的视图,否则为 false\
*/
function isArrayBufferView(val) {
  var result;
  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
    result = ArrayBuffer.isView(val);
  } else {
    result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));
  }
  return result;
}
/**\
* 确定值是否为字符串\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值是字符串,则为 true,否则为 false\
*/
function isString(val) {
  return typeof val === 'string';
}
/**\
* 确定值是否为数字\
*\
* @param {Object} val 要测试的值\
* @returns {布尔} 如果值为数字,则为 true,否则为 false\
*/
function isNumber(val) {
  return typeof val === 'number';
}
/**\
* 确定值是否为对象\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值是对象,则为 true,否则为 false\
*/
function isObject(val) {
  return val !== null && typeof val === 'object';
}
/**\
* 确定值是否为普通对象\
*\
* @param {Object} val 要测试的值\
* @return {boolean} True,如果值是普通对象,否则为 false\
*/
function isPlainObject(val) {
  if (toString.call(val) !== '[object Object]') {
    return false;
  }
  var prototype = Object.getPrototypeOf(val);
  return prototype === null || prototype === Object.prototype;
}
/**\
* 确定值是否为日期\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值是 Date,则为 true,否则为 false\
*/
function isDate(val) {
  return toString.call(val) === '[object Date]';
}
/**\
* 确定值是否为文件\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值是文件,则为 true,否则为 false\
*/
function isFile(val) {
  return toString.call(val) === '[object File]';
}
/**\
* 确定值是否为 Blob\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} True,如果值是 Blob,则为 false\
*/
function isBlob(val) {
  return toString.call(val) === '[object Blob]';
}
/**\
* 确定值是否为函数\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值是函数,则为 true,否则为 false\
*/
function isFunction(val) {
  return toString.call(val) === '[object Function]';
}
/**\
* 确定某个值是否为流\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} 如果值是流,则为 true,否则为 false\
*/
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);
}
/**\
* 确定值是否为表单数据\
*\
* @param {对象} 事物 要测试的值\
* @returns {boolean} True,如果值是 FormData,则为 false\
*/
function isFormData(thing) {
  var pattern = '[object FormData]';
  return thing && (
    (typeof FormData === 'function' && thing instanceof FormData) ||
    toString.call(thing) === pattern ||
    (isFunction(thing.toString) && thing.toString() === pattern)
  );
}
/**\
* 确定某个值是否为 URLSearchParams 对象\
*\
* @param {Object} val 要测试的值\
* @returns {boolean} True,如果值是 URLSearchParams 对象,否则为 false\
*/
function isURLSearchParams(val) {
  return toString.call(val) === '[object URLSearchParams]';
}
/**\
* 修剪字符串开头和结尾多余的空格\
*\
* @param {String} str 要修剪的字符串\
* @returns {String} 释放多余空格的字符串\
*/
function trim(str) {
  return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}
/**\
* 确定我们是否在标准浏览器环境中运行\
*\
* 这允许 axios 在 Web worker 中运行,并进行本机反应。\
* 这两种环境都支持 XMLHttpRequest,但不是完全标准的全局环境。\
*\
* 网络工作者:\
* 窗口类型 -未定义>\
* 文件类型 -未定义>
* 反应原生:\
* navigator.product -> 'ReactNative'\
* 本地脚本\
* navigator.product -> 'NativeScript' or 'NS'\
*/
function isStandardBrowserEnv() {
  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
                                           navigator.product === 'NativeScript' ||
                                           navigator.product === 'NS')) {
    return false;
  }
  return (
    typeof window !== 'undefined' &&
    typeof document !== 'undefined'
  );
}
/**\
* 循环访问为每个项调用函数的数组或对象。\
*\
* 如果'obj'是数组回调将称为传递\
* 每个项的值、索引和完整数组。\
*\
* 如果'obj'是一个对象回调将被称为传递\
* 每个属性的值、键和完整对象。
* @param {对象|Array} obj 要迭代的对象\
* @param {Function} fn 要为每个项调用的回调\
*/
function forEach(obj, fn) {
  // Don't bother if no value provided
  if (obj === null || typeof obj === 'undefined') {
    return;
  }

  // Force an array if not already something iterable
  if (typeof obj !== 'object') {
    /*eslint no-param-reassign:0*/
    obj = [obj];
  }

  if (isArray(obj)) {
    // Iterate over array values
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    // Iterate over object keys
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}
/**\
* 接受 varargs 期望每个参数都是一个对象,然后\
* 不可变地合并每个对象的属性并返回结果。\
*\
* 当多个对象包含相同的键时,后面的对象\
* 参数列表将优先。\
*\
* 例:\
*\
* ""js\
* var result = merge({foo: 123}, {foo: 456});\
* console.log(result.foo);输出 456\
* ```\
*\
* @param {Object} obj1 要合并的对象\
* @returns {对象} 所有合并属性的结果\
*/
function merge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  function assignValue(val, key) {
    if (isPlainObject(result[key]) && isPlainObject(val)) {
      result[key] = merge(result[key], val);
    } else if (isPlainObject(val)) {
      result[key] = merge({}, val);
    } else if (isArray(val)) {
      result[key] = val.slice();
    } else {
      result[key] = val;
    }
  }

  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);
  }
  return result;
}
/**\
* 通过向对象 a 添加对象 b 的属性来扩展对象 a。\
*\
* @param {Object} a 要扩展的对象\
* @param {对象} b 要从中复制属性的对象\
* @param {Object} thisArg 要将函数绑定到的对象\
* @return {对象} 对象 a 的结果值\
*/
function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}