1、手写一个EventBus
// 手写一个事件触发器
class EventEmeitter {
constructor() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || 10;
}
// 监听名为event的事件
on(event, fn) {
if (!this._events[event]) {
(this._events[event] = []).push(fn);
} else {
this._events[event].push(fn);
}
}
// 取消监听某个事件的事件处理回调
off(event, fn) {
let fns = this._events[event];
if (!fns) return false;
if (!fn) {
//如果没有传 fn 的话,就会将event值对应缓存列表中的 fn 都清空
fns && (fns.length = 0);
} else {
for (let i = 0, fnLen = fns.length; i < fnLen; i++) {
let curFn = fns[i];
if (curFn === fn) {
fns.splice(i, 1);
break;
}
}
}
return this;
}
emit(event) {
let fns = this._events[event];
if (!fns || fns.length === 0) {
return false
}
let args = [].slice.call(arguments, 1) fns.forEach(fn => {
fn.apply(this, args)
});
return this;
} // 只监听一次
once(event, fn) {
function onceWrapper(...args) {
fn.apply(this, args);
this.off(event, onceWrapper)
}
this.on(event, onceWrapper) return this;
}
}
EventBus可以参照以下node.js源码
// Copyright Joyent, Inc. and other Node contributors.//// Permission is hereby granted, free of charge, to any person obtaining a// copy of this software and associated documentation files (the// "Software"), to deal in the Software without restriction, including// without limitation the rights to use, copy, modify, merge, publish,// distribute, sublicense, and/or sell copies of the Software, and to permit// persons to whom the Software is furnished to do so, subject to the// following conditions://// The above copyright notice and this permission notice shall be included// in all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE// USE OR OTHER DEALINGS IN THE SOFTWARE.function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined;}module.exports = EventEmitter;// Backwards-compat with node 0.10.xEventEmitter.EventEmitter = EventEmitter;EventEmitter.prototype._events = undefined;EventEmitter.prototype._maxListeners = undefined;// By default EventEmitters will print a warning if more than 10 listeners are// added to it. This is a useful default which helps finding memory leaks.EventEmitter.defaultMaxListeners = 10;// Obviously not all Emitters should be limited to 10. This function allows// that to be increased. Set to zero for unlimited.EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this;};EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isObject(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true;};EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this;};EventEmitter.prototype.on = EventEmitter.prototype.addListener;EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this;};// emits a 'removeListener' event iff the listener was removedEventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this;};EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else if (listeners) { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this;};EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret;};EventEmitter.prototype.listenerCount = function(type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0;};EventEmitter.listenerCount = function(emitter, type) { return emitter.listenerCount(type);};function isFunction(arg) { return typeof arg === 'function';}function isNumber(arg) { return typeof arg === 'number';}function isObject(arg) { return typeof arg === 'object' && arg !== null;}function isUndefined(arg) { return arg === void 0;}
2、手写new
function create(){ // 创建一个控空象 let obj = new Object(); // 获得构造函数, arguments里面的第一个参数 let Con = Array.prototype.shift.call(arguments); // 链接到原型,obj 可以访问到构造函数原型中的属性 obj.prototype = Con.prototype; let result = Con.apply(obj, arguments); // 优先返回构造函数返回的对象 return result instanceof Object ? result : obj; }
3、手写JS柯里化函数
// ES5版本
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function() {
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this, fn, newArgs);
} else {
return fn.apply(this, newArgs);
}
}
}
// ES6 版本
const curry = (fn, ...arr) => (...args) => (
arg => arg.length === fn.length ?
fn(...arg) : curry(fn, ...arg)
)([...arr, ...args])
4、手写instanceof
function myInstanceof(x, y) {
while (x.__proto__ != null) {
if (x.__proto__ === y.prototype) {
return true
}
x.__proto__ = x.__proto__.__proto__
}
if (x.__proto__ == null) {
return false
}
}
5、模拟实现Object.create()
function create(proto) {
function F() {}
F.prototype = proto;
return new F();
}
6、实现一个简单的深克隆-支持数组、对象
function deepCopy(obj) {
if (typeof obj === 'object') {
let result = obj instanceof Array ? [] : {};
for (let i in obj) {
result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i]
}
return result;
} else {
return obj;
}
}
7、求多个数组的交集
const intersection = (...args) => args.reduce((result, cur, index) => {
return index === 0? cur : cur.filter(item => result.includes(item));
},[])