【源码共读】axios的46个工具函数

news2024/11/20 6:24:59

所有工具函数

还是老样子,先看看axios的工具函数有哪些,先心里有个印象,然后再逐个分析。

直接拉到最下面,可以看到axios的工具函数都是统一导出的:

export default {isArray, // 判断是否是数组isArrayBuffer, // 判断是否是 ArrayBufferisBuffer, // 判断是否是 BufferisFormData, // 判断是否是 FormDataisArrayBufferView, // 判断是否是 ArrayBufferViewisString, // 判断是否是字符串isNumber, // 判断是否是数字isBoolean, // 判断是否是布尔值isObject, // 判断是否是对象isPlainObject, // 判断是否是纯对象isUndefined, // 判断是否是 undefinedisDate, // 判断是否是日期isFile, // 判断是否是文件isBlob, // 判断是否是 BlobisRegExp, // 判断是否是正则isFunction, // 判断是否是函数isStream, // 判断是否是 StreamisURLSearchParams, // 判断是否是 URLSearchParamsisTypedArray, // 判断是否是 TypedArrayisFileList, // 判断是否是 FileListforEach, // 遍历对象merge, // 合并对象extend, // 扩展对象trim, // 去除字符串两边的空格stripBOM, // 去除字符串的 BOMinherits, // 继承toFlatObject, // 将对象转换为扁平对象kindOf, // 获取对象的类型kindOfTest, // 判断对象的类型endsWith,// 判断字符串是否以指定的字符串结尾toArray, // 将类数组转换为数组forEachEntry, // 遍历对象的键值对matchAll, // 匹配所有的字符串isHTMLForm, // 判断是否是 HTMLFormhasOwnProperty, // 判断对象是否有指定的属性hasOwnProp: hasOwnProperty,// 判断对象是否有指定的属性reduceDescriptors, // 降低描述符freezeMethods, // 冻结方法toObjectSet, // 将数组或者字符串转换为类似`Set`的对象toCamelCase, // 将字符串转换为驼峰命名noop, // 空函数toFiniteNumber, // 将字符串转换为有限数字findKey, // 查找对象的键global: _global, // 全局对象isContextDefined, // 判断上下文是否定义toJSONObject // 将字符串转换为 JSON 对象
}; 
  • isArray:判断是否是数组
  • isArrayBuffer:判断是否是 ArrayBuffer
  • isBuffer:判断是否是 Buffer
  • isFormData:判断是否是 FormData
  • isArrayBufferView:判断是否是 ArrayBufferView
  • isString:判断是否是字符串
  • isNumber:判断是否是数字
  • isBoolean:判断是否是布尔值
  • isObject:判断是否是对象
  • isPlainObject:判断是否是纯对象
  • isUndefined:判断是否是 undefined
  • isDate:判断是否是日期
  • isFile:判断是否是文件
  • isBlob:判断是否是 Blob
  • isRegExp:判断是否是正则
  • isFunction:判断是否是函数
  • isStream:判断是否是 Stream
  • isURLSearchParams:判断是否是 URLSearchParams
  • isTypedArray:判断是否是 TypedArray
  • isFileList:判断是否是 FileList
  • forEach:遍历对象
  • merge:合并对象
  • extend:扩展对象
  • trim:去除字符串两边的空格
  • stripBOM:去除字符串的 BOM
  • inherits:继承
  • toFlatObject:将对象转换为扁平对象
  • kindOf:获取对象的类型
  • kindOfTest:判断对象的类型
  • endsWith,// 判断字符串是否以指定的字符串结尾
  • toArray:将类数组转换为数组
  • forEachEntry:遍历对象的键值对
  • matchAll:匹配所有的字符串
  • isHTMLForm:判断是否是 HTMLForm
  • hasOwnProperty:判断对象是否有指定的属性
  • hasOwnProp: hasOwnProperty, :判断对象是否有指定的属性
  • reduceDescriptors:降低描述符
  • freezeMethods:冻结方法
  • toObjectSet:将数组或者字符串转换为类似Set的对象
  • toCamelCase:将字符串转换为驼峰命名
  • noop:空函数
  • toFiniteNumber:将字符串转换为有限数字
  • findKey:查找对象的键
  • global: _global:全局对象
  • isContextDefined:判断上下文是否定义
  • toJSONObject:将字符串转换为 JSON 对象

一共有46个工具函数,比Vue2的工具函数多了很多,同时也发现有很多同名的工具函数,例如:

1.isArray
2.isRegExp
3.isFunction
4.isObject
5.isPlainObject
6.extend
7.noop

别看这么多,其中对于类型判断的函数只有少数几个需要关注,其他的都是使用同样的方式来实现的,如下:

  • isArrayBuffer
  • isBuffer
  • isString
  • isNumber
  • isUndefined
  • isDate
  • isFile
  • isBlob
  • isRegExp
  • isFunction
  • isStream
  • isURLSearchParams
  • isFileList
  • isHTMLForm

