1.目标
学习axios源码中实用的工具函数
2.源码分析
2.1 isArray 判断数组
var toString = Object.prototype.toString;
// 通过Object.prototype.toString来获取每个对象的类型
function isArray(val) {
return toString.call(val) === '[object Array]';
}
2.2 isUndefined 判断Undefined
// 如果要判断是否是null,使用Object.prototype.toString
function isUndefined(val) {
return typeof val === 'undefined';
}
2.3 isBuffer 判断Buffer
// a.判断val不是null和undefined
// b.因为Buffer本身是一个类,所以要判断它是否存在构造函数
// c.通过自身的isBuffer判断
// 在node中使用,Buffer.from('a')
function isBuffer(val) {
return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
&& typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
}
2.4 isArrayBuffer 判断ArrayBuffer
var toString = Object.prototype.toString;
function isArrayBuffer(val) {
return toString.call(val) === '[object ArrayBuffer]';
}
2.5 isFormData 判断FormData
// 先判断typeof FormData !== 'undefined',猜测原因是为了兼容IE,相关链接:https://*.com/questions/19486597/formdata-is-undefined-in-ie-only
function isFormData(val) {
return (typeof FormData !== 'undefined') && (val instanceof FormData);
}
2.6 isArrayBufferView判断ArrayBufferView
function isArrayBufferView(val) {
var result;
// 此处主要是为了兼容IE9及以下
if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
result = ArrayBuffer.isView(val);
} else {
result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);
}
return result;
}
2.7 isString判断String
function isString(val) {
return typeof val === 'string';
}
2.8 isNumber判断Number
function isNumber(val) {
return typeof val === 'number';
}
2.9 isObject判断Object
// 看了下测试用例,数组也算对象,如果真实只检测object的话,还建议使用 Object.prototype.toString
function isObject(val) {
return val !== null && typeof val === 'object';
}
2.10 isPlainObject判断PlainObject
// plainObject翻译为中文即为纯对象,所谓的纯对象,就是该对象是通过{}或new Object()创建的。
// 判断是否为“纯对象”,是为了和其他对象区分开比如说null、数组以及宿主对象(所有的DOM和BOM都是数组对象)等。
// 其实就是判断目标对象的原型是不是`null` 或 `Object.prototype
// jQuery中的isPlainObject() 函数用于判断指定参数是否是一个纯粹的对象,返回值为Boolean类型。
//“纯粹的对象”,就是通过 { }、new Object()、Object.create(null) 创建的对象。
//这个方法的作用是为了跟其他的 JavaScript对象如 null,数组,宿主对象(documents),DOM 等作区分,因为这些用 typeof 都会返回object。
var toString = Object.prototype.toString;
function isPlainObject(val) {
if (toString.call(val) !== '[object Object]') {
return false;
}
// Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)
// Object.getPrototypeOf:实际上其实是获取当前对象的__proto__
// Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
var prototype = Object.getPrototypeOf(val);
// Object.getPrototypeOf({}.__proto__)
// Object.create(null)
return prototype === null || prototype === Object.prototype;
}
// 测试用例
// expect(utils.isPlainObject({})).toEqual(true);
// expect(utils.isPlainObject([])).toEqual(false);
// expect(utils.isPlainObject(null)).toEqual(false);
// expect(utils.isPlainObject(Object.create({}))).toEqual(false);
// let b = {}
// expect(utils.isPlainObject(b).toEqual(true);
2.11 isDate判断Date
function isDate(val) {
return toString.call(val) === '[object Date]';
}
2.12 isFile判断File
function isFile(val) {
return toString.call(val) === '[object File]';
}
2.13 isBlob判断Blob
function isBlob(val) {
return toString.call(val) === '[object Blob]';
}
2.14 isFunction判断Function
function isFunction(val) {
return toString.call(val) === '[object Function]';
}
2.15 isStream判断Stream
function isStream(val) {
// pipe是stream的一个方法,fs.createReadStream源码会使用到,
return isObject(val) && isFunction(val.pipe);
}
2.16 isURLSearchParams判断URLSearchParams
// URLSearchParams 接口定义了一些实用的方法来处理 URL 的查询字符串
// var paramsString = "q=URLUtils.searchParams&topic=api"
//var searchParams = new URLSearchParams(paramsString);
//searchParams.has("topic") === true; // true
//searchParams.get("topic") === "api"; // true
//searchParams.getAll("topic"); // ["api"]
//searchParams.get("foo") === null; // true
//searchParams.append("topic", "webdev");
//searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
//searchParams.set("topic", "More webdev");
//searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
//searchParams.delete("topic");
//searchParams.toString(); // "q=URLUtils.searchParams"
function isURLSearchParams(val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;
}
2.17 trim方法
function trim(str) {
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}
2.18 判断是否事标准浏览器
/**
* 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
* nativescript介绍 https://zhuanlan.zhihu.com/p/29452684
* 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'
);
}
2.19 重写forEach方法
/**
* 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.
*
// 在迭代数组和对象的每一项时,执行传入的自定义方法
// 如果obj是数组,callback执行参数为value, index, and complete array
// 如果obj是对象,callback执行参数为value, key, and complete object
// 此覆盖方法貌似只是比forEach能多处理下对象类型,没太想到其他用途
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);
}
}
}
}
2.20 merge方法
/**
* 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.
*
* 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);
} 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;
}
2.21 extend方法
/**
* Extends object a by mutably adding to it the properties of object b.
*/
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;
}
2.22 stripBOM方法
/**
* Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
*/
// 确定json文件是UTF-8 无BOM编码的的。如果有BOM,会在读取第一行的时候出现乱码。
// 所谓 BOM,全称是Byte Order Mark,它是一个Unicode字符,通常出现在文本的开头,用来标识字节序。UTF-8主要的优点是可以兼容ASCII,但如果使用BOM的话,这个好处就荡然无存了。
// 删除UTF-8编码中BOM
function stripBOM(content) {
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
return content;
}
3.知识扩展
3.1 判断数据类型的几种方式
// 数组最好使用typeof判断,这样定义()=> {}的方法,会被Object.prototype.toString识别为Object
1.typeof:仅能判断基础数据类型,如:“number”,”string”,”undefined”,”boolean”,“symbol” (ES6新增)六种,”object”类型建议使用Object.prototype.toString判断
2.instanceof 用于判断一个变量是否某个对象的实例,如:Object Array Function
// 所有的均可使用下边方法判断
3.Object.prototype.toString:对于数组、null、对象来说,其关系错综复杂,使用 typeof 都会统一返回 “object” 字符串。所以用此方法来判断
3.2 Buffer相关知识
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。
3.3 NodeJS中ArrayBuffer与Buffer的异同
- ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区,是一个字节数组,可读但不可直接写。
- Buffer 是 Node.JS 中用于操作 ArrayBuffer 的视图,是 TypedArray的一种。