本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
源码地址:github1s.com/axios/axios…
helpers/bind
'use strict';
// 改变this指向
module.exports = function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
};
复制代码
axios/utils
'use strict';
var bind = require('./helpers/bind');
// utils is a library of generic helper functions non-specific to axios
// utils 并不是一个固定用于 axios 的函数助手库
var toString = Object.prototype.toString; // 定义toString ,不然太长了。
/**
* Determine if a value is an Array
* 确定val值为数组
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
function isArray(val) {
return Array.isArray(val);
}
/**
* Determine if a value is undefined
* 确定value值为undefined
* @param {Object} val The value to test
* @returns {boolean} True if the value is undefined, otherwise false
*/
function isUndefined(val) {
return typeof val === 'undefined';
}
/**
* Determine if a value is a Buffer
* 确定value值为buffer
* @param {Object} val The value to test
* @returns {boolean} True if value is a Buffer, otherwise false
*/
function isBuffer(val) {
// val不是null 和 undefined,val.constructor不是null,不是undifined
// val.constructor.isBuffer是function的话,就调用该函数进行判断。
return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
&& typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
}
/**
* Determine if a value is an ArrayBuffer
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an ArrayBuffer, otherwise false
*/
function isArrayBuffer(val) {
// es6类数组对象,用于在浏览器端操作二进制数据,fetch ajax都有用,所以都可以用js判断对象的方式来判断
return toString.call(val) === '[object ArrayBuffer]';
}
/**
* Determine if a value is a view on an ArrayBuffer
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
*/
function isArrayBufferView(val) {
var result;
if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
result = ArrayBuffer.isView(val); // 判断是否为TypedArray 或 DataView实例
} else {
result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));
}
return result;
}
/**
* Determine if a value is a String
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a String, otherwise false
*/
function isString(val) {
return typeof val === 'string';
}
/**
* Determine if a value is a Number
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Number, otherwise false
*/
function isNumber(val) {
return typeof val === 'number';
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
function isObject(val) {
return val !== null && typeof val === 'object'; // 判断对象
}
/**
* Determine if a value is a plain Object
*
* @param {Object} val The value to test
* @return {boolean} True if value is a plain Object, otherwise false
*/
function isPlainObject(val) { // 是否是普通对象,只有普通对象才处理
if (toString.call(val) !== '[object Object]') {
return false;
}
var prototype = Object.getPrototypeOf(val);
return prototype === null || prototype === Object.prototype;
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
function isDate(val) {
return toString.call(val) === '[object Date]';
}
/**
* Determine if a value is a File
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a File, otherwise false
*/
function isFile(val) { // 表单文件会用到这个
return toString.call(val) === '[object File]';
}
/**
* Determine if a value is a Blob
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Blob, otherwise false
*/
function isBlob(val) { // 表单下载会用到这个
return toString.call(val) === '[object Blob]';
}
/**
* Determine if a value is a Function
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Function, otherwise false
*/
function isFunction(val) {
return toString.call(val) === '[object Function]';
}
/**
* Determine if a value is a Stream
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Stream, otherwise false
*/
function isStream(val) { // 流文件
return isObject(val) && isFunction(val.pipe);
}
/**
* Determine if a value is a FormData
*
* @param {Object} thing The value to test
* @returns {boolean} True if value is an FormData, otherwise 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)
);
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
function isURLSearchParams(val) { // 判断是不是url搜索参数 ?A=1&B=2
return toString.call(val) === '[object URLSearchParams]';
}
/**
* Trim excess whitespace off the beginning and end of a string
*
* @param {String} str The String to trim
* @returns {String} The String freed of excess whitespace
*/
function trim(str) { // 去掉字符串两边的空格
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}
/**
* Determine if we're running in a standard browser environment
*
* This allows axios to run in a web worker, and react-native.
* Both environments support XMLHttpRequest, but not fully standard globals.
*
* web workers:
* typeof window -> undefined
* typeof document -> undefined
*
* react-native:
* navigator.product -> 'ReactNative'
* nativescript
* 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'
);
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
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);
}
}
}
}
/**接受的参数,每一个都是对象,然后不可变的合并每一个,并返回
* Accepts varargs expecting each argument to be an object, then
* immutably merges the properties of each object and returns result.
*
* When multiple objects contain the same key the later object in
* the arguments list will take precedence. // 当多个对象有相同key 优先考虑后面的
*
* Example:
*
* ```js
* var result = merge({foo: 123}, {foo: 456});
* console.log(result.foo); // outputs 456
* ```
*
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
function merge(/* obj1, obj2, obj3, ... */) {
var result = {};
function assignValue(val, key) {
if (isPlainObject(result[key]) && isPlainObject(val)) {
result[key] = merge(result[key], val); // 处理 {a: {c: 1}}, {a: {d: 2}} 的情况,也就是嵌套对象merge需要不断的递归处理
} else if (isPlainObject(val)) {
result[key] = merge({}, val); // 处理 {a: {c : 1}}, {b: 1}, 只有一个是嵌套的内容
} else if (isArray(val)) {
result[key] = val.slice(); // 处理是数组的情况,进行浅拷贝
} else {
result[key] = val; // 其余情况,直接赋值
}
}
for (var i = 0, l = arguments.length; i < l; i++) { // n^2
forEach(arguments[i], assignValue); // 刚刚写的foreach,可以循环对象
}
return result;
}
/**
* Extends object a by mutably adding to it the properties of object b.
*
* @param {Object} a The object to be extended
* @param {Object} b The object to copy properties from
* @param {Object} thisArg The object to bind function to
* @return {Object} The resulting value of object a
*/
function extend(a, b, thisArg) { // 拓展对象a
forEach(b, function assignValue(val, key) {
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg); // 如果是函数,保证函数里面的this不变
} else {
a[key] = val;
}
});
return a;
}
/**
* Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
*
* @param {string} content with BOM
* @return {string} content value without BOM
*/
function stripBOM(content) {
// 文件的第一项编码是 0xFEFF ,就去掉第一个编码,取后面的
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
return content;
}
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
};
复制代码
总结
1. Buffer
node
菜鸟教程:www.runoob.com/nodejs/node…
js中当时还没有操作二进制的容器,但是tcp都是二进制传输,所以buffer应运而生。
2. ArrayBuffer
es6
菜鸟教程:www.runoob.com/w3cnote/es6…
阮一峰es6:es6.ruanyifeng.com/#docs/array…
一般用于WebGL中。操作二进制数据
3. isPlainObject
lodash的解释:lodash.think2011.net/isPlainObje…
检查一个对象是是否是普通对象,也就说不是new的functiton对象,也不是数组。
function Foo() {
this.a = 1;
}
_.isPlainObject(new Foo);
// => false
_.isPlainObject([1, 2, 3]);
// => false
_.isPlainObject({ 'x': 0, 'y': 0 });
// => true
_.isPlainObject(Object.create(null));
// => true
复制代码
4. stripBOM
去掉Bom头,一般在window中的utf-8编码记事本中自动添加,几个隐藏的编码。在记事本中不显示,在网页及其他文件中可能会显示,所以需要去掉。