Axios的工具函数分析

193 阅读3分钟

1. 准备工作

  • 克隆项目 => 跳转到项目路径
git clone git@github.com:axios/axios.git 

cd axios
  • 打开vscode,查看 utils 文件

  • 源码如下
var bind = require('./helpers/bind');

var toString = Object.prototype.toString;

// 将[object String]: 'string'
var kindOf = (function(cache) { // cache:空对象
  return function(thing) { // thing:要判断类型的数据
    var str = toString.call(thing); // 获取 thing 的类型
		
    // chache:{'[object type]':'type'}, 并返回type的小写形式
    return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); 
  };
})(Object.create(null));

function kindOfTest(type) {
  type = type.toLowerCase(); // 对类型做小写处理
  return function isKindOf(thing) {
    return kindOf(thing) === type; // 判断数据类型
  };
}

// 判断是否Date类型
var isDate = kindOfTest('Date');
// 用法:isDate({}) // => false
var isFile = kindOfTest('File');
var isBlob = kindOfTest('Blob');
var isFileList = kindOfTest('FileList');
var isURLSearchParams = kindOfTest('URLSearchParams');
var isArrayBuffer = kindOfTest('ArrayBuffer'); 
var isHTMLForm = kindOfTest('HTMLFormElement');

// 判断是否为Array
function isArray(val) {
  return Array.isArray(val);
}

// 判断是否为undefined
function isUndefined(val) {
  return typeof val === 'undefined';
}

// 判断是否为Buffer
function isBuffer(val) {
  return (
		val !== null // 不为null
		&& !isUndefined(val) // 不为undefined
		&& val.constructor !== null // val.constructor 不为null
		&& !isUndefined(val.constructor) // val.constructor 不为undefined
		// 有val.constructor.isBuffer 方法
    && typeof val.constructor.isBuffer === 'function' 
		&& val.constructor.isBuffer(val) // 等同于Buffer.isBuffer(val)为真
	)
}

// 判断是否是一种 ArrayBuffer 视图
function isArrayBufferView(val) {
  var result;
  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
    // ArrayBuffer.isView方法直接判断
    result = ArrayBuffer.isView(val);
  } else {
    // 判断val.buffer是否是ArrayBuffer
    result = (val) && (val.buffer) && (isArrayBuffer(val.buffer)); 
  }
  return result;
}

// 判断是否为 TypedArray
var isTypedArray = (function(TypedArray) {
  return function(thing) {
    return TypedArray && thing instanceof TypedArray;
  };
})(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array)); // Uint8Array的原型为TypedArray

// 判断是否为String
function isString(val) {
  return typeof val === 'string';
}

// 判断是否为Number
function isNumber(val) {
  return typeof val === 'number';
}

// 判断是否为Function
function isFunction(val) {
  return toString.call(val) === '[object Function]';
}

// 判断是否为Object
function isObject(val) {
  return val !== null && typeof val === 'object';
}

// 判断是否为纯对象
function isPlainObject(val) {
  if (kindOf(val) !== 'object') { // 是Object类型
    return false;
  }

  var prototype = Object.getPrototypeOf(val); // 获取val的原型;
	
  // val的原型值为null或者{}
  return prototype === null || prototype === Object.prototype; 
}

// 判断是否是Stream
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);
}

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)
  );
}

// 去掉首尾空格
function trim(str) {
  return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}

// 判断是否是标准浏览器
function isStandardBrowserEnv() {
  var product;
  if (typeof navigator !== 'undefined' && (
    (product = navigator.product) === 'ReactNative' ||
    product === 'NativeScript' ||
    product === 'NS')
  ) {
    return false;
  }

  return typeof window !== 'undefined' && typeof document !== 'undefined';
}

// 定义forEach方法
function forEach(obj, fn) {
  // obj不为空值
  if (obj === null || typeof obj === 'undefined') {
    return;
  }

  // 不为引用类型
  if (typeof obj !== 'object') {
    /*eslint no-param-reassign:0*/
    obj = [obj];
  }

  // Array类型
  if (isArray(obj)) {
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    for (var key in obj) { // 遍历对象的key
      if (Object.prototype.hasOwnProperty.call(obj, key)) { // 取obj自身属性
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

// 合并对象
function merge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  function assignValue(val, key) {
    if (isPlainObject(result[key]) && isPlainObject(val)) {
      // 当result和val都是是纯对象 且都存在key属性时
      result[key] = merge(result[key], val); 
    } else if (isPlainObject(val)) {
      result[key] = merge({}, val); // 当val是纯对象时
    } else if (isArray(val)) {
      result[key] = val.slice(); // 遍历对象的value为Array,进行slice处理
    } else {
      result[key] = val;
    }
  }

  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue); // 遍历obj1, obj2, obj3
  }
  return result;
}

// 定义bind方法
function bind(fn, thisArg) {
  return function wrap() {
    return fn.apply(thisArg, arguments);
  };
};

// 继承b的属性和方法
function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg); // a的方法中绑定this
    } else {
      a[key] = val;
    }
  });
  return a;
}

