├── README.md └── util.js /README.md: -------------------------------------------------------------------------------- 1 | # tools 2 | a easy project 3 | 4 | 1、isStatic:检测数据是不是除了symbol外的原始数据 5 | -------------------------------- 6 | 7 | ``` 8 | function isStatic(value) { 9 | return( 10 | typeof value === 'string' || 11 | typeof value === 'number' || 12 | typeof value === 'boolean' || 13 | typeof value === 'undefined' || 14 | value === null 15 | ) 16 | } 17 | ``` 18 | 19 | 2、isPrimitive:检测数据是不是原始数据 20 | ------------------------- 21 | 22 | ``` 23 | function isPrimitive(value) { 24 | return isStatic(value) || typeof value === 'symbol' 25 | } 26 | ``` 27 | 28 | 29 | 3、isObject:判断数据是不是引用类型的数据 (例如: arrays, functions, objects, regexes, new Number(0),以及 new String('')) 30 | ------------------------------------------------------------------------ 31 | 32 | ``` 33 | function isObject(value) { 34 | let type = typeof value; 35 | return value != null && (type == 'object' || type == 'function'); 36 | } 37 | ``` 38 | 39 | 4、isObjectLike:检查 value 是否是 类对象。 如果一个值是类对象,那么它不应该是 null,而且 typeof 后的结果是 "object" 40 | ------ 41 | 42 | ``` 43 | function isObjectLike(value) { 44 | return value != null && typeof value == 'object'; 45 | } 46 | ``` 47 | 48 | 5、getRawType:获取数据类型,返回结果为 Number、String、Object、Array等 49 | ----- 50 | 51 | ``` 52 | function getRawType(value) { 53 | return Object.prototype.toString.call(value).slice(8, -1) 54 | } 55 | //getoRawType([]) ==> Array 56 | ``` 57 | 58 | 6、isPlainObject:判断数据是不是Object类型的数据 59 | ------ 60 | 61 | ``` 62 | function isPlainObject(obj) { 63 | return Object.prototype.toString.call(obj) === '[object Object]' 64 | } 65 | ``` 66 | 67 | 7、isArray:判断数据是不是数组类型的数据 68 | ------------------------ 69 | 70 | ``` 71 | function isArray(arr) { 72 | return Object.prototype.toString.call(arr) === '[object Array]' 73 | } 74 | ``` 75 | 将isArray挂载到Array上 76 | 77 | ``` 78 | Array.isArray = Array.isArray || isArray; 79 | ``` 80 | 81 | 8、isRegExp:判断数据是不是正则对象 82 | ------ 83 | 84 | ``` 85 | function isRegExp(value) { 86 | return Object.prototype.toString.call(value) === '[object RegExp]' 87 | } 88 | ``` 89 | 90 | 9、isDate:判断数据是不是时间对象 91 | -------------------- 92 | 93 | ``` 94 | function isDate(value) { 95 | return Object.prototype.toString.call(value) === '[object Date]' 96 | } 97 | ``` 98 | 99 | 10、isNative:判断 value 是不是浏览器内置函数 100 | ------------------------------- 101 | 102 | 内置函数toString后的主体代码块为 [native code] ,而非内置函数则为相关代码,所以非内置函数可以进行拷贝(toString后掐头去尾再由Function转) 103 | 104 | ``` 105 | function isNative(value) { 106 | return typeof value === 'function' && /native code/.test(value.toString()) 107 | } 108 | ``` 109 | 110 | 11、isFunction:检查 value 是不是函数 111 | ------- 112 | 113 | ``` 114 | function isFunction(value) { 115 | return Object.prototype.toString.call(value) === '[object Function]' 116 | } 117 | ``` 118 | 119 | 12、isLength:检查 value 是否为有效的类数组长度 120 | ------- 121 | 122 | ``` 123 | function isLength(value) { 124 | return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= Number.MAX_SAFE_INTEGER; 125 | } 126 | ``` 127 | 128 | 13、isArrayLike:检查 value 是否是类数组 129 | ------------------------------- 130 | 131 | 如果一个值被认为是类数组,那么它不是一个函数,并且value.length是个整数,大于等于 0,小于或等于 Number.MAX_SAFE_INTEGER。这里字符串也将被当作类数组 132 | 133 | ``` 134 | function isArrayLike(value) { 135 | return value != null && isLength(value.length) && !isFunction(value); 136 | } 137 | ``` 138 | 139 | 14、isEmpty:检查 value 是否为空 140 | ------------------------ 141 | 142 | 如果是null,直接返回true;如果是类数组,判断数据长度;如果是Object对象,判断是否具有属性;如果是其他数据,直接返回false(也可改为返回true) 143 | 144 | ``` 145 | function isEmpty(value) { 146 | if (value == null) { 147 | return true; 148 | } 149 | if (isArrayLike(value)) { 150 | return !value.length; 151 | }else if(isPlainObject(value)){ 152 | for (let key in value) { 153 | if (hasOwnProperty.call(value, key)) { 154 | return false; 155 | } 156 | } 157 | return true; 158 | } 159 | return false; 160 | } 161 | ``` 162 | 163 | 15、cached:记忆函数:缓存函数的运算结果 164 | ------ 165 | 166 | ``` 167 | function cached(fn) { 168 | let cache = Object.create(null); 169 | return function cachedFn(str) { 170 | let hit = cache[str]; 171 | return hit || (cache[str] = fn(str)) 172 | } 173 | } 174 | ``` 175 | 176 | 16、camelize:横线转驼峰命名 177 | ------- 178 | 179 | ``` 180 | let camelizeRE = /-(\w)/g; 181 | function camelize(str) { 182 | return str.replace(camelizeRE, function(_, c) { 183 | return c ? c.toUpperCase() : ''; 184 | }) 185 | } 186 | //ab-cd-ef ==> abCdEf 187 | //使用记忆函数 188 | let _camelize = cached(camelize) 189 | ``` 190 | 191 | 17、hyphenate:驼峰命名转横线命名:拆分字符串,使用 - 相连,并且转换为小写 192 | ------ 193 | 194 | ``` 195 | let hyphenateRE = /\B([A-Z])/g; 196 | function hyphenate(str){ 197 | return str.replace(hyphenateRE, '-$1').toLowerCase() 198 | } 199 | //abCd ==> ab-cd 200 | //使用记忆函数 201 | let _hyphenate = cached(hyphenate); 202 | ``` 203 | 204 | 18、capitalize:字符串首位大写 205 | ------ 206 | 207 | ``` 208 | function capitalize(str){ 209 | return str.charAt(0).toUpperCase() + str.slice(1) 210 | } 211 | // abc ==> Abc 212 | //使用记忆函数 213 | let _capitalize = cached(capitalize) 214 | ``` 215 | 216 | 19、extend:将属性混合到目标对象中 217 | --------------------- 218 | 219 | ``` 220 | function extend(to, _from) { 221 | for(let key in _from) { 222 | to[key] = _from[key]; 223 | } 224 | return to 225 | } 226 | ``` 227 | 228 | 20、Object.assign:对象属性复制,浅拷贝 229 | ------- 230 | 231 | ``` 232 | Object.assign = Object.assign || function(){ 233 | if(arguments.length == 0) throw new TypeError('Cannot convert undefined or null to object'); 234 | 235 | let target = arguments[0], 236 | args = Array.prototype.slice.call(arguments, 1), 237 | key 238 | args.forEach(function(item){ 239 | for(key in item){ 240 | item.hasOwnProperty(key) && ( target[key] = item[key] ) 241 | } 242 | }) 243 | return target 244 | } 245 | ``` 246 | 247 | 使用Object.assign可以浅克隆一个对象: 248 | 249 | ``` 250 | let clone = Object.assign({}, target) 251 | ``` 252 | 253 | 简单的深克隆可以使用JSON.parse()和JSON.stringify(),这两个api是解析json数据的,所以只能解析除symbol外的原始类型及数组和对象 254 | 255 | ``` 256 | let clone = JSON.parse( JSON.stringify(target) ) 257 | ``` 258 | 259 | 21、clone:克隆数据,可深度克隆 260 | ------------------- 261 | 262 | 这里列出了原始类型,时间、正则、错误、数组、对象的克隆规则,其他的可自行补充 263 | 264 | ``` 265 | function clone(value, deep) { 266 | if (isPrimitive(value)) { 267 | return value 268 | } 269 | 270 | if (isArrayLike(value)) { //是类数组 271 | value = Array.prototype.slice.call(value) 272 | return deep ? value.map(item => clone(item, deep)) : value 273 | } else if (isPlainObject(value)) { //是对象 274 | let target = {}, key; 275 | for (key in value) { 276 | value.hasOwnProperty(key) && (target[key] = deep ? clone(value[key], deep) : value[key]) 277 | } 278 | return target 279 | } 280 | 281 | let type = getRawType(value) 282 | 283 | switch (type) { 284 | case 'Date': 285 | case 'RegExp': 286 | case 'Error': value = new window[type](value); break; 287 | } 288 | return value 289 | }~~~~ 290 | ``` 291 | 292 | 22、识别各种浏览器及平台 293 | ------- 294 | 295 | ``` 296 | //运行环境是浏览器 297 | let inBrowser = typeof window !== 'undefined'; 298 | //运行环境是微信 299 | let inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; 300 | let weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); 301 | //浏览器 UA 判断 302 | let UA = inBrowser && window.navigator.userAgent.toLowerCase(); 303 | let isIE = UA && /msie|trident/.test(UA); 304 | let isIE9 = UA && UA.indexOf('msie 9.0') > 0; 305 | let isEdge = UA && UA.indexOf('edge/') > 0; 306 | let isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); 307 | let isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); 308 | let isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; 309 | ``` 310 | 311 | 23、getExplorerInfo:获取浏览器信息 312 | ------- 313 | 314 | ``` 315 | function getExplorerInfo() { 316 | let t = navigator.userAgent.toLowerCase(); 317 | return 0 <= t.indexOf("msie") ? { //ie < 11 318 | type: "IE", 319 | version: Number(t.match(/msie ([\d]+)/)[1]) 320 | } : !!t.match(/trident\/.+?rv:(([\d.]+))/) ? { // ie 11 321 | type: "IE", 322 | version: 11 323 | } : 0 <= t.indexOf("edge") ? { 324 | type: "Edge", 325 | version: Number(t.match(/edge\/([\d]+)/)[1]) 326 | } : 0 <= t.indexOf("firefox") ? { 327 | type: "Firefox", 328 | version: Number(t.match(/firefox\/([\d]+)/)[1]) 329 | } : 0 <= t.indexOf("chrome") ? { 330 | type: "Chrome", 331 | version: Number(t.match(/chrome\/([\d]+)/)[1]) 332 | } : 0 <= t.indexOf("opera") ? { 333 | type: "Opera", 334 | version: Number(t.match(/opera.([\d]+)/)[1]) 335 | } : 0 <= t.indexOf("Safari") ? { 336 | type: "Safari", 337 | version: Number(t.match(/version\/([\d]+)/)[1]) 338 | } : { 339 | type: t, 340 | version: -1 341 | } 342 | } 343 | ``` 344 | 345 | 24、isPCBroswer:检测是否为PC端浏览器模式 346 | ---------------------------- 347 | 348 | ``` 349 | function isPCBroswer() { 350 | let e = navigator.userAgent.toLowerCase() 351 | , t = "ipad" == e.match(/ipad/i) 352 | , i = "iphone" == e.match(/iphone/i) 353 | , r = "midp" == e.match(/midp/i) 354 | , n = "rv:1.2.3.4" == e.match(/rv:1.2.3.4/i) 355 | , a = "ucweb" == e.match(/ucweb/i) 356 | , o = "android" == e.match(/android/i) 357 | , s = "windows ce" == e.match(/windows ce/i) 358 | , l = "windows mobile" == e.match(/windows mobile/i); 359 | return !(t || i || r || n || a || o || s || l) 360 | } 361 | ``` 362 | 363 | 25、unique:数组去重,返回一个新数组 364 | ------- 365 | 366 | ``` 367 | function unique(arr){ 368 | if(!isArrayLink(arr)){ //不是类数组对象 369 | return arr 370 | } 371 | let result = [] 372 | let objarr = [] 373 | let obj = Object.create(null) 374 | 375 | arr.forEach(item => { 376 | if(isStatic(item)){//是除了symbol外的原始数据 377 | let key = item + '_' + getRawType(item); 378 | if(!obj[key]){ 379 | obj[key] = true 380 | result.push(item) 381 | } 382 | }else{//引用类型及symbol 383 | if(!objarr.includes(item)){ 384 | objarr.push(item) 385 | result.push(item) 386 | } 387 | } 388 | }) 389 | 390 | return resulte 391 | } 392 | ``` 393 | 394 | 26、Set简单实现 395 | ------- 396 | 397 | ``` 398 | window.Set = window.Set || (function () { 399 | function Set(arr) { 400 | this.items = arr ? unique(arr) : []; 401 | this.size = this.items.length; // Array的大小 402 | } 403 | Set.prototype = { 404 | add: function (value) { 405 | // 添加元素,若元素已存在,则跳过,返回 Set 结构本身。 406 | if (!this.has(value)) { 407 | this.items.push(value); 408 | this.size++; 409 | } 410 | return this; 411 | }, 412 | clear: function () { 413 | //清除所有成员,没有返回值。 414 | this.items = [] 415 | this.size = 0 416 | }, 417 | delete: function (value) { 418 | //删除某个值,返回一个布尔值,表示删除是否成功。 419 | return this.items.some((v, i) => { 420 | if(v === value){ 421 | this.items.splice(i,1) 422 | return true 423 | } 424 | return false 425 | }) 426 | }, 427 | has: function (value) { 428 | //返回一个布尔值,表示该值是否为Set的成员。 429 | return this.items.some(v => v === value) 430 | }, 431 | values: function () { 432 | return this.items 433 | }, 434 | } 435 | 436 | return Set; 437 | }()); 438 | ``` 439 | 440 | 27、repeat:生成一个重复的字符串,有n个str组成,可修改为填充为数组等 441 | ------- 442 | 443 | ``` 444 | function repeat(str, n) { 445 | let res = ''; 446 | while(n) { 447 | if(n % 2 === 1) { 448 | res += str; 449 | } 450 | if(n > 1) { 451 | str += str; 452 | } 453 | n >>= 1; 454 | } 455 | return res 456 | }; 457 | //repeat('123',3) ==> 123123123 458 | ``` 459 | 460 | 28、dateFormater:格式化时间 461 | ------- 462 | 463 | ``` 464 | function dateFormater(formater, t){ 465 | let date = t ? new Date(t) : new Date(), 466 | Y = date.getFullYear() + '', 467 | M = date.getMonth() + 1, 468 | D = date.getDate(), 469 | H = date.getHours(), 470 | m = date.getMinutes(), 471 | s = date.getSeconds(); 472 | return formater.replace(/YYYY|yyyy/g,Y) 473 | .replace(/YY|yy/g,Y.substr(2,2)) 474 | .replace(/MM/g,(M<10?'0':'') + M) 475 | .replace(/DD/g,(D<10?'0':'') + D) 476 | .replace(/HH|hh/g,(H<10?'0':'') + H) 477 | .replace(/mm/g,(m<10?'0':'') + m) 478 | .replace(/ss/g,(s<10?'0':'') + s) 479 | } 480 | // dateFormater('YYYY-MM-DD HH:mm', t) ==> 2019-06-26 18:30 481 | // dateFormater('YYYYMMDDHHmm', t) ==> 201906261830 482 | ``` 483 | 484 | 29、dateStrForma:将指定字符串由一种时间格式转化为另一种 485 | ------- 486 | from的格式应对应str的位置 487 | 488 | ``` 489 | function dateStrForma(str, from, to){ 490 | //'20190626' 'YYYYMMDD' 'YYYY年MM月DD日' 491 | str += '' 492 | let Y = '' 493 | if(~(Y = from.indexOf('YYYY'))){ 494 | Y = str.substr(Y, 4) 495 | to = to.replace(/YYYY|yyyy/g,Y) 496 | }else if(~(Y = from.indexOf('YY'))){ 497 | Y = str.substr(Y, 2) 498 | to = to.replace(/YY|yy/g,Y) 499 | } 500 | 501 | let k,i 502 | ['M','D','H','h','m','s'].forEach(s =>{ 503 | i = from.indexOf(s+s) 504 | k = ~i ? str.substr(i, 2) : '' 505 | to = to.replace(s+s, k) 506 | }) 507 | return to 508 | } 509 | // dateStrForma('20190626', 'YYYYMMDD', 'YYYY年MM月DD日') ==> 2019年06月26日 510 | // dateStrForma('121220190626', '----YYYYMMDD', 'YYYY年MM月DD日') ==> 2019年06月26日 511 | // dateStrForma('2019年06月26日', 'YYYY年MM月DD日', 'YYYYMMDD') ==> 20190626 512 | 513 | // 一般的也可以使用正则来实现 514 | //'2019年06月26日'.replace(/(\d{4})年(\d{2})月(\d{2})日/, '$1-$2-$3') ==> 2019-06-26 515 | ``` 516 | 517 | 30、getPropByPath:根据字符串路径获取对象属性 : 'obj[0].count' 518 | ------- 519 | 520 | ``` 521 | function getPropByPath(obj, path, strict) { 522 | let tempObj = obj; 523 | path = path.replace(/\[(\w+)\]/g, '.$1'); //将[0]转化为.0 524 | path = path.replace(/^\./, ''); //去除开头的. 525 | 526 | let keyArr = path.split('.'); //根据.切割 527 | let i = 0; 528 | for (let len = keyArr.length; i < len - 1; ++i) { 529 | if (!tempObj && !strict) break; 530 | let key = keyArr[i]; 531 | if (key in tempObj) { 532 | tempObj = tempObj[key]; 533 | } else { 534 | if (strict) {//开启严格模式,没找到对应key值,抛出错误 535 | throw new Error('please transfer a valid prop path to form item!'); 536 | } 537 | break; 538 | } 539 | } 540 | return { 541 | o: tempObj, //原始数据 542 | k: keyArr[i], //key值 543 | v: tempObj ? tempObj[keyArr[i]] : null // key值对应的值 544 | }; 545 | }; 546 | ``` 547 | 548 | 31、GetUrlParam:获取Url参数,返回一个对象 549 | ------- 550 | 551 | ``` 552 | function GetUrlParam(){ 553 | let url = document.location.toString(); 554 | let arrObj = url.split("?"); 555 | let params = Object.create(null) 556 | if (arrObj.length > 1){ 557 | arrObj = arrObj[1].split("&"); 558 | arrObj.forEach(item=>{ 559 | item = item.split("="); 560 | params[item[0]] = item[1] 561 | }) 562 | } 563 | return params; 564 | } 565 | // ?a=1&b=2&c=3 ==> {a: "1", b: "2", c: "3"} 566 | ``` 567 | 568 | 32、downloadFile:base64数据导出文件,文件下载 569 | ------- 570 | 571 | ``` 572 | function downloadFile(filename, data){ 573 | let DownloadLink = document.createElement('a'); 574 | 575 | if ( DownloadLink ){ 576 | document.body.appendChild(DownloadLink); 577 | DownloadLink.style = 'display: none'; 578 | DownloadLink.download = filename; 579 | DownloadLink.href = data; 580 | 581 | if ( document.createEvent ){ 582 | let DownloadEvt = document.createEvent('MouseEvents'); 583 | 584 | DownloadEvt.initEvent('click', true, false); 585 | DownloadLink.dispatchEvent(DownloadEvt); 586 | } 587 | else if ( document.createEventObject ) 588 | DownloadLink.fireEvent('onclick'); 589 | else if (typeof DownloadLink.onclick == 'function' ) 590 | DownloadLink.onclick(); 591 | 592 | document.body.removeChild(DownloadLink); 593 | } 594 | } 595 | ``` 596 | 597 | 33、toFullScreen:全屏 598 | ------------------ 599 | 600 | ``` 601 | function toFullScreen(){ 602 | let el = document.documentElement; 603 | let rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullScreen; 604 | 605 | //typeof rfs != "undefined" && rfs 606 | if (rfs) { 607 | rfs.call(el); 608 | }else if (typeof window.ActiveXObject !== "undefined") { 609 | //for IE,这里其实就是模拟了按下键盘的F11,使浏览器全屏 610 | let wscript = new ActiveXObject("WScript.Shell"); 611 | if (wscript != null) { 612 | wscript.SendKeys("{F11}"); 613 | } 614 | }else{ 615 | alert("浏览器不支持全屏"); 616 | } 617 | } 618 | ``` 619 | 620 | 34、exitFullscreen:退出全屏 621 | ------------------------ 622 | 623 | ``` 624 | function exitFullscreen(){ 625 | let el = parent.document; 626 | let cfs = el.cancelFullScreen || el.webkitCancelFullScreen || el.mozCancelFullScreen || el.exitFullScreen; 627 | 628 | //typeof cfs != "undefined" && cfs 629 | if (cfs) { 630 | cfs.call(el); 631 | }else if (typeof window.ActiveXObject !== "undefined") { 632 | //for IE,这里和fullScreen相同,模拟按下F11键退出全屏 633 | let wscript = new ActiveXObject("WScript.Shell"); 634 | if (wscript != null) { 635 | wscript.SendKeys("{F11}"); 636 | } 637 | }else{ 638 | alert("切换失败,可尝试Esc退出") 639 | } 640 | } 641 | ``` 642 | 643 | 35、requestAnimationFrame:window动画 644 | ----------------------------------- 645 | 646 | ``` 647 | window.requestAnimationFrame = window.requestAnimationFrame || 648 | window.webkitRequestAnimationFrame || 649 | window.mozRequestAnimationFrame || 650 | window.msRequestAnimationFrame || 651 | window.oRequestAnimationFrame || 652 | function (callback) { 653 | //为了使setTimteout的尽可能的接近每秒60帧的效果 654 | window.setTimeout(callback, 1000 / 60); 655 | }; 656 | 657 | window.cancelAnimationFrame = window.cancelAnimationFrame || 658 | Window.webkitCancelAnimationFrame || 659 | window.mozCancelAnimationFrame || 660 | window.msCancelAnimationFrame || 661 | window.oCancelAnimationFrame || 662 | function (id) { 663 | //为了使setTimteout的尽可能的接近每秒60帧的效果 664 | window.clearTimeout(id); 665 | } 666 | ``` 667 | 668 | 669 | 36、_isNaN:检查数据是否是非数字值 670 | --------------------- 671 | 672 | 原生的isNaN会把参数转换成数字(valueof),而null、true、false以及长度小于等于1的数组(元素为非NaN数据)会被转换成数字,这不是我想要的。Symbol类型的数据不具有valueof接口,所以isNaN会抛出错误,这里放在后面,可避免错误 673 | 674 | ``` 675 | function _isNaN(v){ 676 | return !(typeof v === 'string' || typeof v === 'number') || isNaN(v) 677 | } 678 | ``` 679 | 680 | 37、max:求取数组中非NaN数据中的最大值 681 | ------- 682 | 683 | ``` 684 | function max(arr){ 685 | arr = arr.filter(item => !_isNaN(item)) 686 | return arr.length ? Math.max.apply(null, arr) : undefined 687 | } 688 | //max([1, 2, '11', null, 'fdf', []]) ==> 11 689 | ``` 690 | 691 | 38、min:求取数组中非NaN数据中的最小值 692 | ------- 693 | 694 | ``` 695 | function min(arr){ 696 | arr = arr.filter(item => !_isNaN(item)) 697 | return arr.length ? Math.min.apply(null, arr) : undefined 698 | } 699 | //min([1, 2, '11', null, 'fdf', []]) ==> 1 700 | ``` 701 | 702 | 39、random:返回一个lower - upper之间的随机数 703 | --------------------------------- 704 | 705 | lower、upper无论正负与大小,但必须是非NaN的数据 706 | 707 | ``` 708 | function random(lower, upper){ 709 | lower = +lower || 0 710 | upper = +upper || 0 711 | return Math.random() * (upper - lower) + lower; 712 | } 713 | //random(0, 0.5) ==> 0.3567039135734613 714 | //random(2, 1) ===> 1.6718418553475423 715 | //random(-2, -1) ==> -1.4474325452361945 716 | ``` 717 | 718 | 40、Object.keys:返回一个由一个给定对象的自身可枚举属性组成的数组 719 | ------- 720 | 721 | ``` 722 | Object.keys = Object.keys || function keys(object) { 723 | if(object === null || object === undefined){ 724 | throw new TypeError('Cannot convert undefined or null to object'); 725 | } 726 | let result = [] 727 | if(isArrayLike(object) || isPlainObject(object)){ 728 | for (let key in object) { 729 | object.hasOwnProperty(key) && ( result.push(key) ) 730 | } 731 | } 732 | return result 733 | } 734 | ``` 735 | 736 | 41、Object.values:返回一个给定对象自身的所有可枚举属性值的数组 737 | ------- 738 | 739 | ``` 740 | Object.values = Object.values || function values(object) { 741 | if(object === null || object === undefined){ 742 | throw new TypeError('Cannot convert undefined or null to object'); 743 | } 744 | let result = [] 745 | if(isArrayLike(object) || isPlainObject(object)){ 746 | for (let key in object) { 747 | object.hasOwnProperty(key) && ( result.push(object[key]) ) 748 | } 749 | } 750 | return result 751 | } 752 | ``` 753 | 754 | 42、arr.fill:使用 value 值来填充 array,从start位置开始, 到end位置结束(但不包含end位置),返回原数组 755 | ------- 756 | 757 | ``` 758 | Array.prototype.fill = Array.prototype.fill || function fill(value, start, end) { 759 | let ctx = this 760 | let length = ctx.length; 761 | 762 | start = parseInt(start) 763 | if(isNaN(start)){ 764 | start = 0 765 | }else if (start < 0) { 766 | start = -start > length ? 0 : (length + start); 767 | } 768 | 769 | end = parseInt(end) 770 | if(isNaN(end) || end > length){ 771 | end = length 772 | }else if (end < 0) { 773 | end += length; 774 | } 775 | 776 | while (start < end) { 777 | ctx[start++] = value; 778 | } 779 | return ctx; 780 | } 781 | //Array(3).fill(2) ===> [2, 2, 2] 782 | ``` 783 | 784 | 43、arr.includes:用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false,可指定开始查询的位置 785 | ------- 786 | 787 | ``` 788 | Array.prototype.includes = Array.prototype.includes || function includes(value, start){ 789 | let ctx = this 790 | let length = ctx.length; 791 | 792 | start = parseInt(start) 793 | if(isNaN(start)){ 794 | start = 0 795 | }else if (start < 0) { 796 | start = -start > length ? 0 : (length + start); 797 | } 798 | 799 | let index = ctx.indexOf(value) 800 | 801 | return index >= start; 802 | } 803 | ``` 804 | 805 | 44、arr.find:返回数组中通过测试(函数fn内判断)的第一个元素的值 806 | ------- 807 | 808 | ``` 809 | Array.prototype.find = Array.prototype.find || function find(fn, ctx){ 810 | fn = fn.bind(ctx) 811 | 812 | let result; 813 | this.some((value, index, arr), thisValue) => { 814 | return fn(value, index, arr) ? (result = value, true) : false 815 | }) 816 | 817 | return result 818 | } 819 | ``` 820 | 821 | 45、arr.findIndex :返回数组中通过测试(函数fn内判断)的第一个元素的下标 822 | ------- 823 | 824 | ``` 825 | Array.prototype.findIndex = Array.prototype.findIndex || function findIndex(fn, ctx){ 826 | fn = fn.bind(ctx) 827 | 828 | let result; 829 | this.some((value, index, arr), thisValue) => { 830 | return fn(value, index, arr) ? (result = index, true) : false 831 | }) 832 | 833 | return result 834 | } 835 | ``` 836 | 837 | 46、performance.timing:利用performance.timing进行性能分析 838 | ------- 839 | 840 | ``` 841 | window.onload = function(){ 842 | setTimeout(function(){ 843 | let t = performance.timing 844 | console.log('DNS查询耗时 :' + (t.domainLookupEnd - t.domainLookupStart).toFixed(0)) 845 | console.log('TCP链接耗时 :' + (t.connectEnd - t.connectStart).toFixed(0)) 846 | console.log('request请求耗时 :' + (t.responseEnd - t.responseStart).toFixed(0)) 847 | console.log('解析dom树耗时 :' + (t.domComplete - t.domInteractive).toFixed(0)) 848 | console.log('白屏时间 :' + (t.responseStart - t.navigationStart).toFixed(0)) 849 | console.log('domready时间 :' + (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0)) 850 | console.log('onload时间 :' + (t.loadEventEnd - t.navigationStart).toFixed(0)) 851 | 852 | if(t = performance.memory){ 853 | console.log('js内存使用占比 :' + (t.usedJSHeapSize / t.totalJSHeapSize * 100).toFixed(2) + '%') 854 | } 855 | }) 856 | } 857 | ``` 858 | 859 | 47、禁止某些键盘事件 860 | ------------- 861 | 862 | ``` 863 | document.addEventListener('keydown', function(event){ 864 | return !( 865 | 112 == event.keyCode || //F1 866 | 123 == event.keyCode || //F12 867 | event.ctrlKey && 82 == event.keyCode || //ctrl + R 868 | event.ctrlKey && 78 == event.keyCode || //ctrl + N 869 | event.shiftKey && 121 == event.keyCode || //shift + F10 870 | event.altKey && 115 == event.keyCode || //alt + F4 871 | "A" == event.srcElement.tagName && event.shiftKey //shift + 点击a标签 872 | ) || (event.returnValue = false) 873 | }); 874 | ``` 875 | 876 | 48、禁止右键、选择、复制 877 | ------------ 878 | 879 | ``` 880 | ['contextmenu', 'selectstart', 'copy'].forEach(function(ev){ 881 | document.addEventListener(ev, function(event){ 882 | return event.returnValue = false 883 | }) 884 | }); 885 | ``` 886 | 887 | github地址:[https://github.com/hfhan/tools][1] 888 | 889 | 890 | [1]: https://github.com/hfhan/tools 891 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | /*JS开发常用工具函数*/ 2 | 3 | /** 4 | 1、isStatic:检测数据是不是除了symbol外的原始数据 5 | */ 6 | function isStatic(value) { 7 | return( 8 | typeof value === 'string' || 9 | typeof value === 'number' || 10 | typeof value === 'boolean' || 11 | typeof value === 'undefined' || 12 | value === null 13 | ) 14 | } 15 | 16 | /** 17 | 2、isPrimitive:检测数据是不是原始数据 18 | */ 19 | function isPrimitive(value) { 20 | return isStatic(value) || typeof value === 'symbol' 21 | } 22 | 23 | /** 24 | 3、isObject:判断数据是不是引用类型的数据 (例如: arrays, functions, objects, regexes, new Number(0),以及 new String('')) 25 | */ 26 | function isObject(value) { 27 | let type = typeof value; 28 | return value != null && (type == 'object' || type == 'function'); 29 | } 30 | 31 | /** 32 | 4、isObjectLike:检查 value 是否是 类对象。 如果一个值是类对象,那么它不应该是 null,而且 typeof 后的结果是 "object" 33 | */ 34 | function isObjectLike(value) { 35 | return value != null && typeof value == 'object'; 36 | } 37 | 38 | /* 39 | 5、getRawType:获取数据类型,返回结果为 Number、String、Object、Array等 40 | */ 41 | function getRawType(value) { 42 | return Object.prototype.toString.call(value).slice(8, -1) 43 | } 44 | //getoRawType([]) ==> Array 45 | 46 | /** 47 | 6、isPlainObject:判断数据是不是Object类型的数据 48 | */ 49 | function isPlainObject(obj) { 50 | return Object.prototype.toString.call(obj) === '[object Object]' 51 | } 52 | 53 | /** 54 | 7、isArray:判断数据是不是数组类型的数据 55 | */ 56 | function isArray(arr) { 57 | return Object.prototype.toString.call(arr) === '[object Array]' 58 | } 59 | /* 60 | 将isArray挂载到Array上 61 | * */ 62 | Array.isArray = Array.isArray || isArray; 63 | 64 | /** 65 | 8、isRegExp:判断数据是不是正则对象 66 | */ 67 | function isRegExp(value) { 68 | return Object.prototype.toString.call(value) === '[object RegExp]' 69 | } 70 | 71 | /** 72 | 9、isDate:判断数据是不是时间对象 73 | */ 74 | function isDate(value) { 75 | return Object.prototype.toString.call(value) === '[object Date]' 76 | } 77 | 78 | /* 79 | 10、isNative:判断 value 是不是浏览器内置函数 80 | 内置函数toString后的主体代码块为 [native code] ,而非内置函数则为相关代码,所以非内置函数可以进行拷贝(toString后掐头去尾再由Function转) 81 | * */ 82 | function isNative(value) { 83 | return typeof value === 'function' && /native code/.test(value.toString()) 84 | } 85 | 86 | /* 87 | 11、isFunction:检查 value 是不是函数 88 | * */ 89 | function isFunction(value) { 90 | return Object.prototype.toString.call(value) === '[object Function]' 91 | } 92 | 93 | /* 94 | 12、isLength:检查 value 是否为有效的类数组长度。 95 | * */ 96 | function isLength(value) { 97 | return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= Number.MAX_SAFE_INTEGER; 98 | } 99 | 100 | /* 101 | 13、isArrayLike:检查 value 是否是类数组。 102 | 如果一个值被认为是类数组,那么它不是一个函数,并且value.length是个整数,大于等于 0,小于或等于 Number.MAX_SAFE_INTEGER。 103 | 这里字符串也将被当作类数组 104 | * */ 105 | function isArrayLike(value) { 106 | return value != null && isLength(value.length) && !isFunction(value); 107 | } 108 | 109 | /* 110 | 14、isEmpty:检查 value 是否为空 111 | 如果是null,直接返回true;如果是类数组,判断数据长度;如果是Object对象,判断是否具有属性;如果是其他数据,直接返回false(也可改为返回true) 112 | * */ 113 | function isEmpty(value) { 114 | if (value == null) { 115 | return true; 116 | } 117 | if (isArrayLike(value)) { 118 | return !value.length; 119 | }else if(isPlainObject(value)){ 120 | for (let key in value) { 121 | if (hasOwnProperty.call(value, key)) { 122 | return false; 123 | } 124 | } 125 | return true; 126 | } 127 | return false; 128 | } 129 | 130 | /** 131 | 15、cached:记忆函数:缓存函数的运算结果。 132 | */ 133 | function cached(fn) { 134 | let cache = Object.create(null); 135 | return function cachedFn(str) { 136 | let hit = cache[str]; 137 | return hit || (cache[str] = fn(str)) 138 | } 139 | } 140 | 141 | /* 142 | 16、camelize:横线转驼峰命名 143 | * */ 144 | let camelizeRE = /-(\w)/g; 145 | function camelize(str) { 146 | return str.replace(camelizeRE, function(_, c) { 147 | return c ? c.toUpperCase() : ''; 148 | }) 149 | } 150 | //ab-cd-ef ==> abCdEf 151 | //使用记忆函数 152 | let _camelize = cached(camelize) 153 | 154 | /** 155 | 17、hyphenate:驼峰命名转横线命名:拆分字符串,使用 - 相连,并且转换为小写 156 | */ 157 | let hyphenateRE = /\B([A-Z])/g; 158 | function hyphenate(str){ 159 | return str.replace(hyphenateRE, '-$1').toLowerCase() 160 | } 161 | //abCd ==> ab-cd 162 | //使用记忆函数 163 | let _hyphenate = cached(hyphenate); 164 | 165 | /** 166 | 18、capitalize:字符串首位大写 167 | */ 168 | function capitalize(str){ 169 | return str.charAt(0).toUpperCase() + str.slice(1) 170 | } 171 | // abc ==> Abc 172 | //使用记忆函数 173 | let _capitalize = cached(capitalize) 174 | 175 | /** 176 | 19、extend:将属性混合到目标对象中 177 | */ 178 | function extend(to, _from) { 179 | for(let key in _from) { 180 | to[key] = _from[key]; 181 | } 182 | return to 183 | } 184 | 185 | /* 186 | 20、Object.assign:对象属性复制,浅拷贝 187 | * */ 188 | Object.assign = Object.assign || function(){ 189 | if(arguments.length == 0) throw new TypeError('Cannot convert undefined or null to object'); 190 | 191 | let target = arguments[0], 192 | args = Array.prototype.slice.call(arguments, 1), 193 | key 194 | args.forEach(function(item){ 195 | for(key in item){ 196 | item.hasOwnProperty(key) && ( target[key] = item[key] ) 197 | } 198 | }) 199 | return target 200 | } 201 | /* 202 | 使用Object.assign可以浅克隆一个对象:let clone = Object.assign({}, target) 203 | 简单的深克隆可以使用JSON.parse()和JSON.stringify(),这两个api是解析json数据的,所以只能解析除symbol外的原始类型及数组和对象 204 | let clone = JSON.parse( JSON.stringify(target) ) 205 | * */ 206 | 207 | /* 208 | 21、clone:克隆数据,可深度克隆 209 | 这里列出了原始类型,时间、正则、错误、数组、对象的克隆规则,其他的可自行补充 210 | * */ 211 | function clone(value, deep){ 212 | if(isPrimitive(value)){ 213 | return value 214 | } 215 | 216 | if (isArrayLike(value)) { //是类数组 217 | value = Array.prototype.slice.call(value) 218 | return value.map(item => deep ? clone(item, deep) : item) 219 | }else if(isPlainObject(value)){ //是对象 220 | let target = {}, key; 221 | for (key in value) { 222 | value.hasOwnProperty(key) && ( target[key] = deep ? clone(value[key], deep) : value[key] ) 223 | } 224 | } 225 | 226 | let type = getRawType(value) 227 | 228 | switch(type){ 229 | case 'Date': 230 | case 'RegExp': 231 | case 'Error': value = new window[type](value); break; 232 | } 233 | return value 234 | } 235 | 236 | /* 237 | 22、识别各种浏览器及平台 238 | * */ 239 | //运行环境是浏览器 240 | let inBrowser = typeof window !== 'undefined'; 241 | //运行环境是微信 242 | let inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; 243 | let weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); 244 | //浏览器 UA 判断 245 | let UA = inBrowser && window.navigator.userAgent.toLowerCase(); 246 | let isIE = UA && /msie|trident/.test(UA); 247 | let isIE9 = UA && UA.indexOf('msie 9.0') > 0; 248 | let isEdge = UA && UA.indexOf('edge/') > 0; 249 | let isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); 250 | let isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); 251 | let isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; 252 | 253 | /* 254 | 23、getExplorerInfo:获取浏览器信息 255 | * */ 256 | function getExplorerInfo() { 257 | let t = navigator.userAgent.toLowerCase(); 258 | return 0 <= t.indexOf("msie") ? { //ie < 11 259 | type: "IE", 260 | version: Number(t.match(/msie ([\d]+)/)[1]) 261 | } : !!t.match(/trident\/.+?rv:(([\d.]+))/) ? { // ie 11 262 | type: "IE", 263 | version: 11 264 | } : 0 <= t.indexOf("edge") ? { 265 | type: "Edge", 266 | version: Number(t.match(/edge\/([\d]+)/)[1]) 267 | } : 0 <= t.indexOf("firefox") ? { 268 | type: "Firefox", 269 | version: Number(t.match(/firefox\/([\d]+)/)[1]) 270 | } : 0 <= t.indexOf("chrome") ? { 271 | type: "Chrome", 272 | version: Number(t.match(/chrome\/([\d]+)/)[1]) 273 | } : 0 <= t.indexOf("opera") ? { 274 | type: "Opera", 275 | version: Number(t.match(/opera.([\d]+)/)[1]) 276 | } : 0 <= t.indexOf("Safari") ? { 277 | type: "Safari", 278 | version: Number(t.match(/version\/([\d]+)/)[1]) 279 | } : { 280 | type: t, 281 | version: -1 282 | } 283 | } 284 | 285 | /* 286 | 24、isPCBroswer:检测是否为PC端浏览器模式 287 | * */ 288 | function isPCBroswer() { 289 | let e = navigator.userAgent.toLowerCase() 290 | , t = "ipad" == e.match(/ipad/i) 291 | , i = "iphone" == e.match(/iphone/i) 292 | , r = "midp" == e.match(/midp/i) 293 | , n = "rv:1.2.3.4" == e.match(/rv:1.2.3.4/i) 294 | , a = "ucweb" == e.match(/ucweb/i) 295 | , o = "android" == e.match(/android/i) 296 | , s = "windows ce" == e.match(/windows ce/i) 297 | , l = "windows mobile" == e.match(/windows mobile/i); 298 | return !(t || i || r || n || a || o || s || l) 299 | } 300 | 301 | /* 302 | 25、unique:数组去重,返回一个新数组 303 | * */ 304 | function unique(arr){ 305 | if(!isArrayLink(arr)){ //不是类数组对象 306 | return arr 307 | } 308 | let result = [] 309 | let objarr = [] 310 | let obj = Object.create(null) 311 | 312 | arr.forEach(item => { 313 | if(isStatic(item)){//是除了symbol外的原始数据 314 | let key = item + '_' + getRawType(item); 315 | if(!obj[key]){ 316 | obj[key] = true 317 | result.push(item) 318 | } 319 | }else{//引用类型及symbol 320 | if(!objarr.includes(item)){ 321 | objarr.push(item) 322 | result.push(item) 323 | } 324 | } 325 | }) 326 | 327 | return resulte 328 | } 329 | 330 | /* 331 | 26、Set简单实现 332 | * */ 333 | window.Set = window.Set || (function () { 334 | function Set(arr) { 335 | this.items = arr ? unique(arr) : []; 336 | this.size = this.items.length; // Array的大小 337 | } 338 | Set.prototype = { 339 | add: function (value) { 340 | // 添加元素,若元素已存在,则跳过,返回 Set 结构本身。 341 | if (!this.has(value)) { 342 | this.items.push(value); 343 | this.size++; 344 | } 345 | return this; 346 | }, 347 | clear: function () { 348 | //清除所有成员,没有返回值。 349 | this.items = [] 350 | this.size = 0 351 | }, 352 | delete: function (value) { 353 | //删除某个值,返回一个布尔值,表示删除是否成功。 354 | return this.items.some((v, i) => { 355 | if(v === value){ 356 | this.items.splice(i,1) 357 | return true 358 | } 359 | return false 360 | }) 361 | }, 362 | has: function (value) { 363 | //返回一个布尔值,表示该值是否为Set的成员。 364 | return this.items.some(v => v === value) 365 | }, 366 | values: function () { 367 | return this.items 368 | }, 369 | } 370 | 371 | return Set; 372 | }()); 373 | 374 | /* 375 | 27、repeat:生成一个重复的字符串,有n个str组成,可修改为填充为数组等 376 | * */ 377 | function repeat(str, n) { 378 | let res = ''; 379 | while(n) { 380 | if(n % 2 === 1) { 381 | res += str; 382 | } 383 | if(n > 1) { 384 | str += str; 385 | } 386 | n >>= 1; 387 | } 388 | return res 389 | }; 390 | //repeat('123',3) ==> 123123123 391 | 392 | /* 393 | 28、dateFormater:格式化时间 394 | * */ 395 | function dateFormater(formater, t){ 396 | let date = t ? new Date(t) : new Date(), 397 | Y = date.getFullYear() + '', 398 | M = date.getMonth() + 1, 399 | D = date.getDate(), 400 | H = date.getHours(), 401 | m = date.getMinutes(), 402 | s = date.getSeconds(); 403 | return formater.replace(/YYYY|yyyy/g,Y) 404 | .replace(/YY|yy/g,Y.substr(2,2)) 405 | .replace(/MM/g,(M<10?'0':'') + M) 406 | .replace(/DD/g,(D<10?'0':'') + D) 407 | .replace(/HH|hh/g,(H<10?'0':'') + H) 408 | .replace(/mm/g,(m<10?'0':'') + m) 409 | .replace(/ss/g,(s<10?'0':'') + s) 410 | } 411 | // dateFormater('YYYY-MM-DD HH:mm', t) ==> 2019-06-26 18:30 412 | // dateFormater('YYYYMMDDHHmm', t) ==> 201906261830 413 | 414 | /* 415 | 29、dateStrForma:将指定字符串由一种时间格式转化为另一种 416 | from的格式应对应str的位置 417 | * */ 418 | function dateStrForma(str, from, to){ 419 | //'20190626' 'YYYYMMDD' 'YYYY年MM月DD日' 420 | str += '' 421 | let Y = '' 422 | if(~(Y = from.indexOf('YYYY'))){ 423 | Y = str.substr(Y, 4) 424 | to = to.replace(/YYYY|yyyy/g,Y) 425 | }else if(~(Y = from.indexOf('YY'))){ 426 | Y = str.substr(Y, 2) 427 | to = to.replace(/YY|yy/g,Y) 428 | } 429 | 430 | let k,i 431 | ['M','D','H','h','m','s'].forEach(s =>{ 432 | i = from.indexOf(s+s) 433 | k = ~i ? str.substr(i, 2) : '' 434 | to = to.replace(s+s, k) 435 | }) 436 | return to 437 | } 438 | // dateStrForma('20190626', 'YYYYMMDD', 'YYYY年MM月DD日') ==> 2019年06月26日 439 | // dateStrForma('121220190626', '----YYYYMMDD', 'YYYY年MM月DD日') ==> 2019年06月26日 440 | // dateStrForma('2019年06月26日', 'YYYY年MM月DD日', 'YYYYMMDD') ==> 20190626 441 | 442 | // 一般的也可以使用正则来实现 443 | //'2019年06月26日'.replace(/(\d{4})年(\d{2})月(\d{2})日/, '$1-$2-$3') ==> 2019-06-26 444 | 445 | /* 446 | 30、getPropByPath:根据字符串路径获取对象属性 : 'dcjgs[0].count' 447 | * */ 448 | function getPropByPath(obj, path, strict) { 449 | let tempObj = obj; 450 | path = path.replace(/\[(\w+)\]/g, '.$1'); //将[0]转化为.0 451 | path = path.replace(/^\./, ''); //去除开头的. 452 | 453 | let keyArr = path.split('.'); //根据.切割 454 | let i = 0; 455 | for (let len = keyArr.length; i < len - 1; ++i) { 456 | if (!tempObj && !strict) break; 457 | let key = keyArr[i]; 458 | if (key in tempObj) { 459 | tempObj = tempObj[key]; 460 | } else { 461 | if (strict) {//开启严格模式,没找到对应key值,抛出错误 462 | throw new Error('please transfer a valid prop path to form item!'); 463 | } 464 | break; 465 | } 466 | } 467 | return { 468 | o: tempObj, //原始数据 469 | k: keyArr[i], //key值 470 | v: tempObj ? tempObj[keyArr[i]] : null // key值对应的值 471 | }; 472 | }; 473 | 474 | /* 475 | 31、GetUrlParam:获取Url参数,返回一个对象 476 | * */ 477 | function GetUrlParam(){ 478 | let url = document.location.toString(); 479 | let arrObj = url.split("?"); 480 | let params = Object.create(null) 481 | if (arrObj.length > 1){ 482 | arrObj = arrObj[1].split("&"); 483 | arrObj.forEach(item=>{ 484 | item = item.split("="); 485 | params[item[0]] = item[1] 486 | }) 487 | } 488 | return params; 489 | } 490 | // ?a=1&b=2&c=3 ==> {a: "1", b: "2", c: "3"} 491 | 492 | 493 | /* 494 | 32、downloadFile:base64数据导出文件,文件下载 495 | * */ 496 | function downloadFile(filename, data){ 497 | let DownloadLink = document.createElement('a'); 498 | 499 | if ( DownloadLink ){ 500 | document.body.appendChild(DownloadLink); 501 | DownloadLink.style = 'display: none'; 502 | DownloadLink.download = filename; 503 | DownloadLink.href = data; 504 | 505 | if ( document.createEvent ){ 506 | let DownloadEvt = document.createEvent('MouseEvents'); 507 | 508 | DownloadEvt.initEvent('click', true, false); 509 | DownloadLink.dispatchEvent(DownloadEvt); 510 | } 511 | else if ( document.createEventObject ) 512 | DownloadLink.fireEvent('onclick'); 513 | else if (typeof DownloadLink.onclick == 'function' ) 514 | DownloadLink.onclick(); 515 | 516 | document.body.removeChild(DownloadLink); 517 | } 518 | } 519 | 520 | //33、toFullScreen:全屏 521 | function toFullScreen(){ 522 | let elem = document.body; 523 | elem.webkitRequestFullScreen 524 | ? elem.webkitRequestFullScreen() 525 | : elem.mozRequestFullScreen 526 | ? elem.mozRequestFullScreen() 527 | : elem.msRequestFullscreen 528 | ? elem.msRequestFullscreen() 529 | : elem.requestFullScreen 530 | ? elem.requestFullScreen() 531 | : alert("浏览器不支持全屏"); 532 | } 533 | 534 | //34、exitFullscreen:退出全屏 535 | function exitFullscreen(){ 536 | let elem = parent.document; 537 | elem.webkitCancelFullScreen 538 | ? elem.webkitCancelFullScreen() 539 | : elem.mozCancelFullScreen 540 | ? elem.mozCancelFullScreen() 541 | : elem.cancelFullScreen 542 | ? elem.cancelFullScreen() 543 | : elem.msExitFullscreen 544 | ? elem.msExitFullscreen() 545 | : elem.exitFullscreen 546 | ? elem.exitFullscreen() 547 | : alert("切换失败,可尝试Esc退出"); 548 | } 549 | 550 | //35、requestAnimationFrame:window动画 551 | window.requestAnimationFrame = window.requestAnimationFrame || 552 | window.webkitRequestAnimationFrame || 553 | window.mozRequestAnimationFrame || 554 | window.msRequestAnimationFrame || 555 | window.oRequestAnimationFrame || 556 | function (callback) { 557 | //为了使setTimteout的尽可能的接近每秒60帧的效果 558 | window.setTimeout(callback, 1000 / 60); 559 | }; 560 | 561 | window.cancelAnimationFrame = window.cancelAnimationFrame || 562 | Window.webkitCancelAnimationFrame || 563 | window.mozCancelAnimationFrame || 564 | window.msCancelAnimationFrame || 565 | window.oCancelAnimationFrame || 566 | function (id) { 567 | //为了使setTimteout的尽可能的接近每秒60帧的效果 568 | window.clearTimeout(id); 569 | } 570 | 571 | /* 572 | 36、_isNaN:检查数据是否是非数字值 573 | 原生的isNaN会把参数转换成数字(valueof),而null、true、false以及长度小于等于1的数组(元素为非NaN数据)会被转换成数字,这不是我想要的 574 | Symbol类型的数据不具有valueof接口,所以isNaN会抛出错误,这里放在后面,可避免错误 575 | * */ 576 | function _isNaN(v){ 577 | return !(typeof v === 'string' || typeof v === 'number') || isNaN(v) 578 | } 579 | 580 | /* 581 | 37、max:求取数组中非NaN数据中的最大值 582 | * */ 583 | function max(arr){ 584 | arr = arr.filter(item => !_isNaN(item)) 585 | return arr.length ? Math.max.apply(null, arr) : undefined 586 | } 587 | //max([1, 2, '11', null, 'fdf', []]) ==> 11 588 | 589 | /* 590 | 38、min:求取数组中非NaN数据中的最小值 591 | * */ 592 | function min(arr){ 593 | arr = arr.filter(item => !_isNaN(item)) 594 | return arr.length ? Math.min.apply(null, arr) : undefined 595 | } 596 | //min([1, 2, '11', null, 'fdf', []]) ==> 1 597 | 598 | /* 599 | 39、random:返回一个lower - upper之间的随机数 600 | lower、upper无论正负与大小,但必须是非NaN的数据 601 | * */ 602 | function random(lower, upper){ 603 | lower = +lower || 0 604 | upper = +upper || 0 605 | return Math.random() * (upper - lower) + lower; 606 | } 607 | //random(0, 0.5) ==> 0.3567039135734613 608 | //random(2, 1) ===> 1.6718418553475423 609 | //random(-2, -1) ==> -1.4474325452361945 610 | 611 | /* 612 | 40、Object.keys:返回一个由一个给定对象的自身可枚举属性组成的数组 613 | * */ 614 | Object.keys = Object.keys || function keys(object) { 615 | if(object === null || object === undefined){ 616 | throw new TypeError('Cannot convert undefined or null to object'); 617 | } 618 | let result = [] 619 | if(isArrayLike(object) || isPlainObject(object)){ 620 | for (let key in object) { 621 | object.hasOwnProperty(key) && ( result.push(key) ) 622 | } 623 | } 624 | return result 625 | } 626 | 627 | /* 628 | 41、Object.values:返回一个给定对象自身的所有可枚举属性值的数组 629 | * */ 630 | Object.values = Object.values || function values(object) { 631 | if(object === null || object === undefined){ 632 | throw new TypeError('Cannot convert undefined or null to object'); 633 | } 634 | let result = [] 635 | if(isArrayLike(object) || isPlainObject(object)){ 636 | for (let key in object) { 637 | object.hasOwnProperty(key) && ( result.push(object[key]) ) 638 | } 639 | } 640 | return result 641 | } 642 | 643 | /* 644 | 42、arr.fill:使用 value 值来填充 array,从start位置开始, 到end位置结束(但不包含end位置),返回原数组 645 | * */ 646 | Array.prototype.fill = Array.prototype.fill || function fill(value, start, end) { 647 | let ctx = this 648 | let length = ctx.length; 649 | 650 | start = parseInt(start) 651 | if(isNaN(start)){ 652 | start = 0 653 | }else if (start < 0) { 654 | start = -start > length ? 0 : (length + start); 655 | } 656 | 657 | end = parseInt(end) 658 | if(isNaN(end) || end > length){ 659 | end = length 660 | }else if (end < 0) { 661 | end += length; 662 | } 663 | 664 | while (start < end) { 665 | ctx[start++] = value; 666 | } 667 | return ctx; 668 | } 669 | //Array(3).fill(2) ===> [2, 2, 2] 670 | 671 | /* 672 | 43、arr.includes:用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false,可指定开始查询的位置 673 | * */ 674 | Array.prototype.includes = Array.prototype.includes || function includes(value, start){ 675 | let ctx = this 676 | let length = ctx.length; 677 | 678 | start = parseInt(start) 679 | if(isNaN(start)){ 680 | start = 0 681 | }else if (start < 0) { 682 | start = -start > length ? 0 : (length + start); 683 | } 684 | 685 | let index = ctx.indexOf(value) 686 | 687 | return index >= start; 688 | } 689 | 690 | /* 691 | 44、arr.find:返回数组中通过测试(函数fn内判断)的第一个元素的值 692 | * */ 693 | Array.prototype.find = Array.prototype.find || function find(fn, ctx){ 694 | fn = fn.bind(ctx) 695 | 696 | let result; 697 | this.some((value, index, arr) => { 698 | return fn(value, index, arr) ? (result = value, true) : false 699 | }) 700 | 701 | return result 702 | } 703 | 704 | /* 705 | 45、arr.findIndex:返回数组中通过测试(函数fn内判断)的第一个元素的下标 706 | * */ 707 | Array.prototype.findIndex = Array.prototype.findIndex || function findIndex(fn, ctx){ 708 | fn = fn.bind(ctx) 709 | 710 | let result; 711 | this.some((value, index, arr) => { 712 | return fn(value, index, arr) ? (result = index, true) : false 713 | }) 714 | 715 | return result 716 | } 717 | 718 | /* 719 | 46、performance.timing:利用performance.timing进行性能分析 720 | * */ 721 | window.onload = function(){ 722 | setTimeout(function(){ 723 | let t = performance.timing 724 | console.log('DNS查询耗时 :' + (t.domainLookupEnd - t.domainLookupStart).toFixed(0)) 725 | console.log('TCP链接耗时 :' + (t.connectEnd - t.connectStart).toFixed(0)) 726 | console.log('request请求耗时 :' + (t.responseEnd - t.responseStart).toFixed(0)) 727 | console.log('解析dom树耗时 :' + (t.domComplete - t.domInteractive).toFixed(0)) 728 | console.log('白屏时间 :' + (t.responseStart - t.navigationStart).toFixed(0)) 729 | console.log('domready时间 :' + (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0)) 730 | console.log('onload时间 :' + (t.loadEventEnd - t.navigationStart).toFixed(0)) 731 | 732 | if(t = performance.memory){ 733 | console.log('js内存使用占比 :' + (t.usedJSHeapSize / t.totalJSHeapSize * 100).toFixed(2) + '%') 734 | } 735 | }) 736 | } 737 | 738 | //47、禁止某些键盘事件 739 | document.addEventListener('keydown', function(event){ 740 | return !( 741 | 112 == event.keyCode || //F1 742 | 123 == event.keyCode || //F12 743 | event.ctrlKey && 82 == event.keyCode || //ctrl + R 744 | event.ctrlKey && 78 == event.keyCode || //ctrl + N 745 | event.shiftKey && 121 == event.keyCode || //shift + F10 746 | event.altKey && 115 == event.keyCode || //alt + F4 747 | "A" == event.srcElement.tagName && event.shiftKey //shift + 点击a标签 748 | ) || (event.returnValue = false) 749 | }); 750 | //48、禁止右键、选择、复制 751 | ['contextmenu', 'selectstart', 'copy'].forEach(function(ev){ 752 | document.addEventListener(ev, function(event){ 753 | return event.returnValue = false 754 | }) 755 | }); 756 | --------------------------------------------------------------------------------