在線JS編輯
JS 編寫規範
阮一峯 ES 6
阮一峯
廖雪峯javascript
<html> <head> <script src='./jquery-2.2.3.min.js'></script> <script> $(function(){ var uploadImage = document.getElementById('upload-image'); var imageInfo = document.getElementById('image-info'); var preview = document.getElementById('preview'); uploadImage.addEventListener('change',function(){ var imageName = uploadImage.value; var imageNameRegEx = /.*?\.(jpg|png|gif|jpeg)/; var file = uploadImage.files[0]; // 清空上次提示信息 imageInfo.innerText = ''; if(!imageNameRegEx.test(imageName)){ imageInfo.innerText='上傳的文件不是圖片格式!' imageInfo.style.color = 'red'; alert('不是正確的圖片格式!'); }else{ preview.style.backgroundImage = ''; var reader = new FileReader(); reader.onload = function(e){ var data = e.target.result; preview.style.backgroundImage = 'url(' + data + ')'; } imageInfo.innerHTML = '<br>文件名:'+file.name+'</br>'+ '<br>文件名:'+file.size+'</br>'+ '<br>文件名:'+file.lastModifiedDate+'</br>' // 以DataURL的形式讀取文件: reader.readAsDataURL(file); } }) }) </script> </head> <body> <div id="preview" style="border: 1px solid #ccc; width: 100%; height: 200px; background-size: contain; background-repeat: no-repeat; background-position: center center;"></div> <form method='post' action="http://www.baidu.com" enctype="multipart/form-data"> <input type='file' name='upload-image' id='upload-image'> </form> <p id='image-info'></p> </body> </html>
<!doctype> <html> <head> <meta charset='utf-8'></meta> <script> window.onload = function(){ var fileInput = document.getElementById('fileInput'); var info = document.getElementById('info'); var preview = document.getElementById('preview'); fileInput.addEventListener('mousedown',function(){ info.innerText = ''; }); fileInput.addEventListener('change',function(){ var shortFileName = fileInput.value; var file = fileInput.files[0]; var shortFileNameRegEx = /^.*?\.(jpg|png|jpeg|gif)$/ // 清空提示信息以及圖片顯示信息 info.innerText = ''; preview.style.backgroundImage = ''; if(!shortFileNameRegEx.test(shortFileName)){ info.innerText='文件不是正確的圖片格式'; info.style.color = 'red'; }else{ var reader = new FileReader(); reader.onload = function(e){ var data = e.target.result; preview.style.backgroundImage = 'url('+data+')' } reader.readAsDataURL(file); } }) } function success(text){ var info = document.getElementById('info'); info.innerText = text; info.style.color = 'green'; } function failed(code){ var info = document.getElementById('info'); info.innerText = 'Code: '+code; info.style.color = 'red'; } function btnClick(){ var request = new XMLHttpRequest||new ActiveXObject('Microsoft.XMLHTTP'); request.onreadystatechange = function(){ var info = document.getElementById('info'); info.innerText = ''; if(request.readyState === 4){ if(request.status === 200){ success(request.responseText); }else{ failed(request.status); } }else{ failed(request.status); } } request.open('GET','http://xxxx:8999/Patientxxx'); request.send(); } </script> </head> <body> <div id="preview" style="border: 1px solid #ccc; width: 100%; height: 200px; background-size: contain; background-repeat: no-repeat; background-position: center center;"></div> <input type='file' id='fileInput' name='fileInput'/> <p id='info'></p> <input type="button" id="btn" onclick="btnClick();" value='提交'/> </body> </html>
<!doctype> <html> <head> <meta charset='utf-8'></meta> <script> //data即getPrice中price.src傳入的返回結果, 是一個JSON字符串 function showPrice(data){ var p=document.getElementById("test-jsonp"); p.innerHTML="當前價格:"+ data['0000001'].name +': ' + data['0000001'].price + ';' + data['1399001'].name + ': ' + data['1399001'].price; } //點擊"刷新"按鈕時, 從'http://api.money.126.net/data/feed/0000001,1399001'請求JSON字符串,並把請求結果傳給自定義函數showPrice() function getPrice(){ var price=document.createElement('script'); var head=document.getElementsByTagName("head")[0]; price.src= 'http://api.money.126.net/data/feed/0000001,1399001?callback=showPrice'; head.appendChild(price); } </script> </head> <body> <p id="test-jsonp">placehoder</p> <button type='button' onclick="getPrice()">刷新</button> </body> </html>
var fn = function(num) { return new Promise(function(resolve, reject) { if (typeof num == 'number') { resolve(num); } else { reject('TypeError'); } }) } fn(2).then(function(num) { console.log('first: ' + num); return num + 1; }) .then(function(num) { console.log('second: ' + num); return num + 1; }) .then(function(num) { console.log('third: ' + num); return num + 1; }); new Promise(function(resolve,reject){ var num = Math.random()*2; console.log(num); if(num>1){ resolve('200 ok!'); }else{ reject('400 error!'); } }).then(function(resolveResult){ console.log(resolveResult);}) .catch(function (rejectResult){ console.log(rejectResult); }); function ajax(method,url,data){ var request = new XMLHttpRequest() || new ActiveXObject('Microsoft.XMLHTTP'); return new Promise(function(resolve,reject){ request.onreadystatechange = function(){ if(request.state === 4){ if(request.status === 200){ resolve(request.responseText); }else{ reject(request.status); } }else{ reject(request.status); } }; request.open(method,url); request.send(); }); } var p = ajax('GET','http:xxx') p.then(function(responseText){ console.log(responseText); }) .catch(function(status){ console.log(status); })
對於下面的表單: <form id="test-form" action="#0" onsubmit="return false;"> <p><label>Name: <input name="name"></label></p> <p><label>Email: <input name="email"></label></p> <p><label>Password: <input name="password" type="password"></label></p> <p>Gender: <label><input name="gender" type="radio" value="m" checked> Male</label> <label><input name="gender" type="radio" value="f"> Female</label></p> <p><label>City: <select name="city"> <option value="BJ" selected>Beijing</option> <option value="SH">Shanghai</option> <option value="CD">Chengdu</option> <option value="XM">Xiamen</option> </select></label></p> <p><button type="submit">Submit</button></p> </form> 輸入值後,用jQuery獲取表單的JSON字符串,key和value分別對應每一個輸入的name和相應的value,例如:{"name":"Michael","email":...} 'use strict'; var json = null; var obj = {}; var input = $('#test-form :input').filter(function(){ return (this.type!=='radio'||this.checked||this.tagName !== 'BUTTON')?true:false; }).map(function(){ obj[this.name] = this.value; }); var json = JSON.stringify(obj); // 顯示結果: if (typeof(json) === 'string') { console.log(json); } else { console.log('json變量不是string!'); }
練習 除了列出的3種語言外,請再添加Pascal、Lua和Ruby,而後按字母順序排序節點: <!-- HTML結構 --> <div id="test-div"> <ul> <li><span>JavaScript</span></li> <li><span>Python</span></li> <li><span>Swift</span></li> </ul> </div> 'use strict'; var testDiv = $('#test-div>ul'); var langs = ['Pascal','Lua','Ruby']; testDiv.find('li').map(function(){langs.push(this.innerText)}); langs.sort(); testDiv.empty(); langs.forEach(function(element){ testDiv.append(`<li><span>${element}</span></li>`); }); // 測試: ;(function () { var s = $('#test-div>ul>li').map(function () { return $(this).text(); }).get().join(','); if (s === 'JavaScript,Lua,Pascal,Python,Ruby,Swift') { console.log('測試經過!'); } else { console.log('測試失敗: ' + s); } })(); var testDiv = $('#test-div>ul'); var li = testDiv.find('li'); var langs = ['Pascal','Lua','Ruby']; li.map(function(){ langs.push($(this).text()); }); console.log(langs); li.remove(); // remove 與 empty 能夠 換着用. langs.sort().forEach(function(element){ testDiv.append('<li><span>'+element+'</span></li>'); }); // 要作便作到極致 var langs = ['Pascal','Lua','Ruby']; var ul = $('#test-div>ul'); langs.forEach(element=>ul.append(`<li><span>${element}</span></li>`)); var li = $('#test-div>ul>li'); li.sort((x,y) => ($(x).text()>$(y).text())?1:-1); ul.empty(); ul.append(li);
練習 對以下的Form表單: <!-- HTML結構 --> <form id="test-form" action="test"> <legend>請選擇想要學習的編程語言:</legend> <fieldset> <p><label class="selectAll"><input type="checkbox"> <span class="selectAll">全選</span><span class="deselectAll">全不選</span></label> <a href="#0" class="invertSelect">反選</a></p> <p><label><input type="checkbox" name="lang" value="javascript"> JavaScript</label></p> <p><label><input type="checkbox" name="lang" value="python"> Python</label></p> <p><label><input type="checkbox" name="lang" value="ruby"> Ruby</label></p> <p><label><input type="checkbox" name="lang" value="haskell"> Haskell</label></p> <p><label><input type="checkbox" name="lang" value="scheme"> Scheme</label></p> <p><button type="submit">Submit</button></p> </fieldset> </form> 綁定合適的事件處理函數,實現如下邏輯: 當用戶勾上「全選」時,自動選中全部語言,並把「全選」變成「全不選」; 當用戶去掉「全不選」時,自動不選中全部語言; 當用戶點擊「反選」時,自動把全部語言狀態反轉(選中的變爲未選,未選的變爲選中); 當用戶把全部語言都手動勾上時,「全選」被自動勾上,並變爲「全不選」; 當用戶手動去掉選中至少一種語言時,「全不選」自動被去掉選中,並變爲「全選」。 'use strict'; var form = $('#test-form'), langs = form.find('[name=lang]'), selectAll = form.find('label.selectAll :checkbox'), selectAllLabel = form.find('label.selectAll span.selectAll'), deselectAllLabel = form.find('label.selectAll span.deselectAll'), invertSelect = form.find('a.invertSelect'); // 重置初始化狀態: form.find('*').show().off(); form.find(':checkbox').prop('checked', false).off(); deselectAllLabel.hide(); // 攔截form提交事件: form.off().submit(function (e) { e.preventDefault(); alert(form.serialize()); }); // TODO:綁定事件 $(function(){ selectAll.change(function(){ if($(this).is(':checked')){ langs.prop('checked',true); selectAllLabel.hide(); deselectAllLabel.show(); }else{ langs.prop('checked',false); deselectAllLabel.hide(); selectAllLabel.show(); } }) invertSelect.click(function(){ langs.map(function(){ $(this).prop('checked',!($(this).is(':checked'))); }); langs.change(); }); //手動選 function is_checkAll(){//全選則返回true var cntBeforeFilter = langs.length; return langs.filter(function(){ return $(this).is(':checked')}).length===cntBeforeFilter ; } langs.change(function(){ if(is_checkAll()){ selectAll.prop('checked',true); selectAllLabel.hide(); deselectAllLabel.show(); }else{ selectAll.prop('checked',false); selectAllLabel.show(); deselectAllLabel.hide(); } }); }); // TODO:綁定事件 //全選 function isAllChecked(){ var flag = true; langs.filter(function(){ if(!$(this).is(':checked')){ flag = false; } }); return flag; } $(function(){ // 全選 selectAll.change(function(){ if(selectAll.is(':checked')){ langs.prop('checked',true); selectAllLabel.hide(); deselectAllLabel.show(); }else{ langs.prop('checked',false); deselectAllLabel.hide(); selectAllLabel.show(); } }); // 手動勾選 langs.change(function(){ if(isAllChecked()){ selectAllLabel.hide(); selectAll.prop('checked',true); deselectAllLabel.show(); }else{ selectAllLabel.show(); selectAll.prop('checked',false); deselectAllLabel.hide(); } }); // 反選 invertSelect.click(function(){ langs.map(function(){ $(this).prop('checked',!($(this).prop('checked'))); }); // 代碼模擬觸發 langs.change(); }); });
$(function(){ $('button').click(function(){ $('div').slideUp(1000); $('div').slideDown(1000); $('div').fadeOut(1000); $('div').fadeIn(1000); $('div').hide(1000); $('div').show(1000); $('div').toggle(1000); $('div').slideToggle(1000); $('div').fadeToggle(1000); }); });
<script id="jquery_183" type="text/javascript" class="library" src="/js/sandbox/jquery/jquery-1.8.3.min.js"></script> <div> <span>jQuery</span> 是一個兼容多瀏覽器的 JavasSript 框架,核心理念是 - write less, do more; <br/> 你可使用此編輯器學習、調試jQuery代碼。 </div> div{ font-size:14px; color:#fff; padding:30px 0; } div span{ color:red; font-size:20px; } div a{ color:#46bb75; } <button> <label> 切換 </label> </button> $(function(){ var div = $('div'); div.slideUp(1000) .delay(1000) .fadeIn(1000) .delay(1000) .animate({ opacity: 0.5, width: '500px', height: '50px' }, 3000,function(){ $(this).css('opacity', '1.0').css('width', '800px').css('height', '20px'); }); // 在3秒鐘內CSS過渡到設定值 }); // 閃爍刪除 'use strict'; function deleteFirstTR() { var tr = $('#test-table>tbody>tr:visible').first(); //tr.fadeOut(2000,()=>$(this).remove()); tr.css('color', '#0F0'); tr.css('background-color', '#800'); for(let i=0;i<=10;i++){ tr.animate({opacity:1.0},function(){ $(this).css('opacity',0.1); }) } setTimeout(() => { tr.remove(); }, 1500); } deleteFirstTR();
$.fn.highlight.default = {backgroundColor: '#00a8e6', color: '#ffffff' }; $.fn.highlight = function(options){ var opts = $.extend({},$.fn.highlight.default,options); return $(this).css('backgroundColor',opts.backgroundColor).css('color',opts.color); } $('#test-highlight2 span').highlight({backgroundColor:'red',color:'green'}); 最終,咱們得出編寫一個jQuery插件的原則: 給$.fn綁定函數,實現插件的代碼邏輯; 插件函數最後要return this;以支持鏈式調用; 插件函數要有默認值,綁定在$.fn.<pluginName>.defaults上; 用戶在調用時可傳入設定值以便覆蓋默認值。
<script id="jquery_183" type="text/javascript" class="library" src="/js/sandbox/jquery/jquery-1.8.3.min.js"></script> <!-- HTML結構 --> <div id="test-external"> <p>如何學習<a href="http://jquery.com">jQuery</a>?</p> <p>首先,你要學習<a href="/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000">JavaScript</a>,並瞭解基本的<a href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a>。</p> </div> div{ font-size:14px; color:#fff; padding:30px 0; } div span{ color:red; font-size:20px; } div a{ color:#46bb75; } $.fn.external = function(){ return this.filter('a').each(function(){ var a = $(this); var url = a.attr('href'); if(url&&(url.indexOf('http://')===0||url.indexOf('https://'))){ a.attr('href','#0') .removeAttr('target') .click(function(){ confirm('您肯定要離開本網站 '+location.href+' 前往'+url+'嗎?'); }); } }); } $('#test-external a').external(); $.fn.external = function(){ var localURL = location.href; return this.filter('a').each(function(){ var a = $(this); var url = a.attr('href'); var urlPrefixRegEx = /^http[s]*?:/// if(url && (urlPrefixRegEx.test(url))){ a.attr('href','#0') .removeAttr('target') .click(function(){ confirm('您肯定離開 '+localURL+' 並訪問 '+url+' 嗎?'); }); }else{ a.click(function(){ if(url.startsWith('//')){ url = url.slice(url.indexOf('/')+2,url.length); }else if(url.startsWith('/')){ url = url.slice(url.indexOf('/')+1,url.length); } url = 'http://'+url confirm('補齊協議以後,您肯定離開 '+localURL+' 並訪問 '+url+' 嗎?'); }); } }); } $('#test-external a').external();
'use strict'; var obj = { name: 'bob', school: 'No.1 middle school', address: 'xueyuan road' }; var upper = _.mapObject(obj, function (value, key) { return "{"+key.toUpperCase()+":"+value.toUpperCase()+"}"; }); console.log(JSON.stringify(upper)); 'use strict'; var obj = { name: 'bob', school: 'No.1 middle school', address: 'xueyuan road' }; // 判斷key和value是否所有是小寫: var r1 = _.every(obj, function (value, key) { return value===value.toLowerCase() && key===key.toLowerCase(); }); var r2 = _.some(obj, function (value, key) { return value===value.toLowerCase() && key===key.toLowerCase(); }); console.log('every key-value are lowercase: ' + r1 + '\nsome key-value are lowercase: ' + r2);
'use strict'; var obj = { name: 'bob', school: 'No.1 middle school', address: 'xueyuan road' }; var upper = _.map(obj, (value, key)=>key.toLowerCase()+':'+value.toUpperCase()); var upperObj = _.mapObject(obj, (value, key)=>value.toUpperCase()) console.log(JSON.stringify(upperObj)); console.log(JSON.stringify(upper)); {"name":"BOB","school":"NO.1 MIDDLE SCHOOL","address":"XUEYUAN ROAD"} ["name:BOB","school:NO.1 MIDDLE SCHOOL","address:XUEYUAN ROAD"] var a1 = [1, 4, 7, -3, -9]; _.every(a1,(x=>x>0)); # false _.some(a1,(x=>x>0)); # true 'use strict'; var obj = { name: 'bob', school: 'No.1 middle school', address: 'xueyuan road' }; // 判斷key和value是否所有是小寫: var r1 = _.every(obj,(value,key)=>key.toLowerCase===key && value.toLowerCase()===value); var r2 = _.some(obj, (value,key)=>key.toLowerCase===key || value.toLowerCase()===value); max / min 這兩個函數直接返回集合中最大和最小的數: 'use strict'; var arr = [3, 5, 7, 9]; _.max(arr); // 9 _.min(arr); // 3 // 空集合會返回-Infinity和Infinity,因此要先判斷集合不爲空: _.max([]) -Infinity _.min([]) Infinity 注意,若是集合是Object,max()和min()只做用於value,忽略掉key: 'use strict'; _.max({ a: 1, b: 2, c: 3 }); // 3 groupBy groupBy()把集合的元素按照key歸類,key由傳入的函數返回: 'use strict'; var scores = [20, 81, 75, 40, 91, 59, 77, 66, 72, 88, 99]; var groups = _.groupBy(scores, function (x) { if (x < 60) { return 'C'; } else if (x < 80) { return 'B'; } else { return 'A'; } }); // 結果: // { // A: [81, 91, 88, 99], // B: [75, 77, 66, 72], // C: [20, 40, 59] // } 可見groupBy()用來分組是很是方便的。 shuffle / sample shuffle()用洗牌算法隨機打亂一個集合: 'use strict'; // 注意每次結果都不同: _.shuffle([1, 2, 3, 4, 5, 6]); // [3, 5, 4, 6, 2, 1] sample()則是隨機選擇一個或多個元素: 'use strict'; // 注意每次結果都不同: // 隨機選1個: _.sample([1, 2, 3, 4, 5, 6]); // 2 // 隨機選3個: _.sample([1, 2, 3, 4, 5, 6], 3); // [6, 1, 4] 'use strict'; var arr = [2, 4, 6, 8]; _.first(arr); // 2 _.last(arr); // 8 flatten()接收一個Array,不管這個Array裏面嵌套了多少個Array,flatten()最後都把它們變成一個一維數組: 'use strict'; _.flatten([1, [2], [3, [[4], [5]]]]); // [1, 2, 3, 4, 5] zip()把兩個或多個數組的全部元素按索引對齊,而後按索引合併成新數組。例如,你有一個Array保存了名字,另外一個Array保存了分數,如今,要把名字和分數給對上,用zip()輕鬆實現: 'use strict'; var names = ['Adam', 'Lisa', 'Bart']; var scores = [85, 92, 59]; _.zip(names, scores); // [['Adam', 85], ['Lisa', 92], ['Bart', 59]] unzip()則是反過來: 'use strict'; var namesAndScores = [['Adam', 85], ['Lisa', 92], ['Bart', 59]]; _.unzip(namesAndScores); // [['Adam', 'Lisa', 'Bart'], [85, 92, 59]] object 有時候你會想,與其用zip(),爲啥不把名字和分數直接對應成Object呢?別急,object()函數就是幹這個的: 'use strict'; var names = ['Adam', 'Lisa', 'Bart']; var scores = [85, 92, 59]; _.object(names, scores); // {Adam: 85, Lisa: 92, Bart: 59} 注意_.object()是一個函數,不是JavaScript的Object對象。 range range()讓你快速生成一個序列,再也不須要用for循環實現了: 'use strict'; // 從0開始小於10: _.range(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 從1開始小於11: _.range(1, 11); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // 從0開始小於30,步長5: _.range(0, 30, 5); // [0, 5, 10, 15, 20, 25] // 從0開始大於-10,步長-1: _.range(0, -10, -1); // [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] 更多完整的函數請參考underscore的文檔:http://underscorejs.org/#arrays 請根據underscore官方文檔,使用_.uniq對數組元素進行不區分大小寫去重: 'use strict'; var arr = ['Apple', 'orange', 'banana', 'ORANGE', 'apple', 'PEAR']; var result = _.uniq(arr,e=>e.toLowerCase()); // 測試 if (result.toString() === ["Apple", "orange", "banana", "PEAR"].toString()) { console.log('測試成功!'); } else { console.log('測試失敗!'); } # 如此煞筆設計,無力吐槽。。。 'use strict'; var s = ' Hello '; var fn = _.bind(s.trim, s); fn(); // 輸出Hello // 偏函數 partial 'use strict'; var pow2N = _.partial(Math.pow, 2); pow2N(3); // 8 pow2N(5); // 32 pow2N(10); // 1024 'use strict'; var cube = _.partial(Math.pow, _, 3); cube(3); // 27 cube(5); // 125 cube(10); // 1000 memoize 若是一個函數調用開銷很大,咱們就可能但願能把結果緩存下來,以便後續調用時直接得到結果。舉個例子,計算階乘就比較耗時: 'use strict'; function factorial(n) { console.log('start calculate ' + n + '!...'); var s = 1, i = n; while (i > 1) { s = s * i; i --; } console.log(n + '! = ' + s); return s; } factorial(10); // 3628800 // 注意控制檯輸出: // start calculate 10!... // 10! = 3628800 用memoize()就能夠自動緩存函數計算的結果: 'use strict'; var factorial = _.memoize(function(n) { console.log('start calculate ' + n + '!...'); var s = 1, i = n; while (i > 1) { s = s * i; i --; } console.log(n + '! = ' + s); return s; }); // 第一次調用: factorial(10); // 3628800 // 注意控制檯輸出: // start calculate 10!... // 10! = 3628800 // 第二次調用: factorial(10); // 3628800 // 控制檯沒有輸出 var factorial = _.memoize(function(x){ if(x>1){ return x*factorial(x-1); }else{ return 1; } }); # 如此煞筆設計,無力吐槽。。。 'use strict'; var s = ' Hello '; var fn = _.bind(s.trim, s); fn(); // 輸出Hello // 偏函數 partial 'use strict'; var pow2N = _.partial(Math.pow, 2); pow2N(3); // 8 pow2N(5); // 32 pow2N(10); // 1024 'use strict'; var cube = _.partial(Math.pow, _, 3); cube(3); // 27 cube(5); // 125 cube(10); // 1000 memoize 若是一個函數調用開銷很大,咱們就可能但願能把結果緩存下來,以便後續調用時直接得到結果。舉個例子,計算階乘就比較耗時: 'use strict'; function factorial(n) { console.log('start calculate ' + n + '!...'); var s = 1, i = n; while (i > 1) { s = s * i; i --; } console.log(n + '! = ' + s); return s; } factorial(10); // 3628800 // 注意控制檯輸出: // start calculate 10!... // 10! = 3628800 用memoize()就能夠自動緩存函數計算的結果: 'use strict'; var factorial = _.memoize(function(n) { console.log('start calculate ' + n + '!...'); var s = 1, i = n; while (i > 1) { s = s * i; i --; } console.log(n + '! = ' + s); return s; }); // 第一次調用: factorial(10); // 3628800 // 注意控制檯輸出: // start calculate 10!... // 10! = 3628800 // 第二次調用: factorial(10); // 3628800 // 控制檯沒有輸出 var factorial = _.memoize(function(x){ if(x>1){ return x*factorial(x-1); }else{ return 1; } }); once 顧名思義,once()保證某個函數執行且僅執行一次。若是你有一個方法叫register(),用戶在頁面上點兩個按鈕的任何一個均可以執行的話,就能夠用once()保證函數僅調用一次,不管用戶點擊多少次: var register = _.once(function () { alert('Register ok!'); }); // 測試效果: register(); register(); register(); delay delay()可讓一個函數延遲執行,效果和setTimeout()是同樣的,可是代碼明顯簡單了: 'use strict'; // 2秒後調用alert(): _.delay(alert, 2000); 若是要延遲調用的函數有參數,把參數也傳進去: 'use strict'; var log = _.bind(console.log, console); _.delay(log, 2000, 'Hello,', 'world!'); // 2秒後打印'Hello, world!':
和Array相似,underscore也提供了大量針對Object的函數。 keys / allKeys keys()能夠很是方便地返回一個object自身全部的key,但不包含從原型鏈繼承下來的: 'use strict'; function Student(name, age) { this.name = name; this.age = age; } var xiaoming = new Student('小明', 20); _.keys(xiaoming); // ['name', 'age'] allKeys()除了object自身的key,還包含從原型鏈繼承下來的: 'use strict'; function Student(name, age) { this.name = name; this.age = age; } Student.prototype.school = 'No.1 Middle School'; var xiaoming = new Student('小明', 20); _.allKeys(xiaoming); // ['name', 'age', 'school'] values 和keys()相似,values()返回object自身但不包含原型鏈繼承的全部值: 'use strict'; var obj = { name: '小明', age: 20 }; _.values(obj); // ['小明', 20] 注意,沒有allValues(),緣由我也不知道。 mapObject mapObject()就是針對object的map版本: 'use strict'; var obj = { a: 1, b: 2, c: 3 }; // 注意傳入的函數簽名,value在前,key在後: _.mapObject(obj, (v, k) => 100 + v); // { a: 101, b: 102, c: 103 } invert invert()把object的每一個key-value來個交換,key變成value,value變成key: 'use strict'; var obj = { Adam: 90, Lisa: 85, Bart: 59 }; _.invert(obj); // { '59': 'Bart', '85': 'Lisa', '90': 'Adam' } extend / extendOwn extend()把多個object的key-value合併到第一個object並返回: 'use strict'; var a = {name: 'Bob', age: 20}; _.extend(a, {age: 15}, {age: 88, city: 'Beijing'}); // {name: 'Bob', age: 88, city: 'Beijing'} // 變量a的內容也改變了: a; // {name: 'Bob', age: 88, city: 'Beijing'} 注意:若是有相同的key,後面的object的value將覆蓋前面的object的value。 extendOwn()和extend()相似,但獲取屬性時忽略從原型鏈繼承下來的屬性。 clone 若是咱們要複製一個object對象,就能夠用clone()方法,它會把原有對象的全部屬性都複製到新的對象中: 'use strict'; var source = { name: '小明', age: 20, skills: ['JavaScript', 'CSS', 'HTML'] }; var copied = _.clone(source); console.log(JSON.stringify(copied, null, ' ')); Run 注意,clone()是「淺複製」。所謂「淺複製」就是說,兩個對象相同的key所引用的value實際上是同一對象: source.skills === copied.skills; // true 也就是說,修改source.skills會影響copied.skills。 isEqual isEqual()對兩個object進行深度比較,若是內容徹底相同,則返回true: 'use strict'; var o1 = { name: 'Bob', skills: { Java: 90, JavaScript: 99 }}; var o2 = { name: 'Bob', skills: { JavaScript: 99, Java: 90 }}; o1 === o2; // false _.isEqual(o1, o2); // true isEqual()其實對Array也能夠比較: 'use strict'; var o1 = ['Bob', { skills: ['Java', 'JavaScript'] }]; var o2 = ['Bob', { skills: ['Java', 'JavaScript'] }]; o1 === o2; // false _.isEqual(o1, o2); // true // 鏈式這個叼。。。 var a1 = [1, 4, 9, 16, 25]; _.chain(a1).map(Math.sqrt).filter(x=>x%2===1).value();
整體上以 let , const 替代 var 爲何?由於 let 是塊級做用域,而 var 是函數做用域。 > let a1 = [1,4,9,16,25] undefined > a2 = [...a1] [ 1, 4, 9, 16, 25 ]
除了將對象自己凍結,對象的屬性也應該凍結。下面是一個將對象完全凍結的函數。 var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };
在多年開發郵箱webmail過程當中,網易郵箱前端團隊積累了很多心得體會,咱們開發了不少基礎js庫,實現了大量前端效果組件,開發了成熟的opoa框架以及api組件,在此向你們作一些分享。今天想先和你們聊聊javascript的編碼規範。 總所周知,javascript是一種語法極其靈活的語言。javascript在設計之初就只是用來爲HTML添加動態效果的。因爲他動態,弱類型 等特性,以及不一樣瀏覽器的兼容性問題,形成了開發成本要比java等語言要高不少。正由於它太靈活,咱們制定了適用於網易郵箱的javascript編碼 規範,儘量多的下降因爲語法靈活形成的問題。如下將具體介紹: 1. 變量命名規範 變量名包括全局變量,局部變量,類變量,函數參數等等,他們都屬於這一類。 基本規範 變量命名都以類型前綴+有意義的單詞組成,單詞首字母都須要大寫。例如:sUserName,nCount。 前綴規範 每一個局部變量都須要有一個類型前綴,按照類型能夠分爲: s:表示字符串。例如:sName,sHtml; n:表示數字。例如:nPage,nTotal; b:表示邏輯。例如:bChecked,bHasLogin; a:表示數組。例如:aList,aGroup; r:表示正則表達式。例如:rDomain,rEmail; f:表示函數。例如:fGetHtml,fInit; o:表示以上未涉及到的其餘對象,例如:oButton,oDate; 例外狀況: 1:做用域不大臨時變量能夠簡寫,好比:str,num,bol,obj,fun,arr。 2:循環變量能夠簡寫,好比:i,j,k等。 爲何須要這樣強制定義變量前綴?正式由於javascript是弱語言形成的。在定義大量變量的時候,咱們須要很明確的知道當前變量是什麼屬性,若是隻經過普通單詞,是很難區分的。 例如: var group = []; group.name = 'myGroup'; //這時候你還能一眼就看出來group是什麼嗎? 又例如: var checked = false; var check = function(){ return true; } if(check){//可能將checked寫成check,因爲不能很快速的發現check是函數,形成邏輯錯誤 //do some thing } 若是咱們寫成: var bChecked = false; var fCheck = function(){ return true; } if(bChecked){ // do some thing } if(fCheck()){ // do other thing } 就清楚不少了。 全局變量以及常量規範 網易郵箱前端是基於「類」的概念來來開發javascript的(稍後會專門介紹),每一個類定義都是在一個閉包函數中,除了在window下有類的定義而外,只容許有兩種變量定義在全局,那就是全局變量和常量。 全局變量使用g做爲前綴,定義在window下。例如gUserName,gLoginTime。 某些做爲不容許修改值的變量認爲是常量,所有字母都大寫。例如:COPYRIGHT,PI。常量能夠存在於函數中,也能夠存在於全局。 看個例子就 很容易明白爲何要這樣定義了: var userName = "dongua"; function checkName(userName){ //存在函數參數userName以及全局變量userName,若是要比較兩個值是否相等,必需寫爲 return window.userName == userName } 若是使用了全局變量的前綴,就十分清晰了。 2. 函數命名規範 統一使用動詞或者動詞[+名詞]形式,例如:fGetVersion(),fSubmitForm(),fInit();涉及返回邏輯值的函數可使用is,has等表示邏輯的詞語代替動詞。 若是有內部函數,使用__f+動詞[+名詞]形式,內部函數必需在函數最後定義。例如: function fGetNumber(nTotal){ if(nTotal<100){ nTotal = 100; } return __fAdd(nTotal); function __fAdd(nNumber){ nNumber++; return nNumber; } } alert(fGetNumber(30));//alert 101 對象方法實現 對象方法命名使用 f+對象類名+動詞[+名詞]形式;例如 fAddressGetEmail 事件響應函數 某事件響應函數命名方式爲觸發事件對象名+事件名或者模塊名+觸發事件對象名+事件名,例如:fDivClick(),fAddressSubmitButtonClic k() 3.其餘注意事項 1:全部命名最好使用英語表示。 2:全部變量名應該明確而必要,儘可能避免沒必要要的容易混淆的縮寫。 3:netease.events.mouse.Handler,而不是 netease.events.mouse.MouseEventHandler。 4:對應的方法應該使用對應的動詞,例如:get/set, add/remove, create/destroy, start/stop, insert/delete, begin/end。 5:應該避免雙重否認意義的變量,例如:bIsNotError, bIsNotFound,不可取。 6:變量應該在最小的範圍內定義,並儘量的保持最少的活動時間。 7:循環變量最好在循環中定義。例如for(var i=0,m=10;i 8:儘可能避免複雜的條件語句,可使用臨時的boolean變量代替。 9:必定要避免在條件中執行語句,例如:if((i=3)>2){},不可取。 10:不要在代碼中重複使用相贊成義的數字,用一個變量代替,好比 nTotal=100; num= total。 網易郵箱頁面在window只容許定義三種變量——1:全局變量;2:常量;3:類。任何業務邏輯都須要經過類方法或者示例方法實現。前兩種變量在以前文章中已經介紹,在此再也不累述,接下來詳細介紹類定義和使用的規範。 定義類是經過一個閉包完成的: (function(){ //第一步:引入存在的類。引入support類 var Support = window.Support; //$是網易郵箱基礎庫「base」的引用稍後會介紹 //第二步:定義類。能夠認爲返回了一個類定義 function(){},並在window下定義一個Image類 var Image = $.createClass("Image"); //能夠認爲是jQuery的extend方法 //第三步:定義類屬性/方法定義 $.Object.extend(Image,{ _language : null, //內部屬性 getSize : fImageGetSize }); //第四步:定義實例屬性/方法定義 $.Object.extend(Image.prototype,{ name : null, url : null, ext : null, width : 0, height : 0, setName : fImageSetName, getName : fImageGetName, init : fImageInit }); //第五步:方法實現 function fImageGetSize(nWidth,nHeight){ return nWidth*nHeight; } function fImageSetName(sName){ var oImage = this; oImage.name = sName; } function fImageGetName(){ var oImage = this; return oImage.name; } function fImageInit(sUrl){ var oImage = this; oImage.url = sUrl; oImage.ext = Support.getExt(sUrl); oImage.width = Support.getWidth(sUrl); oImage.height = Support.getHeight(sUrl); } })(); 咱們能夠看到,這個閉包完成了如下幾件事情: 1.引入這個類須要用到的其餘類。 2.定義這個類。 3.定義類的屬性和方法。 4.定義類的實例屬性和方法。 5.類和實例方法的實現。 在命名上,咱們遵循了一下規則: 1.類名首個字母必需大寫,例如Image,Support等。 2.屬性名須要是有意義的名詞,首字母小寫,例如oImage.width。 3.方法名須要是有意義的動詞[+名詞],首字母小寫,例如Support.getWidth 4.若是不但願被其餘方法調用,須要在屬性或者方法名前面加「_」,例如oImage._language 5.若是不但願被子類調用,須要在屬性或者方法名前加「_」,例如oImage.__fire() 這裏須要特別說明如下幾點: 1.方法的定義不是經過匿名函數來定義,而是集中在類定義的下面來實現。這樣的好處是能在最開始將類的屬性方法定義都羅列出來,便於經過源碼查看到對應屬性和方法。 2.在類/實例方法中,使用局部變量代替this。this不是一個好的玩意兒,一不當心就會被this搞暈。使用局部變量可以儘可能避免這樣的問題,也可以在壓縮混淆的時候效果更好。 3.在實際開發過程當中,每一個類定義都單獨一個js實現。 除了類的定義,閉包不實現 任何其餘邏輯。使用閉包可以將不少變量約束在閉包做用域中,而且可以在壓縮混淆中效果更好,除此以外,使用閉包定義類,在以後將介紹到的動態加載成爲了一件十分容易的事情,稍後會和你們一塊兒分享。