编程题
方法库部分
实现防抖函数(debounce)
const debounce = (fn, delay) => {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};
underscore版本:
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
lodash版本
import isObject from './isObject.js'
import root from './.internal/root.js'
function debounce(func, wait, options) {
let lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime
let lastInvokeTime = 0
let leading = false
let maxing = false
let trailing = true
const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
wait = +wait || 0
if (isObject(options)) {
leading = !!options.leading
maxing = 'maxWait' in options
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
trailing = 'trailing' in options ? !!options.trailing : trailing
}
function invokeFunc(time) {
const args = lastArgs
const thisArg = lastThis
lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}
function startTimer(pendingFunc, wait) {
if (useRAF) {
root.cancelAnimationFrame(timerId)
return root.requestAnimationFrame(pendingFunc)
}
return setTimeout(pendingFunc, wait)
}
function cancelTimer(id) {
if (useRAF) {
return root.cancelAnimationFrame(id)
}
clearTimeout(id)
}
function leadingEdge(time) {
lastInvokeTime = time
timerId = startTimer(timerExpired, wait)
return leading ? invokeFunc(time) : result
}
function remainingWait(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
const timeWaiting = wait - timeSinceLastCall
return maxing
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
: timeWaiting
}
function shouldInvoke(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
}
function timerExpired() {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
timerId = startTimer(timerExpired, remainingWait(time))
}
function trailingEdge(time) {
timerId = undefined
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}
function cancel() {
if (timerId !== undefined) {
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}
function flush() {
return timerId === undefined ? result : trailingEdge(Date.now())
}
function pending() {
return timerId !== undefined
}
function debounced(...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
debounced.cancel = cancel
debounced.flush = flush
debounced.pending = pending
return debounced
}
export default debounce
实现节流函数(throttle)
const throttle = (fn, delay = 500) => {
let flag = true;
return (...args) => {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, args);
flag = true;
}, delay);
};
};
underscore版本:
_.throttle = function(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
};
lodash版本:
import debounce from './debounce.js'
import isObject from './isObject.js'
function throttle(func, wait, options) {
let leading = true
let trailing = true
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading
trailing = 'trailing' in options ? !!options.trailing : trailing
}
return debounce(func, wait, {
leading,
trailing,
'maxWait': wait
})
}
export default throttle
实现lazy-load懒加载
let imgs=document.querySelectorAll('img');
let clientHeight=window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
function lazyLoad(){
let scrollTop=window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
for(let i=0;i
let x=clientHeight + scrollTop-imgs[i].offsetTop;
if(x>0 && x
imgs[i].src=imgs[i].getAttribute('data')
}
}
}
实现拖拽
window.οnlοad=function(){
let drag=document.getElementById('box');
drag.οnmοusedοwn=function(e){
let e = e || window.event;
let diffX=e.clientX-drag.offsetLeft;
let diffY=e.clientY-drag.offsetTop;
drag.οnmοusemοve=function(e){
let left=e.clientX-diffX;
let top=e.clientY-diffY;
if(left<0){
left=0;
}else if(left>window.innerWidth-drag.offsetWidth){
left=window.innerWidth-drag.offsetWidth;
}
if(top<0){
top=0;
}else if(top>window.innerHeight-drag.offsetHeight){
top=window.innerHeight-drag.offsetHeight
}
drag.style.left=left+'px';
drag.style.top=top+'px';
}
drag.οnmοuseup=function(e){
this.οnmοusemοve=null;
this.οnmοuseup=null;
}
}
}
实现基于Promise的ajax函数
function ajax(url,method,fn,type){
return new Promise((resolve,reject)=>{
var xhr=new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200){
//var result=xhr.responseText;
//fn(result);
resolve(JSON.parse(xhr.responseText).count);
}
}
};
xhr.open(method,url+"?"+type,true);
if(method=="post"){
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
}
if(method=="get"){
xhr.send(null);
}else if(method=="post"){
xhr.send(type);
}
}
)
}
实现一个浅克隆
function clone(obj){
let newObj={};
for(let key in obj){
newObj[key]=obj[key];
}
return newObj;
}
实现一个深克隆(deepclone)
简版:
function clone(obj){
if(obj===null){
return null
};
if({}.toString.call(obj)==='[object Array]'){
let newArr=[];
newArr=obj.slice();
return newArr;
};
let newObj={};
for(let key in obj){
if(typeof obj[key]!=='object'){
newObj[key]=obj[key];
}else{
newObj[key]=clone(obj[key]);
}
}
}
完整版:
const clone = parent => {
const isType = (obj, type) => {
if (typeof obj !== "object") return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case "Array":
flag = typeString === "[object Array]";
break;
case "Date":
flag = typeString === "[object Date]";
break;
case "RegExp":
flag = typeString === "[object RegExp]";
break;
default:
flag = false;
}
return flag;
};
const getRegExp = re => {
var flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
const parents = [];
const children = [];
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== "object") return parent;
let child, proto;
if (isType(parent, "Array")) {
child = [];
} else if (isType(parent, "RegExp")) {
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
child = new Date(parent.getTime());
} else {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
const index = parents.indexOf(parent);
if (index != -1) {
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
实现一个深冻结
function deepFreeze(object){
let propNames=Object.getOwnPropertyNames(object);
for(let name of propNames){
let value=object[name];
object[name]=value && typeof value === 'object' ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
实现一个深比较
function deepCompare(a,b){
if(a===null || typeof a!=='object' || b===null || typeof b!=='object'){
return a===b
}
const propsA=Object.getOwnPropertyDescriptors(a);
const propsB=Object.getOwnPropertyDescriptors(b);
if(Object.keys(propsA).length!==Object.keys(propsB).length){
return false
}
return Object.keys(propsA).every(
key=>deepCompare(a[key],b[key])
)
}
解析URL Params为对象
简版
function parseQueryString(url){
let search=url.split('?')[1];
let strs=search.split('&');
let params={};
for(let str of strs){
let arr=str.split('=');
params[arr[0]]=isNaN(arr[1])?arr[1]:Number(arr[1]);
}
return params;
}
完整版
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1];
const paramsArr = paramsStr.split('&');
let paramsObj = {};
paramsArr.forEach(param => {
if (/=/.test(param)) {
let [key, val] = param.split('=');
val = decodeURIComponent(val);
val = /^\d+$/.test(val) ? parseFloat(val) : val;
if (paramsObj.hasOwnProperty(key)) {
paramsObj[key] = [].concat(paramsObj[key], val);
} else {
paramsObj[key] = val;
}
} else {
paramsObj[param] = true;
}
})
return paramsObj;
}
考虑深度集合
function parse(str){
return str.split('&').reduce((o,kv)=>{
const [key,value]=kv.split('=');
if(!value){
return o
}
deep_set(o,key.split(/[\[\]]/g).filter(x=>x),value);
return o
},{})
}
function deep_set(o,path,value){
let i=0;
for(;i
if(o[path[i]]===undefined){
if(path[i+1].match(/^\d+$/)){
o[path[i]]=[]
}else{
o[path[i]]={}
}
}
o=o[path[i]]
}
o[path[i]]=decodeURIComponent(value)
}
为普通对象添加迭代属性
let obj={};
Object.defineProperty(obj,Symbol.iterator,{
enumerable:false,
writable:false,
configurable:true,
value:function(){
let o=this;
let idx=0;
let ks=Object.keys(o);
return {
next:function(){
return {
value:o[ks[idx++]],
done:(idx>ks.length)
}
}
}
}
})
输出字符串中字符的个数
推荐
let str='helloworld';
let dict={};
for(let i=0;i
if(dict[str[i]]===undefined){
dict[str[i]]=1;
}else{
dict[str[i]]+=1;
}
}
编写一个方法 求一个字符串的字节长度
function GetBytes(str){
var len = str.length;
var bytes = len;
for(var i=0; i
if (str.charCodeAt(i) > 255) bytes++;
}
return bytes;
}
查找字符串中出现最多的字符和个数
let str = "abcabcabcbbccccc";
let num = 0;
let char = '';
str = str.split('').sort().join('');
let re = /(\w)\1+/g;
str.replace(re,($0,$1) => {
if(num < $0.length){
num = $0.length;
char = $1;
}
});
console.log(`字符最多的是${char},出现了${num}次`);
字符串查找
a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isContain(a,b);
function isContain(a, b) {
for (let i in b) {
if (a[0] === b[i]) {
let tmp = true;
for (let j in a) {
if (a[j] !== b[~~i + ~~j]) {
tmp = false;
}
}
if (tmp) {
return i;
}
}
}
return -1;
}
实现字符串翻转
var arr=str.split('');
var newArr=[];
for(var i=0;i
newArr[i]=arr[arr.length-i-1];
}
var newStr=newArr.join('')
console.log(str0);
var newStr='';
for(var i=0;i
newStr+=str.charAt(str.length-i-1);
}
console.log(newStr);
var newStr=str.split("").reverse().join("");
console.log(newStr);
var arr=str.split('');
var obj=Array.from(new Set([...arr]));
var newStr='';
for(i of obj){
newStr+=obj[arr.length-i];
}
console.log(newStr)
var arr=str.split('');
var newArr=[];
while(arr.length>0){
newArr.push(arr.pop())
};
var newStr=newArr.join("");
console.log(newStr)
实现字符串的散列函数
function h_str(str,M){
return [...str].reduce((hash,c)=>{
hash=(31*hash+c.charCodeAt(0))%M
return hash
},0)
}
实现数组去重
推荐
let dict={},
result=[],
j=0;
for(let i=0;i
dict[arr[i]]=1;
}
for(result[j++] in dict);
var newArr=Array.from(new Set(arr));
console.log(newArr);
for(var i=0;i
for(j=i+1;j
if(arr[i]==arr[j]){
arr.splice(j,1);
j--;
}
}
}
console.log(arr);
var newArr=[];
for(var i=0;i
if(newArr.indexOf(arr[i])===-1){
newArr.push(arr[i])
}
}
console.log(newArr);
var arr=arr.sort();
var newArr=[arr[0]];
for(var i=1;i
if(arr[i]!==arr[i-1]){
newArr.push(arr[i])
}
}
console.log(newArr);
var newArr=[];
var obj={};
for(var i=0;i
if(!obj[arr[i]]){
newArr.push(arr[i]);
obj[arr[i]]=1
}else{
obj[arr[i]]++
}
}
console.log(newArr);
var newArr=[];
for(var i=0;i
if(!newArr.includes(arr[i])){//检测数组是否有某个值
newArr.push(arr[i]);
}
}
console.log(newArr);
var obj={};
var newArr=arr.filter((item,index,arr)=>{
return obj.hasOwnProperty(typeof item+item)?false:(obj[typeof item+item]=true)
})
console.log(arrM6);
arr.sort(function(a,b){
return a-b;
})
function loop(index){
if(index>=1){
if(arr[index]===arr[index-1]){
arr.splice(index,1);
}
loop(index-1)
}
}
loop(arr.length-1);
console.log(arr);
var map=new Map();
var newArr=[];
for(var i=0;i
if(map.has(arr[i])){
map.set(arr[i],true);
}else{
map.set(arr[i],false);
newArr.push(arr[i]);
}
}
console.log(newArr);
arr=arr.reduce((prev,cur)=>prev.includes(cur)?prev:[...prev,cur],[]);
console.log(arr);
var newArr=[...new Set(arr)];
console.log(newArr);
实现合成函数compose
const compose =(...args)=>args.reduce((prev,current)=>(...values)=>prev(current(...values)));
实现旋转数组
function rotate(nums, k) {
for (let i = 0;i < k; i++) {
nums.unshift(nums.pop());
}
};
实现浮点数的散列函数
function binary_value(val){
const farr=new Float32Array(1)
farr[0]=val
const intBytes=new Int8Array(farr.buffer)
const view=new DataView(intBytes.buffer)
return view.getUnit32()
}
k=>binary_value(k)%M
实现千位分隔符
function parseToMoney(num) {
num = parseFloat(num.toFixed(3));
let [integer, decimal] = String.prototype.split.call(num, '.');
integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
return integer + '.' + (decimal ? decimal : '');
}
function parseToMoney(str){
let re = /(?=(?!\b)(\d{3})+$)/g;
return str.replace(re,',');
}
判断是否是电话号码
function isPhone(tel) {
var regx = /^1[34578]\d{9}$/;
return regx.test(tel);
}
验证是否是邮箱
function isEmail(email) {
var regx = /^([a-zA-Z0-9_\-])+@([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/;
return regx.test(email);
}
验证是否是身份证
function isCardNo(number) {
var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return regx.test(number);
}
实现一个函数柯里化
const curry=(fn,arr=[])=>(...args)=>(
arg=>arg.length===fn.length
? fn(...arg)
: curry(fn,arg)
)([...arr,...args])
实现一个函数反柯里化
const unCurrying=(fn)=>{
return (...args)=>{
return fn.call(...args);
}
}
实现数组扁平化
递归
function flatten(arr){
return [].concat(
...arr.map(x=>Array.isArray(x)?flatten(x):x)
)
}
递归,可以用for去展平拿到想要的第几项,可以忽略后续的非展平项,保证性能
function *flatten(arr){
for(let i=0;i
if(Array.isArray(arr[i])){
yield * flatten(arr[i])
}else{
yield arr[i]
}
}
}
堆栈实现非递归
function *flatten(arr){
let stack=arr.slice.reverse();
while(stack.length){
const item=stack.pop();
if(item.constructor===Array){
stack=stack.concat(item)
}else{
yield item
}
}
}
判断是否是回文数
function isPalindrome(str) {
str = str.replace(/\W/g, '').toLowerCase();
return (str == str.split('').reverse().join(''));
}
实现isArray()方法 判断是否为数组
Array.myIsArray=function(o){
return Object.prototype.toString.call(Object(o)) === '[object Array]'
}
实现isContain()方法,判断是否包含该字符串
function isContain(a, b) {
for (let i in b) {
if (a[0] === b[i]) {
let tmp = true;
for (let j in a) {
if (a[j] !== b[~~i + ~~j]) {
tmp = false;
}
}
if (tmp) {
return i;
}
}
}
return -1;
}
实现isNegZero函数
function isNegZero(n){
n=Number(n);
return (n === n) && (1/n === -Infinity)
}
实现Object.is()函数
Object.is=function(v1,v2){
if(v1===0 && v2===0){
return 1/v1 === 1/v2;
}
if(v1!==v1){
return v2!==v2;
}
return v1 === v2;
}
原理部分
实现Event(event bus)
class EventEmitter {
constructor() {
this._events = this._events || new Map