一共14个,这些函数都是通过kindOf来实现的,上面这些相同的会在源码阅读中移除。

正式阅读

这里的kindOfkindOfTest优先阅读,因为这两个函数是用来判断类型的,上面移除的14个函数都是通过这两个函数来实现的,所以这两个函数非常重要也非常经典。

kindOf

// utils is a library of generic helper functions non-specific to axios

const {toString} = Object.prototype;

const kindOf = (cache => thing => {const str = toString.call(thing);return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
})(Object.create(null)); 

kindOf函数用于获取对象的类型,比如kindOf({})返回的是object

它是一个立即执行函数,返回一个函数,这个函数接收一个参数,这个参数就是要获取类型的对象;

抛开立即执行函数不说,kindOf函数的核心代码只有两行:

const {toString} = Object.prototype;

const kindOf = (thing) => {const str = toString.call(thing);return str.slice(8, -1).toLowerCase();
}; 

这里的处理其实和Vue2中类型判断的处理是一样的,都是通过Object.prototype.toString来获取对象的类型;

kindOf函数的返回值是一个字符串,这个字符串就是对象的类型,不过这里使用了一个缓存,这样就不用每次都去执行str.slice(8, -1).toLowerCase()了;

kindOfTest

const kindOfTest = (type) => {type = type.toLowerCase();return (thing) => kindOf(thing) === type
} 

kindOfTest函数用于判断对象的类型,同样也是一个高阶函数;

它接收一个参数,这个参数就是要判断的类型,返回一个函数,这个函数接收一个参数,这个参数就是要判断的对象;

返回的函数通过函数柯里化的特性,将type参数固定下来,这样就可以通过kindOfTest('object')来判断对象的类型了;

也就是axios中的大多数类型判断都是通过kindOfTest函数来实现的;

isArrayBuffer

const {toString} = Object.prototype;

const kindOf = (cache => thing => {const str = toString.call(thing);return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
})(Object.create(null));

const kindOfTest = (type) => {type = type.toLowerCase();return (thing) => kindOf(thing) === type
}

/**
 * Determine if a value is an ArrayBuffer
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is an ArrayBuffer, otherwise false
 */
const isArrayBuffer = kindOfTest('ArrayBuffer'); 

这个就是使用kindOfTest函数来判断对象的类型,isArrayBuffer函数接收一个参数,这个参数就是要判断的对象,返回一个布尔值,表示这个对象是否是ArrayBuffer类型;

上面过滤掉的14个函数都是通过这种方式来实现的,这里就不一一列举了;

这是一个非常经典的高阶函数应用模式,函数柯里化的应用,可以看看:✨从柯里化讲起,一网打尽 JavaScript 重要的高阶函数

isArray

/**
 * Determine if a value is an Array
 *
 * @param {Object} val The value to test
 *
 * @returns {boolean} True if value is an Array, otherwise false
 */
const {isArray} = Array; 

直接使用解构赋值,将Array.isArray赋值给isArray,和Vue2的实现方式一样;

isBuffer

/**
 * Determine if a value is undefined
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if the value is undefined, otherwise false
 */
const isUndefined = typeOfTest('undefined');

/**
 * Determine if a value is a Function
 *
 * @param {*} val The value to test
 * @returns {boolean} True if value is a Function, otherwise false
 */
const isFunction = typeOfTest('function');

/**
 * Determine if a value is a Buffer
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a Buffer, otherwise false
 */
function isBuffer(val) {return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)&& isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val);
} 

isBuffer函数的实现依赖了isUndefinedisFunction函数,而isUndefinedisFunction函数的实现依赖了typeOfTest函数;

这个在上面的kindOfTest函数中已经介绍过了,所以这里就一口气了解三个函数,当然主角还是isBuffer函数;

isBuffer函数的实现是判断val是否是Buffer,这里的判断需要满足以下条件:

  • val不是null
  • val不是undefined
  • valconstructor不是null
  • valconstructor不是undefined
  • valconstructorisBuffer方法是一个函数
  • valconstructorisBuffer方法返回true

总体来说还是很复杂的,这里的主角是valconstructorisBuffer方法,这个方法是Buffer对象的静态方法;

参考:

  • Buffer.isBuffer()
  • Object.prototype.constructor

isFormData

/**
 * Determine if a value is a FormData
 *
 * @param {*} thing The value to test
 *
 * @returns {boolean} True if value is an FormData, otherwise false
 */
const isFormData = (thing) => {const pattern = '[object FormData]';return thing && ((typeof FormData === 'function' && thing instanceof FormData) ||toString.call(thing) === pattern ||(isFunction(thing.toString) && thing.toString() === pattern));
} 

isFormData函数的实现很简单,就是判断thing是否是FormData,这里的判断需要满足以下条件:

  • thing不是null
  • FormData是一个函数
  • thingFormData的实例
  • thing的类型是[object FormData]
  • thingtoString方法是一个函数
  • thingtoString方法返回[object FormData]
  • thingtoString方法返回[object FormData]

