JSON.stringify遇到循环引用的解决方案
这是一个自定义的safeStringify函数,可以处理循环引用:
function safeStringify(obj, replacer, space, cycleReplacer) {
const stack = new WeakMap();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (stack.has(value)) {
// 循环引用处理
return cycleReplacer ? cycleReplacer(key, value) : '[Circular]';
}
stack.set(value, true);
}
return replacer ? replacer(key, value) : value;
}, space);
}
memoize缓存接口或计算
export function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = safeStringify(args);
if (cache.has(key)) {
return cache.get(key);
}
// 使用箭头函数以保持正确的`this`上下文
const result = (() => fn.apply(this, args))();
cache.set(key, result);
return result;
};
}
计算两地距离
enum DistanceMode {
/** 默认模式,返回值数字,单位是km */
DEFAULT = 'DEFAULT',
/** 中文模式,返回值为字符串,大于1km使用千米,小于1km使用米 */
CHINESE = 'CHINESE',
/** 英文模式,返回值为字符串,大于1km使用km,小于1km使用m */
ENGLISH = 'ENGLISH',
}
type DistanceReturnType<T> = T extends 'DEFAULT'
? number
: T extends 'CHINESE'
? string
: T extends 'ENGLISH'
? string
: never;
/**
* 使用vincenty算法
*
* 若不传第五个参数为默认模式
*
* mode为DEFAULT是默认模式,返回值数字,单位是m,整数。
*
* mode为CHINESE是中文模式,返回值为字符串,大于1km使用千米,保留两位小数,小于1km使用米,整数。
*
* mode为ENGLISH是英文模式,返回值为字符串,大于1km使用km,保留两位小数,小于1km使用m,整数。
* */
export function distance<T extends keyof typeof DistanceMode>(
lat1: number,
lon1: number,
lat2: number,
lon2: number,
mode: T = 'DEFAULT' as T,
): DistanceReturnType<T> {
const a = 6378137; // 地球长半轴长度
const f = 1 / 298.257223563; // 地球扁率
const b = (1 - f) * a; // 地球短半轴长度
const toRadians = (value) => (value * Math.PI) / 180;
const L = toRadians(lon2 - lon1);
const U1 = Math.atan((1 - f) * Math.tan(toRadians(lat1)));
const U2 = Math.atan((1 - f) * Math.tan(toRadians(lat2)));
const sinU1 = Math.sin(U1);
const cosU1 = Math.cos(U1);
const sinU2 = Math.sin(U2);
const cosU2 = Math.cos(U2);
let lambda = L;
let lambdaP;
let iterLimit = 100;
let sinLambda;
let cosLambda;
let sinSigma;
let cosSigma;
let sigma;
let sinAlpha;
let cosSqAlpha;
let cos2SigmaM;
let C;
do {
sinLambda = Math.sin(lambda);
cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt(
cosU2 * sinLambda * (cosU2 * sinLambda) +
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) *
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda),
);
if (sinSigma === 0) {
return 0 as DistanceReturnType<T>; // 两点重合
}
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = (cosU1 * cosU2 * sinLambda) / sinSigma;
cosSqAlpha = 1 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - (2 * sinU1 * sinU2) / cosSqAlpha;
if (isNaN(cos2SigmaM)) {
cos2SigmaM = 0; // 逆极点
}
C = (f / 16) * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambdaP = lambda;
lambda =
L +
(1 - C) *
f *
sinAlpha *
(sigma +
C *
sinSigma *
(cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
} while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);
if (iterLimit === 0) {
return NaN as DistanceReturnType<T>; // 失败,迭代次数达到限制
}
const uSq = (cosSqAlpha * (a * a - b * b)) / (b * b);
const aA =
1 + (uSq / 16384) * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
const bB = (uSq / 1024) * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
const deltaSigma =
bB *
sinSigma *
(cos2SigmaM +
(bB / 4) *
(cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
(bB / 6) *
cos2SigmaM *
(-3 + 4 * sinSigma * sinSigma) *
(-3 + 4 * cos2SigmaM * cos2SigmaM)));
const s = b * aA * (sigma - deltaSigma);
const modeProcessor = {
[DistanceMode.DEFAULT](dis: number) {
return Math.round(dis);
},
[DistanceMode.ENGLISH](dis: number) {
if (dis < 1) {
return `${Math.round(dis)}m`;
} else {
return `${(dis / 1000).toFixed(2)}km`;
}
},
[DistanceMode.CHINESE](dis: number) {
if (dis < 1) {
return `${Math.round(dis)}米`;
} else {
return `${(dis / 1000).toFixed(2)}千米`;
}
},
};
return modeProcessor[mode](s) as DistanceReturnType<T>;
}
javascript ??运算符作用
在 JavaScript 中,?? 是一个逻辑运算符,被称为“空值合并运算符(Nullish Coalescing Operator)”。
该运算符的作用是,当左边的操作数为 null 或 undefined 时,返回右边的操作数,否则返回左边的操作数。换句话说,如果左边的操作数存在(非 null 或 undefined),则返回左边的值,如果不存在,则返回右边的值。
下面是一个例子:
ini
复制代码
javascriptCopy code
let foo = null ?? 'default string'; // 结果是 'default string'
let bar = undefined ?? 'default string'; // 结果是 'default string'
let baz = 'not nullish' ?? 'default string'; // 结果是 'not nullish'
这种运算符在处理可能为 null 或 undefined 的值时非常有用,例如在设置默认值或者处理可选属性时。
值得注意的是,?? 与 ||(逻辑 OR 运算符)的行为不完全相同。|| 运算符会在左边的操作数为任何 JavaScript “假值”(如 0, '', NaN, false, null, undefined)时返回右边的操作数。而 ?? 运算符只在左边的操作数为 null 或 undefined 时才返回右边的操作数。
ini
复制代码
javascriptCopy code
let foo = '' || 'default string'; // 结果是 'default string'
let bar = '' ?? 'default string'; // 结果是 ''
在这个例子中,空字符串是一个“假值”,但不是 null 或 undefined,因此 ?? 运算符会返回空字符串,而 || 运算符会返回 'default string'。
Array.isArray有什么弊端
Array.isArray(obj)是一个用来判断一个对象是否为数组的方法,返回一个布尔值。它的主要弊端是:
- 只能判断一个对象是否为数组,无法判断一个对象是否为类数组对象或可迭代对象。如果想要判断一个类数组对象或可迭代对象是否为数组,需要使用其他方法或者自定义判断逻辑。
- 在一些较旧的浏览器中可能不支持Array.isArray方法,需要使用其他方式来判断一个对象是否为数组。
- 在某些特殊情况下,Array.isArray方法的返回结果可能会与预期不符。例如,如果在多个窗口之间共享一个数组对象,Array.isArray方法可能会返回false。