面试题答案
一键面试兼容性相关挑战分析
- 浏览器版本差异
- IE浏览器:IE 10及以下版本在严格模式下同样不支持
arguments.callee
,但在非严格模式下可用。而IE 11在严格模式和非严格模式下均支持arguments.callee
。 - 其他现代浏览器:Chrome、Firefox、Safari等主流现代浏览器从ES5开始严格模式下就禁用了
arguments.callee
。不过在非严格模式下,ES5之前版本是支持的。
- IE浏览器:IE 10及以下版本在严格模式下同样不支持
- JavaScript引擎差异
- V8引擎(Chrome、Node.js):遵循ECMAScript标准,在严格模式下禁用
arguments.callee
。 - SpiderMonkey引擎(Firefox):同样在严格模式下禁用
arguments.callee
。 - JavaScriptCore引擎(Safari):也是在严格模式下禁用
arguments.callee
。
- V8引擎(Chrome、Node.js):遵循ECMAScript标准,在严格模式下禁用
重构方案
- 使用命名函数表达式 命名函数表达式可以在函数内部引用自身,不受严格模式限制。
// 重构前使用arguments.callee
function factorial1(n) {
if (n === 0 || n === 1) {
return 1;
} else {
return n * arguments.callee(n - 1);
}
}
// 重构后使用命名函数表达式
const factorial2 = (function f(n) {
if (n === 0 || n === 1) {
return 1;
} else {
return n * f(n - 1);
}
})
- 使用尾调用优化(TCF) 现代JavaScript引擎支持尾调用优化,这可以防止递归调用栈溢出。
function factorialHelper(acc, n) {
if (n === 0 || n === 1) {
return acc;
} else {
return factorialHelper(acc * n, n - 1);
}
}
function factorial3(n) {
return factorialHelper(1, n);
}
代码示例与测试用例
- 代码示例整合
// 重构前使用arguments.callee
function factorial1(n) {
if (n === 0 || n === 1) {
return 1;
} else {
return n * arguments.callee(n - 1);
}
}
// 重构后使用命名函数表达式
const factorial2 = (function f(n) {
if (n === 0 || n === 1) {
return 1;
} else {
return n * f(n - 1);
}
})
function factorialHelper(acc, n) {
if (n === 0 || n === 1) {
return acc;
} else {
return factorialHelper(acc * n, n - 1);
}
}
function factorial3(n) {
return factorialHelper(1, n);
}
- 测试用例
// 测试用例
const testValues = [0, 1, 5, 10];
testValues.forEach((value) => {
console.log(`Testing with value: ${value}`);
console.log(`Original (arguments.callee): ${factorial1(value)}`);
console.log(`Named Function Expression: ${factorial2(value)}`);
console.log(`Tail Call Optimization: ${factorial3(value)}`);
});
上述测试用例对不同的输入值进行测试,验证重构前后的函数是否能得到相同的正确结果。同时,在不同浏览器(包括IE等旧版本浏览器)中运行该测试代码,可以验证重构方案的兼容性。