新冠疫情慢慢消退,各行各业裁员跳槽大洗牌,为了收割满意的offer,每天不得不逼着自己天天刷题。下面是来自阿里的面试题,希望可以帮到你。
例1
实现一个 normalize 函数,能将输入的特定的字符串转化为特定的结构化数据。
符串仅由小写字母和[,]组成,且字符串不会包含多余的空格。
示例一: 'abc' --> {value: 'abc'}
示例二:'[abc[bcd[def]]]' -> {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}
通过栈可以用来判断一个表达式中括号是否匹配。栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。数组专门提供了 push() 和 pop() 方法,以便实现类似栈的行为。
function todo(str) {
let s = [];
let list = [];
let obj = {}
for (let i = 0; i < str.length; i++) {
let value = str[i]
switch (value) {
case '[':
s.push({ index: i, value: '[' })
break;
case ']':
let last = s.pop();
list.unshift([last.index, i])
default:
break;
}
}
let [start, end] = list[0]
let parent = obj
for (let i = 1; i < list.length; i++) {
let [a, b] = list[i];
let result = str.slice(start + 1, a) + str.slice(b + 1, end);
start = a;
end = b;
parent.value = result;
parent.children = {};
parent = parent.children;
}
let [x, y] = list[list.length - 1]
parent.value = str.slice(x + 1, y)
return obj
}
例2
实现一个事件收发器 Event 类,继承自此类的对象拥有 on,off,once 和 trigger 方法。
// 例如
const event = new Event();
function log(val) {console.log(val);};
event.on('foo_event', log);
event.trigger('foo_event', 'abc'); // 打印出 abc
event.off('foo_event', log);
event.trigger('foo_event', 'abc'); // 打印出 undefined
这个是Node中的events模块里面的知识,直接上源码。(发布订阅模式大家一定要掌握,这是非常常见的设计模式!)
function checkListener(listener) {
if (typeof listener !== 'function') {
throw new Error('listener', 'Function', listener);
}
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
function EventEmitter() {
EventEmitter.init.call(this)
}
EventEmitter.prototype._events = undefined
EventEmitter.prototype._eventsCount = 0;
EventEmitter.init = function () {
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null)
this._eventsCount = 0;
}
}
function _addListener(target, type, listener) {
let events;
let exiting;
checkListener(listener)
events = target._events
if (events === undefined) {
events = target._events = Object.create(null)
target._eventsCount = 0;
} else {
exiting = events[type]
}
if (exiting === undefined) {
events[type] = listener;
++target._eventsCount;
} else {
if (typeof exiting === 'function') {
exiting = events[type] = [exiting, listener]
} else {
exiting.push(listener)
}
}
return target
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener)
}
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.removeListener = function removeListener (type, listener) {
let list;
let events;
checkListener(listener)
events = this._events;
if (events === undefined) {
return this;
}
list = events[type]
if (list === undefined) {
return this;
}
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) {
this._events = Object.create(null)
} else {
delete events[type];
}
}
}
EventEmitter.prototype.off = EventEmitter.prototype.removeListener
EventEmitter.prototype.trigger = function trigger(type, ...args) {
const events = this._events;
const handler = events[type]
if (handler === undefined) {
console.log(undefined)
return false;
}
if (typeof handler === 'function') {
Reflect.apply(handler, this, args)
} else {
const len = handler.length;
const listeners = arrayClone(handler, len);
for (let i = 0; i < len; i++) {
Reflect.apply(listeners[i], this, args)
}
}
return true
}
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function _onceWrap(target, type, listener) {
let state = { fired: false, wrapFn: undefined, target, type, listener }
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped
}
function onceWrapper(...args) {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
Reflect.apply(this.listener, this.target, args)
}
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener)
this.on(type, _onceWrap(this, type, listener))
return this
}
例3
实现一个 DOM 事件代理方法 delegate,接收一个 dom 元素,有两个方法,分别是添加 on 和 off。
// 例如
delegate(parentElement).on('.childClassname', 'click', callback);
delegate(parentElement).off('.childClassname', 'click', callback);
这里考察事件委托,如果你了解React合成事件原理,把事件都代理到document中,那么你很容易写出来。
function delegate(parentElement) {
function on(childClassname, eventType, listener) {
let list = parentElement.querySelectorAll(childClassname)
Array.from(list, (dom) => {
let eventStore = dom.eventStore || (dom.eventStore = {})
eventStore[eventType] = listener;
})
document.addEventListener(eventType, dispatchEvent, false)
}
function dispatchEvent(event) {
let { type, target } = event
let eventType = type;
while (target) {
let { eventStore } = target;
let listener = eventStore && eventStore[eventType]
if (listener) {
listener.call(target, event)
}
target = target.parentNode;
}
}
function off(childClassname, eventType, listener) {
let list = parentElement.querySelectorAll(childClassname)
Array.from(list, (dom) => {
let eventStore = dom.eventStore
let listener = eventStore && eventStore[eventType]
if (listener) {
eventStore[eventType] = null
}
})
}
return { on, off }
}
解决问题方式多种多样,答案仅供参考,欢迎在评论区留言,说说你对题目的看法。
你以为内容就结束了吗?不,我不仅要告诉你答案,我还要内推,就是这么豪横!
有人想去阿里淘系技术部吗?负责手淘业务,技术是非常有挑战,现在可以内推,简历发送至 pengli.wpl@alibaba-inc.com,如果合适会当天与你联系,处理速度非常快,赶快加入我们吧!!!。
详细内容请看这篇文章介绍:阿里巴巴内推