边界限定条件比较多,可以是多种情况,最主要的还是thingFormData的实例;

参考:

  • FormData

isArrayBufferView

/**
 * Determine if a value is a view on an ArrayBuffer
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
 */
function isArrayBufferView(val) {let result;if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {result = ArrayBuffer.isView(val);} else {result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));}return result;
} 

isArrayBufferView函数的实现依赖了isArrayBuffer函数,同时还使用了ArrayBuffer.isView方法;

参考:

  • ArrayBuffer.isView()

isString

/**
 * Determine if a value is a String
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a String, otherwise false
 */
const isString = typeOfTest('string'); 

isString函数的实现依赖了typeOfTest函数;

isNumber

/**
 * Determine if a value is a Number
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a Number, otherwise false
 */
const isNumber = typeOfTest('number'); 

isNumber函数的实现依赖了typeOfTest函数;

isBoolean

/**
 * Determine if a value is a Boolean
 *
 * @param {*} thing The value to test
 * @returns {boolean} True if value is a Boolean, otherwise false
 */
const isBoolean = thing => thing === true || thing === false; 

isBoolean函数的实现很简单,就是判断thing是否是true或者false

这里用===判断,不会出现隐式类型转换的问题;

isObject

/**
 * Determine if a value is an Object
 *
 * @param {*} thing The value to test
 *
 * @returns {boolean} True if value is an Object, otherwise false
 */
const isObject = (thing) => thing !== null && typeof thing === 'object'; 

isObject函数的实现和Vue2的实现一样,就是判断thing是否是null或者typeof thing是否是object

isPlainObject

const {getPrototypeOf} = Object;

/**
 * Determine if a value is a plain Object
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a plain Object, otherwise false
 */
const isPlainObject = (val) => {if (kindOf(val) !== 'object') {return false;}const prototype = getPrototypeOf(val);return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in val) && !(Symbol.iterator in val);
}; 

isPlainObject函数的实现依赖了kindOf函数,同时还使用了Object.getPrototypeOf方法;

这里的判断对比于Vue2的实现严格了很多,还加上了原型、Symbol.toStringTagSymbol.iterator的判断;

参考:

  • Object.getPrototypeOf()
  • Symbol.toStringTag
  • Symbol.iterator

isUndefined

/**
 * Determine if a value is undefined
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if the value is undefined, otherwise false
 */
const isUndefined = typeOfTest('undefined'); 

isUndefined函数的实现依赖了typeOfTest函数;

isStream

/**
 * Determine if a value is a Stream
 *
 * @param {*} val The value to test
 *
 * @returns {boolean} True if value is a Stream, otherwise false
 */
const isStream = (val) => isObject(val) && isFunction(val.pipe); 

isStream函数的实现依赖了isObjectisFunction函数;

isTypedArray

/**
 * Checking if the Uint8Array exists and if it does, it returns a function that checks if the
 * thing passed in is an instance of Uint8Array
 *
 * @param {TypedArray}
 *
 * @returns {Array}
 */
// eslint-disable-next-line func-names
const isTypedArray = (TypedArray => {// eslint-disable-next-line func-namesreturn thing => {return TypedArray && thing instanceof TypedArray;};
})(typeof Uint8Array !== 'undefined' && getPrototypeOf(Uint8Array)); 

isTypedArray函数的实现是一个立即执行函数,返回一个函数,也是一个高阶函数;

这里的判断是为了兼容IEIE中没有Uint8Array,所以这里判断了Uint8Array是否存在,如果存在,就返回一个函数,这个函数的作用是判断传入的参数是否是Uint8Array的实例;

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.
 *
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 *
 * @param {Boolean} [allOwnKeys = false]
 * @returns {any}
 */
function forEach(obj, fn, {allOwnKeys = false} = {}) {// Don't bother if no value providedif (obj === null || typeof obj === 'undefined') {return;}let i;let l;// Force an array if not already something iterableif (typeof obj !== 'object') {/*eslint no-param-reassign:0*/obj = [obj];}if (isArray(obj)) {// Iterate over array valuesfor (i = 0, l = obj.length; i < l; i++) {fn.call(null, obj[i], i, obj);}} else {// Iterate over object keysconst keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj);const len = keys.length;let key;for (i = 0; i < len; i++) {key = keys[i];fn.call(null, obj[key], key, obj);}}
} 

手动实现了一个forEach函数,这个函数可以遍历对象和数组,如果是对象,就遍历对象的属性,如果是数组,就遍历数组的元素;

看着很长,内部其实还是for循环,只是对obj的类型做了判断,如果是对象就获取对象的key,然后遍历;

参考:

  • Object.getOwnPropertyNames()
  • Object.keys()

merge

function findKey(obj, key) {key = key.toLowerCase();const keys = Object.keys(obj);let i = keys.length;let _key;while (i-- > 0) {_key = keys[i];if (key === _key.toLowerCase()) {return _key;}}return null;
}

