• JavaScript 高级


    在线JS编辑
    JS 编写规范
    阮一峰 ES 6
    阮一峰
    廖雪峰

    操作文件

    <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;  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>
    

    AJAX 存在 跨域问题

    <!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;  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>
    
    

    jsonp 解决跨域

    <!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>
    
    

    Promise 一诺千金

    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,
         '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();
    

    扩展 jq 插件 最终方式 重点关注 $.fn , $.extend()

    $.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上;
    用户在调用时可传入设定值以便覆盖默认值。
    

    针对特定元素扩展 编写 jquery 插件

    <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();
    

    函数式编程库 underscore _

    '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 + '
    some key-value are lowercase: ' + r2);
    
    

    underscore

    '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实现。
    
      除了类的定义,闭包不实现 任何其他逻辑。使用闭包能够将很多变量约束在闭包作用域中,并且能够在压缩混淆中效果更好,除此之外,使用闭包定义类,在之后将介绍到的动态加载成为了一件十分容易的事情,稍后会和大家一起分享。
    
    

    这是为啥

    console.log((!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]])
    sb
    
    console.log(([][[]]+[])[+!![]]+([]+{})[!+[]+!![]])
    nb
    
    try {something} catch (e) { window.location.href ="//stackoverflow.com/search?q=[js]+" +e.message;}
    
    如果有来生,一个人去远行,看不同的风景,感受生命的活力。。。
  • 相关阅读:
    LuoguP4463 [集训队互测2012] calc DP+拉格朗日插值
    一些图的计数
    redis补充1之为什么要用 Redis/为什么要用缓存?
    redis补充4之Redis 和 Memcached 的区别和共同点
    redis补充3之为什么要有分布式缓存?/为什么不直接用本地缓存?
    redis补充2之简单说说有哪些本地缓存解决方案?
    1_java语言概述-注释与API文档等
    1_java语言概述-开发环境搭建
    weblogic 2021.4.20 季度补丁
    mysql source输出记录日志
  • 原文地址:https://www.cnblogs.com/Frank99/p/10243789.html
Copyright © 2020-2023  润新知