入口
const add = createMathOperation((augend, addend) => augend + addend, 0)
createMathOperation
function createMathOperation(operator, defaultValue) {
return (value, other) => {
// 排除任意值为传的情况
if (value === undefined && other === undefined) {
return defaultValue
}
if (value !== undefined && other === undefined) {
return value
}
if (other !== undefined && value === undefined) {
return other
}
// 存在string类型,都转为字符串进行相加
if (typeof value === 'string' || typeof other === 'string') {
value = baseToString(value)
other = baseToString(other)
}
// 默认转换为number类型相加
else {
value = baseToNumber(value)
other = baseToNumber(other)
}
// 返回
// 此处operator 为 (augend, addend) => augend + addend
return operator(value, other)
}
}
流程:判断实参个数->判断实参类型->运算
判断实参个数
关于undefined的处理,用于处理参数未传少传的情况
判断实参类型
需要处理的类型,string boolean number symbol object。
string的默认行为会把另一个值也转换为string,例如我们通常用'' + 1来将其他类型的变量转化为字符串。因此string类型的需要特殊处理
对于boolean,运算中默认可以被转换为0或者1。在其中一个值为string的情况下,会被转换为'true' 'false'
对于symbol,在两个方法中都有特殊处理,string是返回空字符串或调用Symbol.prototype.toString,number返回NaN
对于object
array和数字相加会调用Array.prototype.toString方法,通过baseToNumber转换后,返回NaNdate通过baseToNumber转换后,返回时间戳。通过baseToString转换后返回本地时间格式的字符串,如Fri Jun 18 2021 18:42:53 GMT+0800 (China Standard Time)regexp通过baseToNumber转换后,返回NaN。通过baseToString转换后返回对应的正则表达式null通过baseToNumber转换后,返回0。通过baseToString转换后返回字符串'null'
综上,我们只需要判断number或string类型进行运算即可
baseToString
/** Used as references for various `Number` constants. */
// 生成INFINITY,向下兼容
const INFINITY = 1 / 0
/** Used to convert symbols to primitives and strings. */
// 保存 Symbol.prototype.toString的引用
const symbolToString = Symbol.prototype.toString
/**
* The base implementation of `toString` which doesn't convert nullish
* values to empty strings.
* 基本的toString方法不会吧无效的值转换为空字符串
* @private
* @param {*} value The value to process.
* @returns {string} Returns the string.
*/
function baseToString(value) {
// Exit early for strings to avoid a performance hit in some environments.
// 在最开始作typeof value === 'string'的判断以确保大多数情况下能直接返回value
if (typeof value === 'string') {
return value
}
// 数组,则递归调用baseToString。可能造成堆栈溢出
if (Array.isArray(value)) {
// Recursively convert values (susceptible to call stack limits).
return `${value.map(baseToString)}`
}
// symbol类型,调用Symbol.prototype.toString返回字符串类型的symbol值
// 如Symbol('x') => 'Symbol(x)'。否则返回空字符串
if (isSymbol(value)) {
return symbolToString ? symbolToString.call(value) : ''
}
// 转字符串`${-0}` === '0' true
const result = `${value}`
// -0 === 0 true
// Object.is(0, -0) false
// 额外处理-0的情况
return (result === '0' && (1 / value) === -INFINITY) ? '-0' : result
}
baseToNumber
/** Used as references for various `Number` constants. */
const NAN = 0 / 0
/**
* The base implementation of `toNumber` which doesn't ensure correct
* conversions of binary, hexadecimal, or octal string values.
* `toNumber`的基本实现不能确保正确转换二进制、十六进制或八进制字符串值
*
* @private
* @param {*} value The value to process.
* @returns {number} Returns the number.
*/
function baseToNumber(value) {
// number类型直接返回
if (typeof value === 'number') {
return value
}
// symbol类型返回NAN
if (isSymbol(value)) {
return NAN
}
// 否则利用原生的toNumber进行转换
return +value
}
生成NaN,+undefined同样也可以生成
isSymbol
function isSymbol(value) {
// 保存typeof value的引用
const type = typeof value
// 先利用typeof判断,es2015,更早的版本使用Object.prototype.toString判断
return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]')
}
typeof Symbol === 'function'
typeof Symbol('xx') === 'symbol'
getTag
const toString = Object.prototype.toString
/**
* Gets the `toStringTag` of `value`.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
function getTag(value) {
if (value == null) {
return value === undefined ? '[object Undefined]' : '[object Null]'
}
return toString.call(value)
}
保存了Object.prototype.toString的引用。
利用==,null == undefined。value == null 判断为了处理兼容性问题,自JavaScript 1.8.5起,对null调用Object.prototype.toString返回[object Null]。对undefined调用Object.prototype.toString返回[object Undefined]
通过修改对象的[Symbol.toStringTag]可以修改Object.prototype.toString的返回值