用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 function trim (str ) { if (!str) return "" ; return trimRight(trimLeft(str)); } function trimLeft (str ) { const judgeStr = new String (" \t\r\n" ); if (judgeStr.indexOf(str.charAt(0 ))!==-1 ){ let j = 1 , len = str.length; while (j<len && judgeStr.indexOf(str.charAt(j))!==-1 ){ j++; } str = str.substring(j); } return str; } function trimRight (str ) { const judgeStr = new String (" \t\r\n" ); let i = str.length-1 ; if (judgeStr.indexOf(str.charAt(i))!==-1 ){ while (i>=0 && judgeStr.indexOf(str.charAt(i))!==-1 ){ i--; } str = str.substring(0 , i+1 ); } return str; }
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 function quickSort (arr ) { partition(arr, 0 , arr.length-1 ); return arr; } const partition = function (arr, left, right ) { let idx = subSort(arr, left, right); if (left < idx-1 ){ partition(arr, left, idx-1 ); } if (right > idx){ partition(arr, idx, right); } } const subSort = function (arr, left, right ) { const target = arr[Math .floor((left+right)/2 )]; let i=left, j=right; while (i<=j){ while (i<arr.length && arr[i]<target){ i++; } while (j>=0 && arr[j]>target){ j--; } if (i<=j){ [arr[i], arr[j]] = [arr[j], arr[i]]; i++; j--; } } return i; }
时间负责度
如果每次选择的 target 都是中间大小的,则最优 O(nlogn)
如果每次选择都是最大的(对于升序排序),则退化为冒泡排序。
空间复杂度 O(1)
参考
归并排序 分治思想,先划分数组,然后依次排序,归并。
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 mergeSort (arr ) { if (arr.length<=1 ){ return arr; } let idx = Math .floor(arr.length/2 ); return merge(mergeSort(arr.slice(0 ,idx)), mergeSort(arr.slice(idx))); } const merge = function (left, right ) { let i = 0 , j = 0 , res = []; while (i<left.length && j < right.length){ if (left[i]<right[j]){ res.push(left[i++]); }else { res.push(right[j++]) } } while (i<left.length){ res.push(left[i++]); } while (j<right.length){ res.push(right[j++]); } return res; }
归并排序比较稳定,但是比较吃内存空间。快速排序不稳定,但是不消耗内存空间。
插入排序 1 2 3 4 5 6 7 8 9 10 11 function insertSort (arr ) { for (let i=1 ; i<arr.length; i++){ let temp = arr[i], j=i; while (j-1 >=0 && arr[j-1 ]>temp){ arr[j] = arr[j-1 ]; j--; } arr[j] = temp; } return arr; }
大数相加 js 里面的 Number 用64位浮点数表示,数值范围为 -2^53 ——— 2^53 (包含边界)。如果数值相加后的结果超过这个范围,则会导致精度丢失。
大数相加原理是利用数组可以自动扩容来保存计算结果,并用字符串的形式输入与输出。
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 addBigNum (num1, num2 ) { [num1, num2] = [num1 + '' , num2 + '' ]; let len1 = num1.length, len2 = num2.length; for (let i=0 ; i<(Math .max(len1, len2)-Math .min(len1, len2)); i++){ if (len1<len2){ num1 = '0' + num1; }else { num2 = '0' + num2; } } let res = []; num1 = num1.split('' ).reverse(); num2 = num2.split('' ).reverse(); for (let i=0 ; i<num1.length; i++){ res[i] = res[i]===1 ?res[i]:0 ; let s = res[i] + parseInt (num1[i]) + parseInt (num2[i]); if (s<10 ){ res[i] = s; }else { res[i] = s-10 ; res[i+1 ] = 1 ; } } return res.reverse().join('' ); }
大数相乘 思路与大数相加类似。
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 multiplyBigNUm (num1, num2 ) { [num1, num2] = [num1 + '' , num2 + '' ]; let [len1, len2] = [num1.length, num2.length]; let shortNum, longNum, shortLen, longLen if (len1 < len2){ [shortNum, longNum, shortLen, longLen] = [num1, num2, len1, len2]; }else { [shortNum, longNum, shortLen, longLen] = [num2, num1, len2, len1]; } shortNum = shortNum.split('' ).reverse(); let res = '' ; for (let i=0 ; i<shortLen; i++){ let temp = multiply(longNum, shortNum[i]); for (let j=0 ; j<i; j++) temp += '0' ; res = addBigNum(temp, res); } return res; } function multiply (num, n ) { num = num.split('' ).reverse(); n = parseInt (n); let res = []; for (let i=0 ; i<num.length; i++){ res[i] = res[i]?res[i]:0 ; let tmp = res[i] + parseInt (num[i])*n; if (tmp < 10 ){ res[i] = tmp; }else { res[i] = tmp%10 ; res[i+1 ] = Math .floor(tmp/10 ); } } return res.reverse().join('' ); }
实现 add(1,2,3) == 6, add(1,2,3)() = 6, add(1)(2)(3) = 6, add(1)(2)(3)() = 6 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function add ( ) { let res = [...arguments].reduce((a, b )=> { return a+b; }, 0 ); let innerAdd = function ( ) { if (arguments .length===0 ){ return res; }else { res += [...arguments].reduce((a, b )=> { return a+b; }, 0 ); return innerAdd; } } innerAdd.toString = function ( ) { return res; } innerAdd.toValue = function ( ) { return res; } return innerAdd; }
实现 add(1)(2)(3)().res = 6, add(1,2)(3)().res = 6 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 add ( ) { let res = [...arguments].reduce((a, b )=> { return a+b; }, 0 ); let obj = {res : res}; let innerAdd = function ( ) { if (arguments .length===0 ){ return obj; }else { res += [...arguments].slice(1 ).reduce((a, b )=> { return a+b; }, 0 ); obj.res = res; return innerAdd; } } innerAdd.toString = function ( ) { return res; } innerAdd.toValue = function ( ) { return res; } return innerAdd; }
实现superBaby 一个superBaby
输入 superBaby(“syz”)
输出: “I am syz”
输入 superBaby(“syz”).eat(“banana”)
输出 “I am syz” “Eating banana”
输入 superBaby(“syz”).sleep(5000).eat(“banana”)
输出 “I am syz” // 等待 5000ms 后输出 “Eating banana”
输入 superBaby(“syz”).eat(“banana”).sleep(5000).eat(“apple”).firstSleep(5000)
输出 // 等待 5000ms 后输出 “I am syz” “Eating banana” // 等待 5000ms 后输出 “Eating apple”
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 function SuperBaby (name ) { return new _superBaby(name); } function _superBaby (name ) { let that = this ; let fn = function ( ) { console .log(`I am ${name} ` ); that.next(); } this .task.push(fn); setTimeout (()=> this .next(), 0 ); } _superBaby.prototype = { constructor : _superBaby , task : [], next : function ( ) { let fn = this .task.shift(); fn && fn(); }, eat: function (food ) { let that = this ; let fn = function ( ) { console .log(`Eating ${food} ` ); that.next(); } this .task.push(fn); return this ; }, sleep: function (time ) { let that = this ; let fn = function ( ) { setTimeout (()=> that.next(), time); }; this .task.push(fn); return this ; }, sleepFirst: function (time ) { let that = this ; let fn = function ( ) { setTimeout (()=> that.next(), time); }; this .task.unshift(fn); return this ; } } SuperBaby("syz" ).sleep(5000 ).eat("banana" ).sleepFirst(5000 );
手写 apply、call 及 bind 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 .myApply = function (context, args ) { if ( typeof this !== 'function' ){ throw new TypeError ('Error' ); } context = context || window ; args = args || []; context.fn = this ; const res = context.fn(...args); delete context.fn; return res; } Function .myCall = function (context, ...args ) { if (typeof this !== 'function' ){ throw new TypeError ('Error' ); } context = context || window ; context.fn = this ; const res = context.fn(...args); delete context.fn; return res; } Function .muBind = function (context ) { if (typeof this !== 'function' ) { throw new TypeError ('Error' ) } let that = this ; let args = [...arguments].slice(1 ); context = context || window ; return function ( ) { that.apply(context, args.concat([...arguments])) } }
判断一个对象是否是数组
判断是否为数组的实例
判断原型对象与数组的原型对象是否一致
判断对象的构造函数是否是 Array
调用 Array.isArray() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function judgeArray (arr, pattern ) { switch (pattern){ case 1 : return Array .isArray(arr); break ; case 2 : return arr instanceof Array ; break ; case 3 : return arr.constructor === Array ; break ; case 4 : return Object .getPrototypeOf(arr) === Array .prototype; break ; } }
实现 Promise.all Promise.all 允许定义的 Promise 按照数组传入,并依次执行 Promise 并将结果保存在一个列表中,返回一个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 let p1 = new Promise ((resolve, reject )=> { resolve("只要有爱," ); }); let p2 = new Promise ((resolve, reject )=> { resolve("在哪里都是天堂!" ) }); let p3 = Promise .reject("不要让恨遮蔽了双眼!" );Promise .all([p1, p2]).then(res => { console .log(res) }) Promise .all([p1, p2, p3]).then(res => { console .log(res); }).catch(err => { console .log(err); })
自己实现.
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 Promise .myAll = function (promises ) { return new Promise ((resolve, reject )=> { let res = []; promises.forEach((promise, index )=> { promise.then(data => { res.push(data); if (index===promises.length-1 ){ resolve(res); } }).catch(err => { reject(err); }) }); }) } Promise .myAll([p1, p2]).then(res => { console .log(res) }) Promise .myAll([p1, p2, p3]).then(res => { console .log(res); }).catch(err => { console .log(err); })
模拟 Node.js 的 Event 模块 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 class Event { constructor ( ) { this .fns = {}; } emit (name, value ) { if (!this .fns[name]){ throw Error ("No such event" ); } this .fns[name].forEach(fn => { fn(value); }) } on (name, cb ) { if (!this .fns[name]){ this .fns[name] = []; } this .fns[name].push(cb); } } let event = new Event();event.on('sayName' , function (name ) { console .log(name); }) event.on('sayName' , function (name ) { console .log(`I am ${name} ` ); }) event.emit('sayName' , 'syz' );
深克隆 一般深度克隆可以采用 Json.Parse(Json.stringify()),但是克隆存在一些问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function deepClone (obj ) { if (obj instanceof Date ){ return new Date (obj); } let res = obj instanceof Array ?[]:{}; Object .keys(obj).forEach(key => { res[key] = typeof obj[key] === 'object' ? deepClone(obj[key]):obj[key]; }); return res; } let rawObj = {name :'syz' }let testObj = deepClone(rawObj);testObj.name = 'xz' ; console .log(rawObj)console .log(testObj)
自己实现模版字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 const obj = { name: 'zj' , age: 18 } const str = `我的名字叫${ obj.name } ,今年${obj.age} 岁` function replace (str ) { return str.replace(/\$\{([^}]+)\}/ , function (data ) { return data; }); } console .log(replace(str))
实现展开任意深度的嵌套列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function flatten (arr, depth=-1 ) { if (depth===-1 ){ return [].concat( ...arr.map((item )=> { return Array .isArray(item)?flatten(item):item }) ); } if (depth===1 ){ return arr.reduce((pre, cur )=> pre.concat(cur),[]) }else { return arr.reduce((pre, cur )=> pre.concat(Array .isArray(cur)?flatten(cur, depth-1 ):cur), []); } }
实现金钱的每三位划分 1 2 3 4 5 6 7 let arr = '1234567890' function trans (arr ) { return arr.split('' ).reverse().reduce((pre, cur, idx )=> { return ((idx%3 )?cur:cur+',' ) + pre }) } console .log(trans(arr));
分时函数 对于需要频繁添加大量DOM结构的操作,如果不设计一定的策略,浏览器在短时间渲染这个多DOM结构,会造成浏览器的卡顿。
此时可以设计分时函数,在指定时间间隔内,执行指定次数的函数,添加指定数量的DOM结构。
例如有一个好友列表,需要动态渲染其中成百上千的列表,此时可以采用分时函数。
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 let timeChunk = function (arr, fn, count, time ) { let start = function ( ) { for (let i=0 ; i<Math .min(count || 1 , arr.length); i++){ let content = arr.shift(); fn(content); } } return function ( ) { let t = setInterval (function ( ) { if (arr.length===0 ){ clearInterval (t); t=null ; }else { start(); } }, time); } } let ls = [];for (let i=0 ; i<100 ; i++){ ls.push(i); } let renderFriendList = timeChunk(ls, function (content ) { let div = document .createElement('div' ); div.innerHTML = content; document .body.appendChild(div); }, 8 , 200 ); renderFriendList();