const _global = typeof self === "undefined" ? typeof global === "undefined" ? this : global : self;

const isContextDefined = (context) => !isUndefined(context) && context !== _global;

/**
 * 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:
 *
 * 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, ... */) {const {caseless} = isContextDefined(this) && this || {};const result = {};const assignValue = (val, key) => {const targetKey = caseless && findKey(result, key) || key;if (isPlainObject(result[targetKey]) && isPlainObject(val)) {result[targetKey] = merge(result[targetKey], val);} else if (isPlainObject(val)) {result[targetKey] = merge({}, val);} else if (isArray(val)) {result[targetKey] = val.slice();} else {result[targetKey] = val;}}for (let i = 0, l = arguments.length; i < l; i++) {arguments[i] && forEach(arguments[i], assignValue);}return result;
} 

merge函数用于合并对象,如果对象中有相同的属性,后面的对象会覆盖前面的对象;

这里采用的是递归的方式,如果属性值是对象,就递归调用merge函数,如果是数组,就复制一份;

extend

/**
 * 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
 *
 * @param {Boolean} [allOwnKeys]
 * @returns {Object} The resulting value of object a
 */
const extend = (a, b, thisArg, {allOwnKeys}= {}) => {forEach(b, (val, key) => {if (thisArg && isFunction(val)) {a[key] = bind(val, thisArg);} else {a[key] = val;}}, {allOwnKeys});return a;
} 

extend函数用于扩展对象,如果属性值是函数,就绑定this

trim

/**
 * 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
 */
const trim = (str) => str.trim ?str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); 

trim函数用于去除字符串两边的空格,如果浏览器支持trim函数,就直接调用,否则就用正则表达式去除;

stripBOM

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 *
 * @param {string} content with BOM
 *
 * @returns {string} content value without BOM
 */
const stripBOM = (content) => {if (content.charCodeAt(0) === 0xFEFF) {content = content.slice(1);}return content;
} 

stripBOM函数用于去除字符串的 BOM,如果字符串的第一个字符是0xFEFF,就去除;

可以自己查一下字符串BOM是什么,目前我找到的资料讲解的不是很清楚;

大概的意思就是,在一些编码的文件中,会在文件的开头添加一个特殊的字符,而这些字符是不可见的,所以叫做BOM

如果不去除BOM,就会导致一些问题,比如文件打不开,或者部分文件操作会出错;

inherits

/**
 * Inherit the prototype methods from one constructor into another
 * @param {function} constructor
 * @param {function} superConstructor
 * @param {object} [props]
 * @param {object} [descriptors]
 *
 * @returns {void}
 */
const inherits = (constructor, superConstructor, props, descriptors) => {constructor.prototype = Object.create(superConstructor.prototype, descriptors);constructor.prototype.constructor = constructor;Object.defineProperty(constructor, 'super', {value: superConstructor.prototype});props && Object.assign(constructor.prototype, props);
} 

inherits函数用于继承,constructor是子类,superConstructor是父类;

constructor.prototype = Object.create(superConstructor.prototype, descriptors);这一行代码就是继承父类的原型;

constructor.prototype.constructor = constructor;这一行代码是为了保证子类的constructor属性指向子类;

Object.defineProperty(constructor, 'super', {value: superConstructor.prototype});这一行代码是为了保证子类的super属性指向父类的原型;

props && Object.assign(constructor.prototype, props);这一行代码是为了扩展子类的原型;

toFlatObject

/**
 * Resolve object with deep prototype chain to a flat object
 * @param {Object} sourceObj source object
 * @param {Object} [destObj]
 * @param {Function|Boolean} [filter]
 * @param {Function} [propFilter]
 *
 * @returns {Object}
 */
const toFlatObject = (sourceObj, destObj, filter, propFilter) => {let props;let i;let prop;const merged = {};destObj = destObj || {};// eslint-disable-next-line no-eq-null,eqeqeqif (sourceObj == null) return destObj;do {props = Object.getOwnPropertyNames(sourceObj);i = props.length;while (i-- > 0) {prop = props[i];if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) {destObj[prop] = sourceObj[prop];merged[prop] = true;}}sourceObj = filter !== false && getPrototypeOf(sourceObj);} while (sourceObj && (!filter || filter(sourceObj, destObj)) && sourceObj !== Object.prototype);return destObj;
} 

toFlatObject函数用于将对象转换为扁平对象,也就是将对象的原型链上的属性都拷贝到对象上;

props = Object.getOwnPropertyNames(sourceObj);这一行代码是获取对象的所有属性,包括不可枚举的属性;

destObj[prop] = sourceObj[prop];这一行代码是将对象的属性拷贝到destObj上;

sourceObj = filter !== false && getPrototypeOf(sourceObj);这一行代码是获取对象的原型;

endsWith

