capitalize
函数是一个字符串转换函数,用于把字符串的第一个字母处理为大写字母,剩下的字母处理为小写字母。本文涉及到类型校验(Object.prototype.toString
, isSymbol
),类型转换(toString
),稀疏数组和密集型数组,包含表情的字符串处理。
类型校验
getTag
const toString = Object.prototype.toString
function getTag(value) {
if (value == null) {
// 兼容javascript低版本,特殊处理null和undefined
return value === undefined ? '[object Undefined]' : '[object Null]'
}
return toString.call(value)
}
复制代码
isSymbol
调用了getTag
方法
function isSymbol(value) {
const type = typeof value
return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]')
}
复制代码
类型转换
toString
将传入的value转换为字符串。数组递归处理(可能会堆栈溢出)。特殊处理-0
的情况。引入上面的isSymbol
函数
import isSymbol from './isSymbol.js'
/** Used as references for various `Number` constants. */
const INFINITY = 1 / 0
/**
* Converts `value` to a string. An empty string is returned for `null`
* and `undefined` values. The sign of `-0` is preserved.
* value转换为string类型。回null或undefined会返空字符串。-0会被保留
*
* @since 4.0.0
* @category Lang
* @param {*} value The value to convert.
* @returns {string} Returns the converted string.
* @example
*
* toString(null)
* // => ''
*
* toString(-0)
* // => '-0'
*
* toString([1, 2, 3])
* // => '1,2,3'
*/
function toString(value) {
if (value == null) {
return ''
}
// Exit early for strings to avoid a performance hit in some environments.
// string类型直接返回
if (typeof value === 'string') {
return value
}
// 数组类型
if (Array.isArray(value)) {
// Recursively convert values (susceptible to call stack limits).
// 数组项不为null或undefined的情况下,递归调用自身进行转换
return `${value.map((other) => other == null ? other : toString(other))}`
}
// symbol类型调用symbol的toString方法
if (isSymbol(value)) {
return value.toString()
}
// 处理-0的情况
const result = `${value}`
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result
}
复制代码
处理字符串的第一个字母
createCaseFirst
该函数依赖于castSlice
函数,hasUnicode
函数以及stringToArray
函数。
castSlice
函数,相比较于Array.prototype.slice
方法,该函数调用自己实现的slice
函数并生成一个密集型数组。源码地址。关于密集型数组和稀疏数组:
console.log(new Array(5), new Array(5).fill(undefined))
// [ <5 empty items> ] 稀疏数组
// [ undefined, undefined, undefined, undefined, undefined ] 密集数组
复制代码
hasUnicode
函数用于校验传入的字符串是否包含Unicode
码表上对应的字符(这里主要是校验是否存在特殊的unicode字符。源码地址。如果存在,就交给stringToArray
函数处理,以正确的解析对应的unicode字符
),校验范围如下:
// 星芒层
const rsAstralRange = '\\ud800-\\udfff'
// https://www.unicode.org/charts/PDF/U0300.pdf
const rsComboMarksRange = '\\u0300-\\u036f'
// https://www.unicode.org/charts/PDF/UFE20.pdf
const reComboHalfMarksRange = '\\ufe20-\\ufe2f'
// https://www.unicode.org/charts/PDF/U20D0.pdf
const rsComboSymbolsRange = '\\u20d0-\\u20ff'
// https://www.unicode.org/charts/PDF/U1AB0.pdf
const rsComboMarksExtendedRange = '\\u1ab0-\\u1aff'
// https://www.unicode.org/charts/PDF/U1DC0.pdf
const rsComboMarksSupplementRange = '\\u1dc0-\\u1dff'
const rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange
// fe0e fe0f https://www.unicode.org/charts/PDF/UFE00.pdf
const rsVarRange = '\\ufe0e\\ufe0f'
/** Used to compose unicode capture groups. */
// ZWJ https://www.unicode.org/charts/PDF/U2000.pdf ZERO WIDTH JOINER
const rsZWJ = '\\u200d'
/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
const reHasUnicode = RegExp(`[${rsZWJ + rsAstralRange + rsComboRange + rsVarRange}]`)
复制代码
stringToArray
函数,正如上文中提到的,为了正确解析包含特殊unicode
编码的字符串,比如包含表情的字符串。源码地址。
createCaseFirst
利用传入的methodName
对字符串的第一个字母进行处理。如toUpperCase
,toLowerCase
等存在于String.prototype
原型链上的方法
import castSlice from './castSlice.js'
import hasUnicode from './hasUnicode.js'
import stringToArray from './stringToArray.js'
/**
* Creates a function like `lowerFirst`.
* 根据传入的methodname 生成对应的函数
*
* @private
* @param {string} methodName The name of the `String` case method to use.
* @returns {Function} Returns the new case function.
*/
function createCaseFirst(methodName) {
return (string) => {
// string 为空字符串 不做任何操作
if (!string) {
return ''
}
// 包含unicode码,调用内部的asciiToArray和unicodeToArray方法
const strSymbols = hasUnicode(string)
? stringToArray(string)
: undefined
// string 字符不包含unicode 默认取string的第一个字符。否则转化为数组后,取第一个字符
const chr = strSymbols
? strSymbols[0]
: string[0]
// string截取剩下的字符串。包含unicode情况,截取数组的1到最后一项并转换为字符串
const trailing = strSymbols
? castSlice(strSymbols, 1).join('')
: string.slice(1)
// 调用第一个字符串对应的方法执行 prototype上对应的函数 并 追加后续字符串
// 只对字符串的第一个字符进行操作
return chr[methodName]() + trailing
}
}
复制代码
upperFirst
调用createCaseFirst
函数,传入toUpperCase
作为参数
/**
* Converts the first character of `string` to upper case.
* 字符串的第一个字母转换成大写字母
*
* @since 4.0.0
* @category String
* @param {string} [string=''] The string to convert.
* @returns {string} Returns the converted string.
* @see camelCase, kebabCase, lowerCase, snakeCase, startCase, upperCase
* @example
*
* upperFirst('fred')
* // => 'Fred'
*
* upperFirst('FRED')
* // => 'FRED'
*/
const upperFirst = createCaseFirst('toUpperCase')
复制代码
capitalize
调用toString
函数和upperFirst
。先对传入的值调用toString
函数,然后调用String.prototype.toLowerCase
方法,最后调用upperFirst
实现功能
const capitalize = (string) => upperFirst(toString(string).toLowerCase())
复制代码