1、防抖和节流函数 防抖的主要思想是,当事件被触发后,延迟一定时间再执行相关操作。如果在这个延迟期内再次触发了同样的事件,就会重新计时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const debounce = function (fn, delay ) { let timer = null ; return function ( ) { const that = this ; if (timer){ clearTimeout (timer); timer = null ; } timer = setTimeout (function ( ){ fn.apply (that, arguments ); }, delay) } } window .addEventListener ('resize' , debounce (()=> { console .log ('防抖执行' ); }, 300 ));
节流的主要思想是,在一定时间内只允许函数执行一次,无论事件触发了多少次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const throttle = function (fn, delay ){ let lastTime = 0 ; return function ( ) { const now = Date .now (); if (now - lastTime > delay){ fn.apply (this , arguments ); lastTime = now; } } } window .addEventListener ('scroll' , throttle (()=> { console .log ('节流执行' ); }, 300 ));
2、实现apply方法 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 Function .prototype .myApply = function (thisArg, argsArray ){ if (typeof this !== 'function' ){ throw new TypeError ('myApply muse be called on a function' ); } thisArg = (thisArg == null ? globalThis : Object (thisArg)); const fnKey = Symbol ('fn' ); thisArg[fnKey] = this ; const result = argsArray ? thisArg[fnKey](...argsArray) : thisArg[fnKey](); delete thisArg[fnKey]; return result; } function greet (m1, m2 ) { return `${m1} , ${this .name} , ${m2} ` ; } const person = { name : 'frank' } greet.myApply (person, ['hello' , 'come on' ]);
3、实现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 26 27 28 29 Function .prototype .myCall = function (thisArg, ...arg ){ if (typeof this !== 'function' ){ throw new TypeError ('myCall must be called on a function' ); } thisArg = (thisArg == null ? globalThis : Object (thisArg)) const fnKey = Symbol ('fn' ); thisArg[fnKey] = this ; const result = thisArg[fnKey](...arg); delete thisArg[fnKey]; return result; } function greet (m1, m2 ) { return `${m1} , ${this .name} , ${m2} ` ; } const person = { name : 'frank' } greet.myCall (person, 'hello' , 'come on' );
4、实现bind方法 bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 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 const obj = { name : 'zc' , age : 12 } function say (sex ){ console .log (`name: ${this .name} , age: ${this .age} , sex: ${sex} , 参数: ${[...arguments ]} ` ); } const mySay = say.bind (obj, 'female' );mySay (111 ); Function .prototype .myBind = function (context ) { const that = this ; const args = Array .prototype .slice .call (arguments , 1 ); return function ( ) { const newArgs = Array .prototype .slice .call (arguments ); return that.apply (context, [...args, ...newArgs]); } } const mySay1 = say.myBind (obj, 'female' );mySay1 (222 );
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 let str = 'afjghdfraaaasdenas' ;function findMaxDuplicateChar (str ) { if (str.length === 1 ) return str; let charObj = {}; for (let i=0 ; i<str.length ; i++) { if (!charObj[str.charAt (i)]){ charObj[str.charAt (i)] = 1 ; } else { charObj[str.charAt (i)] += 1 ; } } let maxChar = '' , maxValue = 1 ; for (let key in charObj) { if (charObj[key] >= maxChar) { maxChar = key; maxValue = charObj[key]; } } return { maxChar, maxValue } }
6、统计字符串中第一个唯一字符 1 2 3 4 5 6 7 8 9 10 11 12 let str = 'leetcode' ; function findFirstUniqChar (str ) { for (let i=0 ; i<str.length (); i++) { let curChar = str[i]; if (str.lastIndexOf (curChar) === str.indexOf (curChar)) { return i; } } return -1 ; }
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 let arr = [1 , 2 , 1 , 1 , 4 , 5 , 6 ];function findMostItemByArr (arr ) { let mostItem, indexs = [], obj = {}; for (let i=0 ; i<arr.length ; i++) { let item = arr[i].toString (); obj[item] ? obj[item].push (i) : obj[item] = [].concat (i); } let tempArr = Object .entries (obj); mostItem = parseInt (tempArr[0 ][0 ]); indexs = tempArr[0 ][1 ]; for (const [key, value] of tempArr) { if (indexs.length < value.length ) { mostItem = parseInt (key); indexs = value; } } return { mostItem, indexs, } }
8、数组扁平化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function flatten (arr ) { const result = []; for (let i = 0 ; i < arr.length ; i++) { if (Array .isArray (arr[i])) { result = result.concat (flatten (arr[i])); } else { result = result.concat (arr[i]); } } return result; } if (!Array .prototype .flat ) { Array .prototype .flat = function ( ) { return [].concat (...this .map (item => Array .isArray (item) ? item.flat () : [item])); } }
9、讲数组扁平化并去除其中重复数据, 最终得到一个升序且不重复的数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let arr = [ [1 , 2 , 2 ], [3 , 4 , 5 , 5 ], [6 , 7 , 8 , 9 , [11 , 12 , [12 , 13 , [14 ] ] ] ], 10 ];Array .prototype .flat = function ( ) { return [].concat (...this .map (item => Array .isArray (item) ? item.flat () : [item])); } Array .prototype .unique = function ( ) { return [...new Set (this )]; } arr.flat ().unique ().sort ((a, b ) => a -b);
10、旋转数组 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
输入: [1,2,3,4,5,6,7] 和 k = 3 输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
1 2 3 4 5 6 7 8 9 let arr = [1 , 2 , 3 , 4 , 5 , 6 , 7 ];function rotate (arr, k ) { for (let i=0 ; i<k; i++) { let temp = arr.pop (); arr.unshift (); } return arr; }
11、版本比较 根据两个版本号,当前版本v1和比较版本v2,判断app是否为旧版本。
示例1:v1=”1.1”, v2=”1.1”, 则为旧版本 (不规范的版本号)
示例2:v1=”2.5”,v2=”2.4”, 则为新版本
示例3: v1=”5.4.3.2”, v2=”5.4.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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 var compareVersion = function (version1, version2 ) { if (!version1 || !version2) return 0 const v1 = String (version1).split ('.' ); const v2 = String (version2).split ('.' ); const maxLen = Math .max (v1.length , v2.length ); for (let i = 0 ; i < maxLen; i++){ const num1 = Number (v1[i]) || 0 ; const num2 = Number (v2[i]) || 0 ; if (num1 > num2) return 1 ; if (num1 < num2) return -1 ; } return 0 ; }; function compareVersion (v1, v2 ) { const a1 = v1.split ('.' ) const a2 = v2.split ('.' ) const len = Math .max (a1.length , a2.length ) for (let i = 0 ; i < len; i++) { const s1 = a1[i] || '0' const s2 = a2[i] || '0' const n1 = parseInt (s1, 10 ) const n2 = parseInt (s2, 10 ) if (n1 > n2) return 1 if (n1 < n2) return -1 if (s1.length !== s2.length ) { return s1.length < s2.length ? -1 : 1 } } return 0 }
11、数据转化为树状结构 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 let arr = [ { id : 1 , name : '1' , pid : 0 , }, { id : 2 , name : '1-1' , pid : 1 , }, { id : 3 , name : '1-1-1' , pid : 2 , }, { id : 4 , name : '1-2' , pid : 1 , } ]; function json2Tree (data, parentId = 0 ) { return data .filter (item => item.pid === parentId) .map (item => { const children = json2Tree (data, item.id ); return { ...item, children : children.length ? children : undefined }; }); } console .log (JSON .stringify (json2Tree (arr), null , 2 ));[ { "id" : 1 , "name" : "1" , "pid" : 0 , "children" : [ { "id" : 2 , "name" : "1-1" , "pid" : 1 , "children" : [ { "id" : 3 , "name" : "1-1-1" , "pid" : 2 } ] }, { "id" : 4 , "name" : "1-2" , "pid" : 1 } ] } ]
12、找出字符串中所有字母的所有可能的排列,假定字母不重复 例如:”abc” 可能的排列为:”abc”, “acb”, “bac”, “bca”, “cab”, “cba”
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 allPermutationsBacktrack (str ){ const res = []; const chars = str.split ('' ); const used = new Array (chars.length ).fill (false ); function backtrack (path ){ if (path.length === chars.length ){ res.push (path.join ('' )); return ; } for (let i = 0 ; i < chars.length ; i++){ if (used[i]) continue ; used[i] = true ; path.push (chars[i]); backtrack (path); path.pop (); used[i] = false ; } } backtrack ([]); return res; } console .log (allPermutationsBacktrack ('abc' ));
13、实现字符串模板 1 2 3 4 5 6 7 8 9 10 function tpl (template, data ) { }tpl ('<div class={{className}}>{{name}}</div>' , {className : 'class1' ,name : 'test' }); <div class ="class" > test</div >
1 2 3 4 5 function tpl (str, data ) { return str.replace (/{{\s*(\w+)\s*}}/g , (match, key ) => { return key in data ? data[key] : '' ; }); }
14、实现字符串大数相加 大数相加指的是两个超出编程语言内置数值精度范围的整数相加,通常用字符串(或数组)来存储并模拟计算的过程。
1 2 3 4 Number .MAX_SAFE_INTEGER console .log (9007199254740991 + 1 ); console .log (9007199254740991 + 2 );
实现思路
反转字符串(或从末位开始遍历),方便低位到高位逐位相加。
用 carry 保存进位。
把对应位数字相加,计算新的一位和新的进位。
最后如果 carry > 0,别忘了加到结果最高位。
反转结果字符串得到最终值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function plus (num1, num2 ) { let i = num1.length - 1 ; let j = num2.length - 1 ; let carry = 0 ; let res = []; while (i >= 0 || j >= 0 || carry) { const n1 = i >= 0 ? parseInt (num1[i], 10 ) : 0 ; const n2 = j >= 0 ? parseInt (num2[j], 10 ) : 0 ; const sum = n1 + n2 + carry; res.push (sum % 10 ); carry = Math .floor (sum / 10 ); i--; j--; } return res.reverse ().join ('' ); } console .log (plus ("123456789123456789" , "987654321987654321" ));
15、实现安全取值 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 getValSafe (obj, keys ) {} const obj = { a : { b : { c : { d : 'safe' } } } }console .log (get (obj, 'a.b.c.d' )); function getValSafe (obj, keys ) { let result = obj; keys.split ('.' ).forEach (key => { if (result && result[key]) { result = result[key]; } else { result = undefined ; } }); return result; } function getValSafe (obj, keys ) { return keys.reduce ((acc, key ) => (acc && acc[key] !== undefined ? acc[key] : undefined ), obj); }