/**
 * Determines whether a string ends with the characters of a specified string
 *
 * @param {String} str
 * @param {String} searchString
 * @param {Number} [position= 0]
 *
 * @returns {boolean}
 */
const endsWith = (str, searchString, position) => {str = String(str);if (position === undefined || position > str.length) {position = str.length;}position -= searchString.length;const lastIndex = str.indexOf(searchString, position);return lastIndex !== -1 && lastIndex === position;
} 

endsWith函数用于判断字符串是否以指定的字符串结尾;

它接收三个参数,第一个参数是要判断的字符串,第二个参数是要判断的字符串,第三个参数是开始判断的位置;

position参数是可选的,如果没有传递,那么就默认为字符串的长度,超过字符串的长度就取字符串的长度;

position参数减去searchString的长度,就是开始判断的位置;

然后通过indexOf方法来判断searchString是否在str中,如果在,那么就返回true,否则返回false

toArray

/**
 * Returns new array from array like object or null if failed
 *
 * @param {*} [thing]
 *
 * @returns {?Array}
 */
const toArray = (thing) => {if (!thing) return null;if (isArray(thing)) return thing;let i = thing.length;if (!isNumber(i)) return null;const arr = new Array(i);while (i-- > 0) {arr[i] = thing[i];}return arr;
} 

toArray函数用于将类数组转换为数组;

不同于Vue2中的toArray函数,axios中的明显更严谨一些,但是效果却是一样的;

Vue2中使用了各种隐式转换,而axios中则是使用了严谨的判断,对比下来Vue2中的toArray函数更加简洁;

Vue2toArray函数:

export function toArray(list, start) {start = start || 0let i = list.length - startconst ret = new Array(i)while (i--) {ret[i] = list[i + start]}return ret
} 

forEachEntry

/**
 * For each entry in the object, call the function with the key and value.
 *
 * @param {Object<any, any>} obj - The object to iterate over.
 * @param {Function} fn - The function to call for each entry.
 *
 * @returns {void}
 */
const forEachEntry = (obj, fn) => {const generator = obj && obj[Symbol.iterator];const iterator = generator.call(obj);let result;while ((result = iterator.next()) && !result.done) {const pair = result.value;fn.call(obj, pair[0], pair[1]);}
} 

forEachEntry函数用于遍历对象的键值对;

它接收两个参数,第一个参数是要遍历的对象,第二个参数是回调函数;

forEachEntry函数通过Symbol.iterator来获取对象的迭代器,然后通过while循环来遍历对象的键值对;

参考:

  • Symbol.iterator

matchAll

/**
 * It takes a regular expression and a string, and returns an array of all the matches
 *
 * @param {string} regExp - The regular expression to match against.
 * @param {string} str - The string to search.
 *
 * @returns {Array<boolean>}
 */
const matchAll = (regExp, str) => {let matches;const arr = [];while ((matches = regExp.exec(str)) !== null) {arr.push(matches);}return arr;
} 

matchAll函数用于匹配所有的字符串;

它接收两个参数,第一个参数是正则表达式,第二个参数是要匹配的字符串;

matchAll函数通过while循环来匹配所有的字符串,然后将匹配到的字符串放入数组中;

参考:

  • RegExp.prototype.exec()

hasOwnProperty

/* Creating a function that will check if an object has a property. */
const hasOwnProperty = (({hasOwnProperty}) => (obj, prop) => hasOwnProperty.call(obj, prop)
)(Object.prototype); 

hasOwnProperty函数用于判断对象是否有指定的属性;

是一个高阶函数,还是一个立即执行函数,主要是通过Object.prototype.hasOwnProperty来判断对象是否有指定的属性;

hasOwnProp: hasOwnProperty, // 判断对象是否有指定的属性

没有代码,只是一个别名,注释表示:避免 ESLint 无原型内置检测的别名

reduceDescriptors

const reduceDescriptors = (obj, reducer) => {const descriptors = Object.getOwnPropertyDescriptors(obj);const reducedDescriptors = {};forEach(descriptors, (descriptor, name) => {if (reducer(descriptor, name, obj) !== false) {reducedDescriptors[name] = descriptor;}});Object.defineProperties(obj, reducedDescriptors);
} 

reduceDescriptors函数用于降低描述符;

它接收两个参数,第一个参数是要降低的对象,第二个参数是回调函数;

reduceDescriptors函数通过Object.getOwnPropertyDescriptors来获取对象的所有属性描述符,然后通过forEach函数来遍历对象的所有属性描述符;

参考:

  • Object.getOwnPropertyDescriptors()
  • Object.defineProperties()

freezeMethods

/**
 * Makes all methods read-only
 * @param {Object} obj
 */

