instanceOf 1 2 3 4 5 6 7 8 9 10 function _instanceof (a,b ){ while (a){ if (a.__proto__ === b.prototype ){ return true }else { a = a.__proto__ ; } return false ; } }
reduce 1 2 3 4 5 6 7 8 9 10 11 Array .prototype .myReduce = function (fn, initialValue ) { const arr = this ; const [begin] = arr; let pre = initialValue || begin; const startIndex = initialValue === void 0 ? 1 : 0 ; for (let i = startIndex; i < arr.length ; i++) { pre = fn (pre, arr[i]); } return pre; };
防抖 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 debounce (func, wait ) { let timeout = null ; return function ( ) { let context = this ; let args = arguments ; if (timeout) { clearTimeout (timeout); timeout = null ; } timeout = setTimeout (() => { func.apply (context, args); }, wait); }; } const debounce = (fn, delay = 300 ) => { let timer = null ; return function (...args ) { clearTimeout (timer); timer = setTimeout (() => fn (...args), delay); }; };
节流 1 2 3 4 5 6 7 8 9 10 11 12 function throttle (func, wait ) { let previous = 0 ; return function ( ) { let now = Date .now (); let context = this ; let args = arguments ; if (now - previous > wait) { func.apply (context, args); previous = now; } }; }
判断是否是数组 1 2 3 4 let arr=[]; Array.isArray(arr); //or Object.prototype.toString.call(arr) === '[Object Array]';
时间戳转字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function getTime(_time,joiner,joiner2){ if(!time){ return ''; } let time = new Date(_time); const year = time.getFullYear();//获取年份 const month = time.getMonth()+1;//get the month of the time const date = time.getDate()<10 ? '0'+time.getDate():time.getDate();//get the day of the time; const hour = time.getHours(); const minute = time.getMinutes(); const seconds = time.getSeconds(); const result = `${year}${joiner}${month}${joiner}${date} ${hour}${joiner2}${minute}${joiner2}${seconds}`; return result; }
深合并 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 * @see merge * @param {Object} obj1 Object to merge * @returns {Object} Result of all merge properties */ function deepMerge(/* obj1, obj2, obj3, ... */) { var result = {}; function assignValue(val, key) { if (typeof result[key] === 'object' && typeof val === 'object') { result[key] = deepMerge(result[key], val); } else if (typeof val === 'object') { result[key] = deepMerge({}, val); } else { result[key] = val; } } for (var i = 0, l = arguments.length; i < l; i++) { forEach(arguments[i], assignValue); } return result; }
正则表达式 1 2 3 4 5 const matchTime = /(?<=\s+\-\s+)(.)+|(.)+(?=\s+\-\s+)/g;//将xx年xx月xx日-xx年xx月xx日的时间格式切割 const regx_positiveInt = /^[0-9]*[1-9][0-9]*$/; //正整数 const regx_positiveAndZeroInt = /^\d+$/; //非负整数(正整数和0) const regx_number = /^[0-9]*$/;//数字 const regx_ip=/^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;//IP地址
大数相加 由于 JavaScript 中不管整数还是小数都是 Number 类型,它的实现是遵循 IEEE 754 标准,储存方式是以双精度浮点数储存的,可以表示十进制的 15 或者 16 位有效数字,所以当进行运算的数字超过这个范围的时候,就会用有精度缺失的问题,导致计算不准确,所以在大数加减的时候需要转成字符串来做,或者 es6+有个 BigInt 类型也支持做大数加减,或者可以选择 npm 上的大数加减插件,比如 number-precision
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var addStrings = function(num1, num2) { const maxLen = Math.max.apply(null, [num1.length, num2.length]); let result = ""; let ans = 0; if(num1.length<maxLen){ num1 = num1.padStart(maxLen, "0"); } if(num2.length<maxLen){ num2 = num2.padStart(maxLen, "0"); } for (let i = maxLen - 1; i >= 0; --i) { const temp =ans+Number(num1[i]) + Number(num2[i]); result=(temp % 10)+result; ans = Math.floor(temp / 10); } console.log(result); return ans===1?ans+ result:result; };
深克隆 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 const isObject = (item)=>{ return Object.prototype.toString.call(item) === '[object Object]'; } const isArray = (item)=>{ return Object.prototype.toString.call(item) === '[object Array]'; } const isDate = item =>{ return Object.prototype.toString.call(item) === '[object Date]'; } const isFunction = item =>{ return Object.prototype.toString.call(item) === '[object Function]'; } const deepClone=function(obj){ const cloneObj=isArray(obj)?[]:isObject(obj)?{}:''; for(let key in obj){ if(obj.hasOwnProperty(key)){ if(isObject(obj[key])||isArray(obj[key])){ Object.assign(cloneObj,{ [key]: deepClone(Reflect.get(obj,key)) }); } else{ cloneObj[key] = obj[key]; } } } return cloneObj; } function deepCopy(original) { if (Array.isArray(original)) { return original.map(elem => deepCopy(elem)); } else if (typeof original === 'object' && original !== null) { return Object.fromEntries( Object.entries(original) .map(([k, v]) => [k, deepCopy(v)])); } else { // Primitive value: atomic, no need to copy return original; } }
深层更新 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 deepUpdate(original, keys, value) { if (keys.length === 0) { return value; } const currentKey = keys[0]; if (Array.isArray(original)) { return original.map( (v, index) => index === currentKey ? deepUpdate(v, keys.slice(1), value) // (A) : v); // (B) } else if (typeof original === 'object' && original !== null) { return Object.fromEntries( Object.entries(original).map( (keyValuePair) => { const [k,v] = keyValuePair; if (k === currentKey) { return [k, deepUpdate(v, keys.slice(1), value)]; // (C) } else { return keyValuePair; // (D) } })); } else { // Primitive value return original; } }
深层冻结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function deepFreeze(value) { if (Array.isArray(value)) { for (const element of value) { deepFreeze(element); } Object.freeze(value); } else if (typeof value === 'object' && value !== null) { for (const v of Object.values(value)) { deepFreeze(v); } Object.freeze(value); } else { // Nothing to do: primitive values are already immutable } return value; }
基于现有对象的自定义方法 PS:(不建议直接污染原型链,类型 Date.prototype.xxx=function(){}这样的写法)
个人比较喜欢寄生组合继承或者 es6 的 calss 继承,比如要写一个基于 Date 的 format 方法
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 class DateSelf extends Date{ constructor(){ super(); } getAll(begin,end){ let arr=[]; var ab = begin.split("-"); var ae = end.split("-"); var db = new Date(); db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]); var de = new Date(); de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]); var unixDb = db.getTime(); var unixDe = de.getTime(); for (var k = unixDb; k <= unixDe;) { // arr.push((new Date(parseInt(k))).format()); arr.push((DateSelf.formatter(parseInt(k)))) k = k + 24 * 60 * 60 * 1000; } return arr; } } DateSelf.formatter=function(time){ var s = ''; const k = new Date(time) var mouth = (k.getMonth() + 1)>=10?(k.getMonth() + 1):('0'+(k.getMonth() + 1)); var day = k.getDate()>=10?k.getDate():('0'+k.getDate()); s += k.getFullYear() + '-'; // 获取年份。 s += mouth + "-"; // 获取月份。 s += day; // 获取日。 return (s); // 返回日期。 }
判断对象类型 1 2 3 4 5 6 7 8 Object.toType = (function toType(global) { return function(obj) { if (obj === global) { return "global"; } return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase(); } })(this)
用 promsie 异步加载图像 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 functin load(image,attributes){ if (!image) { return Promise.reject(); } else if (typeof image === 'string') { /* Create a <img> from a string */ const src = image; image = new Image(); Object.keys(attributes || {}).forEach( name => image.setAttribute(name, attributes[name]) ); image.src = src; } else if (image.length !== undefined) { /* Treat as multiple images */ // Momentarily ignore errors const reflected = [].map.call(image, img => load(img, attributes).catch(err => err)); return Promise.all(reflected).then(results => { const loaded = results.filter(x => x.naturalWidth); if (loaded.length === results.length) { return loaded; } return Promise.reject({ loaded, errored: results.filter(x => !x.naturalWidth) }); }); } else if (image.tagName.toUpperCase() !== 'IMG') { return Promise.reject(); } const promise = new Promise((resolve, reject) => { if (image.naturalWidth) { // If the browser can determine the naturalWidth the // image is already loaded successfully resolve(image); } else if (image.complete) { // If the image is complete but the naturalWidth is 0px // it is probably broken reject(image); } else { image.addEventListener('load', fulfill); image.addEventListener('error', fulfill); } function fulfill() { if (image.naturalWidth) { resolve(image); } else { reject(image); } image.removeEventListener('load', fulfill); image.removeEventListener('error', fulfill); } }); promise.image = image; return promise; }
获取类型 ({}).toString.call(obj) 的用法与 Object.prototype.toString.call(obj)一样。这两个返回的格式是[object [class]].
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 const toType = (obj) =>{ return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() } toType({a: 4}); //"object" toType([1, 2, 3]); //"array" (function() {console.log(toType(arguments))})(); //arguments toType(new ReferenceError); //"error" toType(new Date); //"date" toType(/a-z/); //"regexp" toType(Math); //"math" toType(JSON); //"json" toType(new Number(4)); //"number" toType(new String("abc")); //"string" toType(new Boolean(true)); //"boolean" //typeof 也可以判断类型,但是只能获取基本类型,引用类型统一返回object,所以尽量用toType的方法;instanceof也可以,然鹅instanceof是基于原型链判断的,如果继承的话 class MyArray extends Array{ constructor(){ super() } } const myArray = new MyArray(); console.log(`Array: ${myArray instanceof Array}`); console.log(`MyArray :${myArray instanceof MyArray}`); //这样两个返回的都是true
几个内置对象没有构造函数,因此不能用 instanceof 来判断类型
1 Math instanceof Math //TypeError
一个 window 可以包含多个 iframe 框架,意味着包含多个全局上下文,因此意味着每个类型有多个构造器,在这样的环境下,给定的对象类型不能保证是给定的构造函数的实例
1 2 3 4 5 6 7 const ifFrame = document.createElement('iframe'); document.body.appendChild(ifFrame); const IFrameArray = window.frames[1].Array; const array = new IFrameArray(); array instanceof IFrameArray //true array instanceof Array // false
由于 array 的 constructor 是 iframe 中的 Array,因此即使 array 与 此时的 window 上下文中的 Array 都是数组范畴,但是 instanceof 是基于原型链来判断的,array 的原型链是并没有指向当前 window 下的 Array,因此 array instanceof Array 为 false
之前的 toType 有个问题,当 toType 的传参是 window,window 内置方法以及 DOM 时,有很长一串的结果返回时
1 2 3 4 5 6 7 8 9 10 11 toType(window); //"global" (Chrome) "domwindow" (Safari) "window" (FF/IE9) "object" (IE7/IE8) toType(document); //"htmldocument" (Chrome/FF/Safari) "document" (IE9) "object" (IE7/IE8) toType(document.createElement('a')); //"htmlanchorelement" (Chrome/FF/Safari/IE) "object" (IE7/IE8) toType(alert); //"function" (Chrome/FF/Safari/IE9) "object" (IE7/IE8)
改进一下 toType 方法,将这个方法挂载到 Object 上,但是不要挂载到 prototype 上,将这个方法写成 IIFE+闭包
1 2 3 4 5 6 7 8 9 10 11 12 Object.toType = (function toType(global) { return function(obj) { if (obj === global) { return "global"; } return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase(); } })(this) Object.toType(window); //"global" (all browsers) Object.toType([1,2,3]); //"array" (all browsers) Object.toType(/a-z/); //"regexp" (all browsers) Object.toType(JSON); //"json" (all browsers)
这个方法也不是万能的,如果扔进来一个未知类型,则会抛出异常
1 Object.toType(fff)//ReferenceError
玩个好玩的,应该可以用具名组匹配来获取类型
1 2 3 4 5 6 7 8 9 10 11 Object.toType = (function(global){ const reg = /(?<type>(?<=\s+?)[A-Za-z]+)/; return function(obj){ if(obj === global){ return 'global'; } const matchObj = reg.exec(Object.prototype.toString.call(obj)); const type = matchObj.groups.type; return type; } })(this)
takeRightWhile 1 2 const takeRightWhile = (arr, func) => arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []);
用法
1 takeRightWhile([1, 2, 3, 4], n => n < 3); // [3, 4]
zip 1 2 3 4 5 6 const zip = (...arrays) => { const maxLength = Math.max(...arrays.map(x => x.length)); return Array.from({ length: maxLength }).map((_, i) => { return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]); }); };
用法
1 2 zip(['a', 'b'], [1, 2], [true, false]); // [['a', 1, true], ['b', 2, false]] zip(['a'], [1, 2], [true, false]); // [['a', 1, true], [undefined, 2, false]]
聚合函数 1 2 3 4 5 6 //单个参数 const pipe = (...fns) => x => { return fns.reduceRight((v,fn)=>{ return fn(v); },x) }
用法
1 2 3 4 5 6 const square = v => v+1; const double = v => v+2; const addOne = v => v+3; const res = pipe(square,double,addOne); res(3)//9
elementIsVisibleInViewport 1 2 3 4 5 6 7 8 const elementIsVisibleInViewport = (el, partiallyVisible = false) => { const { top, left, bottom, right } = el.getBoundingClientRect(); const { innerHeight, innerWidth } = window; return partiallyVisible ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) && ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth)) : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth; };
用法
1 2 elementIsVisibleInViewport(el); // false - (not fully visible) elementIsVisibleInViewport(el, true); // true - (partially visible)
聚合函数(2) 1 2 //第一个函数允许多个参数,剩余函数仅允许一个参数 const composeRight = (...fns) => fns.reduce((f,g)=>(...args)=>g(f(...args)));
用法
1 2 3 const add =(x,y) => x+y; const square = x => x**2; const addAndSquare = composeRight(add,square);
uniqueSymmetricDifference 1 2 3 const uniqueSymmetricDifference = (a, b) => [ ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))]) ];
用法
1 uniqueSymmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4]
获取长度 1 2 3 4 5 6 7 8 const size = val => Array.isArray(val) ? val.length : val && typeof val === 'object' ? val.size || val.length || Object.keys(val).length : typeof val === 'string' ? new Blob([val]).size : 0;
initialArrayWithRangeRight 1 2 3 4 5 const initialArrayWithRangeRight = (end,start = 0, step = 1) => { return Array.from({length:Math.ceil(end-start+1)/step}).map((v,i,arr)=>{ return (arr.length-i-1)*step+start; }); }
用法
1 initialArrayWithRangeRight(5)// [5,4,3,2,1,0]
创建加密 hash 1 2 3 4 5 6 7 8 const hashBrowser = val => crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => { let hexes = [], view = new DataView(h); for (let i = 0; i < view.byteLength; i += 4) hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8)); return hexes.join(''); });
用法
1 hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
自定义事件 1 2 const triggerEvnet = (el, eventType, detail ) => el.dispatchEvent (new CustomEvent (eventType, detail));
用法
1 triggerEvnet (document .getElementById ("id" ), "click" );
isWritableStream 检查给定参数是否是可写的流体
1 2 3 4 5 6 const isWriteable = (val ) => val !== null && typeof val === "object" && typeof val.pipe === "function" && typeof val._write === "function" && typeof val._writableState === "object" ;
Node 环境中使用
1 2 const fs = require ("fs" );isWritableStream (fs.createWriteStream ("test.txt" ));
打乱数组顺序 洗牌算法
1 2 3 4 5 6 7 8 const shuffle = (arr ) => { let m = arr.length ; while (m) { const i = Math .floor (Math .random () * m--); [arr[i], arr[m]] = [arr[m], arr[i]]; } return arr; };
反解套 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const unflattenObject = (obj ) => Object .keys (obj).reduce ((acc, k ) => { if (k.indexOf ("." ) !== -1 ) { const keys = k.split ("." ); Object .assign ( acc, JSON .parse ( "{" + keys .map ((v, i ) => (i !== keys.length - 1 ? `"${v} ":{` : `"${v} ":` )) .join ("" ) + obj[k] + "}" .repeat (keys.length ) ) ); } else acc[k] = obj[k]; return acc; }, {});
example
1 unflattenObject ({ "a.b.c" : 1 , d : 1 });