数组去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function unique (arr ) { let res = []; for (let i = 0 ; i < arr.length ; i++) { if (res.indexOf (arr[i]) < 0 ) { res.push (arrp[i]); } } return res; } function unique (arr ) { let res = []; for (let item of arr) { if (!res.includes (item)) { res.push (item); } } return res; } function unique (arr ) { const res = arr.filter (function (item, index, array ) { return array.indexOf (item) === index; }); return res; } const unique = (arr ) => [...new Set (arr)];const unique = (arr ) => Array .from (new Set (arr));
数组扁平化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let arr = [1 , [2 , [3 ]]];function flatArr (arr ) { let res = arr.reduce ((accu, curr ) => { return accu.concat (Array .isArray (curr) ? flatArr (curr) : curr); }, []); return res; } console .log (flatArr (arr));
浅拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function shallowCopy (obj ) { if (typeof obj !== 'object' ) return ; let newObj = obj instanceof Array ? [] : {}; if (obj === null ) return obj; if (obj instanceof Date ) return new Date (obj); if (obj instanceof RegExp ) return new RegExp (obj); for (let key in obj) { if (obj.hasOwnProperty (key)) { newObj[key] = obj[key]; } } return newObj; }
深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function deepCone (obj ) { if (typeof obj !== 'object' ) return obj; let newObj = obj instanceof Array ? [] : {}; if (obj === null ) return obj; if (obj instanceof Date ) return new Date (obj); if (obj instanceof RegExp ) return new RegExp (obj); for (let key in obj) { if (obj.hasOwnProperty (key)) { newObj[key] = typeof obj[key] === 'object' ? deepClone (obj[key]) : obj[key]; } } return newObj; }
实现一个 compose(组合)函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function compose (...fn ) { if (!fn.length ) return (v ) => v; if (fn.length === 1 ) return fn[0 ]; return fn.reduce ((accu, curr ) => (...args ) => accu (curr (...args)) ); } 例子: function fn1 (x ) { return x + 1 ; } function fn2 (x ) { return x + 2 ; } function fn3 (x ) { return x + 3 ; } function fn4 (x ) { return x + 4 ; } const res = compose (fn1, fn2, fn3, fn4);console .log (res (5 ));
函数柯里化
函数柯里化就是让我们传递参数的方式不在局限于一次传完,可以分步,所以柯里化的核心就在于等到接收到的参数等于函数参数时再调用函数把所有参数传递进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function curry (fn, ...args ) { return function ( ) { args = [...args, ...arguments ]; if (args.length < fn.length ) { return curry (fn, ...args); } else { return fn.apply (null , args); } }; } function bar (a, b, c ) { return a + b + c; } const f = curry (bar);console .log (f (1 )(2 )(3 ));console .log (f (1 , 2 )(3 ));console .log (f (1 , 2 , 3 ));
assign 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Object .assign = function (target, ...source ) { if (target === null || target === undefined ) { throw new TypeError ('Cannot convert undefined or null to object' ); } let result = Object (target); source.forEach (function (obj ) { if (obj !== null ) { for (let key in obj) { if (obj.hasOwnProperty (key)) { result[key] = obj[key]; } } } }); return result; };
防抖函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function debounce (func, wait ) { let timer = null ; return function ( ) { if (timer) { clearTimeout (timeout); } timer = setTimeout (() => { func.apply (this , arguments ); timer=null ; }, wait); }; }
节流函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function throttle (func, wait ) { const previous = 0 ; return function ( ) { const now = +new Date (); if (now - previous > wait) { func.apply (this , arguments ); previous = now; } }; }
如何把字符串中大小写取反 1 2 3 4 5 6 7 let str = 'LiBoShi' ;str = str.replace (/[a-zA-Z]/g , (content ) => { return content.toUpperCase () === content ? content.toLowerCase () : content.toUpperCase (); });
从 S 中查找 T 字符串,找到返回索引值,没有则返回-1,类似 indexOf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function myIndexOf (T ) { let index = -1 ; for (let i = 0 ; i <= this .length - T.length ; i++) { if (this .substr (i, T.length ) === T) { return (index = i); } } return index; } String .prototype .myIndexOf = myIndexOf;function myIndexOf (T ) { let reg = new RegExp (T); let res = reg.exec (this ); return res === null ? -1 : res.index ; }
new 操作符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function newFn (fn, ...args ) { const obj = Object .create (fn.prototype ); const result = fn.apply (obj, args); return typeof result === 'object' && result !== null ? result : obj; } function Person (name ) { this .name = name; } const p = newFn (Person , 'Jerome' );console .log ('p.name :>> ' , p.name );
call 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Function .prototype .myCall = function (context, ...args ) { const ctx = context || window ; const fn = Symbol (); ctx[fn] = this ; const res = ctx[fn](...args); delete ctx[fn]; return res; }; let obj = { desc : function (from , to ) { console .log (`${this .name} 来自${from } 去往${to} ` ); }, }; let person = { name : 'lbs' , }; obj.desc .myCall (person, '北京' , '上海' );
apply 1 2 3 4 5 6 7 8 9 10 11 Function .prototype .myApply = function (context, args = [] ) { const ctx = context || window ; const fn = Symbol (); ctx[fn] = this ; const res = ctx[fn](...args); delete ctx[fn]; return res; };
bind 1 2 3 4 5 6 7 8 9 10 11 12 13 Function .prototype .myBind = function (content, ...args ) { const fn = this ; return function newFn (...newFnArgs ) { if (this instanceof newFn) { return new fn (...args, ...newFnArgs); } return fn.myApply (context, [...args, ...newFnArgs]); }; };
instanceof 1 2 3 4 5 6 7 8 9 10 11 function _instanceof (left, right ) { if (!left.__proto__ ) return false ; if (right === Object || left.__proto__ === right.prototype ) return true ; return _instanceof (left.__proto__ , right); } _instanceof (/123/ , RegExp );
手写 Object.is 1 2 3 4 5 6 7 8 9 function is (x, y ) { if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { return x !== x && y !== y; } }
ajax 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function ajax (method, url, headers, body ) { return new Promise ((resolve, reject ) => { let req = null ; try { req = new XMLHttpRequest (); } catch { req = new ActiveXObject ('Microsoft.XMLHTTP' ); } req.open (method, url); for (let key in headers) { req.setRequestHeader (key, headers[key]); } req.onreadystatechange (() => { if (req.readystate === 4 ) { if (req.status >= 200 && req.status <= 300 ) { resolve (req.responseText ); } else { reject (req); } } }); req.send (body); }); }
map 1 2 3 4 5 6 7 8 9 10 11 12 Array .prototype .map = function (fn, toThis ) { const arr = this ; const result = []; const _this = toThis || Object .create (null ); for (let i = 0 ; i < arr.length ; i++) { const item = fn.call (_this, arr[i], i, arr); result.push (item); } return result; };
手写 promise 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 class Promise1 { static PENDING = 'pending' ; static FULFILLED = 'fulfilled' ; static REJECTED = 'rejected' ; constructor (executor ) { this .status = Promise .PENDING ; this .value = null ; this .reason = null ; this .onFulfilledCallbacks = []; this .onRejectedCallbacks = []; try { executor (this .resolve .bind (this ), this .reject .bind (this )); } catch (e) { this .reject (e); } } resolve (value ) { if (this .status === Promise .PENDING ) { this .status = Promise .FULFILLED ; this .value = value; setTimeout (() => { this .onFulfilledCallbacks .forEach ((cb ) => cb (this .value )); }); } } reject (reason ) { if (this .status === Promise .PENDING ) { this .status = Promise .REJECTED ; this .reason = reason; setTimeout (() => { this .onRejectedCallbacks .forEach ((cb ) => cb (this .reason )); }); } } then (onFulfilled, onRejected ) { if (typeof onFulfilled !== 'function' ) { onFulfilled = () => this .value ; } if (typeof onRejected !== 'function' ) { onRejected = () => this .reason ; } return new Promise1 ((resolve, reject ) => { if (this .status === Promise .PENDING ) { return new Promise1 ((resolve, reject ) => { this .onFulfilledCallbacks .push (() => { this .parse (onFulfilled (this .value ), resolve, reject); }); }); return new Promise1 ((resolve, reject ) => { this .onRejectedCallbacks .push (() => { this .parse (onRejected (this .value ), resolve, reject); }); }); } if (this .status === Promise .FULFILLED ) { setTimeout (() => { return new Promise1 ((resolve, reject ) => { this .parse (onFulfilled (this .value ), resolve, reject); }); }); } if (this .status === Promise .REJECTED ) { setTimeout (() => { return new Promise1 ((resolve, reject ) => { this .parse (onRejected (this .reason ), resolve, reject); }); }); } }); } parse (result, resolve, reject ) { try { if (result instanceof Promise1 ) { result.then (resolve, reject); } else { resolve (result); } } catch (error) { reject (error); } } static resolve (value ) { return new Promise1 ((resolve, reject ) => { if (value instanceof Promise1 ) { value.then (resolve, reject); } else { resolve (value); } }); } static reject (reason ) { return new Promise1 ((resolve, reject ) => { reject (reason); }); } static all (promises ) { const result = []; return new Promise1 ((resolve, reject ) => { promises.forEach ((promise ) => { promise.then ( (value ) => { result.push (value); if (result.length === promises.length ) { resolve (result); } }, (reason ) => { reject (reason); } ); }); }); } static race (promises ) { return new Promise1 ((resolve, reject ) => { promises.forEach ((promise ) => { promise.then ( (value ) => { resolve (value); }, (reason ) => { reject (reason); } ); }); }); } } let aaa = new Promise1 ((resolve, reject ) => { resolve ('lbs' ); }).then ((value ) => { console .log (value); return '666' ; }); aaa.then (() => { console .log (123 ); }); console .log (aaa);
手写 promise.allSettled 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 function PromiseAllSettled (promiseArr ) { return new Promise ((resolve, reject ) => { if (!Array .isArray (promiseArr)) { reject (new TypeError ('arguments must be an array' )); } const res = []; let count = 0 ; for (let i = 0 ; i < promiseArr.length ; i++) { Promise .resolve (promiseArr[i]) .then ((value ) => { res[i] = { status : 'fulfilled' , value, cost : Date .now () - startTime, }; }) .catch ((reason ) => { res[i] = { status : 'rejected' , reason, cost : Date .now () - startTime, }; }) .finally (() => { count++; if (count == promiseLen) { resolve (res); } }); } }); }
手写 event bus 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class EventEmitter { constructor ( ) { this .events = {}; this .maxListeners = maxListeners || Infinity ; } emit (event, ...args ) { const cbs = this .events [event]; if (!cbs) { console .log ('没有这个事件函数' ); return this ; } cbs.forEach ((cb ) => cb.apply (this , args)); return this ; } on (event, cb ) { if (!this .events [event]) { this .events [event] = []; } if ( this .maxListeners !== Infinity && this .events [event].length >= this .maxListeners ) { console .log ('当事件超过了最大监听数' ); return this ; } this .events [event].push (cb); return this ; } once (event, cb ) { const fn = (...args ) => { this .off (event, fn); cb.apply (this , args); }; this .on (event, func); return this ; } off (event, cb ) { if (!cb) { this .events [event] = null ; } else { this .events [event] = this .events [event].filter ((item ) => item !== cb); } return this ; } }
继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 function Person ( ) { this .name = 'lbs' ; } function Student ( ) { Person .call (this ); this .age = 18 ; } const s = new Student ();function Person ( ) { this .name = 'lbs' ; } function Student ( ) { this .age = 18 ; } Student .prototype = new Person ();const s = new Student ();function Person (obj ) { this .name = obj.name ; } function Student (obj ) { Person .call (this , obj); this .age = obj.age ; } Student .prototype = Object .create (Person .prototype );Student .prototype .constructor = Student ;Student .prototype = Object .create (Person .prototype , { constructor : { value : Student , enumerable : false , writable : true , configurable : true , }, }); const student = new Student ({ name : 'lbs' , age : 18 });console .log (student);
createElement 手写 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 const symbolFor = Symbol .for ;const REACT_ELEMENT_TYPE = symbolFor ('react.element' );const RESERVED_PROPS = { key : true , ref : true , __self : true , __source : true , }; function createElement (type, config, children ) { const props = {}; let key = null ; if (config !== null ) { key = config.key ; } for (let propName in config) { if (!RESERVED_PROPS .hasOwnProperty (propName)) { props[propName] = config[propName]; } } const childrenLength = arguments .length - 2 ; if (childrenLength === 1 ) { props.children = children; } else if (childrenLength > 1 ) { const childArray = Array (childrenLength); for (let i = 0 ; i < childrenLength; i++) { childArray[i] = arguments [i + 2 ]; } props.children = childArray; } const element = { $$typeof : REACT_ELEMENT_TYPE , type, key, props, }; }
编写正则,验证一个 6 ~ 16 位的字符串,必须同时包含大小写字母和数字 1 2 3 4 5 6 7 正向预查 ?= 必须 反向预查 ?!必须不 let reg = /(?!^[a-zA-Z]+$)(?!^[0-9]+$)(?!^[a-z0-9]+$)(?!^[A-Z0-9]+$)^[a-zA-Z0-9]{6,16}$/ ;补充:数字、字母、下划线组成字符串,必须有_ let reg = /(?=_)\w/ ;let reg = /(?!^[a-zA-Z0-9]+$)^\w{1,10}$/ ; 限制1 -10 位
获取所有属性为 name,值为 value 的元素集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function getElements (property, value ) { let elements = document .getElementsByTagName ('*' ); let arr = []; elements = Array .from (elements); elements.forEach ((item ) => { let itemValue = item.getAttribute (propertype); if (property === 'class' ) { const reg = new RegExp (`\b${value} \b` ); if (reg.test (itemValue)) { arr.push (item); } } if (itemValue === value) { arr.push (item); } }); return arr; }
英文字母汉字组成的字符串,用正则给英文单词前后加空格 1 2 3 4 5 6 7 8 let str = '中国hello你好' ;let reg = /\b[a-z]+\b/gi ;str = str .replace (reg, (value ) => { return ` ${value} ` ; }) .trim ();
js 实现斐波那契数列的几种方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 function fibonacci (n ) { if (n === 1 || n === 2 ) { return 1 ; } return fibonacci (n - 1 ) + fibonacci (n - 2 ); } function fibonacci (n, res1 = 1 , res2 = 1 ) { if (n <= 2 ) return res2; return fibonacci (n - 1 , res2, res1 + res2); } function fibonacci (n ) { let num1 = 1 ; let num2 = 2 ; let sum = 1 ; for (let i = 3 ; i < n; i++) { sum = num1 + num2; num1 = num2; num2 = sum; } return sum; } function fibonacci (n ) { const arr = [0 , 1 , 1 ]; if (n < 0 ) { throw new Error ('输入的数字不能小于0' ); } if (n >= 3 ) { for (let i = 3 ; i <= n; i++) { arr[i] = arr[i - 1 ] + arr[i - 2 ]; } } return arr[n]; }
js 并发调度器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const sleep = (time ) => new Promise ((resolve ) => setTimeout (resolve, time));const scheduler = new Scheduler (2 );const addTask = (time, val ) => { scheduler.add (() => { return sleep (time).then (() => console .log (val)); }); }; addTask (1000 , '1' );addTask (500 , '2' );addTask (300 , '3' );addTask (400 , '4' );class Scheduler { constructor (max ) { this .max = max; this .count = 0 ; this .queue = []; } add (p ) { this .queue .push (p); this .start (); } start ( ) { if (this .count >= this .max || !this .queue .length ) return ; this .count ++; this .queue .shift ()() .finally (() => { this .count --; this .start (); }); } }
并发加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function limitLoad (urls, handler, limit ) { let promises = []; const limitUrls = urls.slice (0 , limit); const restUrls = urls.slice (limit); promises = limitUrls.map ((url, index ) => { return handler (url).then (() => { return index; }); }); let p = Promise .race (promises); for (let i = 0 ; i < restUrls.length ; i++) { p = p.then ((index ) => { promises[index] = handler (restUrls[i]).then (() => { return index; }); return Promise .race (promises); }); } } function loadImg (url ) { return new Promise ((resolve, reject ) => { setTimeout (() => { console .log (url.info + '---OK!!!' ); resolve (); }, url.time ); }); } let urls = [ { info : 1 , time : 2000 }, { info : 2 , time : 1000 }, { info : 3 , time : 3000 }, { info : 4 , time : 4000 }, { info : 5 , time : 5000 }, ]; limitLoad (urls, loadImg, 3 );
并发控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import axios from 'axios' ;const requestQueue = (concurrency = 6 ) => { const queue = []; const current = 0 ; const dequeue = ( ) => { while (current < concurrency && queue.length ) { current++; const curRequest = queue.shift (); curRequest .then (() => {}) .catch (() => {}) .finally (() => { current--; dequeue (); }) } } return (request ) => { queue.push (request); dequeue (); } } const enqueue = requestQueue (3 );for (let i = 0 ; i < reqs.length ; i++) { enqueue (() => axios.get ('/api/test' + i)) }
前端内存处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 1. 内存的生命周期 内存分配:声明变量、函数对象的时候,js会自动分配内存 内存使用 内存回收 2. js中的垃圾回收机制 引用计数:缺点循环引用无法清除 标记清除 3. 常见内存泄漏 全局变量(记得手动回收) 未被清除的定时器 闭包 dom的引用 4. 怎么避免内存泄漏 减少不必要的全局变量 使用完数据,及时解除引用 实现sizeOf,传入object,计算其所占字节大小 number : 8 字节string : 2 字节boolean; 4 字节 const seen = new WeakSet ();function sizeOfObject (obj ) { if (obj === null ) { return 0 ; } let bytes = 0 ; const keys = Object .keys (obj); for (let i = 0 ; i < keys.length ; i++) { const key = keys[i]; bytes += calculator (key); if (typeof obj[key] === 'object' && obj[key] !== null ) { if (seen.has (obj[key])) { continue ; } seen.add (obj[key]) } bytes += calculator (obj[key]) } } function calculator (obj ) { const objType = typeof obj; switch (objType) { case 'string' : { return obj.length * 2 } case 'boolean' : { return 4 } case 'number' : { return 8 } case 'object' : { if (Array .isArray (obj)) { return obj.map (calculator).reduce ((accu, curr ) => { return accu + curr }, 0 ) } else { return sizeOfObject (obj) } } default : { return 0 } } }
数据结构就是在计算机中存储和组织数据的方式。
算法(Algorithm)解决问题的逻辑或步骤
封装函数使字符串以驼峰式命名
已知字符串 foo = ‘get-element-by-id’,写一个函数将其转换为驼峰式命名“getElementById”
1 2 3 4 5 6 var foo = 'get-element-by-id' ;var arr = foo.split ('-' );for (var i = 1 ; i < arr.length ; i++) { arr[i] = arr[i].charAt (0 ).toUpperCase () + arr[i].substring (1 ); } console .log (arr.join ('' ));
1 2 3 4 5 6 7 8 9 10 11 //封装 function toString(foo) { var arr = foo.split('-'); for(var i = 1; i < arr.length; i++) { arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length - 1) } return arr.join(''); } console.log(toString('get-element-by-id'))
把 the-first-name 变成 theFirstName
1 2 3 4 5 6 var reg = /-(\w)/g var str = "the-first-name" console.log(str.replace(reg, function($, $1){ return $1.toUpperCase() }) )
把 aabb 换成 bbaa
1 2 3 4 5 6 7 8 var reg = /(\w)\1(\w)\2/g ;var str = 'aabb' ;console .log ( str.replace (reg, function ($, $1 , $2 ) { return $2 + $2 + $1 + $1 ; }) );
正则简单的去重
1 2 3 var str = "aaaabbbbbccccc" ;var reg = /(\w)\1*/g ;console .log .replace (reg,"$1" ));
定制化输出特定数组
随机生成一个长度为 10 的整数类型的数据
例如 [2, 10, 3, 35, 5, 11, 10, 11, 20]
将其排列成一个新数组,要求新数组形式如下:
[[2, 3, 5], [10, 11],[20],[35]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function getRandomNumber (min, max ) { min = Math .ceil (min); max = Math .floor (max); return Math .floor (Math .random () * (max - min + 1 ) + min); } let arr = Array .from ({ length : 10 }, () => getRandomNumber (0 , 99 ));arr = [...new Set (arr)]; arr.sort ((a, b ) => a - b); const map = {};arr.forEach ((item ) => { const key = Math .floor (item / 10 ); if (!map[key]) { map[key] = []; } map[key].push (item); }); const result = [];for (const key in map) { result.push (map[key]); } console .log (result);
深度比较 isEqual 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function isEqual (obj1, obj2 ) { if (!isObject (obj1) || !isObject (obj2)) { return obj1 === obj2; } if (obj1 === obj2) { return true ; } const obj1Keys = Object .keys (obj1); const obj2Keys = Object .keys (obj2); if (obj1Keys.length !== obj2Keys.length ) { return false ; } for (let key in obj1) { if (!isEqual (obj1[key], obj2[key])) { return false ; } } return true ; }
根据数组的 key 去重 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const dedup = (data, getKey = () => {} ) => { const dateMap = data.reduce ((pre, cur ) => { const key = getKey (cur); if (!pre[key]) { pre[key] = cur; } return pre; }, {}); return Object .values (dateMap); }; let data = [ { id : 1 , v : 1 }, { id : 2 , v : 2 }, { id : 1 , v : 1 }, ]; console .log (dedup (data, (item ) => item.id ));
react 自定义封装不会反复创建的定时器(setInterval) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { useRef, useState } from 'react' ;const useTimer = (step = 1 ) => { const timer = useRef (null ); const [num, setNum] = useState (0 ); const start = ( ) => { const timeout = setInterval (() => { setNum ((num ) => num + 1 ); }, step * 1000 ); timer.current = timeout; }; const clear = ( ) => { setNum (0 ); clearInterval (timer.current ); }; return { num, start, clear, }; };
修改下面代码,顺序输出 0-99 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function print (n ) { setTimeout (() => { console .log (n); }, Math .floor (Math .random () * 1000 )); } for (var i = 0 ; i < 100 ; i++) { print (i); } function print (n ) { setTimeout ( (() => { console .log (n); return () => {}; })(), Math .floor (Math .random () * 1000 ) ); } for (var i = 0 ; i < 100 ; i++) { print (i); } function print (n ) { setTimeout ( () => { console .log (n); }, 10 , Math .floor (Math .random () * 1000 ) ); } for (var i = 0 ; i < 100 ; i++) { print (i); }
for 循环和 splice 的坑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const arr = ['a' , 'a' , 'a' , 'd' , 'e' , 'f' ];for (let i = 0 ; i < arr.length ; i++) { if (arr[i] === 'a' ) { arr.splice (i, 1 ); i--; } } const arr = ['a' , 'a' , 'a' , 'd' , 'e' , 'f' ];for (let i = arr.length - 1 ; i >= 0 ; i--) { if (arr[i] === 'a' ) { arr.splice (i, 1 ); } } const arr = ['a' , 'a' , 'a' , 'd' , 'e' , 'f' ];for (let index in arr) { if (arr[index] === 'a' ) { arr.splice (index, 1 ); index--; } } console .log (arr);
复杂度
程序执行时需要的计算量和内存空间
复杂度是数量级,不是具体的数字
一般是针对一个具体的算法,而非一个完整的系统
将一个数组旋转 K 步
输入一个数组[1, 2, 3, 4, 5, 6, 7]
k=3,即旋转 3 步
输出[5, 6, 7, 1, 2, 3, 4]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 function rotate1 (arr: number [], k: number ): number [] { const length = arr.length ; if (!k || length === 0 ) return arr; const step = Math .abs (k % length); for (let i = 0 ; i < step; i++) { const n = arr.pop (); if (n != null ) { arr.unshift (n); } } return arr; } function rotate2 (arr: number [], k: number ): number [] { const length = arr.length ; if (!k || length === 0 ) return arr; const step = Math .abs (k % length); const part1 = arr.slice (-step); const part2 = arr.slice (0 , length - step); const part3 = part1.concat (part2); return arr; } const arr = [];for (let i = 0 ; i < 10 * 10000 ; i++) { arr.push (i); } console .time ('rotate1' );rotate1 (arr, 9 * 10000 );console .time ('rotate1' );console .time ('rotate2' );rotate2 (arr, 9 * 10000 );console .time ('rotate2' );
两个栈实现一个队列
请用两个栈实现一个队列
API:add delete length
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Queue { private stack1 : number [] = []; private stack2 : number [] = []; add (n: number ) { this .stack1 .push (n); } delete (): number | null { let res; while (stack1.length ) { const n = stack1.pop (); if (n != null ) { stack2.push (n); } } res = stack2.pop (); while (stack2.length ) { const n = stack2.pop (); if (n != null ) { stack1.push (n); } } return res || null ; } get length (): number { return this .stack1 .length ; } }
定义一个 js 函数,反转单向链表 链表是一种物理结构(非逻辑结构),类似数组 数组需要一段连续的内存空间,而链表是零散的 链表节点的数据结构{ value, next?, prev? }
链表 vs 数组 都是有序结构 链表:查询慢 O(n),新增和删除快 O(1) 数组:查询快 O(1),新增和删除慢 O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 interface ILinkListNode { value : number ; next?: ILinkListNode ; } function createLinkList (arr: number [] ): ILinkListNode { const length = arr.length ; if (length === 0 ) throw new Error ('arr is empty' ); let curNode : ILinkListNode = { value : arr[length - 1 ], }; if (length === 1 ) return curNode; for (let i = length - 2 ; i >= 0 ; i--) { curNode = { value : arr[i], next : curNode, }; } return curNode; } const arr = [100 , 200 , 300 ];const list = createLinkList (arr);console .info ('list' , list);function reverseLinkList (listNode: ILinkListNode ): ILinkListNode { let prevNode : ILinkListNode | undefined ; let curNode : ILinkListNode | undefined ; let nextNode : ILinkListNode | undefined = listNode; while (nextNode) { if (curNode && !prevNode) { delete curNode.next ; } if (curNode && prevNode) { curNode.next = prevNode; } prevNode = curNode; curNode = nextNode; nextNode = nextNode?.next ; } curNode!.next = prevNode; return curNode!; } const reverseList = reverseLinkList (list);console .info ('reverseList' , reverseList);
栈(封装) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 function Stack ( ) { this .items = []; Stack .prototype .push = function (element ) { this .items .push (element); }; Stack .prototype .pop = function ( ) { return this .items .pop (); }; Stack .prototype .peek = function ( ) { return this .items [this .items .length - 1 ]; }; Stack .prototype .isEmpty = function ( ) { return !this .items .length ; }; Stack .prototype .size = function ( ) { return this .item .length ; }; Stack .prototype .toString = function ( ) { let res = '' ; for (let i = 0 ; i < this .items .length ; i++) { res += `${this .items[i]} ` ; } return res; }; } const stack = new Stack ();function dec2bin (decimalNumber ) { let stack = new Stack (); let binary = '' ; while (decimalNumber > 0 ) { stack.push (decimalNumber % 2 ); decimalNumber = Math .floor (decimalNumber / 2 ); } while (!stack.isEmpty ()) { binary += stack.pop (); } return binary; }
深度封装 typeof 判断 function myTypeof(val) {
var type = typeof(val)
var res = {
'[object Object]' : 'object',
'[object Array]' : 'array',
'[object Number]' : 'object number',
'[object String]' : 'object string',
'[object Boolean]' : 'object boolean'
}
if (val === null) {
return 'null'
} else if (type == 'object') {
var str = Object.prototype.toString.call(val)
return res[str]
} else {
return type
}
}
翻转链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 var reverseList = function (head ) { if (head === null || head.next === null ) return head; let res = reverseList (head.next ); head.next .next = head; head.next = null ; return res; }; var reverseList = function (head ) { let pre = null ; let cur = head; if (cur === null || cur.next === null ) { return cur; } while (cur) { const t = cur.next ; cur.next = pre; pre = cur; cur = t; } return pre; };
打乱数组 1. 常见的sort打乱数组的方法
function shuffle(arr) {
return arr.sort (function () {
return Math.random() - 0.5
})
}
var arr = [1,2,3,4,5,6,7]
shuffle(arr)
更加简洁的ES6写法
function shuffle(arr) {
return arr.sort(() => Math.random() - 0.5)
}
但是这种写法有问题,并不能真正地随机打乱数组,经过大量的实验发现
每个元素仍然有很大的几率出现在它原来的位置附近。
2.洗牌算法
从最后一个数据开始往前,每次随机一个位置,将两者的位置进行交换,直到数组交换完毕。
ES6实现:
function shuffle(arr) {
let i = arr.length;
while(i) {
let j = Math.floor(Math.random() * i--);
[arr[j], arr[i]] = [arr[i], arr[j]];
}
return arr
}
var arr = [1,2,3,4,5,6,7]
shuffle(arr)
用链表实现队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 interface ILinkListNode { value : number ; next : ILinkListNode | null ; } class Queue { private head : ILinkListNode | null = null ; private tail : ILinkListNode | null = null ; private len = 0 ; add (n: number ) { const newNode : ILinkListNode = { value : n, next : null , }; if (this .head === null ) { this .head = newNode; } if (this .tail ) { this .tail .next = newNode; } this .tail = newNode; this .len ++; } delete (): number | null { if (this .head === null || this .len <= 0 ) return null ; const value = this .head .value ; this .head = this .head .next ; this .len --; return value; } get length (): number { return this .len ; } } const queue = new Queue ();queue.add (100 ); queue.add (200 ); queue.add (300 ); console .log ('length' , queue.length ); console .log (queue.delete ()); console .log ('length2' , queue.length );
链表和数组,哪个实现队列更快?
空间复杂度都是 O(n)
add 时间复杂度:链表 O(1),数组 O(1);
delete 时间复杂度:链表 O(1),数组 O(n);
斐波那契数列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function fibonacci (n: number ): number { if (n <= 0 ) return 0 ; if (n === 1 ) return 1 ; return fibonacci (n - 1 ) + fibonacci (n - 2 ); } function fibonacci (n: number ): number { if (n <= 0 ) return 0 ; if (n === 1 ) return 1 ; let n1 = 1 ; let n2 = 0 ; let res = 0 ; for (let i = 2 ; i <= n; i++) { res = n1 + n2; n2 = n1; n1 = res; } return res; }
青蛙跳台阶(动态规划思想解决问题)
一只青蛙,一次可以跳 1 级,也可以跳 2 级
问:青蛙跳到 n 级台阶,总共有多少种方式?
将数组中的 0 移动到末尾
如输入[1, 0, 3, 0, 11, 0],输出[1, 3, 11, 0, 0, 0]
只移动 0 ,其他顺序不变
必须在原数组进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 function moveZero1 (arr: number [] ): void { const length = arr.length ; if (length === 0 ) return ; let zeroCount = 0 ; for (let i = 0 ; i < length - zeroCount; i++) { if (arr[i] === 0 ) { arr.push (0 ); arr.splice (i, 1 ); i--; zeroCount++; } } } function moveZero2 (arr: number [] ): void { const length = arr.length ; if (length === 0 ) return ; let i; let j = -1 ; for (i = 0 ; i < length; i++) { if (arr[i] === 0 ) { if (j < 0 ) { j = i; } } if (arr[i] !== 0 && j >= 0 ) { const n = arr[i]; arr[i] = arr[j]; arr[j] = n; j++; } } } const arr = [0 , 1 , 2 , 0 , 3 , 0 , 0 , 4 ];moveZero2 (arr);console .log (arr);
计算字符串中连续最多的字符以及次数
输入’abbbcccccccddeee1234412’
计算得到连续最多的字符是’c’,7 次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 interface IRes { char : string ; length : number ; } function findContinuousChar1 (str: string ): IRes { const res : IRes = { char : '' , length : 0 , }; const length = str.length ; if (length === 0 ) return res; let tempLength = 0 ; for (let i = 0 ; i < length; i++) { tempLength = 0 ; for (let j = i; j < length; j++) { if (str[i] === str[j]) { tempLength++; } if (str[i] !== str[j] || j === length - 1 ) { if (tempLength > res.length ) { res.char = str[i]; res.length = tempLength; } if (i < length - 1 ) { i = j - 1 ; } break ; } } } return res; } interface IRes { char : string ; length : number ; } function findContinuousChar2 (str: string ): IRes { const res : IRes = { char : '' , length : 0 , }; const length = str.length ; if (length === 0 ) return res; let tempLength = 0 ; let i = 0 ; let j = 0 ; for (; i < length; i++) { if (str[i] === str[j]) { tempLength++; } if (str[i] !== str[j] || i === length - 1 ) { if (tempLength > res.length ) { res.char = str[j]; res.length = tempLength; } tempLength = 0 ; if (i < length - 1 ) { j = i; i--; } } } return res; } const str = 'abbbcccccccddeee1234412' ;console .log (findContinuousChar1 (str));
对称数(回文)
求 1 - 10000 之间的所有对称数(回文)
例如:0, 1, 2, 11, 22, 101, 232,1221
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 function findPalindromeNumbers1 (max: number ): number [] { const res : number [] = []; if (max < 0 ) return res; for (let i = 1 ; i <= max; i++) { const s = i.toString (); if (s === s.split ('' ).reverse ().join ('' )) { res.push (i); } } return res; } console .log (findPalindromeNumbers1 (200 ));function findPalindromeNumbers2 (max: number ): number [] { const res : number [] = []; if (max <= 0 ) return res; for (let i = 1 ; i <= max; i++) { let n = i; let rev = 0 ; while (n > 0 ) { rev = rev * 10 + (n % 10 ); n = Math .floor (n / 10 ); } if (i === rev) res.push (i); } } console .log (findPalindromeNumbers2 (200 ));
最长回文串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function longestPalindrome (s: string ): string { let res = '' ; for (let i = 0 ; i < s.length ; i++) { const s1 = palindrome (s, i, i); const s2 = palindrome (s, i, i + 1 ); res = res.length > s1.length ? res : s1; res = res.length > s2.length ? res : s2; } return res; } function palindrome (s, l, r ) { while (l >= 0 && r < s.length && s[l] == s[r]) { l--; r++; } return s.substr (l + 1 , r - l - 1 ); }
判断字符串是否括号匹配
一个字符串 s 可能包含{}()[]三种括号
判断 s 是否是括号匹配的
如(a{b}c)匹配,而{a(b 或者{a(b}c)就是不匹配的
栈 vs 数组 栈:逻辑结构,理论模型,不管如何实现,不受任何语言的限制。 数组:物理结构,真实的功能实现,受限于编程语言。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 function isMatch (left: string , right: string ): boolean { if (left === '{' && right === '}' ) return true ; if (left === '(' && right === ')' ) return true ; if (left === '[' && right === ']' ) return true ; return false ; } function matchBracket (str: string ): boolean { const length = str.length ; if (length === 0 ) return true ; const stack = []; const leftSymbols = '{[(' ; const rightSymbols = ')]}' ; for (let i = 0 ; i < length; i++) { const s = str[i]; if (leftSymbols.includes (s)) { stack.push (s); } else if (rightSymbols.includes (s)) { const top = stack[stack.length - 1 ]; if (isMatch (top, s)) { stack.pop (); } else { return false ; } } } return stack.length === 0 ; }
高效的字符串前缀匹配
有一个英文单词库(数组),里面有几十万个英文单词
输入一个字符串,快速判断是不是某一个单词的前缀
(说明思路,不用写代码)
思路一:
遍历单词库数组
indexOf 判断前缀
实际时间复杂度超过 O(n),因为 indexOf 的计算量
思路二(对象取 key 时间复杂度为 O(1)):
英文字母一共就 26 个,可以提前把单词库数组拆分为 26 个
第一层 26 个,第二层、第三层,继续拆分…
最后把单词库拆分为一颗树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 树结构 const wordsTree = { a: { a: {...} b: {...} }, b: { a: {...} }, c: { a: {...} } ... }
数字千分位格式化
将数字千分位格式化,输出字符串
如输入数字 12050100,输出字符串 12,050,100
(注意:逆序判断)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 function format1 (n: number ): string { n = Math .floor (n); const reverseArr = n.toString ().split ('' ).reverse (); return reverseArr.reduce ((prev, curr, index ) => { if (index % 3 === 0 ) { if (prev) { return curr + ',' + prev; } else { return curr; } } else { return curr + prev; } }, '' ); } function format2 (n: number ): string { n = Math .floor (n); let res = '' ; const str = n.toString (); const length = str.length ; for (let i = length - 1 ; i >= 0 ; i--) { const j = length - i; if (j % 3 === 0 ) { if (i === 0 ) { res = str[i] + res; } else { res = ',' + str[i] + res; } } else { res = str[i] + res; } } return res; } 把"1000000000" 变成"100.000.000" 这种写法,把后面往前面查,三位加个点 var str = '100000000' ;var reg = /(?=(\B)(\d{3})+$)/g ;console .log (str.replace (reg, '.' )) || string .replace (/\B(?=(\d{3})+(?!\d))/g , '.' ) || (25435345.22 ).toLocaleString ('en-US' );
切换字母大小写
输入一个字符串,切换其中字母的大小写
如:输入字符串 12aBc34,输出字符串 12AbC34
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 function switchLetterCase1 (s: string ): string { let res = '' ; const length = s.length ; if (length === 0 ) return res; const reg1 = /[a-z]/ ; const reg2 = /[A-Z]/ ; for (let i = 0 ; i < length; i++) { const c = s[i]; if (reg1.test (c)) { res += c.toUpperCase (); } else if (reg2.test (c)) { res += c.toLowerCase (); } else { res += c; } } return res; } function switchLetterCase2 (s: string ): string { let res = '' ; const length = s.length ; if (length === 0 ) return res; const reg1 = /[a-z]/ ; const reg2 = /[A-Z]/ ; for (let i = 0 ; i < length; i++) { const c = s[i]; const code = c.charCodeAt (0 ); if (code >= 65 && code <= 90 ) { res += c.toLowerCase (); } else if (code >= 97 && code <= 122 ) { res += c.toUpperCase (); } else { res += c; } } return res; }
「扁平数组」转「树形结构」 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function treeing (arr ) { let tree = []; let map = {}; for (let item of arr) { const newItem = (map[item.id ] = { ...item, children : [], }); if (map[item.pid ]) { const parent = map[item.pid ]; parent.children .push (newItem); } else { tree.push (newItem); } } return tree; }
「树形结构」转「扁平结构」 1 2 3 4 5 6 7 8 9 10 function flatten (tree, arr = [] ) { tree.forEach ((item ) => { const { children, ...rest } = item; arr.push (rest); if (children.length ) { flatten (children, arr); } }); return arr; }
将树状结构转换为属性平铺的结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 const entry = { a : { b : { c : { dd : 'abcdd' , }, }, d : { ee : 'adee' , }, f : 'af' , }, }; const output = { 'a.b.c.dd' : 'abcdd' , 'a.d.ee' : 'adee' , 'a.f' : 'af' , }; function flatObj (obj, preKey = '' , result = {} ) { for (const key in obj) { if (obj.hasOwnProperty (key)) { const newKey = `${preKey} ${key} ` ; if (typeof obj[key] === 'object' ) { flatObj (obj[key], `${newKey} .` , result); } else { result[newKey] = obj[key]; } } } return result; } function flatObj2 (obj ) { const queue = Object .entries (obj); const result = {}; while (queue.length ) { const [key, value] = queue.pop (); for (const [k, v] of Object .entries (value)) { if (typeof v === 'object' ) { queue.push ([`${key} .${k} ` , v]); } else { result[`${key} .${k} ` ] = v; } } } return result; } flatObj (entry);flatObj2 (entry);
将平铺属性的数据结构转换为树状数据结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 const entry = { 'a.b.c.dd' : 'abcdd' , 'a.d.ee' : 'adee' , 'a.f' : 'af' , }; const output = { a : { b : { c : { dd : 'abcdd' , }, }, d : { ee : 'adee' , }, f : 'af' , }, }; function map (entry ) { const result = {}; for (const key in entry) { const value = entry[key]; const keyMap = key.split ('.' ); if (!result[keyMap[0 ]]) { result[keyMap[0 ]] = {}; } let tmp = result[keyMap[0 ]]; let length = keyMap.length ; for (let i = 1 ; i < length; i++) { if (!tmp[keyMap[i]]) { if (i === length - 1 ) { tmp[keyMap[i]] = value; } else { tmp[keyMap[i]] = {}; } } tmp = tmp[keyMap[i]]; } } return result; } function map2 (entry ) { function getNest (key ) { const lastIndex = key.lastIndexOf ('.' ); const value = entry[key]; if (lastIndex !== -1 ) { delete entry[key]; const preKey = key.substring (0 , lastIndex); const restKey = key.substring (lastIndex + 1 ); if (!entry[preKey]) { entry[preKey] = { [restKey]: value }; } else { entry[preKey][restKey] = value; } if (/./ .test (preKey)) { getNest (preKey); } } } for (const key in entry) { getNest (key); } return entry; } map (entry);map2 (entry);
实现 jsonp,传入 url、callback 和 callbackName 三个参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function jsonp (url, callback, callbackName ) { const script = document .createElement ('script' ); script.src = `${url} ?type=jsonp&callbackName=${callbackName} }` ; script.onload = callback; document .body .appendChild (script); window [callbackName] = function (data ) { console .log (data); document .body .removeChild (script); }; } jsonp ( 'http://www.xxx.com/xxx' , function (data ) { console .log (data); }, 'fn' );
实现计时器 timer,仅暴露 start、stop、reset 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function Timer ( ) { let second = 0 ; let refId = null ; const clear = function ( ) { if (refId !== null ) { clearInterval (refId); } }; const start = function ( ) { refId = setInterval (() => { console .log (second); second += 1 ; }, 1000 ); }; const stop = function ( ) { clear (); }; const reset = function ( ) { second = 0 ; clear (); }; return { start, stop, reset }; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <a class ="link" href="http://www.taobao.com" >淘宝</a> <a class ="link" href ="http://www.jd.com" > 京东</a > <script > const links = document .getElementsByClassName ('link' )const listener = function (e ) { const href = e.target .getAttribute ('href' ) if (href.indexOf ('.taobao.com' ) === -1 ) { const result = confirm ('确定离开吗' ) if (result === false ) { e.preventDefault () e.stopPropagation () } } } for (let i = 0 , len = links.length ; i < len; i++) { link[i].addEventListener ('click' , listener, false ) } </script >
实现 destructuringArray 方法 1 2 3 4 5 6 function destructuringArray (arr, str ) { const variables = str.replace (/[\[\]]/g , '' ); return new Function (`str=${arr} ;return {${variables} }` )(); } destructuringArray ([1 , [2 , 4 ], 3 ], '[a,[b],c]' );
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class LRUCache { cache : Map <number , number >; constructor (public capacity: number ) { this .cache = new Map (); this .capacity = capacity; } get (key : number ): number { if (this .cache .has (key)) { const value = this .cache .get (key); this .cache .delete (key); this .cache .set (key, value); return value; } return -1 ; } put (key : number , value : number ): void { if (this .cache [key]) { this .cache .delete (key); } else { if (this .cache .size >= this .capacity ) { this .cache .delete (this .cache .keys ().next ().value ); } } this .cache .set (key, value); } } const lRUCache = new LRUCache (2 );lRUCache.put (1 , 1 ); lRUCache.put (2 , 2 ); lRUCache.get (1 ); lRUCache.put (3 , 3 ); lRUCache.get (2 ); lRUCache.put (4 , 4 ); lRUCache.get (1 ); lRUCache.get (3 ); lRUCache.get (4 );
比较版本号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function Compare (verson1, verson2 ) { let v1Arr = verson1.split ('.' ); let v2Arr = verson2.split ('.' ); let i = 0 ; let j = 0 ; while (i < v1Arr.length || j < v2Arr.length ) { let str1 = v1Arr[i] || 0 ; let str2 = v2Arr[j] || 0 ; if (str1 - str2 > 0 ) { return 1 ; } else if (str1 - str2 < 0 ) { return -1 ; } i++; j++; } return 0 ; }
找出字符串中重复次数最多的字符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function findMaxStr (str ) { let res = {}; for (let i = 0 ; i < str.length ; i++) { if (!res[str.charAt (i)]) { res[str.charAt (i)] = 1 ; } else { res[str.charAt (i)] = res[str.charAt (i)] + 1 ; } } let iMax = 0 ; let target = '' ; for (let key in res) { if (res[key] > iMax) { iMax = res[key]; target = key; } } console .log ('res:' , res); console .log ('出现次数最多的是:' + target + ', 出现' + iMax + '次' ); } findMaxStr ('sabcdEs' );
实现 url 的 parse 解析? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function parseUrl (url ) { const parser = document .createElement ('a' ); parser.href = url; return { protocol : parser.protocol , host : parser.host , hostname : parser.hostname , port : parser.port , pathname : parser.pathname , search : parser.search , hash : parser.hash , params : (function ( ){ let ret = {}, let seg = a.search .replace (/^\?/ ,'' ).split ('&' ), for (let i = 0 ;i<seg.length ;i++) { if (!seg[i]) { continue ; } let s = seg[i].split ('=' ); ret[s[0 ]] = s[1 ]; } return ret; })(), }; } const url = 'https://www.example.com:8080/path/to/page?query=string#hash' ;const parsedUrl = parseUrl (url);console .log (parsedUrl);
实现对象数组 group 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function group (arr, fn ) { const result = {}; for (let index = 0 ; index < arr.length ; index++) { const element = arr[index]; const category = fn (element, index, arr); if (result[category]) { result[category].push (element); } else { result[category] = [element]; } } return result; } const orderList = [ { nickName : 'steven' , productName : '西瓜' , price : 29 , province : 'henan' , }, { nickName : '对方的' , productName : '杨梅' , price : 22 , province : 'shanxi' , }, ]; const result = group (orderList, ({ province } ) => province);console .log (result);
怎么可以使用 for-of 来遍历对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Object .prototype [Symbol .iterator ] = function ( ) { const keys = Object .keys (this ); let index = 0 ; return { next : () => { const value = this [keys[index]]; const done = index > keys.length - 1 ; index++; return { value, done, }; }, }; }; const obj = { key : '1' , value : '2' , }; for (const item of obj) { console .log (item); }
实现 lodash.get 1 2 3 4 5 6 7 8 9 10 11 12 function get (obj, path ) { let newPath = []; if (Array .isArray (path)) { newPath = path; } else { newPath = path.replace (/\[/g , '.' ).replace (/\]/ , '' ).split ('.' ); } return newPath.reduce ((obj = {}, key ) => obj[key], obj); } const object = { a : [{ b : { c : 3 } }] };console .log (get (object, 'a[0].b.c' ));
数组随机排序 shuffle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function shuffle (arr ) { arr.sort (() => Math .random () - 0.5 ); } function shuffle (arr ) { let i = arr.length ; while (--i) { let j = Math .floor (Math .random () * i); [arr[j], arr[i]] = [arr[i], arr[j]]; } }
二分查找 O(log2n) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function binarySearch (elements, value, _start, _end ) { let end = _end || elements.length - 1 ; let start = _start || 0 ; let povitIndex = Math .floor ((start + end) / 2 ); if (elements[povitIndex] === value) { return povitIndex; } if (value < elements[povitIndex]) { return binarySearch (elements, value, 0 , povitIndex - 1 ); } else { return binarySearch (elements, value, povitIndex + 1 , end); } return false ; }
冒泡排序 O(n^2)
重复地遍历要排序的数组,比较相邻的元素并交换位置,直到整个数组都已经排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function bubbleSort (elements ) { let elementLength = elements.length ; for (let i = 0 ; i < elementLength - 1 ; i++) { for (let j = 0 ; j < elementLength - i - 1 ; j++) { if (elements[j] > elements[j + 1 ]) { let temp = elements[j]; elements[j] = elements[j + 1 ]; elements[j + 1 ] = temp; } } } console .log (elements); } let elements = [2 , 4 , 3 , 7 , 5 ];bubbleSort (elements);
快速排序 O(最好 nlogn,最慢 n^2,平均 nlog2n)
基本思想是选择一个基准元素,然后将数组中的元素分为小于基准元素和大于基准元素的两部分,再对这两部分分别进行排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function quickSort1 (arr: number[] ): number[] { const length = arr.length ; if (length === 0 ) return arr; const midIndex = Math .floor (length / 2 ); const midValue = arr.splice (midIndex, 1 )[0 ]; const left : number[] = []; const right : number[] = []; for (let i = 0 ; i < arr.length ; i++) { const n = arr[i]; if (n < midValue) { left.push (n); } else { right.push (n); } } return quickSort1 (left).concat (midValue, quickSort1 (right)); } const arr = [2 , 7 , 5 , 2 , 3 , 1 ];console .log (quickSort1 (arr));
插入排序(n^2)
将数组分为已排序和未排序两部分,然后将未排序部分的第一个元素插入到已排序部分的正确位置上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function insertSort (arr ) { let handle = [arr[0 ]]; for (let i = 1 ; i < arr.length ; i++) { let newItem = arr[i]; for (let j = handle.length - 1 ; j >= 0 ; j--) { if (newItem > handle[j]) { handle.splice (j + 1 , 0 , newItem); break ; } if (j === 0 ) { handle.unshift (newItem); } } } return handle; } let arr = [2 , 1 , 5 , 4 , 8 , 6 ];console .log (insertSort (arr));
选择排序(n^2)
将数组分为已排序和未排序两部分,然后从未排序部分选择最小的元素并放到已排序部分的末尾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function selectSort (arr ) { let index; for (let i = 0 ; i < arr.length - 1 ; i++) { index = i; for (let j = i + 1 ; j < arr.length ; j++) { if (arr[index] > arr[j]) { index = j; } } if (index !== i) { [arr[i], arr[index]] = [arr[index], arr[i]]; } } return arr; } let arr = [6 , 4 , 9 , 8 , 1 , 3 , 2 ];console .log (selectSort (arr));
归并排序(nlogn)
将数组分成两个子数组,分别对这两个子数组进行排序,然后将排序后的子数组合并成一个有序的数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function merge (left, right ) { let temp = []; while (left.length > 0 && right.length > 0 ) { if (left[0 ] < right[0 ]) { temp.push (left.shift ()); } else { temp.push (right.shift ()); } } return temp.concat (left, right); } function mergeSort (arr ) { if (arr.length == 1 ) { return arr; } else { let mid = parseInt (arr.length / 2 ); let left = arr.slice (0 , mid); let right = arr.slice (mid); return merge (mergeSort (left), mergeSort (right)); } }
合并两个有序数组? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function mergeArr (arr1, arr2 ) { let mergedArr = []; let pointer1 = 0 ; let pointer2 = 0 ; while (pointer1 < arr1.length && pointer2 < arr2.length ) { if (arr1[pointer1] < arr2[pointer2]) { mergedArr.push (arr1[pointer1]); pointer1++; } else { mergedArr.push (arr2[pointer2]); pointer2++; } } while (pointer1 < arr1.length ) { mergedArr.push (arr1[pointer1]); pointer1++; } while (pointer2 < arr2.length ) { mergedArr.push (arr2[pointer2]); pointer2++; } return mergedArr; }
手写useState、useEffect钩子函数? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 let memoizedState = [];let corsor = 0 ;function useState (initialValue ) { memoizedState[cursor] = memoizedState[cursor] || initialValue; const currentCursor = cursor; function setState (newState) { if (typeof newState === 'function' ) { newState = newState (memoizedState[currentCursor]); } memoizedState[currentCursor] = newState; render (); } return [memoizedState[cursor++], setState]; } function useEffect (callback, deps) { const _deps = memoizedState[cursor]; const hasChange = _deps ? !deps.every ((dep, i ) => dep === _deps[i]) : true ; if (!deps || hasChange) { callback (); memoizedState[cursor] = deps; } cursor++; }
如何让 var [a, b] = {a: 1, b: 2} 解构赋值成功? 1 2 3 4 5 6 Object .prototype [Symbol .iterator ] = function ( ) { return Object .values (this )[Symbol .iterator ]() } var [a, b] = {a : 1 , b : 2 };console .log (a, b);
如何使用 react hooks 实现 useFetch 请求数据? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import {useState, useEffect} from 'react' ;function useFetch (url ) { const [data, setData] = useState (null ); const [loading, setLoading] = useState (true ); const [error, setError] = useState (null ); useEffect (() => { const fetchData = async ( ) => { try { const res = await fetch (url); const data = await res.json (); setData (data); setLoading (false ); } catch (e) { setError (e); setLoading (false ); } } fetchData (); },[url]) return [data, loading, error] }
如何在 React 中使用 Render Prop 组件来请求数据? 组件封装:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import {useState, useEffect} from 'react' ;const DataFetcher = ({url, children} ) => { const [data, setData] = useState (null ); const [loading, setLoading] = useState (true ); const [error, setError] = useState (null ); useEffect (() => { const fetchData = async ( ) => { try { const res = await fetch (url); const data = await res.json (); setData (data); setLoading (false ); } catch (error) { setError (error); setLoading (false ); } } fetchData (); }, [url]) return children ({data, loading, error}) }
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import DataFetcher from './DataFetcher' ;const MyComponent = ( ) => { return ( <DataFetcher url ="/api/data" > { ({data, loading, error}) => { if (loading) { return <div > loading...</div > } if (error) { return <div > error...{error.message}</div > } return ( <div > success-{data} </div > ) } } </DataFetcher > ) }
持续更新中