前端笔试题(拼多多)
前言
今天面试了拼多多的前端岗位,考了四道笔试题。笔试有点慌,答得一般。分享给同行看看,希望你遇到了能全过!
实现Promise串行执行任务
实现Promise串行执行,并在失败时重试指定次数的函数。如果所有重试都失败,则整个序列会停止执行。
javascript 代码解读复制代码function promiseSeries(tasks, retryTimes) {
// 内部函数,用于执行单个任务,并重试指定次数
function runTask(task, retryCount) {
return new Promise((resolve, reject) => {
task()
.then(resolve)
.catch((error) => {
if (retryCount > 0) {
console.log(`任务失败,正在重试... 剩余重试次数:${retryCount - 1}`);
runTask(task, retryCount - 1).then(resolve).catch(reject);
} else {
reject(error);
}
});
});
}
// 串行执行任务
let result = Promise.resolve();
tasks.forEach((task) => {
result = result.then(() => runTask(task, retryTimes));
});
return result;
}
// 示例使用
const tasks = [
() => new Promise((resolve, reject) => setTimeout(resolve, 1000, '任务1完成')),
() => new Promise((resolve, reject) => setTimeout(reject, 1000, '任务2失败')),
() => new Promise((resolve, reject) => setTimeout(resolve, 1000, '任务3完成'))
];
promiseSeries(tasks, 2)
.then(() => console.log('所有任务完成'))
.catch((error) => console.log(`执行失败:${error}`));
在这个例子中,tasks
是一个包含Promise任务的数组,每个任务都是一个返回Promise的函数。retryTimes
是每个任务失败时重试的次数。如果任务失败并且重试次数用尽,整个序列会停止,并返回一个拒绝的Promise。如果所有任务都成功完成,最终会返回一个解决的Promise。
实现一个compile 函数
实现一个compile函数,支持多级引用变量以及数组,能够解析嵌套的属性和数组索引。下面是一个compile
函数实现:
javascript 代码解读复制代码function compile(str, obj) {
const exp = /\$\{([^}]+)\}/g;
function matchFn(match, p1) {
let curObj = obj;
let value;
p1.split(".").forEach((part) => {
const arrExp = /(\w+)\[(\d+)\]/;
const matchRes = part.match(arrExp);
if (matchRes) {
const [_, name, index] = matchRes;
value = curObj[name] && curObj[name][index];
if (value && typeof value !== "object") {
return value;
}
if (!value) throw new Error(`Property not found: ${match}`);
curObj = value;
} else {
value = curObj[part];
if (value && typeof value !== "object") {
return value;
}
if (!value) throw new Error(`Property not found: ${match}`);
curObj = value;
}
});
return value;
}
return str.replace(exp, matchFn);
}
const template =
"Hello, ${user.name}! Your balance is${user.balance}. You have ${user.items[0]} in your cart. and${user.items[2].kk}";
const exprObj = {
user: {
name: "Alice",
balance: 100.5,
items: ["Item1", "Item2", { kk: 1 }],
},
};
const compiledString = compile(template, exprObj);
console.log(compiledString);
在这个实现中,我们使用了正则表达式来匹配形如${obj.prop}
或${arr[0]}
的插值表达式。在replaceMatch
函数中,我们首先将变量路径(如user.name
或user.items[0]
)分割成多个部分,然后遍历这些部分,逐步解析出最终的值。
注意,这个实现假设表达式对象exprObj
的结构是固定的,并且所有的路径都是有效的。如果路径中的某个属性或索引不存在,函数将返回原始的插值表达式。此外,这个实现不处理循环引用或复杂的对象结构。如果需要处理更复杂的情况,可能需要一个更健壮的路径解析器。
法二:使用with函数实现
javascript 代码解读复制代码function compile(template, context) {
// 替换模板中的插值表达式为 JavaScript 模板字符串语法
const compiledTemplate = template.replace(
/{{(.*?)}}/g,
(match, p1) => `\${${p1.trim()}}`
);
// 动态生成一个函数,用于替换模板中的插值表达式
const compiledFunction = new Function(
"context",
`
with (context) {
return \`${compiledTemplate}\`;
}
`
);
// 调用函数并返回结果
return compiledFunction(context);
}
const template =
"Hello, ${user.name}! Your balance is${user.balance}. You have ${user.items[0]} in your cart. and${user.items[2].kk}";
const exprObj = {
user: {
name: "Alice",
balance: 100.5,
items: ["Item1", "Item2", { kk: 1 }],
},
};
const compiledString = compile(template, exprObj);
console.log(compiledString);
使用with函数是非常取巧的,面试如果想要快速实现,可以使用第法二。
ES6 class关键字的实现
用ES6声明一个class,在es5怎么实现。
javascript 代码解读复制代码function inheritPrototype(subClass, superClass) {
// 复制一份父类的原型
var p = copy(superClass.prototype);
// 修正构造函数
p.constructor = subClass;
// 设置子类原型
subClass.prototype = p;
}
function Parent(name, id){
this.id = id;
this.name = name;
this.list = ['a'];
this.printName = function(){
console.log(this.name);
}
}
Parent.prototype.sayName = function(){
console.log(this.name);
};
function Child(name, id){
Parent.call(this, name, id);
// Parent.apply(this, arguments);
}
inheritPrototype(Child, Parent);
分析打印结果
给出下列代码的打印结果,分析原因。
javascript 代码解读复制代码const temp = {}
class Test {
a(){}
b = () => { }
c = 1;
d = {};
e = temp;
static f = temp
}
const test1 = new Test();
const test2 = new Test();
console.log(test1.a === test2.a)
console.log(test1.b === test2.b)
console.log(test1.c === test2.c)
console.log(test1.d === test2.d)
console.log(test1.e === test2.e)
console.log(test1 === test2)
console.log(Test.f === Test.f)
以下是给定代码的输出结果及其原因分析:
javascript 代码解读复制代码console.log(test1.a === test2.a); // true
console.log(test1.b === test2.b); // false
console.log(test1.c === test2.c); // true
console.log(test1.d === test2.d); // false
console.log(test1.e === test2.e); // true
console.log(test1 === test2); // false
console.log(Test.f === Test.f); // true
原因分析:
console.log(test1.a === test2.a); // true
a
是一个在Test
类原型上定义的方法。由于test1
和test2
都是Test
类的实例,它们会共享原型上的方法,因此test1.a
和test2.a
指向同一个函数。
console.log(test1.b === test2.b); // false
b
是一个通过赋值创建的箭头函数,它是实例自己的属性,而不是原型上的属性。因此,test1.b
和test2.b
是两个不同的函数实例。
console.log(test1.c === test2.c); // true
c
是一个原始值(在这种情况下是数字),原始值是按值传递的,而不是按引用传递。但是,这里比较的是test1.c
和test2.c
的值,由于它们都被赋值为1
,所以比较结果是true
。
console.log(test1.d === test2.d); // false
d
是一个对象,它是通过赋值创建的实例自己的属性。test1.d
和test2.d
是两个不同的对象实例。
console.log(test1.e === test2.e); // true
e
被赋值为同一个外部对象temp
的引用。因此,test1.e
和test2.e
指向同一个对象。
console.log(test1 === test2); // false
test1
和test2
是Test
类的两个不同实例,因此它们不相等。
console.log(Test.f === Test.f); // true
f
是一个静态属性,它属于Test
类本身,而不是类的实例。静态属性在类定义时只会被创建一次,因此Test.f
指向的是同一个对象。
评论记录:
回复评论: