图里还差几个style、class。props转换的函数
参考
准备
- 读贡献指南,了解怎么跑,项目结构等等
找到shared模块。
shared: Internal utilities shared across multiple packages (especially environment-agnostic utils used by both runtime and compiler packages).
且 可以匹配构建
The build script builds all public packages (packages without private: true in their package.json).
Packages to build can be specified with fuzzy matching:
# build runtime-core only
yarn build runtime-core
# build all packages matching "runtime"
yarn build runtime --all
所以,要执行的命令为
git clone https://github.com/vuejs/vue-next.git
cd vue-next
yarn build shared
---效果
$ yarn build shared
yarn run v1.22.10
$ node scripts/build.js shared
C:\Users\82454\Desktop\vue3\vue-next\packages\shared\src\index.ts 鈫?packages\shared\dist\shared.esm-bundler.js...
created packages\shared\dist\shared.esm-bundler.js in 3.6s
C:\Users\82454\Desktop\vue3\vue-next\packages\shared\src\index.ts 鈫?packages\shared\dist\shared.cjs.js...
created packages\shared\dist\shared.cjs.js in 2s
C:\Users\82454\Desktop\vue3\vue-next\packages\shared\src\index.ts 鈫?packages\shared\dist\shared.cjs.prod.js...
created packages\shared\dist\shared.cjs.prod.js in 1.8s
Done in 8.55s.
可以发现虽然打包出的package里有很多模块,但只有shared有dist文件夹
接下来看 vue-next/packages/shared/dist/shared.esm-bundler.js
工具函数
- 导出的函数有
export { EMPTY_ARR, EMPTY_OBJ, NO, NOOP, PatchFlagNames, babelParserDefaultPlugins, camelize, capitalize, def, escapeHtml, escapeHtmlComment, extend, generateCodeFrame, getGlobalThis, hasChanged, hasOwn, hyphenate$1 as hyphenate, invokeArrayFns, isArray$1 as isArray, isBooleanAttr, isDate$1 as isDate, isFunction, isGloballyWhitelisted, isHTMLTag, isIntegerKey, isKnownHtmlAttr, isKnownSvgAttr, isMap$1 as isMap, isModelListener, isNoUnitNumericStyleProp, isObject$1 as isObject, isOn, isPlainObject$1 as isPlainObject, isPromise, isReservedProp, isSSRSafeAttrName, isSVGTag, isSet$1 as isSet, isSpecialBooleanAttr, isString$1 as isString, isSymbol, isVoidTag, looseEqual, looseIndexOf, makeMap, normalizeClass, normalizeProps, normalizeStyle, objectToString$1 as objectToString, parseStringStyle, propsToAttrMap, remove, slotFlagsText, stringifyStyle, toDisplayString, toHandlerKey, toNumber, toRawType, toTypeString$1 as toTypeString };
EMPTY_ARR 空数组
//如果是开发环境,返回一个不可修改的数组,否则返回一个空数组
const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
EMPTY_OBJ 空对象
同上
const EMPTY_OBJ = (process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
NO 永远返回false的函数
const NO = () => false;
NOOP 返回空对象的函数
const NOOP = () => { };
// 很多库的源码中都有这样的定义函数,比如 jQuery、underscore、lodash 等
// 使用场景:1. 方便判断, 2. 方便压缩
// 1. 比如:(判断环境用的么?目前认知不够)
const instance = {
render: NOOP
};
// 条件
const dev = true;
if(dev){
instance.render = function(){
console.log('render');
}
}
// 可以用作判断。
if(instance.render === NOOP){
console.log('i');
}
// 2. 再比如:
// 方便压缩代码
// 如果是 function(){} ,不方便压缩代码
PatchFlagNames 是否diff的标志?
/**
* 不是很懂,应该是和diff判断有关的,2的指数,用 与或 来判断是否相应的diff吧应该
* dev only flag -> name mapping
*/
const PatchFlagNames = {
[1 /* TEXT */]: `TEXT`,
[2 /* CLASS */]: `CLASS`,
[4 /* STYLE */]: `STYLE`,
[8 /* PROPS */]: `PROPS`,
[16 /* FULL_PROPS */]: `FULL_PROPS`,
[32 /* HYDRATE_EVENTS */]: `HYDRATE_EVENTS`,
[64 /* STABLE_FRAGMENT */]: `STABLE_FRAGMENT`,
[128 /* KEYED_FRAGMENT */]: `KEYED_FRAGMENT`,
[256 /* UNKEYED_FRAGMENT */]: `UNKEYED_FRAGMENT`,
[512 /* NEED_PATCH */]: `NEED_PATCH`,
[1024 /* DYNAMIC_SLOTS */]: `DYNAMIC_SLOTS`,
[2048 /* DEV_ROOT_FRAGMENT */]: `DEV_ROOT_FRAGMENT`,
[-1 /* HOISTED */]: `HOISTED`,
[-2 /* BAIL */]: `BAIL`
};
babelParserDefaultPlugins babel 解析默认插件
/** 啃不动
* List of @babel/parser plugins that are used for template expression
* transforms and SFC script transforms. By default we enable proposals slated
* for ES2020. This will need to be updated as the spec moves forward.
* Full list at https://babeljs.io/docs/en/next/babel-parser#plugins
*/
const babelParserDefaultPlugins = [
'bigInt',
'optionalChaining',
'nullishCoalescingOperator'
];
makeMap 检查key是否存在
//isHTMLTag('html')==>true
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
function makeMap(str, expectsLowerCase) {
const map = Object.create(null);
const list = str.split(',');
for (let i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val];
}
const HTML_TAGS = 'html,body,base,head,link,meta,style,title,address,article,aside,footer,' +
'header,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,' +
'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,' +
'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,' +
'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,' +
'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,' +
'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,' +
'option,output,progress,select,textarea,details,dialog,menu,' +
'summary,template,blockquote,iframe,tfoot';
const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS);
camelize、hyphenate 转驼峰-缓存
const cacheStringFunction$1 = (fn) => {
const cache = Object.create(null);
return ((str) => {
const hit = cache[str];
return hit || (cache[str] = fn(str));
});
};
const camelizeRE = /-(\w)/g;//匹配 -加数字、大小写字母和下划线 如-a -B
const camelize = cacheStringFunction$1((str) => {
//'aa-a-a'.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));返回 "aaAA"
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
});
//'aaAaaA'.replace(/\B([A-Z])/g, '-$1').toLowerCase() "aa-aaa-a"
const hyphenateRE$1 = /\B([A-Z])/g;
const hyphenate$1 = cacheStringFunction$1((str) => str.replace(hyphenateRE$1, '-$1').toLowerCase());
用法应该是这样:
camelize('aa-a') 得到 'aaA',第二次转化相同字符串可以不用正则替换而从缓存读取
camelize为return里的函数
{
const cache = Object.create(null);
return ((str) => {
const hit = cache[str];
return hit || (cache[str] = str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')););
});
}
第一次执行camelize('aa-a')时,cache['aa-a']为空,给cache['aa-a']赋值'aaA'后返回'aaA'
第二次执行,cache['aa-a']有值,直接返回
capitalize 首字母大写-缓存
const capitalize = cacheStringFunction$1((str) => str.charAt(0).toUpperCase() + str.slice(1));
//'aaAAAA'.charAt(0).toUpperCase() + 'aaAAAA'.slice(1) "AaAAAA"
两个函数用了同一个对象名cache缓存,但是函数重复构建,cache不会覆盖
toHandlerKey
const toHandlerKey = cacheStringFunction$1((str) =>
str ? `on${capitalize(str)}` : ``
);
def 定义对象属性
const def = (obj, key, value) => {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
value
});
};
escapeHtml 转义html符号
正则.exec(字符串)=》
若有匹配到=》['字符串',index:匹配到的序列号]
若无=》null
// 将字符串中的"'&<>转成相应的编码
const escapeRE = /["'&<>]/;
function escapeHtml(string) {
const str = '' + string;
const match = escapeRE.exec(str);
//var str = "Visit W3School";
//var patt = new RegExp("W3School","g");
//console.log(patt.exec(str))
//["W3School", index: 6, input: "Visit W3School", groups: undefined]
if (!match) {
return str;
}
let html = '';
let escaped;
let index;
let lastIndex = 0;
for (index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escaped = '"';
break;
case 38: // &
escaped = '&';
break;
case 39: // '
escaped = ''';
break;
case 60: // <
escaped = '<';
break;
case 62: // >
escaped = '>';
break;
default:
continue;
}
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
lastIndex = index + 1;
html += escaped;
}
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
escapeHtmlComment 注释符号转义
//'<!-- xxxx -->'.replace(/^-?>|<!--|-->|--!>|<!-$/g,'') 输出 " xxxx "
// https://www.w3.org/TR/html52/syntax.html#comments
const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g;
function escapeHtmlComment(src) {
return src.replace(commentStripRE, '');
}
extend 继承/合并
const extend = Object.assign;
// 例子:
const data = { name: '若川' };
const data2 = entend(data, { mp: '若川视野', name: '是若川啊' });
console.log(data); // { name: "是若川啊", mp: "若川视野" }
console.log(data2); // { name: "是若川啊", mp: "若川视野" }
console.log(data === data2); // true
//数组和数组
extend([3,4,5], [1,2])=>[1,2,5]
//数组合到对象 [1,2]=>{0:1,1:2}
extend({test1:'test1',result1:'result1'}, [1,2,3]);
//{ '0': 1, '1': 2, '2': 3, test1: 'test1', result1: 'result1' }
//对象合到数组里
[ 1, 2, 3, test2: 'test2', result2: 'result2' ]
generateCodeFrame
放弃,驾驭不住
function generateCodeFrame(source, start = 0, end = source.length) {
// Split the content into individual lines but capture the newline sequence
// that separated each line. This is important because the actual sequence is
// needed to properly take into account the full line length for offset
// comparison
let lines = source.split(/(\r?\n)/);
// Separate the lines and newline sequences into separate arrays for easier referencing
const newlineSequences = lines.filter((_, idx) => idx % 2 === 1);
lines = lines.filter((_, idx) => idx % 2 === 0);
let count = 0;
const res = [];
for (let i = 0; i < lines.length; i++) {
count +=
lines[i].length +
((newlineSequences[i] && newlineSequences[i].length) || 0);
if (count >= start) {
for (let j = i - range; j <= i + range || end > count; j++) {
if (j < 0 || j >= lines.length)
continue;
const line = j + 1;
res.push(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`);
const lineLength = lines[j].length;
const newLineSeqLength = (newlineSequences[j] && newlineSequences[j].length) || 0;
if (j === i) {
// push underline
const pad = start - (count - (lineLength + newLineSeqLength));
const length = Math.max(1, end > count ? lineLength - pad : end - start);
res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length));
}
else if (j > i) {
if (end > count) {
const length = Math.max(Math.min(end - count, lineLength), 1);
res.push(` | ` + '^'.repeat(length));
}
count += lineLength + newLineSeqLength;
}
}
break;
}
}
return res.join('\n');
}
getGlobalThis 全局对象
像作用域链,也像原型链
const getGlobalThis = () => {
return (_globalThis ||
(_globalThis =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {}));
};
hasChanged 变化
//检测值的变化 和===的不同之处只有两个:一是+0不等于-0,二是 NaN 等于自身。
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
// -0===0 true 但1/-0为负无穷,1/0为正无穷,可以通过这个判断是否为正负零
if (!Object.is) {
Object.is = function(x, y) {
// SameValue algorithm
if (x === y) { // Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
};
}
hasOwn 检测是否属性是否拥有
const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
// .call 则是函数里 this 显示指定以为第一个参数,并执行函数。
//Object.prototype.hasOwnProperty.call(x,y)
hasOwn({__proto__: { a: 1 }}, 'a') // false
hasOwn({ a: undefined }, 'a') // true
hasOwn({}, 'a') // false
hasOwn({}, 'hasOwnProperty') // false
hasOwn({}, 'toString') // false
// 是自己的本身拥有的属性,不是通过原型链向上查找的。
invokeArrayFns 执行数组里的函数
const invokeArrayFns = (fns, arg) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg);
}
};
isArray 判断数组
const isArray = Array.isArray;
isArray([]); // true
const fakeArr = { __proto__: Array.prototype, length: 0 };
isArray(fakeArr); // false
fakeArr instanceof Array; // true
// 所以 instanceof 这种情况 不准确
isDate 是否Data对象
const isDate$1 = (val) => val instanceof Date;
isFunction
const isFunction = (val) => typeof val === 'function';
// 判断数组有多种方法,但这个是比较常用也相对兼容性好的。
isIntegerKey key是否为数字
const isString$1 = (val) => typeof val === 'string';
const isIntegerKey = (key) => isString$1(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key;
// 例子:
isInegerKey('a'); // false
isInegerKey('0'); // true
isInegerKey('011'); // false
isInegerKey('11'); // true
// 其中 parseInt 第二个参数是进制。
// 字符串能用数组取值的形式取值。
// 还有一个 charAt 函数,但不常用
'abc'.charAt(0) // 'a'
// charAt 与数组形式不同的是 取不到值会返回空字符串'',数组形式取值取不到则是 undefined
isMap 判断是不是 Map 对象
const objectToString$1 = Object.prototype.toString;
const toTypeString$1 = (value) => objectToString$1.call(value);
const isMap = (val) => toTypeString(val) === '[object Map]';
//Object.prototype.toString.call(new Map())
// 例子:
const map = new Map();
isMap(map); // true
isObject 是否对象
const isObject$1 = (val) => val !== null && typeof val === 'object';
// 例子:
isObject(null); // false
isObject({name: '若川'}); // true
// 判断不为 null 的原因是 typeof null 其实 是 object
isPlainObject 是否纯粹对象
const isPlainObject$1 = (val) => toTypeString$1(val) === '[object Object]';
//纯粹不纯粹有啥区别??
isOn 判断是否on+非小写字母
const onRE = /^on[^a-z]/;
const isOn = (key) => onRE.test(key);
// 例子:
isOn('onChange'); // true
isOn('onchange'); // false
isOn('on3change'); // true
isPromise
//判断是否是对象,是否有then和catch方法
const isPromise = (val) => {
return isObject$1(val) && isFunction(val.then) && isFunction(val.catch);
};
// 判断是不是Promise对象
const p1 = new Promise(function(resolve, reject){
resolve('若川');
});
isPromise(p1); // true
isSSRSafeAttrName 是否安全属性词
//isSSRSafeAttrName('/t')
//VM2655:9 unsafe attribute name: /t
//false
const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/;
const attrValidationCache = {};
function isSSRSafeAttrName(name) {
if (attrValidationCache.hasOwnProperty(name)) {
return attrValidationCache[name];
}
const isUnsafe = unsafeAttrCharRE.test(name);
if (isUnsafe) {
console.error(`unsafe attribute name: ${name}`);
}
return (attrValidationCache[name] = !isUnsafe);
}
isSet
const isSet$1 = (val) => toTypeString$1(val) === '[object Set]';
isString
const isString = (val) => typeof val === 'string';
//toTypeString$1(val) === '[object String]'; 用这个应该也行
isSymbol
const isSymbol = (val) => typeof val === 'symbol';
looseEqual 判断对象是否相同
不像Object.is要引用地址相同,属性和值相同即可
function looseCompareArrays(a, b) {
if (a.length !== b.length)
return false;
let equal = true;
for (let i = 0; equal && i < a.length; i++) {
equal = looseEqual(a[i], b[i]);
//这里高能,如果是我会if(!equal)return false,他这样写特别有逼格耶
}
return equal;
}
function looseEqual(a, b) {
//引用地址相同,值也没啥好比的了
if (a === b)
return true;
//若都是日期对象,比时间戳
let aValidType = isDate(a);
let bValidType = isDate(b);
if (aValidType || bValidType) {
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
}
//若都是数组,比长度,再比值
aValidType = isArray(a);
bValidType = isArray(b);
if (aValidType || bValidType) {
return aValidType && bValidType ? looseCompareArrays(a, b) : false;
}
aValidType = isObject(a);
bValidType = isObject(b);
//若都是对象,比key数量,比key,比key值,key值比对跟深拷贝类似
if (aValidType || bValidType) {
/* istanbul ignore if: this if will probably never be called */
if (!aValidType || !bValidType) {
return false;
}
const aKeysCount = Object.keys(a).length;
const bKeysCount = Object.keys(b).length;
if (aKeysCount !== bKeysCount) {
return false;
}
for (const key in a) {
const aHasKey = a.hasOwnProperty(key);
const bHasKey = b.hasOwnProperty(key);
if ((aHasKey && !bHasKey) ||
(!aHasKey && bHasKey) ||
!looseEqual(a[key], b[key])) {
return false;
}
}
}
//比字符串,String(对象):[object Object]
return String(a) === String(b);
}
looseIndexOf 在数组找和参数值相同的序列号
function looseIndexOf(arr, val) {
return arr.findIndex(item => looseEqual(item, val));
}
normalizeClass
//console.log(normalizeClass(["a c ", "b"])); ==>a c b
function normalizeClass(value) {
let res = '';
if (isString(value)) {
res = value;
}
else if (isArray(value)) {
// 类似数组 join(' ')
for (let i = 0; i < value.length; i++) {
const normalized = normalizeClass(value[i]);
if (normalized) {
res += normalized + ' ';
}
}
}
else if (isObject(value)) {
for (const name in value) {
if (value[name]) {
res += name + ' ';
}
}
}
return res.trim();
}
normalizeClass 用于将标签属性 class 转化为标准的 class。
const str = 'a b c'
const arr = [
{
a: true,
b: false,
c: true,
},
]
const obj = {
a: true,
b: false,
c: true,
}
console.log(normalizeClass(str)) // a b c
console.log(normalizeClass(arr)) // a c
console.log(normalizeClass(obj)) // a c
normalizeStyle
const listDelimiterRE = /;(?![^(]*\))/g;
const propertyDelimiterRE = /:(.+)/;
function parseStringStyle(cssText) {
const ret = {};
cssText.split(listDelimiterRE).forEach(item => {
if (item) {
const tmp = item.split(propertyDelimiterRE);
tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
}
});
return ret;
}
function normalizeStyle(value) {
if (isArray(value)) {
const res = {};
for (let i = 0; i < value.length; i++) {
const item = value[i];
const normalized = isString(item)
? parseStringStyle(item)
: normalizeStyle(item);
if (normalized) {
for (const key in normalized) {
res[key] = normalized[key];
}
}
}
return res;
}
else if (isString(value)) {
return value;
}
else if (isObject(value)) {
return value;
}
}
normalizeStyle 用于将标签的内联 style 里的属性转化为标准的 style 属性。
const str = 'margin:10px;'
const arr = [
{
margin: '10px',
borderWidth: '10px',
},
]
const obj = {
margin: '10px',
borderWidth: '10px',
}
console.log(normalizeStyle(str)) // margin:10px;
console.log(normalizeStyle(arr)) // { margin: '10px', borderWidth: '10px' }
console.log(normalizeStyle(obj)) // { margin: '10px', borderWidth: '10px' }
normalizeProps
function normalizeProps(props) {
if (!props)
return null;
let { class: klass, style } = props;
if (klass && !isString(klass)) {
props.class = normalizeClass(klass);
}
if (style) {
props.style = normalizeStyle(style);
}
return props;
}
normalizeProps 用于 props 上的 class 和 style 属性转化。
const props = {
class: [{ a: true, b: false, c: true }],
style: [{ margin: '10px', borderWidth: '10px' }],
}
console.log(normalizeProps(props)) // { class: 'a c', style: { margin: '10px', borderWidth: '10px' } }
stringifyStyle
const hyphenateRE = /\B([A-Z])/g;
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());
const isNoUnitNumericStyleProp = /*#__PURE__*/ makeMap(`animation-iteration-count,border-image-outset,border-image-slice,` +
`border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,` +
`columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,` +
`grid-row,grid-row-end,grid-row-span,grid-row-start,grid-column,` +
`grid-column-end,grid-column-span,grid-column-start,font-weight,line-clamp,` +
`line-height,opacity,order,orphans,tab-size,widows,z-index,zoom,` +
// SVG
`fill-opacity,flood-opacity,stop-opacity,stroke-dasharray,stroke-dashoffset,` +
`stroke-miterlimit,stroke-opacity,stroke-width`);
function stringifyStyle(styles) {
let ret = '';
if (!styles || isString(styles)) {
return ret;
}
for (const key in styles) {
const value = styles[key];
const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key);
if (isString(value) ||
(typeof value === 'number' && isNoUnitNumericStyleProp(normalizedKey))) {
// only render valid values
ret += `${normalizedKey}:${value};`;
}
}
return ret;
}
stringifyStyle 用于将对象格式的样式转化为对象格式并且将样式属性转化。
const str = 'margin:10px;'
const obj = {
margin: '10px',
borderWidth: '10px',
}
console.log(stringifyStyle(str)) // ''
console.log(stringifyStyle(obj)) // margin:10px;border-width:10px;
propsToAttrMap
const propsToAttrMap = {
acceptCharset: 'accept-charset',
className: 'class',
htmlFor: 'for',
httpEquiv: 'http-equiv'
};
remove 数组删除某值
const remove = (arr, el) => {
const i = arr.indexOf(el);
if (i > -1) {
arr.splice(i, 1);
}
};
toDisplayString 展示相应的字符串形式
//toDisplayString({a:2,b:3})
//{
// "a": 2,
// "b": 3
//}
const objectToString = Object.prototype.toString;
const replacer = (_key, val) => {
// can't use isRef here since @vue/shared has no deps
if (val && val.__v_isRef) {
return replacer(_key, val.value);
}
else if (isMap(val)) {
return {
[`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val]) => {
entries[`${key} =>`] = val;
return entries;
}, {})
};
}
else if (isSet(val)) {
return {
[`Set(${val.size})`]: [...val.values()]
};
}
else if (isObject(val) && !isArray(val) && !isPlainObject(val)) {
return String(val);
}
return val;
};
const toDisplayString = (val) => {
return val == null
? ''
: isArray(val) || (isObject(val) && val.toString === objectToString)
? JSON.stringify(val, replacer, 2)
: String(val);
};
console.log(
toDisplayString(
new Map([
["a", 2],
["b", 3],
])
)
);
==>
{
"Map(2)": {
"a =>": 2,
"b =>": 3
}
}
toNumber 转数字
const toNumber = (val) => {
const n = parseFloat(val);
return isNaN(n) ? val : n;
};
toNumber('111'); // 111
toNumber('a111'); // 'a111'
parseFloat('a111'); // NaN
isNaN(NaN); // true
其实
isNaN本意是判断是不是NaN值,但是不准确的。 比如:isNaN('a')为true。 所以ES6有了Number.isNaN这个判断方法,为了弥补这一个API。
Number.isNaN('a') // false
Number.isNaN(NaN); // true
toRawType 类似typeof
const toRawType = (value) => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString$1(value).slice(8, -1);
};
//toRawType(''); 'String'
小结
- 缓存那里有点东西
- 正则果然写工具函数还是必备的东西呢
- 川哥还是yyds
- replace、Object.freeze