前集回顾
我们在开开心心做几道JavaScript机试题 - 01中吐了槽,也顺势展开了机试题之旅,本章我们暂时压抑自己的吐槽之心,继续就题目前行。仍然希望对各位正确认识JavaScript
这门语言,已经在面试过程中遇到这些问题时,如何思考!
项目地址:fe-interview
答题之路
11 - 请尝试完成一个类似'_.find'的模块
本题主要考查对数组的工具函数的理解、认识。很多朋友习惯了遇事儿就喷一堆
for loop
在那恶心人,忽略变量污染、不必要的遍历操作、含义模糊等问题可能带来的潜在隐患。对抱有这种错误认识的朋友,只要考考这些常见的数组快捷函数的实现原理,原形毕露!^^
答案:
var find = function(array, func) {
for (var i = 0; i < array.length; i++) {
if (func(array[i], i)) {
return array[i];
}
}
};
module.exports = find;
关于这一点,可以阅读:Functional programming in Javascript: map, filter and reduce
12 - 请尝试完成一个类似'_.findlast'的模块
本题和上题的唯一区别在于,寻找最后一个符合条件的指定元素
答案:
var findlast = function(array, func) {
for (var i = array.length - 1; i > -1; i--) {
if (func(array[i], i)) {
return array[i];
}
}
};
module.exports = findlast;
13 - 请尝试完成一个查找出现频率最高元素的模块
这个考察的就是对数组和
literal object
的组合使用
答案:
var findmost = function(array, identity) {
var occurrence = {};
var most;
for (var i = 0; i < array.length; i++) {
var item = array[i];
var id = identity ? identity(item) : item;
if (!occurrence[id]) {
occurrence[id] = {count: 1, raw: item};
} else {
occurrence[id].count++;
}
if (!most || (most !== id && occurrence[id].count > occurrence[most].count)) {
most = id;
}
}
return occurrence[most].raw;
};
module.exports = findmost;
我只用了
for loop
形式,欢迎更简洁写法的PR
14 - 请尝试完成一个类似'_.difference'的模块
本题考查了两个数组的比较,查找第二数组里没有出现的第一个数组的元素。其中对
isNaN
有比较细节的针对
答案:
var isNaN = Number.isNaN;
var difference = function(arr1, arr2) {
return arr1.reduce(function(previous, i) {
var found = arr2.findIndex(function(j) {
return j === i || (isNaN(i) && isNaN(j));
});
return (found < 0 && previous.push(i), previous);
}, []);
};
module.exports = difference;
我的答案用了
reduce
来获取最终结果;如果有朋友对found < 0 && previous.push(i)
不明白,可以看:短路求值。如果对,
的使用不了解,那说明你没看上一章
15 - 请尝试完成一个类似'_.camelCase'的模块
本题考查对字符串的各种处理手段
答案:
var camelcase = function(str) {
return str.toLowerCase()
.replace(/(\s+|-+)(.)/g, function(matched, separator, letter) {
return letter.toUpperCase();
});
};
module.exports = camelcase;
我这里用了一套组合技,先
toLowerCase
,再配合正则表达式,用一个replace
结束战斗
16 - 请尝试完成一个类似'_.times'的模块
本题考查了对数组便捷函数的理解认识,重点在于第二个参数是
callback
,很多人都被String
和Boolean
给吓到了,想不通为什么结果会是那个样子。其实String和Boolean都是构造函数,可以直接调用的。
答案:
var times = function(n, func) {
return Array
.apply([], new Array(n))
.map(function(item, index) {
return func(index);
});
};
module.exports = times;
17 - 请尝试完成一个类似'_.filter'的模块
不多说了,依旧围绕数组。虽然数组的题比较多,但在面试过程中可以选择做答,不必全部都写
答案:
var filter = function(arr, iteratee) {
return arr.reduce(function(previous, item) {
return (iteratee(item) && previous.push(item), previous);
}, []);
};
module.exports = filter;
我的答案仍然利用了
reduce
和短路求值
18 - 请尝试完成一个简单的thunkify函数
这个题目对部分朋友来说,可能又过分了。但相信我,题目不是为了羞辱谁,而是考察关于
JavaScript
,你到底知道多少?函数式编程不是写Javascript
的必需条件,但函数式表达的简洁性,对于代码更深入的理解还是很有帮助的。
关于什么是thunk
?我们先来看看简单定义:“一个封装了特定行为使其可以延迟执行的函数,通常我们称之为thunk
”,如果希望了解更多关于thunk
的内容,看Thoughts On Thunks
答案:
var thunkify = function(func) {
return function() {
var _this = this;
var args = Array.prototype.slice.call(arguments);
return function(cb) {
try {
func.apply(_this, args.concat([cb]));
} catch (e) {
cb(e);
}
};
};
};
module.exports = thunkify;
关于
arguments
为什么需要Array.prototype.slice.call(arguments)
转换成Array
,看arguments
19 - 请尝试完成一个类似'_.zipObject'的模块
答案:
var zipobject = function(arr1, arr2) {
return arr1.reduce(function(previous, key, index) {
return (previous[key] = arr2[index], previous);
}, {});
};
module.exports = zipobject;
本题考查数组合并/压缩,我用了
reduce
来处理。通过最近这几题可以看出来,reduce
真的能做很多事情哦!
20 - 请尝试完成一个类似'_.once'的模块
本题考查对“缓存”的理解,很多人会被这个概念唬住,以为又是什么浏览器缓存啦,服务器缓存啦,甚至有人想到了
redis
、memcached
等工具,然后被吓得半死。其实没那么牛逼,你就是写一个变量,只要不销毁,都可以叫缓存。
答案:
var once = function(func) {
var value,
executed;
return function() {
var args = Array.prototype.slice.call(arguments);
if (!executed) {
executed = true;
value = func.apply(this, args);
return value;
}
return value;
};
};
module.exports = once;
通过设置一个
executed
的标记来判断该函数是否已经执行过了
21 - 请尝试完成一个类似'_.flatMap'的模块
本题考查对
map
的理解。
答案:
var flatmap = function(array, iteratee) {
return Array.prototype.concat.apply([], array.map(iteratee));
};
module.exports = flatmap;
一个小技巧,对于二维数组,利用
concat
函数处理,代码更简洁
22 - 请尝试完成一个简单的middleware模块
多少人写了很久的
express
,结果搞不清middleware
是什么,怎么工作的,如何书写middleware
,怎么使用middleware
。这一切都源于对“中间件”这个概念的模糊,以及对express
中“中间件”的实现原理的不解
答案:
var Middleware = function() {
this.pool = [];
};
Middleware.prototype.use = function(cb) {
this.pool.push(cb.bind(this));
};
Middleware.prototype.start = function(cb) {
var _this = this;
var pullOut = function() {
if (_this.pool.length === 0) {
return cb.call(_this);
}
_this.pool.shift()(pullOut);
};
pullOut();
};
module.exports = Middleware;
关于
middleware
的详细介绍,可以看guide。
23 - 请尝试完成一个URL解析模块
本题考查对
URl
的理解,各部分都是什么意思。在我们过往的经历中,经常发现候选人搞错一些概念,譬如:什么是query parameter
,protocol
指的是哪一段,domain
又是哪一段,我们常说的hash/fragment
是什么?分清楚各部分是什么,方便分解这道题
关于URL
,你可能想看到如下图解:
答案:
var urlparser = function(url) {
var result = /^(?:(https?):)\/\/([\.\-\w]+)(?:([\/\w]+))?(?:\?([\w=&]+))?$/.exec(url);
var parsed = {protocol: result[1], host: result[2]};
if (result[3]) {
parsed.path = result[3];
}
if (result[4]) {
parsed.query = result[4].split('&')
.map((query) => query.split('='))
.reduce((params, pairs) => (params[pairs[0]] = pairs[1], params), {});
}
return parsed;
};
module.exports = urlparser;
这个答案我用了正则和
map
、reduce
,略显臃肿。强烈欢迎更优解法
24 - 请尝试完成一个类似'_.throttle'的模块
这又是个有故事的题,关于“节流阀”,是一种降低计算频率的处理。最常见的使用场景:当页面滚动时计算某一个值,相信如果你直白的在
onScroll
里写了计算行为后会发现,每移动一个像素都会触发一次计算,如果计算量还有一点大的话,卡不死你。这时候就用到了节流阀
的技巧。关于“节流阀”更多详情,请看throttle
答案:
var throttle = function(func, wait) {
var last,
timer;
return function() {
var args = Array.prototype.slice.call(arguments);
var _this = this,
now = new Date().getTime();
if (typeof last === 'undefined') {
last = now;
return func.apply(_this, args);
}
clearTimeout(timer);
if (now - last > wait) {
last = new Date().getTime();
return func.apply(_this, args);
}
timer = setTimeout(function() {
last = new Date().getTime();
func.apply(_this, args);
}, wait + last - now);
};
};
module.exports = throttle;
25 - 请尝试完成一个类似angularjs的依赖注入模块
用过
AngularJS
的朋友肯定都见识到了“依赖注入”的威力(当然如果你是java转型的程序员,更应该有体会),依赖注入是一种解藕手段,我们在java中通常如是介绍:“依赖注入是实现IoC
(控制反转)一种方式”。关于依赖注入,你也可以看手写依赖注入
答案:
var Di = function() {
this.instanceStore = {};
};
Di.prototype.register = function(name, inst) {
this.instanceStore[name] = inst;
};
Di.prototype.run = function(arr) {
var _this = this,
lastIndex = arr.length - 1;
arr[lastIndex].apply(null,
arr.slice(0, lastIndex)
.map(function(name) {
var Inst = _this.instanceStore[name];
if (!Inst) {
throw new Error('You are expecting a non-exist instance');
}
return typeof Inst === 'function' ? new Inst() : Inst;
}));
};
module.exports = Di;
注意我在
run
实现里的typeof Inst === 'function' ? new Inst() : Inst;
使得如果注册的是一个构造函数,那么每次使用都会注入一个新的实例,这和AngularJS
里使用单例的策略不同,这里留下一个问题,如果我希望使用单例策略,答案要做如何修改?
这是余下的15个题目,随时欢迎PR,欢迎指正,欢迎优化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。