// 去除 BOM(用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的标记)
function stripBOM(content) {
  if (content.charCodeAt(0) === 0xFEFF) { // 检测第一个字符是否为 BOM
    content = content.slice(1); // 取 去掉第一个字符后的 数据
  }
  return content;
}

// 原型继承
function inherits(constructor, superConstructor, props, descriptors) {
  // constructor的原型指向superConstructor的原型
  constructor.prototype = Object.create(superConstructor.prototype, descriptors); 
	
  // constructor.prototype.constructor从superConstructor.prototype.constructor 切回constructor
  constructor.prototype.constructor = constructor; 
	
  // constructor.prototype添加属性
  props && Object.assign(constructor.prototype, props);
}

// 遍历对象及其原型链上的数据,并添加到destObj中
function toFlatObject(sourceObj, destObj, filter, propFilter) {
  var props;
  var i;
  var prop;
  var merged = {};

  destObj = destObj || {}; // 目标对象
  
  if (sourceObj == null) return destObj;

  do {
    props = Object.getOwnPropertyNames(sourceObj); // 获取sourceObj自身的属性
    i = props.length;
    while (i-- > 0) {
      prop = props[i];
      if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) { // propFilter为空或propFilter中过滤了prop
        destObj[prop] = sourceObj[prop]; // destObj添加prop
        merged[prop] = true; // 设置 merged[prop]
      }
    }
		
    // filter不为false时,sourceObj重新赋值为sourceObj.prototype
    // filter为false时 sourceObj重新赋值为false
    sourceObj = filter !== false && Object.getPrototypeOf(sourceObj);
  } while (sourceObj && (!filter || filter(sourceObj, destObj)) && sourceObj !== Object.prototype); // 满足sourceObj不为{},有值,且未被filter时,继续遍历, 此时sourceObj为原sourceObj的原型

  return destObj;
}

// 判断是否以searchString结尾
function endsWith(str, searchString, position) {
  str = String(str);
  if (position === undefined || position > str.length) {
    position = str.length;
  }
  position -= searchString.length;
  var lastIndex = str.indexOf(searchString, position);
  return lastIndex !== -1 && lastIndex === position;
}

// 转化成Array
function toArray(thing) {
  if (!thing) return null; // 为空值返回null
  if (isArray(thing)) return thing; // 为Array返回自身
  var i = thing.length;
  if (!isNumber(i)) return null; // thing.length 不为Number,返回null
  var arr = new Array(i); // 创建长度为thing.length的数组
  while (i-- > 0) {
    arr[i] = thing[i]; // 往数组中添加值
  }
  return arr;
}

// 遍历 entry
function forEachEntry(obj, fn) {
  var generator = obj && obj[Symbol.iterator];

  var iterator = generator.call(obj); // 生成迭代器

  var result;

  // 执行next,done为false时跳出循环
  while ((result = iterator.next()) && !result.done) {
    var pair = result.value;
    fn.call(obj, pair[0], pair[1]);
  }
}

// 匹配字符中所有满足条件的
function matchAll(regExp, str) { // XXX:regExp必须为全局匹配,否则会溢出
  var matches;
  var arr = [];

  while ((matches = regExp.exec(str)) !== null) {
    arr.push(matches);
  }

  return arr;
}

// 判断是否为自身的元素
var hasOwnProperty = (function resolver(_hasOwnProperty) {
  return function(obj, prop) {
    return _hasOwnProperty.call(obj, prop);
  };
})(Object.prototype.hasOwnProperty);

// 导出这些方法
module.exports = {
  isArray: isArray,
  isArrayBuffer: isArrayBuffer,
  isBuffer: isBuffer,
  isFormData: isFormData,
  isArrayBufferView: isArrayBufferView,
  isString: isString,
  isNumber: isNumber,
  isObject: isObject,
  isPlainObject: isPlainObject,
  isUndefined: isUndefined,
  isDate: isDate,
  isFile: isFile,
  isBlob: isBlob,
  isFunction: isFunction,
  isStream: isStream,
  isURLSearchParams: isURLSearchParams,
  isStandardBrowserEnv: isStandardBrowserEnv,
  forEach: forEach,
  merge: merge,
  extend: extend,
  trim: trim,
  stripBOM: stripBOM,
  inherits: inherits,
  toFlatObject: toFlatObject,
  kindOf: kindOf,
  kindOfTest: kindOfTest,
  endsWith: endsWith,
  toArray: toArray,
  isTypedArray: isTypedArray,
  isFileList: isFileList,
  forEachEntry: forEachEntry,
  matchAll: matchAll,
  isHTMLForm: isHTMLForm,
  hasOwnProperty: hasOwnProperty
};