事件订阅发布
class EventEmitter {
constructor() {
this._events = {}
}
on(evtName, handler) {
if (!this._events) this._events = {}
if (this._events[evtName]) {
this._events[evtName].push(handler)
} else {
this._events[evtName] = [handler]
}
}
emit(evtName, data) {
if (!this._events) this._events = {}
if (this._events[evtName]) {
this._events[evtName].forEach(fn => fn(data))
}
}
off(evtName, handler) {
if (!this._events) this._events = {}
if (this._events[evtName]) {
this._events[evtName] = this._events[evtName].filter(fn => fn !== handler &&fn.l!==handler)
}
}
//只触发一次,就卸载
once(evtName, handler) {
const once=(data)=>{
handler(data)
this.off(evtName,once)
}
once.l=handler
this.on(evtName,once)
}
}
//设置最大监听值
class EventEmitter {
constructor() {
this._events = {}
this.maxListeners=maxListeners||Infinity
}
on(evtName, handler) {
if (!this._events) this._events = {}
if(this.maxListener!+=Infinity&&this.events[event].length>=this.maxListeners){
console.warn(`当前事件${event}超过最大监听数`)
}
if (this._events[evtName]) {
this._events[evtName].push(handler)
} else {
this._events[evtName] = [handler]
}
}
emit(evtName, data) {
if (!this._events) this._events = {}
if (this._events[evtName]) {
this._events[evtName].forEach(fn => fn(data))
}
}
off(evtName, handler) {
if (!this._events) this._events = {}
if (this._events[evtName]) {
this._events[evtName] = this._events[evtName].filter(fn => fn !== handler &&fn.l!==handler)
}
}
//只触发一次,就卸载
once(evtName, handler) {
const once=(data)=>{
handler(data)
this.off(evtName,once)
}
once.l=handler
this.on(evtName,once)
}
}
函数柯里化
记录每次调用时传入的参数,并且和函数的参数个数进行比较,如果不满足个数就返回新函数,如果传入的个数和参数一致,执行原来的函数。
function curring(fn){
const inner=(...args)=>{
return args.length>=fn.length?fn(...args):(...userArgs)=>inner(...args,...userArgs);
}
return inner();
}
实现连续add函数
function Add() {
const nums = [...arguments];
function AddPro() {
nums.push(...arguments);
return AddPro;
}
AddPro.sumOf = () => {
return nums.reduce((a, b) => a + b);
}
return AddPro;
}
compose
function compose(...fn){
if(!fn.length)return v=>v;
if(fn.length===1)return fn[0];
return fn.reduce((acc,cur)=>(...args)=>acc(cur(...args)))
}
Promise
乞丐版
const PENDING="PENDING";
const FULFILLED="FULFILLED";
const REJECTED="REJECTED";
class Promise{
constructor(executor){
this.status=PENDING;
this.value=undefined;
this.reason=undefined;
this.onResolvedCallbacks=[];
this.onRejectedCallbacks=[];
const resolve=(value)=>{
if(this.status===PENDING){
this.value=value;
this.status=FULFILLED;
this.onResolvedCallbacks.forEach(fn=>fn())
}
}
const reject=(reason)=>{
if(this.status===PENDING){
this.reason=reason;
this.status=REJECTED;
this.onRejectedCallbacks.forEach(fn=>fn())
}
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
if(this.status===PENDING){
this.onResolvedCallbacks.push(
()=>{
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(
()=>{
onRejected(this.reason)
})
}
if(this.status===FULFILLED){
onFulfilled(this.value)
}
if(this.status===REJECTED){
onRejected(this.reason)
}
}
}
promiseA+版
实现思路
- 普通的promise
- promise 执行器传入异步代码 callbacks队列保存事件
- promise.then 执行也可能异常 所以都包上try catch
- then链式调用返回promise2 为了拿到primose2 加上setTimeout
- 处理resolve(x) x可能是promise
- resolvePromise
- 返回的x=promise2 reject(new TypeError('error'));
- 返回的是promise
- 返回的不是promise
- called 兼容别人写的promise 保证状态只改变一次
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
// 核心流程
if (promise2 === x) {
return reject(new TypeError('错误'))
}
// 我可能写的promise 要和别人的promise兼容,考虑不是自己写的promise情况
if ((typeof x === 'object' && x !== null) || typeof x === 'function') { // 有可能是promise
// 别人的promise可能调用成功后 还能调用失败~~~ 确保了别人promise符合规范
let called = false;
try { // 有可能then方法是通过defineProperty来实现的 取值时可能会发生异常
let then = x.then;
if (typeof then === 'function') {
// 这里我就认为你是promise了 x.then 这样写会触发getter可能会发生异常
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject); // 直到解析他不是promise位置
}, r => { // reason
if (called) return;
called = true;
reject(r);
});
} else { // {} {then:{}}
resolve(x); // 常量
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x); // 说明返回的是一个普通值 直接将他放到promise2.resolve中
}
}
class Promise {
constructor(executor) {
this.status = PENDING; // promise默认的状态
this.value = undefined; // 成功的原因
this.reason = undefined; // 失败的原因
this.onResolvedCallbacks = []; // 存放成功的回调方法
this.onRejectedCallbacks = []; // 存放失败的回调方法
const resolve = (value) => { // 成功resolve函数
if(value instanceof Promise){
return value.then(resolve,reject)
}
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED; // 修改状态
// 发布
this.onResolvedCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => { // 失败的reject函数
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED // 修改状态
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// then中的参数是可选的
then(onFulfilled, onRejected) { // onFulfilled, onRejected
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; }
// 用于实现链式调用
let promise2 = new Promise((resolve, reject) => {
// 订阅模式
if (this.status == FULFILLED) { // 成功调用成功方法
setTimeout(() => {
try {
let x = onFulfilled(this.value);
// 此x 可能是一个promise, 如果是promise需要看一下这个promise是成功还是失败 .then ,如果成功则把成功的结果 调用promise2的resolve传递进去,如果失败则同理
// 总结 x的值 决定是调用promise2的 resolve还是reject,如果是promise则取他的状态,如果是普通值则直接调用resolve
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) { // 失败调用失败方法
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status == PENDING) { // 代码是异步调用resolve或者reject的
this.onResolvedCallbacks.push(() => { // 切片编程 AOP
setTimeout(() => {
try {
// todo...
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
// todo...
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
})
return promise2
}
static resolve(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
static reject(value){
return new Promise((resolve,reject)=>{
reject(value);
})
}
catch(errorFn){
return this.then(null,errorFn)
}
}
// npm install promises-aplus-tests -g
// 延迟对象 帮我们减少一次套用 : 针对目前来说 应用不是很广泛
Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve= resolve;
dfd.reject = reject;
});
return dfd;
}
Promise.all
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) return reject(new Error("传入参数必须是数组"))
let result = [];
let times = 0;
const processSuccess = (index,val)=>{
result[index] = val;
if(++times === promises.length){
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) { // 并发 多个请求一起执行的
let p = promises[i]
if(p && typeof p.then === 'function'){
p.then((data)=>{
processSuccess(i,data)
},reject); // 如果其中某一个promise失败了 直接执行失败即可
}else{
processSuccess(i,p)
}
}
});
}
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) return reject(new Error("传入参数必须是数组"))
let res = [],
count = 0,
len = promises.length;
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(val => {
count++;
res[i] = val;
if (count === len) {
resolve(res)
}
}).catch(e => reject(e))
}
})
}
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) return reject(new Error("传入参数必须是数组"))
let res = [],
count = 0,
len = promises.length;
const processSuccess = (index, val) => {
res[index] = val
if (++count == len) {
resolve(res)
}
}
for (let i = 0; i < len; i++) {
let p = promises[i]
Promise.resolve(p).then(val => processSuccess(i, val), reject)
}
})
}
Promise.race
Promise.race=function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
let p=promises[i]
if(p && typeof p.then === 'function'){
p.then(resolve,reject)
}else{
resolve(p)
}
}
})
}
可以abort的promise
function warp(p1){
let p=new Promise((resolve,reject)=>{abort=reject})
let p2=Promise.race([p,p1])
p2.abort=abort;
reject p2;
}
Promise.finally
Promise.finally=function(cb){
return this.then((data)=>{
return Promise.resolve(cb()).then(()=>data)
},()=>{
return Promise.resolve(cb()).then(()=>throw err)
} )
}
promiseify
function promiseify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, data) => {
if (err) return reject(err)
resolve(data)
}
})
}
}
function promiseifyAll(obj){
let o={};
for(key in obj){
if(typeof obj[key]==='function'){
o[key+'Promise']=promiseify(o[key])
}
}
return o;
}
promise实现并发限制
function limitLoad(urls, handler, limit) {
const sequence = [].concat(urls);
let promises = [];
promises = sequence.splice(0, limit).map((url, index) => {
return handler(url).then(() => {
return index
})
});
let p = Promise.race(promises);
for (let i = 0; i < sequence.length; i++) {
p = p.then((res) => {
promises[res] = handler(sequence[i]).then(() => {
return res;
})
return Promise.race(promises)
})
}
}
promise实现串行
function executePromises(createPromises) {
var result = Promise.resolve();
createPromises.forEach(function (createPromise) {
result = result.then(createPromise);
});
return result;
}
function createPromise() {
return new Promise((resolve)=>{
doSomthing()
resolve()
});
}
const promiseChain=promiseList.reduce((acc,cur)=>{
return acc.then(cur)
},Promise.resolve())
sleep
function sleep(time){
return new Promise((resolve,reject)=>{
settimeout(()=>{resolve()},time)
})
}
按顺序输出queue
class Queue{
constructor(){
this.queue = [];
}
task(delay, callback){
this.queue.push({
delay,
callback
});
return this;
}
async start(){
for(let i=0;i<this.queue.length;i++){
await new Promise((resolve)=>{
setTimeout(resolve, this.queue[i].delay)
}).then(()=>{
this.queue[i].callback();
})
}
}
}
防抖和节流
debounce
function debounce(func, wait, immediate) {
let timeout, result;
let debounced = function() {
if (timeout) {
clearTimeout(timeout);
}
if (immediate) {
let callNow = !timeout;
+ if (callNow) result = func.apply(this, arguments);
}
timeout = setTimeout(() => {
func.apply(this, arguments);
+ timeout = null;
}, wait);
return result;
}
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
}
throttle
- 先写一个普通的throttle
- now-previous>wait func执行 previous=now
- 加上trailing 最后一次也触发 (默认触发)
- later setTimeout 清除定时器
- 再加上leading==false 延迟第一次点击吧,不要马上触发
- 让第一次走setimeout的逻辑 previous = now
- later那里判断leading==false 0
function(func, wait, options) {
let timeout, context, args, result;
let previous = 0;
if (!options) options = {};
let later = function() {
+ previous = options.leading === false ? 0 : Date.now();
result = func.apply(context, args);
if (!timeout) context = args = null;
};
let throttled = function() {
context = this;
args = arguments;
let now = Date.now();
+ if (!previous && options.leading === false) previous = now;
let remaining = wait - (now - previous);
if (remaining <= 0) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
+ } else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
timeout = context = args = null;
};
return throttled;
};
co
function co(it) {
return new Promise((resolve, reject) => {
function next(data) {
let {
value,
done
} = it.next(data);
if (done) {
resolve(value)
} else {
Promise.resolve(value).then(next, reject)
}
}
next();
})
}
链表
class Node {
constructor(element, next) {
this.element = element;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
_node(index) {
if (index < 0 || index > this.size) throw new Error('越界');
let current = this.head;
for (let i = 0; i < index; i++) {
current = current.next
}
return current
}
add(index, element) {
if (arguments.length === 1) {
element = index
index = this.size
}
if (index < 0 || index > this.size) throw new Error('越界');
if (index === 0) {
let head = this.head;
this.head = new Node(element, head)
} else {
let prevNode = this._node(index - 1);
prevNode.next = new Node(element, prevNode.next)
}
this.size++;
}
remove(index) {
if (index < 0 || index > this.size) throw new Error('越界');
if (index === 0) {
this.head = this.head.next;
}else{
let prevNode=this._node(index-1)
prevNode.next=prevNode.next.next
}
this.size--;
}
}
反转链表
//解法一:递归
function reverse(head) {
if (head == null || head.next == null) return head;
let newHead = reverse(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
//解法二:创建一个新链表
function reverse(head) {
if (head == null || head.next == null) return head;
let newHead = null;
while (head !== null) {
let temp = head.next;
head.next = newHead;
newHead = head;
head = temp;
}
return newHead;
}
树
class Node {
constructor(element, parent) {
this.element = element;
this.parent = parent;
this.left = null;
this.right = null;
}
}
class Tree {
constructor() {
this.root = null;
}
add(element) {
if (this.root === null) {
return this.root = new Node(element);
}
// 可以用递归,用循环就可以了
let currentNode = this.root; // 更新当前节点
let parent;
let compare;
while (currentNode) {
compare = currentNode.element < element;
parent = currentNode; // 遍历前先记录节点
if (compare) { // 作比较 更新节点
// 接着以右边的为根节点
currentNode = currentNode.right
} else {
currentNode = currentNode.left // 插入8的时候 右边没有人了
}
}
// compare; // 放左还是放右边
// parent; // 放到谁的身上
let node = new Node(element, parent)
if (compare) {
parent.right = node
} else {
parent.left = node
}
}
}
前序遍历
preorderTraversal() {
const traversal = (node) => {
if (node === null) return
console.log(node.element); // 先访问根节点
traversal(node.left); // 在访问左子树
traversal(node.right);// 在访问右子树
}
traversal(this.root);
}
中序遍历
inorderTraversal() {
const traversal = (node) => {
if (node === null) return
traversal(node.left);
console.log(node.element);
traversal(node.right);
}
traversal(this.root);
}
后序遍历
postorderTraversal() {
const traversal = (node) => {
if (node === null) return
traversal(node.left);
traversal(node.right);
console.log(node.element);
}
traversal(this.root);
}
层序遍历
levelOrderTraversal() {
if (this.root == null) return;
let stack = [this.root];
let currentNode = null;
let index = 0;
while (currentNode = stack[index++]) {
console.log(currentNode.element);
if (currentNode.left) {
stack.push(currentNode.left);
}
if (currentNode.right) {
stack.push(currentNode.right);
}
}
}
反转二叉树
invertTree(){
if (this.root == null) return;
let stack = [this.root];
let currentNode = null;
let index = 0;
while (currentNode = stack[index++]) {
let tmp = currentNode.left;
currentNode.left = currentNode.right;
currentNode.right = tmp
if (currentNode.left) {
stack.push(currentNode.left);
}
if (currentNode.right) {
stack.push(currentNode.right);
}
}
return this.root;
}
列表转树
[
{
id: 1,
text: '节点1',
parentId: 0 //这里用0表示为顶级节点
},
{
id: 2,
text: '节点1_1',
parentId: 1 //通过这个字段来确定子父级
}
...
]
转成
[
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id:2,
text: '节点1_1',
parentId:1
}
]
}
]
function listToTree(data) {
let temp = {};
let treeData = [];
for (let i = 0; i < data.length; i++) {
temp[data[i].id] = data[i];
}
for (let i in temp) {
if (+temp[i].parentId != 0) {
if (!temp[temp[i].parentId].children) {
temp[temp[i].parentId].children = [];
}
temp[temp[i].parentId].children.push(temp[i]);
} else {
treeData.push(temp[i]);
}
}
return treeData;
}
深拷贝
function copy(obj, hash = new WeakMap()) {
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== "object" || obj === null) return obj;
if (hash.get(obj)) return hash.get(obj);
let res = obj instanceof Array ? [] : {};
hash.set(obj, res);
for (const [k, v] of Object.entries(obj)) {
res[k] = copy(v, hash);
}
return res;
}
function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return target;
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
深度比较
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(注意,参与 equal 的一般不会是函数)
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 两个都是对象或数组,而且不相等
// 1. 先取出 obj1 和 obj2 的 keys ,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 为基准,和 obj2 一次递归比较
for (let key in obj1) {
// 比较当前 key 的 val —— 递归!!!
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3. 全相等
return true
}
Lazyman
- 实现一个LazyMan,可以按照以下方式调用: LazyMan(“Hank”)输出: Hi! This is Hank!
- LazyMan(“Hank”).sleep(10).eat(“dinner”)输出 Hi! This is Hank! //等待10秒.. Wake up after 10 Eat dinner~
- LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出 Hi This is Hank! Eat dinner~ Eat supper~
- LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出 //等待5秒 Wake up after 5 Hi This is Hank! Eat supper
class Lazy {
constructor(name) {
this.sleepFirstTime = 0;
this.promise = Promise.resolve().then(
() => this.sleepFirstTime && this._sleep(this.sleepFirstTime)
).then(() => {
console.log(`Hi! This is ${name}!`);
});
}
sleepFirst(time) {
this.sleepFirstTime = time;
return this;
}
eat(food) {
this.promise = this.promise.then(() => {
console.log(`Eat ${food}~`);
});
return this;
}
sleep(time) {
this.promise = this.promise.then(() => this._sleep(time));
return this;
}
_sleep(time) {
return new Promise((next) => {
setTimeout(() => {
console.log(`Wake up after ${time}`);
next();
}, time);
});
}
}
function LazyMan(name) {
return new Lazy(name);
}
call/bind/apply
!function (proto) {
function getContext(context) {
context = context || window;
let type = typeof context;
if (['number', 'string', 'boolean', 'null'].includes(type)) {
context=Object(context)
}
return context;
}
function call(context, ...args) {
context = getContext(context);
let symbol=Symbol('fn')
context[symbol] = this;
let result = context[symbol](...args);
delete context[symbol];
return result;
}
function apply(context, args) {
context = getContext(context);
let symbol=Symbol('fn')
context[symbol] = this;
let result = context[symbol](...args);
delete context[symbol];
return result;
}
function bind(context, ...bindArgs) {
return (...args) => this.call(context, ...bindArgs, ...args);
}
proto.call = call;
proto.apply = apply;
proto.bind = bind;
}(Function.prototype)
Object.create
Object.create = function(obj) {
function Fn() {};
Fn.prototype = obj;
return new Fn();
}
new
function New(fn, ...args) {
let instance = Object.create(fn.prototype);
let result = fn.call(instance, ...args);
return typeof result === "object" ? result : instance;
}
instanceof
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left);
while(true) {
if(proto == null) return false;
if(proto == right.prototype) return true;
proto = Object.getPrototypeof(proto);
}
}
二分查找
function bsearch(A, x) {
let l = 0,
r = A.length - 1,
guess;
while (l <= r) {
guess = Math.floor((l + r) / 2)
if (A[guess] === x) return guess
else if (A[guess] > x) r = guess - 1
else l = guess + 1
}
return -1
}
数组去重
//1.双层for循环 NaN没有去掉
function uniq(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
//2.先排序后比较 NaN没有去掉,对象去重复失效
function uniq(arr) {
arr.sort();
for (let i = 0; i < arr.length - 1; i++) {
arr[i] === arr[i + 1] && arr.splice(i + 1, 1) && i--;
}
return arr;
}
//3.indexof NaN被去掉了
function uniq(arr) {
let res = [];
for (let i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) === i) {
res.push(arr[i]);
}
}
return res;
}
function uniq2(arr) {
return arr.filter((item, index)=> {
return arr.indexOf(item) === index
})
}
//4.reduce
function uniq6(arr){
return arr.reduce((acc,cur)=>{
if(!acc.includes(cur)){
acc.push(cur)
}
return acc
},[])
}
let uniq2 = arr.sort().reduce((acc, current) => {
if(acc.length === 0 || acc[acc.length-1] !== cur) {
acc.push(cur);
}
return acc;
}, []);
//5.includes 可以去重NaN
function uniq(arr) {
let res = [];
for (let i = 0; i < arr.length; i++) {
if (!res.includes(arr[i])) {
res.push(arr[i]);
}
}
return res;
}
//6.Map 可以去重NaN
function uniq(arr) {
let map = new Map();
for (let i = 0; i < arr.length; i++) {
!map.has(arr[i]) && map.set(arr[i], true);
}
return [...map.keys()];
}
//7.set 可以去重NaN
function uniq(arr) {
return [...new Set(arr)];
}
数组扁平化
// 1.reduce
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
};
function flat(arr, num = 1) {
return num > 0
? arr.reduce(
(pre, cur) =>
pre.concat(Array.isArray(cur) ? flat(cur, num - 1) : cur),
[]
)
: arr.slice();
}
// 2.map
function flatten2(arr) {
let res = [];
arr.map(item => {
if(Array.isArray(item)) {
res = res.concat(flatten2(item));
} else {
res.push(item);
}
});
return res;
}
//3.while ...
function flatten(arr){
while (arr.some(item => Array.isArray(item))){
arr = [].concat(...arr);
}
return arr;
}
//4.stack
function flat(arr) {
const result = [];
const stack = [].concat(arr); // 将数组元素拷贝至栈,直接赋值会改变原数组
//如果栈不为空,则循环遍历
while (stack.length !== 0) {
const val = stack.pop();
if (Array.isArray(val)) {
stack.push(...val); //如果是数组再次入栈,并且展开了一层
} else {
result.unshift(val); //如果不是数组就将其取出来放入结果数组中
}
}
return result;
}
//5.jion split 只用于数字
let flatten1 = a.join(',').split(',').map(Number)
let flatten2 = JSON.stringify(a).replace(/\[|\]/g, '').split(',').map(Number);
对象扁平化
function objectFlat(obj = {}) {
const res = {}
function flat(item, preKey = '') {
Object.entries(item).forEach(([key, val]) => {
const newKey = preKey ? `${preKey}.${key}` : key
if (val && typeof val === 'object') {
flat(val, newKey)
} else {
res[newKey] = val
}
})
}
flat(obj)
return res
}
反转字符串
var reverseString = function(s) {
const n = s.length;
for (let left = 0, right = n - 1; left < right; ++left, --right) {
[s[left], s[right]] = [s[right], s[left]];
}
};
JSON.Stringify
function jsonStringify(obj) {
let type = typeof obj;
if (type !== "object") {
if (/string|undefined|function/.test(type)) {
obj = '"' + obj + '"';
}
return String(obj);
} else {
let json = []
let arr = Array.isArray(obj)
for (let k in obj) {
let v = obj[k];
let type = typeof v;
if (/string|undefined|function/.test(type)) {
v = '"' + v + '"';
} else if (type === "object") {
v = jsonStringify(v);
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
}
}
数组乱序
//Fisher–Yates Shuffle洗牌算法
//先从数组末尾开始,选取最后一个元素,与数组中随机一个位置的元素交换位置
//然后在已经排好的最后一个元素以外的位置中,随机产生一个位置,让该位置元素与倒数第二个元素进行交换
function shuffle(arr) {
let len = arr.length;
while (len) {
let i = (Math.random() * len--) >> 0;
let temp = arr[len];
arr[len] = arr[i];
arr[i] = temp;
}
return arr;
}
//用洗牌算法随机取数
function getRandomArrElement(array, count) {
let arr = array.slice(0),
i = arr.length,
min = i - count,
temp,
index;
while (i > min) {
index = Math.floor((i--) * Math.random());
temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
return arr.slice(min);
}
//在范围内生成随机数
function setRangeRandom(min, max) {
let n = max - min;
if (n == 0) {
return max
} else if (n < 0) {
[max, min] = [min, max];
n = Math.abs(n);
}
return Math.random()* n+min;
}
JSON.parse
//法一
(new Function('return ' + jsonStr))();
//法二
function jsonParse(opt) {
return eval('(' + opt + ')');
}
寄生组合继承
function Super() {}
function Sub() {
Super.call(this)
}
Sub.prototype = new Super();
Sub.constructor = Sub;
实现中划线与驼峰的互相转换
function camelize(str) {
return (str + '').replace(/-\D/g, function(match) {
return match.charAt(1).toUpperCase()
})
}
function hyphenate(str) {
return (str + '').replace(/[A-Z]/g, function(match) {
return '-' + match.toLowerCase();
})
}
实现ajax
function ajax({url, methods, headers}) {
return new Promise((resolve, reject) => {
let xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xhr.open(methods,url);
for(let key in headers) {
let value = headers[key]
xhr.setRequestHeader(key, headers[key]);
}
xhr.onreadystatechange = () => {
if(xhr.readyState === 4) {
if(xhr.status === 200 || xhr.status === 304) {
resolve(xhr.response);
} else {
reject(new Error(xhr.responseText))
}
}
}
xhr.send();
})
}
实现最大并发数
function asyncPool(poolLimit, array, iteratorFn) {
let i = 0;
const ret = [];
const executing = [];
const enqueue = function () {
// 边界处理,array为空数组
if (i === array.length) {
return Promise.resolve();
}
// 每调一次enqueue,初始化一个promise
const item = array[i++];
const p = Promise.resolve().then(() => iteratorFn(item, array));
// 放入promises数组
ret.push(p);
// promise执行完毕,从executing数组中删除
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
// 插入executing数字,表示正在执行的promise
executing.push(e);
// 使用Promise.rece,每当executing数组中promise数量低于poolLimit,就实例化新的promise并执行
let r = Promise.resolve();
if (executing.length >= poolLimit) {
r = Promise.race(executing);
}
// 递归,直到遍历完array
return r.then(() => enqueue());
};
return enqueue().then(() => Promise.all(ret));
}
function sendRequest(urls, num, callback) {
(function request(res) {
urls.length ? Promise.all(urls.splice(0, num).map(url => fetch(url))).then(r => request(res.concat(r))) : callback(res);
})([]);
}
vue2响应式
class Dep {
static stack = []
static target = null
deps = null
constructor() {
this.deps = new Set()
}
depend() {
if (Dep.target) {
this.deps.add(Dep.target)
}
}
notify() {
this.deps.forEach(w => w.update())
}
static pushTarget(t) {
if (this.target) {
this.stack.push(this.target)
}
this.target = t
}
static popTarget() {
this.target = this.stack.pop()
}
}
// reactive
function reactive(o) {
if (o && typeof o === 'object') {
Object.keys(o).forEach(k => {
defineReactive(o, k, o[k])
})
}
return o
}
function defineReactive(obj, k, val) {
let dep = new Dep()
Object.defineProperty(obj, k, {
get() {
dep.depend()
return val
},
set(newVal) {
val = newVal
dep.notify()
}
})
if (val && typeof val === 'object') {
reactive(val)
}
}
// watcher
class Watcher {
constructor(effect) {
this.effect = effect
this.update()
}
update() {
Dep.pushTarget(this)
this.value = this.effect()
Dep.popTarget()
return this.value
}
}
vue3响应式
/**
* Vue3 响应式原理
*
*/
// 判断是不是对象
function isObject(val) {
return typeof val === "object" && val !== null;
}
function hasOwn(target, key) {
return target.hasOwnProperty(key);
}
// WeakMap: 弱引用映射表
// 原对象 : 代理过的对象
let toProxy = new WeakMap();
// 代理过的对象:原对象
let toRaw = new WeakMap();
// 响应式核心方法
function reactive(target) {
// 创建响应式对象
return createReactiveObject(target);
}
function createReactiveObject(target) {
// 如果当前不是对象,直接返回即可
if (!isObject(target)) {
return target;
}
// 如果已经代理过了,就直接返回代理过的结果
let proxy = toProxy.get(target);
if (proxy) {
return proxy;
}
// 防止代理过的对象再次被代理
if (toRaw.has(target)) {
return target;
}
let baseHandler = {
get(target, key, receiver) {
// Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。
let res = Reflect.get(target, key, receiver);
// 收集依赖/订阅 把当前的key和effect做映射关系
track(target, key);
// 在get取值的时候才去判断该值是否是一个对象,如果是则递归(这里相比于Vue2中的默认递归,其实是一种优化)
return isObject(res) ? reactive(res) : res;
},
set(target, key, value, receiver) {
// 这里需要区分是新增属性还是修改属性
let hasKey = hasOwn(target, key);
let oldVal = target[key];
let res = Reflect.set(target, key, value, receiver);
if (!hasKey) {
console.log("新增属性");
trigger(target, "add", key);
} else if (oldVal !== value) {
console.log("修改属性");
trigger(target, "set", key);
}
return res;
},
deleteProperty(target, key) {
let res = Reflect.deleteProperty(target, key);
return res;
},
};
let observed = new Proxy(target, baseHandler);
toProxy.set(target, observed);
toRaw.set(observed, target);
return observed;
}
// 栈 先进后出 {name:[effect]}
let activeEffectStacks = [];
let targetsMap = new WeakMap();
// 如果target中的key发生变化了,就执行数组里的方法
function track(target, key) {
// 拿出栈顶函数
let effect = activeEffectStacks[activeEffectStacks.length - 1];
if (effect) {
// 获取target对应依赖表
let depsMap = targetsMap.get(target);
if (!depsMap) {
targetsMap.set(target, (depsMap = new Map()));
}
// 获取key对应的响应函数集
let deps = depsMap.get(key);
// 动态创建依赖关系
if (!deps) {
depsMap.set(key, (deps = new Set()));
}
if (!deps.has(effect)) {
deps.add(effect);
}
}
}
function trigger(target, type, key) {
let depsMap = targetsMap.get(target);
if (depsMap) {
let deps = depsMap.get(key);
if (deps) {
// 将当前key对应的effect依次执行
deps.forEach((effect) => {
effect();
});
}
}
}
// 响应式 副作用
function effect(fn) {
const rxEffect = function () {
try {
// 捕获异常
// 运行fn并将effect保存起来
activeEffectStacks.push(rxEffect);
return fn();
} finally {
activeEffectStacks.pop();
}
};
// 默认应该先执行一次
rxEffect();
// 返回响应函数
return rxEffect;
}
setTimeout实现setInterval
function timerFun(){
//todo...
let timer=setTimeout(()=>{
timerFun();
clearTimeout(timer)
},1000);
}
requestAnimationFrame实现setInterval
// setInterval实现
function setInterval(callback, interval) {
let timer
const now = Date.now
let startTime = now()
let endTime = startTime
const loop = () => {
timer = window.requestAnimationFrame(loop)
endTime = now()
if (endTime - startTime >= interval) {
startTime = endTime = now()
callback(timer)
}
}
timer = window.requestAnimationFrame(loop)
return timer
}
let a = 0
setInterval(timer => {
console.log(a)
a++
if (a === 3) window.cancelAnimationFrame(timer)
}, 1000)
// 0
// 1
// 2
requestAnimationFrame实现setTimeout
// setTimeout 实现
function setTimeout(callback, interval) {
let timer
const now = Date.now
let startTime = now()
let endTime = startTime
const loop = () => {
timer = window.requestAnimationFrame(loop)
endTime = now()
if (endTime - startTime >= interval) {
callback(timer)
window.cancelAnimationFrame(timer)
}
}
timer = window.requestAnimationFrame(loop)
return timer
}
let a = 0
setTimeout(timer => {
console.log(a)
a++
}, 1000)
// 0
大数相加
function add(a ,b){
let maxLength = Math.max(a.length, b.length);
//用0去补齐长度
a = a.padStart(maxLength , 0);//"0009007199254740991"
b = b.padStart(maxLength , 0);//"1234567899999999999"
//定义加法过程中需要用到的变量
let t = 0;
let f = 0; //"进位"
let sum = "";
for(let i=maxLength-1 ; i>=0 ; i--){
t = parseInt(a[i]) + parseInt(b[i]) + f;
f = Math.floor(t/10);
sum = t%10 + sum;
}
if(f==1){
sum = "1"+ sum;
}
return sum;
}
LRU
var LRUCache = function(capacity) {
this.capacity = capacity;
this.map = new Map();
};
LRUCache.prototype.get = function(key) {
if(this.map.has(key)){
let temp=this.map.get(key)
this.map.delete(key);
this.map.set(key, temp);
return temp
}else{
return -1
}
};
LRUCache.prototype.put = function(key, value) {
if(this.map.has(key)){
this.map.delete(key);
}
this.map.set(key,value);
if(this.map.size > this.capacity){
this.map.delete(this.map.keys().next().value);
}
};
版本号
//排序
arr.sort((a, b) => {
let i = 0;
const arr1 = a.split(".");
const arr2 = b.split(".");
while (true) {
const s1 = arr1[i];
const s2 = arr2[i];
i++;
if (s1 === undefined || s2 === undefined) {
return arr2.length - arr1.length;
}
if (s1 === s2) continue;
return s2 - s1;
}
});
//比大小
var compareVersion = function(version1, version2) {
let arr1=version1.split("."),arr2=version2.split('.');
let maxLen=Math.max(arr1.length,arr2.length);
for(let i=0;i<maxLen;i++){
arr1[i]=arr1[i]===undefined?0:parseInt(arr1[i]);
arr2[i]=arr2[i]===undefined?0:parseInt(arr2[i]);
if(arr1[i]>arr2[i]){return 1}else if(arr1[i]<arr2[i])return -1;
}
return 0;
};
trim
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '')
}
使用css绘制几何图形
//三角形
.triangle {
width: 0;
height: 0;
border-width: 50px;
border-style: solid;
border-color: #8D0EEE transparent transparent transparent;
}
//菱形
.diamond {
width: 100px;
height: 100px;
transform: rotate(45deg);
background: #0CCEf2;
}
//扇形
.sector{
border-radius:80px 0 0;
width: 80px;
height: 80px;
background: #666;
}
css绘制曲线
.ball{
height: 100px;
width: 100px;
border-radius: 50%;
position: absolute;
bottom: 40px;
left: 40px;
background: greenyellow;
}
//定义动画的流程
.run_top_right {
display: block;
animation: run-right-right 3s 0.4s 1 linear, run-right-top 3s 0.4s 1 cubic-bezier(.66,.1,1,.41);
animation-fill-mode: forwards;
}
//向上走的动画初始及结尾值
@keyframes run-right-top {
0% {
bottom: 40px;
}
100% {
bottom: 800px;
}
}
//向上右的动画初始及结尾值
@keyframes run-right-right {
0% {
left: 40px;
transform: scale(0.7);
}
100% {
left: 600px;
transform: scale(0.45);
}
}
左右固定中间自适应布局
//优缺点
//假设高度未知 float absolute grid不能用了
//兼容性
//浮动
脱离文档流
兼容性好
.left{
float: left;
width: 300px;
}
.right{
float: right;
width: 300px;
}
.center{
}
//绝对定位
脱离文档流
.left{
left: 0;
width: 300px
position: absolute;
}
.right{
right: 0;
width: 300px;
position: absolute;
}
.center{
left: 300px;
right: 300px;
position: absolute;
}
//flex
ie8不支持
父容器{
display: flex;
}
.right,.left{
width: 300px;
}
.center{
flex: 1;
}
//tablecell
兼容性好
两边会同时增高
父容器{
display: table;
width: 100%
}
.left{
display: table-cell;
width: 300px
}
.right{
display: table-cell;
width: 300px;
}
.center{
display: table-cell;
}
//网格布局
代码简洁
父容器{
display: grid;
width: 100%;
grid-template-rows: 100px;
grid-template-columns: 300px auto 300px;
}
水平垂直居中
.parent {
display: table-cell;
vertical-align: middle;
}
.son {
margin: 0 auto;
}
.parent2 {
position: relative;
}
.son2 {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.parent3 {
position: relative;
}
.son3 {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.parent4 {
display: flex;
justify-content: center;
align-items: center;
}
.son4 {}
.parent5 {
display: flex;
justify-content: center;
}
.son5 {
align-self: center;
}
.parent6 {
display: grid;
}
.son6 {
align-self: center;
justify-self: center;
}
.parent7 {
display: grid;
}
.son7{
margin:auto;
}