const freezeMethods = (obj) => {reduceDescriptors(obj, (descriptor, name) => {// skip restricted props in strict modeif (isFunction(obj) && ['arguments', 'caller', 'callee'].indexOf(name) !== -1) {return false;}const value = obj[name];if (!isFunction(value)) return;descriptor.enumerable = false;if ('writable' in descriptor) {descriptor.writable = false;return;}if (!descriptor.set) {descriptor.set = () => {throw Error('Can not rewrite read-only method '' + name + ''');};}});
} 

freezeMethods函数用于冻结方法;

它接收一个参数,是要冻结的对象,freezeMethods函数通过reduceDescriptors函数来降低对象的所有属性描述符;

toObjectSet

const toObjectSet = (arrayOrString, delimiter) => {const obj = {};const define = (arr) => {arr.forEach(value => {obj[value] = true;});}isArray(arrayOrString) ? define(arrayOrString) : define(String(arrayOrString).split(delimiter));return obj;
} 

toObjectSet函数用于将数组或者字符串转换为类似Set的对象;

它接收两个参数,第一个参数是要转换的数组或者字符串,第二个参数是分隔符;

toObjectSet函数通过isArray函数来判断第一个参数是否是数组,如果是数组,就直接遍历数组,将数组的每一项作为obj的属性,值为true

如果第一个参数不是数组,就将第一个参数转换为字符串,然后通过split函数来分割字符串,将分割后的每一项作为obj的属性,值为true

toCamelCase

const toCamelCase = str => {return str.toLowerCase().replace(/[_-\s]([a-z\d])(\w*)/g,function replacer(m, p1, p2) {return p1.toUpperCase() + p2;});
}; 

toCamelCase函数用于将字符串转换为驼峰命名;

Vue2中也有类似的函数:camelize

两者的实现的原理都是相同的,都是通过正则表达式来匹配字符串,然后通过回调函数来替换匹配到的字符串;

不同的是正则不一样,Vue2中的正则是/-(\w)/gaxios中的正则是/[_-\s]([a-z\d])(\w*)/g

可以看到Vue2中的正则只匹配了-,而axios中的正则匹配了_-\s,考虑的边界情况更多;

noop

const noop = () => {}; 

空函数,和Vue2中的noop函数一样;

toFiniteNumber

const toFiniteNumber = (value, defaultValue) => {value = +value;return Number.isFinite(value) ? value : defaultValue;
} 

toFiniteNumber函数用于将字符串转换为有限数字;

它接收两个参数,第一个参数是要转换的字符串,第二个参数是默认值;

使用的是+运算符将字符串隐式转换为数字,然后通过Number.isFinite函数来判断是否是有限数字,如果是有限数字,就返回转换后的数字,否则返回默认值;

findKey

function findKey(obj, key) {key = key.toLowerCase();const keys = Object.keys(obj);let i = keys.length;let _key;while (i-- > 0) {_key = keys[i];if (key === _key.toLowerCase()) {return _key;}}return null;
} 

findKey函数用于查找对象的键;

它接收两个参数,第一个参数是要查找的对象,第二个参数是要查找的键;

findKey函数通过Object.keys函数来获取对象的所有键,然后通过while循环来遍历这些键,将键转换为小写,然后和要查找的键进行比较,如果相等,就返回这个键,否则返回null

global: _global

const _global = typeof self === "undefined" ? typeof global === "undefined" ? this : global : self; 

_global变量用于存储全局对象;

它通过typeof运算符来判断selfglobalthis是否存在,如果存在,就将它们赋值给_global

参考:

  • self
  • global
  • this

isContextDefined

const isContextDefined = (context) => !isUndefined(context) && context !== _global; 

isContextDefined函数用于判断上下文是否定义;

它接收一个参数,就是要判断的上下文;

toJSONObject

const toJSONObject = (obj) => {const stack = new Array(10);const visit = (source, i) => {if (isObject(source)) {if (stack.indexOf(source) >= 0) {return;}if(!('toJSON' in source)) {stack[i] = source;const target = isArray(source) ? [] : {};forEach(source, (value, key) => {const reducedValue = visit(value, i + 1);!isUndefined(reducedValue) && (target[key] = reducedValue);});stack[i] = undefined;return target;}}return source;}return visit(obj, 0);
} 

toJSONObject函数用于将对象转换为 JSON 对象;

它接收一个参数,就是要转换的对象;

toJSONObject函数通过visit函数来实现对象的转换;

visit函数接收两个参数,第一个参数是要转换的对象,第二个参数是栈的索引;

visit函数首先判断对象是否是对象,如果是对象,就判断栈中是否存在这个对象,如果存在,就返回;

然后判断对象是否有toJSON方法,如果没有,就将对象添加到栈中,然后创建一个新的对象,如果对象是数组,就创建一个空数组,否则创建一个空对象;

然后通过forEach函数来遍历对象的所有键,然后通过visit函数来递归遍历对象的值,如果值不是undefined,就将值添加到新的对象中;

最后将对象从栈中移除,然后返回新的对象;

对比 Vue2

vue2

  • vue2中的工具函数写的都非常简洁
  • vue2中对于类型判断使用了多种方式
  • vue2中使用的es6的语法比较少
  • vue2中的工具函数导出都是一个函数一个导出

axios

  • axios中的工具函数写的比较复杂
  • axios中对于类型判断只使用了一种方式
  • axios中使用的es6的语法比较多
  • axios中的工具函数导出都是一个对象一个导出
  • axios中的工具函数对于类型的种类判断较多
  • axios中的工具函数主要集中在数据的处理
  • axios中的工具函数函数签名较全面

这里没必要说优缺点,因为这些都是个人的看法,不要因为写axios的点比较多就认为axios的工具函数写的比较好,这里只是为了对比vue2axios的工具函数,看看他们的不同之处。

对于vue2的工具函数来说,它的侧重点是针对vue2的,所以会比较关注vue系统运行过程中的处理,对于数据类型种类这一块就没axios这么全面;

对于axios的工具函数来说,它的侧重点是针对网络请求,而且还是跨平台的,所以会比较关注数据的处理,对于数据类型种类这一块就比vue2这么全面;

总结

axios的工具函数使用了很多高阶函数来处理,整体对于闭包、高阶函数、函数柯里化等都有一定的应用;

axios的工具函数对于数据类型种类非常全面,囊括浏览器到node的大多数数据类型;

axios的工具函数对于函数签名非常全面,对于函数的参数类型、返回值类型都有非常详细的描述,写的非常规范,这个值得我们学习;

主要还是高阶函数的应用,这个在vue2中也有一定的应用,但是axios的应用更加明显和全面,就是看着有点扣脑阔,但是这个对于个人的提升还是很有帮助的。

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[机缘参悟-95] :不同人生和社会问题的本质

事情的本质是物极必反&#xff08;轮回、周期&#xff09; 社会的本质是优胜劣汰&#xff08;迭代、发展&#xff09; 道德的本质是伦理秩序&#xff08;未定、秩序&#xff09; 战争的本质是资源占用&#xff08;弱肉、强食&#xff09; 商业的本质是价值交换 金钱的本质…

同事这样用Redis,把我害惨了

首先说下问题现象&#xff1a;内网sandbox环境API持续1周出现应用卡死&#xff0c;所有api无响应现象 刚开始当测试抱怨环境响应慢的时候 &#xff0c;我们重启一下应用&#xff0c;应用恢复正常&#xff0c;于是没做处理。但是后来问题出现频率越来越频繁&#xff0c;越来越多…

MySQL实现主从复制(Windows)的明细操作步骤

文章目录一、教学视频地址二、设计思路三、具体步骤一、教学视频地址 视频地址&#xff1a;视频链接 二、设计思路 准备两个5.7版本的MySQL&#xff0c;一个用作主数据库&#xff0c;另一个用作从数据库。 把主数据库做为写入数据库&#xff0c;从数据库作为读数据库。 三…

【云原生 Kubernetes】基于 KubeAdmin 搭建k8s集群

一、前言 在上一篇&#xff0c;我们基于minikube搭建了一个单节点的k8s集群&#xff0c;作为学习和练习使用的话问题不大&#xff0c;但如果想深入学习和了解k8s的相关技术体系&#xff0c;还是需要搭建真正的集群才能更接近生产环境的应用&#xff0c;本篇将基于KubeAdmin&…

深度学习炼丹-数据预处理和增强

前言一&#xff0c;Normalization 概述 1.1&#xff0c;Normalization 定义1.2&#xff0c;什么情况需要 Normalization1.3&#xff0c;Data Normalization 方法1.4&#xff0c;示例代码 二&#xff0c;normalize images 2.1&#xff0c;图像 normalization 定义2.2&#xff0c…

Spring-Cloud-Gateway-07

前言 1、什么是网关 网关是微服务最边缘的服务&#xff0c;直接暴露给用户&#xff0c;用来做用户和微服务的桥梁 没有网关&#xff1a;客户端直接访问我们的微服务&#xff0c;会需要在客户端配置很多的ip&#xff1a;port&#xff0c;如果user-service并发比较大&#xff0c…

深度学习YoloV3案例

目录1 数据获取2 TFrecord文件2.1 什么是TFrecord文件2.2 将数据转换为TFRecord文件2.3 读取TFRecord文件2.4 数据处理3 模型构建4 模型训练4.1 损失函数的计算4.2 正负样本的设定4.3 模型训练4.3.1 获取数据集4.3.2 加载模型4.3.3 模型训练5 模型预测6 总结1 数据获取 根据要…

计算机工作原理简单介绍

文章目录一、冯诺依曼体系结构二、CPU基本工作流程CPU工作流程三、操作系统操作系统的基本功能四、进程&#xff08;process&#xff09;/任务&#xff08;task&#xff09;操作系统如何管理进程描述一个进程&#xff08;进程的相关属性&#xff09;组织若干进程CPU的分配内存的…

推荐系统,计算广告模型论文,代码与数据集汇总

Rec-Models 更多细节参考项目&#xff1a;https://github.com/JackHCC/Rec-Models https://github.com/JackHCC/Rec-Models &#x1f4dd; Summary of recommendation, advertising and search models. Recall Papers PaperResourceOthers[2019阿里SDM模型] SDM: Sequen…

Zebec Chain缘何能成为新晋应用链,熊市下又为何值得我们关注?

流支付生态 Zebec 正处于发展的火热阶段&#xff0c;Zebec此前于12月20日举办的为期3天的Web3.0 TechHive Summit 2022 大会&#xff0c;目前已经落幕&#xff0c;此次大会参会项目多达34个囊括了公链、钱包、DID、GameFi等多个主流行业赛道&#xff0c;并围绕行业安全、发展趋…

ConvLSTM时空预测实战代码详解

写在前面 时空预测是很多领域都存在的问题&#xff0c;不同于时间序列&#xff0c;时空预测不仅需要探究时间的变化&#xff0c;也需要关注空间的变化。许多预测问题都只片面的关注时间问题&#xff0c;如预测某人未来3年患某种病的概率&#xff0c;食堂就餐人数等&#xff0c…

CSS--圆角边框

单独对四个角进行设置&#xff1a; boder-top-left-radius&#xff1a;30px&#xff1b; //左上角 boder-top-right-radius&#xff1a;30px&#xff1b; //右上角 boder-bottom-left-radius&#xff1a;30px&#xff1b; //右下角 boder-bottom-right-radius&#xff1a;30px&…

群晖 Sonology NAS DS920+ 拆机装机方法

文章内容&#xff1a;群晖 Sonology NAS DS920 拆机方法 关键词组&#xff1a;群晖&#xff0c;Sonology, nas, ds920, 拆机, 外壳 使用软件&#xff1a;无 虚拟环境&#xff1a;无 操作系统&#xff1a;无 目录一、事件起因三、拆装机方法一、事件起因 起初&#xff0c;由于机…

OpenCV环境下实现图像任意角度旋转的原理及代码

OpenCV环境下实现图像任意角度旋转的原理及代码 实现图像任意角度旋转的原理如下&#xff1a; Step01-把图像原点从左上角转换到旋转中心点。 Step02-利用极坐标系计算出旋转后各点的坐标。 Step03-确定旋转后图像的左边界、右边界、上边界、下边界&#xff0c;进而得出旋转后…

计数排序 [数据结构与算法][Java]

计数排序 计数排序和基数排序都是桶排序的一种应用 适用场景: 量大但是范围小 比如对10000个数进行排序, 但是这10000个数中只有10种数字(0 - 9)典型题目: 某大型企业数万名员工年龄排序如何快速得知高考名次(腾讯面试) 这里我们以某大型企业数万名员工年龄排序来进行一个…

RV1126笔记十四:吸烟行为检测及部署<二>

若该文为原创文章,转载请注明原文出处。 PC下yolov5环境搭建 我使用的训练环境是Windows10+MiniConda 接下来记录搭建全过程 备注:条件允许就使用ubuntu物理机,最好要有显卡,训练有显卡速度会快很多,没有显卡,训练300轮,亲测大概40小时,不值得。 一、miniconda 安装…

从零了解进程(操作系统定位,进程的概念,特征,虚拟地址)

目录 操作系统的定位 进程的概念 如何描述进程? 如何组织进程? 进程的特征 1.pid 2.内存指针 3.文件描述符 4.进程调度的相关属性 (1)进程的状态 (2)优先级 (3)上下文 (4)记账信息 进程是如何利用cpu资源的? 进程的虚拟地址 物理地址 内存随机访问的特性 为…

【ML实验7】人脸识别综合项目(PCA、多分类SVM)

实验代码获取 github repo 山东大学机器学习课程资源索引 实验目的 实验环境 实验内容 PCA 两种方法EVD-PCA和SVD-PCA的实现、效率对比见我之前的博客一个PCA加速技巧&#xff0c;这里补充SVD方法的数学推导&#xff1a; 首先&#xff0c;设方阵AAA的特征值分解为AUΣUTAU\S…

ZKP应用:石头剪刀布游戏

1. 引言 开源代码见&#xff1a; https://github.com/spalladino/zkp-tests 对比了分别使用&#xff1a; Iden3团队的circom语言&#xff08;易于学习ZKP&#xff09;ZCash团队的Halo2框架Aztec团队的Noir语言&#xff08;最友好&#xff09; 编写石头剪刀布游戏的ZKP证明…

IPv6(计算机网络-网络层)

目录 IPv6 的特点 IPv6 数据报的格式 IPv6 分组的格式 IPv6 的扩展首部 从计算机本身发展以及从互联网规模和网络传输速率来看&#xff0c;现在 IPv4已很不适用。 要解决 IP 地址耗尽的问题的措施&#xff1a; 采用无类别编址 CIDR&#xff0c;使 IP 地址的分配更加合理…