高阶函数实现AOP(面向切面编程)
Function.prototype.before = function (beforefn) { let _self = this; // 缓存原函数的援用 returnfunction () { // 代理函数 beforefn.apply(this, arguments); // 执行前置函数 return _self.apply(this, arguments); // 执行原函数 } } Function.prototype.after = function (afterfn) { let _self = this; returnfunction () { letset = _self.apply(this, arguments); afterfn.apply(this, arguments); returnset; } } let func = () => console.log('func'); func = func.before(() => { console.log('===before==='); }).after(() => { console.log('===after==='); }); func();
输入后果:
===before===func===after===
当咱们 new 一个类的时候 都产生了什么
/** * new2 new关键字的代码实现演示 * @param {function} func 被new的类 (构造函数) */function new2(func) { // 创立了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象 let o = Object.create(func.prototype); // (在构造函数中this指向以后实例)让这个类作为一般函数值行 并且外面this为实例对象 let k = func.call(o); // 最初再将实例对象返回 如果你在类中显示指定返回值k, // 留神如果返回的是援用类型则将默认返回的实例对象o代替掉 return typeof k === 'object' ? k : o;}// 试验functionM() { // 行将被new的类 this.name = 'liwenli';}let m = new2(M); // 等价于 new M 这里只是模仿console.log(m instanceof M); // instanceof 检测实例console.log(m instanceof Object);console.log(m.__proto__.constructor === M);
Object.create 兼容实现
let obj1 = {id: 1}; Object._create = (o) => { let Fn = function() {}; // 长期的构造函数 Fn.prototype = o; return new Fn; } let obj2 = Object._create(obj1); console.log(obj2.__proto__ === obj1); // true console.log(obj2.id); // 1 // 原生的Object.create let obj3 = Object.create(obj1); console.log(obj3.__proto__ === obj1); // true console.log(obj3.id); // 1
currying 函数柯理化
curry 柯理化的实现(递归调用 + valueOf)
知识点:valueOf 浏览器环境下 当咱们以log(fn)这种模式取值时,会隐式调用fn本身的valueOf 所以失去的是valueOf的返回值
functionfn() {};fn.valueOf = () => console.log('valueof');console.log(fn); // valueofconst mul = x => { const result = y => mul(x * y); // 递归调用mul result.valueOf = () => x; return result;}console.log(mul(2)(3)); // 6// 在下面mul每执行一次,就会返回一个valueOf被改写后的新函数result 并且result执行会在外面调用mul(x * y)// 在result函数的valueOf里保留着 由上一次x * y 传进来的后果x, 也就是上一次x*y 会作为这一次的输入 仍然叫x// 第一次mul(2) 此时 x为2 return resultresult 为 result = y => mul(2 * y); // 第二次 mul(2)(3) 等价于 第一个mul返回的result(3), result执行 => mul(2 * 3) 再次调用mul 将2*3 = 6 的后果作为mul参数// 最初mul(6) x = 6 在返回一个新函数result 此时result的valueOf = () => 6// log(mul(2)(3)) 相当于log的最初返回的result 隐式调用valueOf 返回 6
curry 将多参数函数转换为接管繁多参数的函数
function fe(a, b, c) { return a + b + c;}function curry(fe) { let args = []; // 参数汇合 let len = args.length; returnfunctionbar() { args = [...args, ...arguments]; // 收集参数 if (args.length >= fe.length) { return fe.apply(this, args); } return bar; }}console.log(curry(fe)(1)(2)(3)); // 6
currying 局部求值
// currying 函数柯理化 let currying = function(fn) { let args = []; returnfunctionfe() { if (arguments.length === 0) { return fn.apply(this, args); } [].push.apply(args, arguments); return fe; } } let count = currying(function (...rest) { return rest.reduce((prev, cur) => prev + cur, 0); }); console.log(count(100)(200)(10)()); // 310
收集参数 提早执行 达到指定次数才执行
// 参数收集 指定次数后执行 function fn(...rest) {console.log(rest);}; function after(fn, time = 1) { let params = []; returnfunction(...rest) { params = [...params, ...rest]; if (--time === 0) { fn.apply(this, params); } } } let newFn = after(fn, 3); // 执行3次 外部fn才会执行 newFn(2); newFn(3); newFn(4);
函数节流
throttle 策略的电梯。保障如果电梯第一个人进来后,50毫秒后准时运送一次,不期待。如果没有人,则待机。
let throttle = (fn, delay = 50) => { // 节流 管制执行间隔时间 避免频繁触发 scroll resize mousemove let stattime = 0; returnfunction (...args) { let curTime = new Date(); if (curTime - stattime >= delay) { fn.apply(this, args); stattime = curTime; } } }
防抖动
debounce 策略的电梯。如果电梯里有人进来,期待50毫秒。如果又人进来,50毫秒期待从新计时,直到50毫秒超时,开始运送。参考 前端手写面试题具体解答
let debounce = (fn, time = 50) => { // 防抖动 管制闲暇工夫 用户输出频繁 let timer; returnfunction (...args) { let that = this; clearTimeout(timer); timer = setTimeout(fn.bind(that, ...args), time); } }
深度拷贝兼容写法(不包含原型属性)
function deepCopy(obj) { if (typeof obj !== 'object') return obj; if (typeof window !== 'undefined' && window.JSON) { // 浏览器环境下 并反对window.JSON 则应用 JSON return JSON.parse(JSON.stringify(obj)); } else { let newObj = obj.constructor === Array ? [] : {}; for(let key in obj) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } return newObj; }}let obj = {a: 1, b: [12]};let newObj = deepCopy(obj);newObj.b[1] = 100;console.log(obj);console.log(newObj);
Function的bind实现
Function.prototype._bind = function(context) { let func = this; let params = [].slice.call(arguments, 1); returnfunction() { params = params.concat([].slice.call(arguments, 0)); func.apply(context, params); }}let obj = {id: 24}function fn1(a) { console.log(this, arguments);}let foo = fn1._bind(obj, obj.id);
函数组合串联compose(koa reduce中间件)
// 组合串联let fn1 = (a) => a + 1;let fn2 = (b) => b + 2;let fn3 = (c) => c + 3;let funs = [fn1, fn2, fn3];let compose = (func) => { return arg => func.reduceRight((composed, fn) => fn(composed), arg);}console.log(compose(funs)(100)); // 相当于fn1(fn2(fn3(100)))
co函数
function* fn(a) { a = yield a; let b = yield 2; let c = yield 3; return a + b + c;}function co(fn, ...args) { let g = fn(...args); return new Promise((resolve, reject) => { function next(lastValue) { let { value, done } = g.next(lastValue); if (done) { resolve(value); } else { if (value instanceof Promise) { value.then(next, (val) => reject(val)); } else { next(value) } } } next(); });}co(fn, 100).then(value => { console.log(value); // 105});
如何被动停止Promise调用链
const p1 = new Promise((resolve, reject) => { setTimeout(() => { // 异步操作 resolve('start') }, 1000);});p1.then((result) => { console.log('a', result); return Promise.reject('中断后续调用'); // 此时rejected的状态将间接跳到catch里,剩下的调用不会再持续}).then(result => { console.log('b', result);}).then(result => { console.log('c', result);}).catch(err => { console.log(err);});// a start// 中断后续调用
bluebird.promisify实现(将异步回调函数api 转换为promise模式)
// promisify.jsmodule.exports = { promisify(fn){ returnfunction () { var args = Array.from(arguments); return new Promise(function (resolve, reject) { fn.apply(null, args.concat(function (err) { if (err) { reject(err); } else { resolve(arguments[1]) } })); }) } }}// main.jsconst fs = require('fs');const {promisify} = require('./promisify.js');const readFile = promisify('fs.readFile'); // 转换异步读取// 异步文件 由回调函数模式变成promise模式readFile('./1.txt', 'utf8').then(data => { console.log(data);}).catch(err => { console.log(err);});
window.requestAnimationFrame兼容性解决
window._requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); };})();
字符串是否合乎回文规定
let str = 'My age is 0, 0 si ega ym.';办法一function palindrome(params) { params = params.replace(/[\W\s_]/ig, ''); return params.toLowerCase() === params.split('').reverse().join('').toLowerCase();}console.log(palindrome(str));办法二function palindrome(params) { params = params.replace(/[\W\s_]/ig, '').toLowerCase(); for (var i = 0, j = params.length-1; i<j; i++, j--) { if (params[i] !== params[j]) { returnfalse; } } returntrue;}
解构
// 将 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4}const targetArray = [1, [2, 3], 4];const formater = "[a, [b], c]";const destructuringArray = (values, keys) => { try { const obj = {}; if (typeof keys === 'string') { keys = JSON.parse(keys.replace(/\w+/g, '"$&"')); } const iterate = (values, keys) => keys.forEach((key, i) => { if(Array.isArray(key)) iterate(values[i], key) else obj[key] = values[i] }) iterate(values, keys) return obj; } catch (e) { console.error(e.message); }}
数组展平
将[[1, 2], 3, [[[4], 5]]] 展平为 [1, 2, 3, 4, 5]
let arr = [[1, 2], 3, [[[4], 5]]]; // 数组展平function flatten(arr) { return [].concat( ...arr.map(x => Array.isArray(x) ? flatten(x) : x) )}
二分查找
const arr = [1, 2, 3, 4, 5, 6, 7, 8];// 二分查找 递归 由两头开始往两边查找 前提是有序的数组 返回对应的索引地位function binarySearch1(arr, dest, start = 0, end = data.length) { if (start > end) { return -1 } let midIndex = Math.floor((start + end) / 2); // 两头地位索引 let mid = arr[midIndex]; // 两头值 if (mid == dest) { return midIndex; } if (dest < mid) { // 要找的比两头值小 就从两头往结尾找 始终到0 return binarySearch1(arr, dest, 0, midIndex - 1); } if (dest > mid) { // 要找的比两头值大 就从两头往后找 始终到end完结 return binarySearch1(arr, dest, midIndex + 1, end); } return -1; // 找不到返回-1}console.log(binarySearch1(arr, 7, 3, 6)); // 6// 非递归 arr前提有序数组 (从小到大)返回对应的索引地位 function binarySearch2(arr, dest) { let max = arr.length - 1; let min = 0; while (min <= max) { let mid = Math.floor((max + min) / 2); // mid两头地位索引 if (dest < arr[mid]) { // 如果要找的这项比两头项还要小 阐明应该在mid两头地位后面 批改最大边界值max=mid-1 max = mid - 1; } elseif (dest > arr[mid]) { // 如果要找的这项比两头项还要大 阐明应该在mid两头地位的前面 批改最小边界值min=mid+1 min = mid + 1; } else { return mid; } } return -1; // 找不到返回-1}console.log(binarySearch2(arr, 3)); // 2
找出数组中反复呈现过的元素
// 例如:[1,2,4,4,3,3,1,5,3]// 输入:[1,3,4]let arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];// 办法一function repeat1(arr){ var result = [], map = {}; arr.map(function(num){ if(map[num] === 1) result.push(num); // 等于1阐明之前呈现过一次 这次反复呈现了 map[num] = (map[num] || 0) + 1; // 奥妙之处 开始第一次呈现无值 记为 0 + 1 = 1 下一次从1开始累加 }); return result;}console.log(repeat1(arr));// 办法二function repeat(arr) { let result = arr.filter((x, i, self) => { return self.indexOf(x) === i && self.lastIndexOf(x) !== i }); // return result;}console.log(repeat(arr));
数组中依照数字反复呈现的次数进行排序
// 如果次数雷同 则依照值排序 比方 2, 2, 2和 1, 1, 1 应排序为 [1, 1, 1, 2, 2, 2]// 比方 [1,2,1,2,1,3,4,5,4,5,5,2,2] => [3, 4, 4, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2]let arr = [9, 7, 7, 1, 2, 1, 2, 1, 3, 4, 5, 4, 5, 5, 2, 2];function sortArray(arr) { let obj = {}; let newArr = []; for(let i = 0; i < arr.length; i++) { let cur = arr[i]; if(obj[cur]){ obj[cur].push(cur); continue; } obj[cur] = [cur]; } for(let k in obj) { if(obj.hasOwnProperty(k)) { newArr.push(obj[k]) } } newArr.sort((a, b) => { if(a.length === b.length){ return a[0] - b[0]; } return a.length - b.length; }); newArr = newArr.reduce((prev, cur) => prev.concat(cur)); return newArr; } console.log(sortArray(arr)); // [ 3, 9, 4, 4, 7, 7, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2 ]
不必循环,创立一个长度为 100 的数组,并且每个元素的值等于它的下标。
// 办法一 递归写法function createArray(len, arr = []) { if (len > 0) { arr[--len] = len; createArray(len, arr); } return arr;}console.log(createArray(100)); // 办法二// 上面评论中@MaDivH 提供的实现办法 长度为 100 的数组 Array(100).fill().map((_,i)=>i+1);// 办法三[...Array(100).keys()]
依据关键词找出 所在对象id
var docs = [ { id: 1, words: ['hello', "world"] }, { id: 2, words: ['hello', "hihi"] }, { id: 3, words: ['haha', "hello"] }, { id: 4, words: ['world', "nihao"] }];findDocList(docs, ['hello']) // 文档id1,文档id2,文档id3findDocList(docs, ['hello', 'world']) // 文档id1function findDocList(docs, word = []) { if (word.constructor !== Array) return; let ids = []; for (let i = 0; i < docs.length; i++) { let {id, words} = docs[i]; let flag = word.every((item) => { return words.indexOf(item) > -1; }); flag && ids.push(id); } return ids;}findDocList(docs, ['hello', 'world']);
getElementsByClassName 兼容写法
function getByClass(cName) { if ('getElementsByClassName'in this) { return this.getElementsByClassName(cName); } cName = cName.replace(/(^\s+|\s+$)/g, '').split(/\s+/g); let eles = this.getElementsByTagName('*'); for (let i = 0; i < cName.length; i++) { let reg = new RegExp(`(^| )${cName[i]}( |$)`); let temp = []; for (let j = 0; j < eles.length; j++) { let cur = eles[j]; let {className} = cur; if (reg.test(className)) { temp.push(cur); } } eles = temp; } return eles; } console.log(content.getByClass('c1 c2 '));
插入排序
插入排序 从后往前比拟 直到碰到比以后项 还要小的前一项时 将这一项插入到前一项的前面
function insertSort(arr) { let len = arr.length; let preIndex, current; for (let i = 1; i < len; i++) { preIndex = i - 1; current = arr[i]; // 以后项 while (preIndex >= 0 && arr[preIndex] > current) { arr[preIndex + 1] = arr[preIndex]; // 如果前一项大于以后项 则把前一项往后挪一位 preIndex-- // 用以后项持续和后面值进行比拟 } arr[preIndex + 1] = current; // 如果前一项小于以后项则 循环完结 则将以后项放到 前一项的前面 } return arr;}
抉择排序
抉择排序 每次拿以后项与前面其余项进行比拟 失去最小值的索引地位 而后把最小值和以后项替换地位
function selectSort(arr) { let len = arr.length; let temp = null; let minIndex = null; for (let i = 0; i < len - 1; i++) { // 把以后值的索引作为最小值的索引一次去比拟 minIndex = i; // 假如以后项索引 为最小值索引 for (let j = i + 1; j < len; j++) { // 以后项前面向一次比小 if (arr[j] < arr[minIndex]) { // 比假如的值还要小 则保留最小值索引 minIndex = j; // 找到最小值的索引地位 } } // 将以后值和比拟出的最小值替换地位 if (i !== minIndex) { temp = arr[i] arr[i] = arr[minIndex]; arr[minIndex] = temp; } } return arr;}
冒泡排序
冒泡排序 相邻两项进行比拟 如果以后值大于后一项 则替换地位
function bubleSort(arr) { let length = arr.length; let temp = null; for (let i = 0; i < length - 1; i++) { // 管制轮数 let flag = false; // 以后这轮是否替换过标识 for (let l = 0; l < length - i - 1; l++) { // 管制每轮比拟次数 if (arr[l] > arr[l + 1]) { temp = arr[l]; arr[l] = arr[l + 1]; arr[l + 1] = temp; flag = true; // 如果产生过替换flag则为true } } if (!flag) { // 优化 如果从头到尾比拟一轮后 flag仍然为false阐明 曾经排好序了 没必要在继续下去 temp = null; return arr; } }}
疾速排序(递归)
function quickSort(arr) { if (arr.length <= 1) return arr; let midIndex = Math.floor(arr.length / 2); let midNum = arr.splice(midIndex, 1)[0]; let left = []; let right = []; for(let i = 0; i < arr.length; i++) { let cur = arr[i]; if (cur <= midNum) { left.push(cur); } else { right.push(cur); } } return quickSort(left).concat(midNum, quickSort(right));}let arr = [2, 4, 12, 9, 22, 10, 18, 6];quickSort(arr);
数组去重几种办法
const arr = [1, 2, 1, 2, 3, 4, 2, 1, 3];// 1 ES6let newArr = [...new Set(arr)];// 2const arr = [1, 2, 1, 2, 3, 4, 'l', 2, 1, 3, 'l'];const newArr = arr.filter(function(ele, index, array) { return index === array.indexOf(ele)});console.log(newArr); // [ 1, 2, 3, 4, 'l' ]// 3Array.prototype.unique2 = function() { let newArr = []; let len = this.length; for(let i = 0; i < len; i++) { let cur = this[i]; if(newArr.indexOf(cur) === -1) { newArr[newArr.length] = cur; } } return newArr;}console.log(arr.unique1());// 4Array.prototype.unique3 = function() { let newArr = this.slice(0); let len = this.length; let obj = {}; for(let i = 0; i < len; i++) { let cur = newArr[i]; if(obj[cur]) { newArr[i] = newArr[newArr.length - 1]; newArr.length--; i--; continue; } obj[cur] = cur; } return newArr;}console.log(arr.unique3());// 5Array.prototype.unique4 = function() { let json = {}, newArr = [], len = this.length; for(var i = 0; i < len; i++) { let cur = this[i]; if (typeof json[cur] == "undefined") { json[cur] = true; newArr.push(cur) } } return newArr;}console.log(arr.unique4());
千分符
办法一
// 解决数字let str1 = 2123456789;let str2 = 2123456789.12;console.log(str1.toLocaleString()); // 2,123,456,789console.log(str2.toLocaleString()); // 2,123,456,789.12
办法二
// 解决字符串 let str1 = '2123456789'; let str2 = '2123456789.12'; // 利用正向预查 匹配 结尾一个数字\d 前面匹配这个数字前面必须是三个数字为一组为结尾或小数为结尾 function thousandth(str) { let reg = /\d(?=(?:\d{3})+(?:\.\d+|$))/g; return str.replace(reg, '$&,'); } console.log(thousandth(str1)); // 2,123,456,789 console.log(thousandth(str2)); // 2,123,456,789.12
在一个数组中 如a、b两项, 要保障a和b两项的差 与 a和b两项索引的差 的相加后的后果max 是数组中其余两项max 中的最大值 找出符合条件两项a, b的值 (不能够排序 或扭转数组地位) 如:
let max = (a - b) + (a的索引- b的索引);
求a b
答案:
// 思路:其实也就是找出数组中以后的每一项与本身索引相加后的和的最大值以及与索引相加后的最小值的和 找出符合条件的两项即可 如 let result = (maxItem-minItem) + (maxIndex-minIndex) 等价于 (maxItem+maxIndex) - (minItem+minIndex)// let arr = [1, 2, 3, 4, 5, 6]; // 最简略的测试数组 最小项1 最大项6// 很显然这个数组中最大值6与索引相加(6+5)是当中最大值11 1与索引相加(1+0)为当中的最小值1(6 + 5)-(1+0)= 10// 假如法let arr = [2, 10, 9, 1, 8, 3, 4];let minItem = arr[0]; // 假如第一项与本身索引的和是最小值 索引为0因而省略let maxItem = arr[0]; // 假如第一项与本身索引的和是最大值 索引为0因而省略let min = minItem; // 最小项let max = maxItem; // 最大项let minIndex = 0; // 最小项索引let maxIndex = 0; // 最大项索引for(let i = 1; i < arr.length; i++) { let cur = arr[i] + i; // 以后项和本身索引的和 cur < minItem ? (minItem = cur, min = arr[i], minIndex = i) : null; cur > maxItem ? (maxItem = cur, max = arr[i], maxIndex = i) : null;}console.log(maxItem, minItem); // 最大项与索引的和 最小项与索引的和console.log(max, min); // 最大项 最小项console.log(maxIndex, minIndex); // 最大项的索引 最小项的索引
检测 字符串中括号表达式是否均衡
// 如 balance('[()') = false; balance('[()()()]') = true// 一function match(a, b) { return (a === '(' && b === ')') || (a === ')' && b === '(') || (a === '[' && b === ']') || (a === ']' && b === '[');}function balance(str) { if (str.length % 2 === 0) { let len = str.length; for (let i = 0, j = len - 1; i < len / 2; i++, j--) { if (!match(str[i], str[j])) { returnfalse; } } returntrue; } returnfalse;}console.log(balance('[()()()]')); // trueconsole.log(balance('[()')); // falseconsole.log(balance('[]()')); // false// 二function is_balance(str) { return [...str].reduce((stack, c) => { match(stack[stack.length - 1], c) ? stack.pop() : stack.push(c); return stack; }, []).length === 0;}console.log(is_balance('[()()()]')); // trueconsole.log(is_balance('[()')); // falseconsole.log(is_balance('[]()')); // false
求相邻两项最大和
// 一let arr1 = [-1, 3, 1, -5, 2]; // 如 [2, 4, -4, -3] => 4function sum(arr) { let prev = arr[0]; let sumArr = []; let len = arr.length; for(let i = 1; i < len; i++) { let cur = arr[i]; sumArr.push(cur + prev); prev = cur; } return Math.max(...sumArr);}console.log(sum(arr1));// 二function maxsum(arr) { const M = [arr[0]]; let max = M[0]; for(let i = 1; i < arr.length; i++) { M[i] = Math.max(arr[i], M[i - 1] + arr[i]); max = Math.max(M[i], max); } return max;}
字符串去除相邻的反复项 如:'aabbccddeexxxxaa' => 'abcdexa'
// 正则表达式let str = 'aabbccddeexxxxaa';function uniq1(str) { // return str.replace(/([a-z])(\1){1,}/g, '$1'); return str.replace(/(.)(?=\1)/g, '');}console.log(uniq1(str));// 数组形式function uniq2(str) { let arr = str.split(''); let newArr = [arr[0]]; for(let i = 1; i < arr.length; i++) { let cur = arr[i]; if (cur !== newArr[newArr.length - 1]) { newArr.push(cur); } } return newArr.join('');}console.log(uniq2(str));