JavaScript & Ajax & jQuery TODO: 逐步用原生方法替换jQuery,参考You-Dont-Need-jQuery
判断是否是非数字: isNaN(a)
生产环境直接全局屏蔽掉console.log
的输出,只需要复写即可:console.log=()=>{}
包含关系(超集): TypeScript > ES2016 > ES2015 > ES5
常用CDN
基本语法
?.
可选链optionalChaining
,a?.b
,表示如果a对象存在那么取a.b
属性,否则直接返回null
,而不会因为找不到属性报错,但是这个语法在vue2
的template
中无法使用。这居然是14.x才开始能用的
??
双问号,a ?? b
,如果左边的值为null
或者undefined
,那么就返回右边的值,需要注意的是左边为false
的时候依然是左边的值
js也是有switch语句的
if
条件语句如果有逗号,其实是按照最后一个值作为判断的: if (a = '123', b = '234', a > b)
变量
1 2 3 4 5 6 7 var $a; let $b; const $c; window .test = 123 ; a = {...b} const [a, ...b] = [1 , 2 , 3 , 4 ]
对象/字典 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 object instanceof constructor var copyObj = Object .assign ({}, original_obj); var copyObj = Object .assign ({}, new Obj ); Object .keys (obj); Object .values (obj); Object .entries (obj); Object .keys (obj).length == 0 ; Object .assign ({}, {}); "key" in obj var a = {}a[abc] = 'def' ; var a = { [abc]: 'def' , [`${a} ` ]: 'def' , }
数组 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 Array .from ('abc' ) Array .from (['abc' , 'def' ]) Array .from ([1 , 2 ], x => x+ x) Array .from ({length :10 },(item, index )=> index+1 ) let myArr : number[] = [] arr = [...arr1] arr = [...arr1, arr2] arr = [...'hello' ] arrA.concat (arrB) arr.indexOf ('元素' ) arr.includes ('元素' ); JSON .stringify (Array ) JSON .stringify (data, null , '' ) arr.toString (): 数组转字符串,中间会自动加上逗号 arr.join ('' ): 数组转字符串,分隔符可自定义 arr.push (obj) arr.push (...arr2) arr.pop (obj) arr.unshift (obj) arr.shift (obj) arr.slice (start, end) arr.slice (-1 )[0 ] arr.sort () arr instance of Array arr.filter (Boolean ) for (var index in arr) {} a.forEach (function (value, key, arr ) {}); arr.map ((value ) => {console .log (value); return newValue;}) arr.filter (function (value, key, arr ) {return true }); arr.some (function (value, key, arr ){}); arr.every (function (value, key, arr ){}); arr.slice (0 ).reverse ().map (function ( ){}); $.each ($array, function (k, v ){}); $.inArray ('a' , $arr): 判断数组是否包含某个元素 delete a['a' ] delete a.b Array .isArray (arr) Array .isArray (arr) && arr.length === 0
数字/布尔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Math .floor (0.2 ); Math .ceil (0.2 ); Math .round (0.2 ); Math .abs (-1 ); Math .trunc () Math .sign () Math .random () var a = 100 ;a.toString (); num.toString (8 ); num.toFixed (2 ); obj%1 === 0
字符串 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 58 59 60 61 62 63 64 65 66 var re = new RegExp ("a|b" , "i" ); text.replace (/[-[\]{}()*+?.,\\^$|#\s]/g , '\\$&' ); str.startsWith ("hello" ); str.endsWith ("world" ); a.toUpperCase () === b.toUpperCase () str.match (/<title>(.*?)<\/title>/ ) str.match (/<title>(.*?)<\/title>/g ) str.match (/<title>(<abc>.*?)<\/title>/ ) str.matchAll () str.replace (/\s+/g , "" ) str.replaceAll ('abc' , 'd' ) str.trim () / str.replace (/^\s+|\s+$/g , "" ); str.trimLeft () / str.replace ( /^\s*/ , '' ) str.trimRight () / str.replace (/(\s*$)/g , "" ) str.replace (/[\r\n]/g , ' ' ) JSON .parse (text) str.replace (reg, function (s, value ){}) str.replace ('/abc(.*?)def/' , function (a, b ) { return 'newstring' ; }) str.replace (/_(.*?)_/g , "<div>$1</div>" ) str.indexOf (substring) str.includes (substring) str.repeat (n) string.slice (start, end); str.split ('#' ) str.split (/\s+/ ) str.split ('...' , n) str.toUpperCase () str.toLowerCase () parseInt (数字) parseInt (num, 10 ) parseFloat (num) btoa (str); atob (str); str.padStart (3 , '0' ) str.padEnd (3 , '0' ) a = encodeURI (uri); b = decodeURI (uri); a = encodeURIComponent (uri); b = decodeURIComponent (uri); `我是${name} ` ;util.format ('this is %s' , 'foo' ); var str = "123" ;var n = Number (str);if (!isNaN (n)){ alert ("it is" ); } name.charAt (0 ).toUpperCase () + name.slice (1 );
时间处理moment/luxon/dayjs
时区列表
moment作者已经不推荐使用moment.js
,他自己又搞了个luxon
,但我更推荐使用dayjs
需要注意的是moment.date(12)
等方法会更改对象本身,所以在函数之间传递的时候最好克隆一个新的对象moment(moment())
format格式
moment在线测试
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 new Date ().getTime () new Date ().toLoczaleTimeString ('en-US' , { timeZone : 'Eurrpo/American' , hourCycle : 'h23' , year : 'numeric' , month : '2-digit' , day : '2-digit' , hour : '2-digit' , minute : '2-digit' , second : '2-digit' }) date.toISOString () var today = Date .parse (new Date ()); Date .parse (1234567890000 ); today.setTime (today.getTime () - 24 *60 *60 *1000 ); today.setDate (today.getDate () - 1 ); today.getYear (); today.getFullYear (); today.getMonth () + 1 ; String (today.getMonth () + 1 ).padStart (2 , '0' ); today.Day () + 1 ; today.getDate (); String (today.getDate ()).padStart (2 , '0' ); today.getHours (); today.getMinutes (); today.getSeconds (); today.getMilliseconds (); dayjs.unix (1318781876 ) dayjs ('2021-03-02T04:00:00.000Z' ).format ('MMM D, YYYY' ) dayjs ().format ('YYYY-MM-DD' ) dayjs ().add (1 , 'days' ) dayjs ().subtract (7 , 'year' ) dayjs ('2018-10-1' ).isBefore ('2018-1-1' ) dayjs.extend (utc) dayjs.extend (timezone) dayjs ("" ).tz ("America/New_York" ))import duration from 'dayjs/plugin/duration' ;dayjs.extend (duration) import relativeTime from 'dayjs/plugin/relativeTime' ;dayjs.extend (relativeTime) dayjs.duration (1 , "minutes" ).humanize (); dayjs.duration (24 , "hours" ).humanize (); dayjs.duration (1 , "minutes" ).huminize (true ); dayjs.duration (-1 , "minutes" ).huminize (true ); date1.diff (date2, 'month' ) DateTime .now ();today.toMillis () dur ().toHuman () moment ('2020-04-29 00:00:00' ); moment ('20200429' , 'YYYYMMDD' ) moment (new Date ()).add (1 , 'days' ); moment (new Date ()).add (-1 , 'days' ); moment (new Date ()).subtract (2 , 'hours' ); moment ().set ('hour' , 13 ) moment ().day () moment ().weekday () moment ().day (10 ) moment ().days () moment ().iosWeekday () moment ().daysInMonth () moment ().date () moment ().date (30 ) moment ().month () + 1 moment ().year () moment ().dayOfYear () moment ().isSame ('2021-04-17' , 'day' ); moment ().isSameOrBefore ();moment ().format (); moment ().format ("dddd, MMMM Do YYYY, h:mm:ss a" ); moment ().format ("YYYY-MM-DD HH:mm:ss" ); moment ().format ("ddd, hA" ); moment ().format ("[Today is] dddd" ); moment ().format ("hh:MM A" ); moment ().isoWeekday (); moment ('gibberish' ).format ('YYYY MM DD' ); moment ().diff (moment[]) moment ().diff (moment[], 'days' ) moment ().unix () moment.max (moment[]); moment.min (monent[]); moment ().startOf ('weeks' ) moment ().endOf ('weeks' )
URL Params处理 1 2 3 4 5 6 7 8 9 let searchParams = new URLSearchParams (url.split ('?' )[1 ])for (const [key, value] of mySearchParams.entries ()) {}let builder = new URLSearchParams ()builder.set ('field1' , 'value' ) builder.set ('feild2' , JSON .stringify (myObj)) builder.toString ()
Buffer 1 Buffer .from ('abc' , 'utf8' )
函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function containsAll (haystack, ...needles ) { console .log (haystack, needles); } function fn ( ) { console .log (arguments ) } const fn = function ( ) {}const fn = ( ) => {}const fn = name => {} const fn = num => num * num const fn = name => ({name})
类 1 2 3 4 5 6 7 8 9 10 11 12 13 class Person { name = 'test' ; get fullName () { return this .name } set fullName (name) { this .name = name } }
文件/文件夹
主要是使用Nodejs
中的fs
模块
下面很多方法默认都是异步方法,一般加上Async
就是其同步的方法,但是同步方法要加上try...catch...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fs.statAsync (path); fs.stat (path, function (exists ) {}); fs.access (path, 权限, function (err ){}); const fileBuffer = fs.readFileSync (path.join (__dirname, "../public/index.html" )) fileBuffer.toString () fs.readdir ('目录名' , 'utf-8' , function (err, data ) { data.forEach (function (item, index) ) { fs.reradFile ('文件名' , 'utf-8' , function (err, content ) { fs.writeFile ('文件名' , "abc" , "utf-8" , function (err ) { }); }); } }); const path = require ('path' )path.resolve ('../.env' )
FileList
错误处理
有时候dom的一些操作无法用try catch到错误,原因是它可能是异步的,需要在方法后用.catch
来捕获异常
1 2 3 4 5 6 7 8 9 10 try { } catch (error) { console .log (error.toString ()) console .log (error.stack ) throw error } finally { }
网络请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var xmlHttp = new XMLHttpRequest ();xmlHttp.open ("GET" , "https://haofly.net" , false ); xmlHttp.send (); console .log (xmlHttp.responseText );var xmlHttp = new XMLHttpRequest ();xmlHttp.onload = function (e ) { console .log (xmlHttp.status , xmlHttp.responseText ); } xmlHttp.onreadystatechange = function ( ) { console .log (xmlHttp.readyState ) }; xmlHttp.open ("GET" , 'https://haofly.net' , true ); xmlHttp.send (); const params = 'foo=test&foo1=test1' xmlHttp.open ('POST' , url, true ); xmlHttp.setRequestHeader ('Content-type' , 'application/x-www-form-urlencoded' ); xmlHttp.send (params);
进程/线程/Shell命令执行
使用child_process
模块
可用来执行shell
命令
1 2 3 var p = require ('child_process' );p.exec ("ls abc" , function (error, stdout, stderr ) {}); p.execSync ("ls abc" );
DOM操作 元素查找
一般getElements**
方法返回的是一个NodeList,并不是普通的列表对象但是不能用forEach
来遍历,只能用for循环遍历,因为它是一个HTMLCollection
对象,也可以转换为列表对象Array.from(nodeList)
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 document .querySelector (".myclass" ); document .querySelectorAll ('div.abc, div.def' );document .getElementById ('xxx' );document .getElementByClassName ('myclass' );document .getElementByTagName ('p' );ele.parentElement ; ele.parentNode ; ele.children ele.closet ('td' ) ele.getElementsByTagName ('td' ); ele.getElementsByClassName ('myclass' ); ele.firstElementChild ; ele.lastElementChild ele.nextElementSibling ; ele.previousElementSibling ; $('p' ) $('p' )[0 ] $('p#intro' ) $('p.intro' ) $('p:first' ) $('p a:first' ) $('p[name=abc]' ) $('*[data-abc="22"]' ); $('body >div:first-child' ) $('*' ) $('[id^="test"]' ) $('[id$="test"]' ) $('[id*="test"]' ) $('[id!="test"]' ) $(this ) $(this ).next () $(this ).nextAll () $(this ).prev () $(this ).prevAll () $(this ).siblings () $(this ).parent () $(this ).parents ('myclass' ) $(this ).children ('myclass' ) $(this ).nextAll ('cl' ) $('p' ).find ('input' ) $('p' ).last () $('input:checked' ) $('div:visiable' ) document .getElementById ('test:abc' ) $('img' ).index ($(myImg)) $('select option[value="abc"]' );
同步等待元素存在可见 由于有些元素不一定是在window.load
之后就全部展示的,有时候我们需要通过滚动页面才能让某些元素出现,这个时候就需要一个比较方便的方法用于等待元素出现。如果是异步的需求,可以直接用setInterval
每隔几秒去检查一次(注意不能用while循环去一直判断,因为会占用计算时间,导致异步任务无法完成),但是对于复杂的需求,比如等待一个元素出现后需要等待另外的元素出现,或者是需要同步判断一系列的元素,那么就需要另外的方法,目前能找到的最好的方法是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function rafAsync ( ) { return new Promise (resolve => { requestAnimationFrame (resolve); }); } async function checkElement (selector, index = 0 ) { let querySelector = document .querySelectorAll (selector)[index]; while (querySelector === null || querySelector === undefined ) { querySelector = document .querySelectorAll (selector)[index]; await rafAsync () } return querySelector; } checkElement ('#my_tag' ).then ((element ) => { console .log (element); })
获取元素内容 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 ele.attributes ; ele.getAttribute ('class' ); ele.setAttribute ('class' , 'highlight' ); ele.hasAttribute ('class' ); ele.removeAttribute ('class' ); ele.value ; ele.style .fontSize ele.innerText getComputedStyle (ele) getComputedStyle (ele, '::before' ) videoEle.videoHeight / myVideo.videoWidth videoEle.clientHeight / myVideo.clientWidth videoEle.currentTime videoEle.ended videoEle.paused $('[id=xxx]' ); $('#check' ).prop ('checked' ) $('div' ).prop ('classList' ) $('div' ).prop ('classList' ).remove ('d-none' ) $('div' ).prop ('classList' ).add ('d-none' ) $('div' ).height () $('div' ).height (20 ) $('select' ).val () $('select option:selected' ).text (); $('div' ).data ('abc' ); $('div' ).removeAttr ('required' ); $('div' ).hasClass ('foo' ); $("p" ).is (":visible" ); $('div' ).prop ('tagName' );
编辑元素 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 option = document .createElement ('option' ); option.setAttribute ('value' , 'value1' ); text = document .createTextNode ('ppp' ); ele.appendChild (text); ele.removeChild (text); ele.replaceChild (el1, el2); selection.innerHtml = '<option>a</option>' ; parentElement.insertBefore (newElement, referenceElement); ele.classList .add ("mystel" , "secondClass" ); ele.classList .remove ('mystyle' , 'secondClass' ); ele.classList .toggle ("mystle" ); ele.classList .contains ('mystyle' ); ele.classList .item (0 ); ele.style .color = '#fff' ; document .getElementById ("input" ).value = "test" ; ele.disabled = true ; html ('' ) append () prepend () after () before () remove () empty () clone () addClass ('' ) removeClass ('class1 class2' ) $('p' ).css ('color' , 'red' ) $('button' ).prop ('disabled' , true ) $('p' ).hide () $('p' ).show () $('img' ).attr ('src' , 'xxx' ) $('div' )[0 ].scrollIntoView () $('div' ).animate ({scrollTop : $('div' ).offset ().top - 10 }) myVideo.play () myVideo.pause () myVideo.load () var a_tag = document .createElement ('a' ); parent_tag.after (a_tag); a_tag.outerHTML = '<a class="..." name="">ok</a>' ; $("#<form_id>" ).trigger ("reset" ); canvasEle.getContext ("2d" ).strokeRect (20 , 20 , 150 , 100 )
元素事件listen event
对于动态生成的元素,绑定事件需要绑定在父元素上才能生效,或者直接绑定在document上,$(document).on('click', '#myButton', function(){})
对于input
框,当失去焦点的时候才会触发onchange
,输入事件应该是oninput
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 ele.onchange = function ( ) {}; ele.onchange = funciton () {}; ele.addEventListener ('click' , func (e) {}); ele.removeEventListener ('change' , func () {}); videoEle.addEventListener ('resize' , () => {}); ele.addEventListener ('DOMNodeInserted' , func (){}); window .onload = function ( ) {}; document .onkeyup = function (e ) {}; window .addEventListener ("unhandledrejection" , event => { console .warn (`UNHANDLED PROMISE REJECTION: ${event.reason} ` ); }); window .addEventListener ('error' , function (event ) { ... })change () blur () click dbclick focus () hover mouseenter mousedown mousesleave mouseup resize () document .getElementById ('' ).dispatchEvent (new MouseEvent ('mouseenter' )) $('a' ).trigger ('click' ) $('#myModal' ).modal ('show' ) $('p' ).bind ('click' , function ( ){}); $('#myModal' ).on ('shown.bs.modal' , function ( ) {}); $('#myModal' ).on ('hidden.bs.modal' , function ( ) {}); $('#myModal' ).on ('hidden' , function ( ) {}); $('#myModal' ).on ('hide' , function ( ) {});
页面属性 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 document .cookie document .cookie = 'abc=123' ; document .cookie = 'abc=123; expires=' + date.toGMTString () + ';' window .location .href window .location .pathname window .location .origin window .location .hostname window .lcoation .href = 'url' window .location .back () window .history .pushState ({"html" :test.html ,"pageTitle" :response.pageTitle },"" , urlPath); document .referrer location.reload () window .stop () window .getSelection ().toString (); var getUrlParameter = function getUrlParameter (sParam ) { var sPageURL = decodeURIComponent (window .location .search .substring (1 )), sURLVariables = sPageURL.split ('&' ), sParameterName, i; for (i = 0 ; i < sURLVariables.length ; i++) { sParameterName = sURLVariables[i].split ('=' ); if (sParameterName[0 ] === sParam) { return sParameterName[1 ] === undefined ? true : sParameterName[1 ]; } } }; getUrlParameter ('page' )$(document ).ready (function ); (function ( ) { })();
Canvas 1 2 3 4 5 6 const c = document .getElementById ('myCanvas' )const ctx = c.getContext ('2D' ) ctx.font = '' ctx.fillText ('test' , 10 , 20 ) ctx.fillStyle = 'red' ctx.textAlign = 'center'
特殊函数 1 2 3 4 5 6 7 var t = window .setTimeout (func, delay); var t = window .setInterval (func, delay); var t = setImmediate (func); clearInterval (t) debugger ;
Module 语法 js里面一个模块就是一个独立的文件,文件内部的变量,外部是无法获取到的,需要用export
使内部的变量暴露给外部,例如:
1 2 3 4 5 6 7 export var name = 'hao' ;export {var1, var2, var3}; export function myfunc (x, y ) {}; export {var1 as var2}; export default function ( ) {}; exports .myFunc = function ( ) {}
Async/Promise
异步执行代码能防止单线程的js被阻塞,但是却让响应的顺序不可预计。
如果要同时保证异步函数的顺序,要么就需要一层一层使劲的嵌套,除非使用Promise,它允许将这种回调函数的嵌套改为链式调用,然后将回调放到then中。
但是then依然不够直观,所以就有Async和await了。写起来的代码就像是同步的了。async函数内部可以有一个或多个异步操作,一旦遇到await就会立即返回一个pending状态的Promise对象,然后回到主线程执行住线程代码。等到await的异步请求被resolve/reject后,才会继续执行async函数内后面的部分。
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 58 59 let asyncFunc = async ( ) => { await new Promise ((resove, reject ) => { console .log ("actions" ); setTimeout (() => { console .log ("模拟异步返回结果" ); }, 0 ); }); return "ok" ; } let promise = asyncFunc ();promise.then (value => { console .log (value); }); mockFetch = (url ) => { return new Promise ((resolve, reject ) => { const code = 200 ; process.nextTick (() => status === 200 ? resolve ({ json : function ( ) { return { msg : "ok" , }; }, }) : reject ({ error : "error" }), ); }); }; mockFetch ("https://api.github.com/users/haoflynet/repos" );await new Promise (r => setTimeout (r, 1000 ));async function waitUntil (condition ) { return await new Promise (resolve => { const interval = setInterval (() => { if (condition) { resolve ('foo' ); clearInterval (interval); }; }, 1000 ); }); } await Promise .all (_.map (arr, async (item) => { await ... })); for await (const item of items) { await ... }
Ajax
如果要接收stream响应,nginx的location也需要添加配置proxy_buffering off; fastcgi_buffering off;
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 $('myForm' ).serializeArray (); $.ajax ({ url : 'url' , dataType : 'json' , type : 'POST' , data : data, xhrFields : { responseType : 'stream' , onprogress : function (e ) { console .log (e.currentTarget .response ); } } beforeSend : function (xhr ) { }, error : function (re ){ }, success : function (re ){ }, complete : function (re ) { if (re.statusText == "success" ) { console .log ("Sent successfully" ); } else { console .log ("Not Sent" ); } } }); $(document ).ajaxComplete (function ( ) { $( ".log" ).text ( "Triggered ajaxComplete handler." ); });
直接发送POST请求
1 2 3 4 5 $.post ('some.php' , {name : 'haofly' }) .done (function (msg ){ }) .fail (function (xhr, status, error ) { });
jQuery Effects特效
能实现一些简单的效果,例如blind(百叶窗特效)、bounce(反弹特效)、clip(剪辑特效)、drop(降落特效)、explode(爆炸特效)、fade(淡入淡出特效)、fode(折叠特效)、highlight(突出特效)、puff(膨胀特效)、pulsate(跳动特效)、scale(缩放特效)、shake(震动特效)
1 2 $('#mydiv' ).fadeout (); $('#mydiv' ).fadein ().delay (1000 ).fadeout ();
调试技巧
代码中打断点直接debugger;
语句,这样浏览器会自动在该处断点,对于会有js压缩的代码调试非常有用
浏览器console.log 打印出来的对象,如果没有点击展开,那么点开的时候会是最后一次该对象的值。
第三方库 lodash/常用帮助函数
_camelCase
1 2 3 _.camelCase ('Foo Bar' ); _.camelCase ('--foo-bar--' ); _.camelCase ('__FOO_BAR__' );
_.chain
lodash中最重要的部件之一
可以将普通的对象变为可链式执行的对象
_.chain(arr)....value()
也可隐式调用_(arr)...
就不用.value()
这里的.value()
是一个延迟计算操作,但是有些方法是不能加在链条中的,例如reduce
会被立即计算
1 2 3 4 5 6 7 8 9 10 11 12 13 const arr = [1 ,2 ,3 ,4 ,5 ]_.chain (arr) .filter (n => n % 2 === 0 ) .map (n => n * n) .uniq () .sum () .value () a = _.chain (arr).filter (n => n % 2 === 0 ).map (n => n * n).sum () a.value () arr.push (6 ) a.value ()
_.cloneDeep
1 newObj = _.cloneDeep (oldObj);
_.countBy 1 _.countBy ([6.1 , 4.2 , 6.3 ], Math .floor )
_.chunk
1 2 _.chunk ([1 ,2 ,3 ,4 ,5 ], 2 ) _.chunk ('abcdefg' , 2 ).map (chunk => chunk.join ('' ))
_.debounce(func, [wait=0])
函数防抖,防止函数短时间内重复执行,例如api请求,搜索的查询
1 _.debounce (() => {apicall}, 100 )
every 必须所有回调都返回true
,最终结果就为true
,否则就为false
. 有个妙用就是在实现在forEach中break的功能
filter 过滤数组,将返回为true
(满足条件)的元素组成为一个新数组
1 2 3 4 Array .from ([1 ,2 ,3 ]).filter (item => item > 2 ) Array .from ([1 ,2 ,3 ]).filter ((item, index ) => function ({ // 带索引 return true ; } ))
find 返回回调结果为true
的第一个元素
_.find 返回回调结果为true
的第一个元素
1 2 3 _.find (users, function (item ) {item.age > 20 }) _.find (users, {'age' : 20 , 'active' : true }) _.find (users, 'active' }
findIndex 返回回调结果为true
的第一个元素的索引位置
_.flatMap 对数组中的所有值运用函数,函数的返回值即是一个新的数组,如果不返回则是undefined
1 2 3 4 5 6 function duplicate (n ) { return [n, n]; } _.flatMap ([1 , 2 ], duplicate);
_.flatMapDeep
类似于flatMap
,但是它会递归将值中的数组全部展开
_.floor(number, [precision=0])
四舍五入,可以指定保留几位小数,且小数位不足时不会补0,比toFixed好用
1 2 3 _.floor (4.006 ) _.floor (0.046 , 2 ) _.floor (4 )
forEach 对数组中每一个值运用函数,但是无需返回值,只是单纯的遍历
_.forIn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Foo ( ) { this .a = 1 ; this .b = 2 ; } Foo .prototype .c = 3 ; _.forIn (new Foo , function (value, key ) { console .log (key); }); _.forIn ('string' , function (value, index ) { })
_.get
1 2 3 4 5 6 _get (user, 'name' , 'defaultvalue' )_get (user, 'parent.name' , 'devaultvalue' )var object = { 'a' : [{ 'b' : { 'c' : 3 } }] }_.get (object, 'a[0].b.c' ) _.get (object, ['a' , '0' , 'b' , 'c' ])
_.groupBy
根据第二个参数的返回值来进行分组,返回的是一个key=> [item]对象,key为返回值,[item]为对象列表
1 2 3 4 _.groupBy ([6.1 , 4.2 , 6.3 ], function (item ) { return Math .floor (item); });
_.intersection
1 _.intersection ([2 , 1 ], [4 , 2 ], [1 , 2 ])
_.invert
1 2 const obj = {a : 1 , b : 2 , c : 3 }_.invert (obj)
_.isEqual 1 2 3 _.isEqual (_.sortBy (a), _.sortBy (b)) _.isEqual (a.sort (), b.sort ()) _.isEqual (a, b)
_.isMatchWith isMatchWith(object, source, [customizer])
, 具有基本的isMatch功能,并且能添加customizer
进行定制化的比较。判断source是否包含在object里,customizer返回true或者false。我fuck,这个函数只要source里面有key没在object,立马就返回false了,都不执行customizer的
isNaN 判断value是否是NaN
_.nth
1 2 3 _.nth (arr, -1 ); _.nth (arr, -2 ); _.nth (str, -1 );
_.kebabCase
1 2 3 _.kebabCase ('Foo Bar' ); _.kebabCase ('fooBar' ); _.kebabCase ('__FOO_BAR__' );
_.last
map 对数组中每一个值运用函数,返回一个新的值作为新数组,没有返回值的位置会被设置为undefined
1 2 3 4 5 6 7 8 myArr.map (Match .sqrt ) myArr.map ((item, index ) => {}) await Promise .all (_.map (['a' ,'b' ], async (item) => { await ... }))
_.map
遍历对象/字典时,callback第一个参数是value不是key
_.mapKeys
值不变,将每个返回值作为key,可作用于数组和对象上
1 2 3 4 5 var arr = [1 , 2 , 3 ];_.mapKeys (arr, (item ) => item.toString ()) var obj = { 'a' : 1 , 'b' : 2 , 'c' : 3 };_.mapKeys (obj, (item ) => item.toString ())
_.mapValues
和_.mapKeys
类似,不过这个目的是修改value,而不是key
1 2 3 4 5 var obj = { 'a' : 1 , 'b' : 2 , 'c' : 3 };_.MapValues (obj, (value ) => item.toString ()) var arr = [1 , 2 , 3 ]; _.mapKeys (arr, (item ) => item.toString ())
_.max 1 2 _.max ([1 , 2 , 3 ]) _.max ([])
_.maxBy 1 _.maxBy (objects, 'field' )
_.merge
1 2 3 var obj1 = { 'a' : [{ 'b' : 2 }, { 'd' : 4 }] }var obj2 = { 'a' : [{ 'c' : 3 }, { 'e' : 5 }] }_.merge (obj1, obj2)
_.min
1 2 _.min ([4 , 2 , 8 , 6 ]) _.min ([])
_.orderBy
1 2 3 4 5 6 7 8 var users = [ { 'user' : 'fred' , 'age' : 48 }, { 'user' : 'barney' , 'age' : 34 }, { 'user' : 'fred' , 'age' : 40 }, { 'user' : 'barney' , 'age' : 36 } ]; _.orderBy (users, ['user' , 'age' ], ['asc' , 'desc' ]);
_.pick
1 2 const obj = {'a' : 1 , 'b' : 2 , 'c' : 3 }_.pick (obj, ['a' , 'c' ])
_.pickBy 1 2 const obj = {'a' : 1 , 'b' : '2' , 'c' : 3 }_.pickBy (obj, _.isNumber )
_.random
1 2 _.random (0 , 100 ) _.random (0 , 100 , true )
_.reduce
能够将元素一次进行计算,第一个参数为上一次计算的结果
1 2 3 4 5 6 7 8 9 var arr = [1 ,2 ,3 ];_.reduce (arr, function (result, o ) {return result + o}); const users = [ {num : 1 }, {num : 2 } ] _.reduce (users, (result, user ) => {result.num += user.num ; return result})
_.snakeCase
1 2 3 _.snakeCase ('Foo Bar' ); _.snakeCase ('fooBar' ); _.snakeCase ('--FOO-BAR--' );
some 只要其中一个值返回true
,那么整个表达式的结果就是true
1 Arrays .from ([12 , 22 , 33 ]).some (item => item > 30 )
_.sortBy 1 2 _.sortBy ([{a : 123 }, {a :222 }], ['a' ]) _.sortBy ([{a : 123 }, {a :222 }], function (o ) => o.a )
_.startCase
1 2 3 _.startCase ('--foo-bar--' ); _.startCase ('fooBar' ); _.startCase ('__FOO_BAR__' );
_.template 创建一个预编译的模板方法来进行字符串的格式化
1 2 3 4 5 6 7 8 var compiled = _.template ('hello ${ user }!' );compiled ({ 'user' : 'pebbles' });_.templateSettings .interpolate = /{{([\s\S]+?)}}/g ; var compiled = _.template ('hello {{ user }}!' );compiled ({ 'user' : 'mustache' });
_.take
_.toPath 转化value为属性路径的数组
1 2 _.toPath ('a.b.c' ) _.toPath ('a[0].b.c' )
_.uniq 创建一个去重后的array
数组副本
_.uniqBy
1 2 _.uniqBy ([{}, {}], (item ) => item.field ) _.uniqBy ([{}, {}], 'field1' )
upperFirst
_.xorWith
得到在两个数组中都不存在的元素组成的数组,第三个参数是一个比较函数,用于比较是否相等
1 2 _.xorWith ([3 ], [1 ,2 ], _.isEqual ) _.xorWith ([3 , 1 ], [1 ,2 ], _.isEqual )
常用帮助方法 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 58 59 60 61 62 window .getQuery = function (key ) { const urlParams = new URLSearchParams (window .location .search ) return urlParams.get (key) } window .addQuery = function (key, value ) { var searchParams = new URLSearchParams (window .location .search ) searchParams.set (key, value) return searchParams.toString () } window .getCookie = function (cname ) { const name = cname + "=" const ca = document .cookie .split (';' ) for (let i = 0 ; i < ca.length ; i++) { const c = ca[i].trim () if (c.indexOf (name) === 0 ) return c.substring (name.length , c.length ) } return "" } window .setCookie = function (key, value ) { document .cookie = `${key} =${value} ` } window .convertNumberToMoney = function (money ){ if (money && money!=null ){ money = String (money) var left=money.split ('.' )[0 ],right=money.split ('.' )[1 ] right = right ? (right.length >=2 ? '.' +right.substr (0 ,2 ) : '.' +right+'0' ) : '.00' var temp = left.split ('' ).reverse ().join ('' ).match (/(\d{1,3})/g ) return (Number (money)<0 ?"-" :"" ) + temp.join (',' ).split ('' ).reverse ().join ('' )+right } else if (money===0 ) { return '0.00' ; }else { return "" ; } function retry (fn, times, delay=3000 ) { return new Promise (function (resolve, reject ) { function try ( ) { fn () .then (res => resolve (res)) .catch (err => { if (times === 0 ) { reject (err); } else { times--; setTimeout (try (), delay); } }) } try (); }) }
推荐阅读
TroubleShooting
根据select的选项不同跳转到不同的页面 :<select onchange="location.href=this.options[this.selectedIndex].value;">
Ajax请求无论是GET还是POST都突然变成了OPTIONS请求 可能是因为把本地代码提交到服务器时,发生了跨域请求错误,url里面写的是本地的端口地址,这时候只需要修改本地的端口地址修改为相对于服务器的本地地址即可
停止js的冒泡 反正就三种方法,随便试
1 2 3 4 5 6 7 8 event.stopPropagation (); event.preventDefault (); return false
select标签disabled掉过后表单提交不上去那个字段 : 我也不知道什么原因,但是确实是这样的,可以用stackoverflow 里的方法:
1 2 3 4 5 <select name ="myselect" disabled ="disabled" > <option value ="myselectedvalue" selected ="selected" > My Value</option > .... </select > <input type ="hidden" name ="myselect" value ="myselectedvalue" />
禁用radion标签
1 2 <input type="radio" name="foo" value="Y" checked> <input type ="radio" name ="foo" value ="N" disabled >
在Laravel中如果出现TokenMismatchException ,有可能是Laravel的CSRF机制造成的,解决办法参见http://www.golaravel.com/laravel/docs/5.0/routing/ ,即 首先在meta中添加
1 <meta name ="csrf-token" content ="{{ csrf_token() }}" />
然后设置ajaxSetup:
1 2 3 4 5 6 7 8 9 10 11 12 <script type="text/javascript" > $(function ( ){ $.ajaxSetup ({ headers : { 'X-CSRF-TOKEN' : $('meta[name="csrf-token"]' ).attr ('content' ) } }); $('#publish' ).bind ('click' , function ( ){ $.ajax ({ 。。。 }); </script>
Laravel 5 要使用Input获取输入的信息 ,必须先use Input
,看来Laravel 5 对命名空间的管理更加严格了
Ajax请求总是执行error部分代码/Ajax文件上传 ,原因可能是返回数据的格式不对,一定要返回dataType所规定的数据格式 上传文件,需要特殊的几个参数和变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $('input#uploadh' ).bind ('change' , function ( ){ var f = this .files ; var formdata = new FormData (); formdata.append ('image' , f[0 ]); $.ajax ({ url : "{{ url('uploadimg') }}" , type : "POST" , data : formdata, dataType : "json" , processData : false , contentType : false , success : function (data ){ alert ('成功' ); } }); });
如果是通过一个button而不是input提交的话,那么可以这样使用.需要注意的是,只能一个文件一个文件地append,后台才能通过request.FILES看到
1 2 3 4 5 $('button#uploadFile' ).on ('click' ,function ( ){ var f = $('input#uploadFile' )[0 ].files ; var formadata = new FormData (); formdata.append ('image' , f[0 ]); });
获取当前元素的父元素 ,使用target,但有时候也可以不用target…我也是醉了 获取同级的元素:prev()和next()
```javascript $(‘button#post’).bind(‘click’, function(ele){ $.ajax({
url: port,
type: "POST",
dataType: "json",
error: function(error){
alert('出错啦');
},
success: function(data){
alert($(ele.target).parent().parent().attr('id'));
}
});
1 2 3 4 5 6 7 8 * 在ajax的url里面,默认是相对于当前地址的url,例如 ```tex 当前地址是http://localhost/a,那么url: 'publish'表示http://localhost/publish 当前地址是http://localhost/a/b,那么url: 'publish'表示http://localhost/hehe/publish 只有写为url: '/publish'才表示相对于根域名,即http://localhost/publish
给生成的元素动态绑定事件 :SegmentFault说直接用.on方法可以实现1.7之前.live的动态绑定功能,但是我就是不行,这里使用.on的另外一种方法,绑定到document上去就行了,原理就是将事件委托给父元素,然后由父元素绑定给子元素:
1 2 3 $(document ).on ('click' , 'button' , function ( ){ alert ('dg' ); });
绑定回车事件 :
1 2 3 4 $(document ).on ('keypress' , 'input' , function (event ){ if (event.keyCode == '13' ){ alert ('success' ); });
提交表单时,如果想增加额外的参数,可以添加动态添加一个隐藏标签:
1 2 var input = $("<input>" ).attr ("type" , "hidden" ).attr ("name" , "字段名" ).val ("value" );$('#form1' ).append ($(input));
<select>
元素的选择<option>
事件是change
,而获得所选择元素使用的是val()
,默认被选择:<option selected="true" value="xxx">xxx</option>
,获取文本内容用text()
避免表单回车自动提交 :有时候想在表单提交前进行一些操作,但又不想在回车时自动提交表单(当只有input的时候,会强制提交),这时候只需要在button的回车事件中添加return false
即可
无法获取iframe里面的内容 : 一个iframe表示一个窗口,并且还对应不同的域名,默认情况,放任一个网页,脚本都默认在最上层的窗口上面,在谷歌浏览器的审查元素
视图下的Console
的左上角可以选择定位到哪个iframe
,如果是爬虫或者油猴脚本,要注意对应iframe的url。
onclick的时候将标签本身作为参数 : onclick="dothing(this);"
onclick的时候直接阻止冒泡 : <span onclick="event.stopPropagation(); alert('ok');"></span>
js实现点击自动复制到剪贴板 :
1 2 3 4 5 6 7 8 9 10 11 12 var text_tag = document .getElementById ("text" );text_tag.select (); document .execCommand ("Copy" );var tempInput = document .createElement ("input" );tempInput.style = "position: absolute; left: -1000px; top: -1000px" ; tempInput.value = value; document .body .appendChild (tempInput);tempInput.select (); document .execCommand ("copy" );document .body .removeChild (tempInput);
打开新标签页 : window.open(pageURL,name,parameters) / window.open(url, '_blank')
$(‘form’).serialize()表单序列化时无法正确获取checkbox
的值 : 可以在checkbox
前添加一个隐藏的input
,两者使用同样的name
,这样在表单提交的时候会提交两个值,但是后端都是选择的后面那个值
1 2 <input type ="hidden" name ="option" value ="false" /> <input type ="checkbox" name ="option" value ="true" />
隐藏CNZZ“站长统计”四个字 : 直接在添加这行js:
Unexpected token o in JSON at position 1 : 原因是在使用JSON.parse(str)
的时候,传入的不是字符串而是一个对象,即[object Object]
,把[
理解为了数组的开始,但是o
就无法理解了。
Uncaught TypeError: a.indexOf is not a function : 版本问题。$(window).load(function(){})
在高版本中已经废弃了,需要用$(window).on('load', function(){})
替代。如果仍然有问题,可以直接引入一个兼容包<script src="https://code.jquery.com/jquery-migrate-1.4.1.min.js"></script>
出发表单submit事件但是不想要表单自动提交并刷新网页 :
1 2 3 4 $('#contactForm' ).submit (function ( ) { sendContactForm (); return false ; });
限制input框输入特殊字符
1 2 3 4 5 6 7 8 $(".myinput" ).keypress (function (e ) { if ((event.which != 46 || $(this ).val ().indexOf ('.' ) != -1 ) && (event.which < 48 || event.which > 57 ) ) { return false ; } });
上传图片后实时预览图片
1 2 3 4 5 6 7 8 9 10 11 12 13 function readURL (input ) { if (input.files && input.files [0 ]) { var reader = new FileReader (); reader.onload = function (e ) { $('#file' ).attr ('src' , e.target .result ); } reader.readAsDataURL (input.files [0 ]); } } $("#img_preview" ).change (function ( ) { readURL (this ); });
select元素实现重复点击取消选择
1 2 3 4 $('select option' ).on ('click' , function (e ) { this .selected = !this .selected ; e.preventDefault (); });
添加英文数字索引前缀 :得到1st 2nd 3rd等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function ordinal_suffix_of (i ) { var j = i % 10 , k = i % 100 ; if (j == 1 && k != 11 ) { return i + "st" ; } if (j == 2 && k != 12 ) { return i + "nd" ; } if (j == 3 && k != 13 ) { return i + "rd" ; } return i + "th" ; }
JS Input 延时触发/延迟触发 : 常用于autocomplete,不想每次都去查询接口,而是间隔很短时间去查询:
1 2 3 4 5 6 7 8 const timer = null ;function onInputChange ( ){ const value = document .getElementById ("input" ).value ; clearTimeout (timer); timer = setTimeout (function ( ) { console .log (value); }, 500 );
Property ‘style’ does not exist on type ‘Element’ : 需要强制声明其类型: Array.from(document.getElementsByClassName('test') as HTMLCollectionOf<HTMLElement>)
moment Not in a recognized ISO format : 这是moment无法自动解析其他格式的时间,这时候需要制定格式,例如moment('2021-6-28', 'YYYY-M-D')
input输入框仅允许输入数字,去掉字符串中的非数字字符 :
1 2 3 4 5 6 7 8 9 10 11 12 $('input' ).on ('keyup' , function ( ) { $(this ).val ($(this ).val ().repalce (/[^0-9]/g , '' )) }) - **dayjs_1.default is not a function **: 可以尝试在tsconfig中添加配置`esModuleInterop: true` - **显示精确的实时时间,精确到秒**: 我发现angular的get方法并不能在date变化的时候实时更新,这样还不如直接用interval : `` `javascript setInterval(() => { this.now = new Date(); });
Uncaught TypeError: Illegal invocation : 发生于使用多层调用内置函数的情况,例如:
1 2 3 4 5 var obj = { alert : alert};obj.alert ('hello' ); var obj = { alert : alert.bind (window ) }obj.alert ('hello' );
扩展阅读