代码改变世界

Javascript开发经验V1

2012-05-29 17:03  臭小子1983  阅读(337)  评论(0编辑  收藏  举报

一、简化代码

采用更为简短的写法,不仅可以减少输入的字符数,还可以减少文件大小。大部分采用简单写法的代码,执行效率都有轻微提高。


1.1 简化常用对象定义:

使用 var obj = {}; 代替 var obj = new Object();

使用 var arr = []; 代替 var arr = new Array();


1.2 精简if语句

三元操作符可以有效精简只涉及赋值传值操作的if语句,比如

1 var score = 60, grade;
2 if (score < 60) {
3   grade = “不及格”;
4 }
5 
6 else {
7   grade = “及格”;
8 }

可以精简为:

1 var score = 60;
2 var grade = score < 60 ? “不及格” : “及格”;

三元操作符也支持嵌套,但是嵌套的层次太多会影响程序的可读性,这方面要多加斟酌。

 

1.3 使用JSON

JSON是一种轻量级的数据格式,轻量级首先体现在它的结构定义非常简单。

 1 var obj = {};
 2 obj.p1 = ‘a’;
 3 obj.p2 = ‘b’;
 4 obj.p3 = ‘c’;
 5 
 6 可精简为:
 7 
 8 var obj = {
 9 p1 : ‘a’,
10 p2 : ‘b’,
11 p3 : ‘c’
12 };

 

二、使用高效率的代码

网上流传的效率优化文章非常多,一些比较专业的Javascript书籍也谈到了不少,因此,这里就只列出一些很少谈到的。

2.1 精简循环体

循环的效率很大程度上是由循环体决定的,与之相比,用for还是while的差别就太小了。考虑如下的代码,其功能是为某一批元素添加事件:

 1 function addEvent(elems, eventName, handler) {
 2     for (var i = 0, len = elems.length; i < len; i++) {
 3         if (window.attachEvent) {
 4             elems[i].attachEvent(”on” + eventName, handler);
 5         }
 6         else if (window.addEventListener) {
 7             elems[i].addEventListener(eventName, handler, false);
 8         }
 9     }
10 }        

循环每执行一次,都会判断window对象的attachEvent或addEventListener是否存在,其实这个仅判断一次也就够了;此外,“”on” + eventName”的字符串拼接也会重复执行。优化如下:

 1 function addEvent(elems, eventName, handler) {
 2     var i = -1, len = elems.length;
 3     if (window.attachEvent) {
 4         eventName = “on” + eventName;
 5         while (++i < len) {
 6             elems[i].attachEvent(eventName, handler);
 7         }
 8     } 
 9     else if (window.addEventListener) {
10         while (++i < len) {
11             elems[i].addEventListener(eventName, handler, false);
12         }
13     }
14 }    

 

2.2 尽量使用原生的函数而不是自定义函数

当你对Javascript的内置类型变量执行某项操作时,你应该先查查这项操作是否有原生的方法。

要生成一个数组的副本,你会怎么做呢?遍历数组元素然后逐个赋值到另一个数组,这似乎是唯一的方法。其实,原生的Array.prototype.slice就可以达到复制的目的。这个方法可以从某个数组返回选定的元素,且不影响原来的数组。如果参数留空,返回的就是全部元素。

Array.prototype.slice还可以对某些不是数组而又能通过数字索引访问的类型进行操作,比如arguments:

1 function test() {
2     alert(Array.prototype.slice.call(arguments));
3 }
4 test(1, 2, 3); // output “1,2,3″

在Firefox下,它甚至可以对HtmlCollection进行操作。可惜在IE下不行。


另一个例子是数组排序,一般情况下,我们不需要另外写排序算法,用原生的Array.prototype.sort就够了。sort方法只有一个参数,该参数是一个函数,决定两个相比较的元素谁在前谁在后,默认是按照字符顺序排序,比如11会排在2之前。要按数字大小排序,可以这样写:

1 var arr = [11, 2, 0, 12, 33];
2 arr.sort(
3     function(a, b) {
4           return a - b;
5     }
6 );

也可以按照对象的某个属性进行排序:

 1 var arr = [
 2     { id : 11 },
 3     { id : 0 },
 4     { id : 22 }
 5 ];
 6 arr.sort(
 7         function(a, b) {
 8             return a.id - b.id;
 9         }
10 );

 

2.3 数组去重复

 1 function unique(arr) {
 2     var result = [], isRepeated;
 3     for (var i = 0, len = arr.length; i < len; i++) {
 4         isRepeated = false;
 5         for (var j = 0, len = result.length; j < len; j++) {
 6             if (arr[i] == result[j]) {
 7                 isRepeated = true;
 8                 break;
 9             }
10         }
11         if (!isRepeated) {
12             result.push(arr[i]);
13         }
14     }
15     return result;
16 }

Array类型并没有提供去重复的方法,如果要把数组的重复元素干掉,那得自己想办法:

  总体思路是把数组元素逐个搬运到另一个数组,搬运的过程中检查这个元素是否有重复,如果有就直接丢掉。从嵌套循环就可以看出,这种方法效率极低。我们可以用一个hashtable的结构记录已有的元素,这样就可以避免内层循环。恰好,在Javascript中实现hashtable是极为简单的,改进如下:

 1 function unique(arr) {
 2     var result = [], hash = {};
 3     for (var i = 0, elem; (elem = arr[i]) != null; i++) {
 4         if (!hash[elem]) {
 5             result.push(elem);
 6             hash[elem] = true;
 7         }
 8     }
 9     return result;
10 }

 

三、使代码更易读、更好维护

无论是在开发中还是开发后,保持代码清晰易读可以更快更准确地修改代码。


3.1 连接HTML字符串
相信做前端开发的朋友都受过这个折磨:连接HTML的时候被可恶的单引号、双引号搞得头昏脑胀。比如:

 1 element.innerHTML = ‘<a href=”‘ + url + ‘” onclick=”alert(\” + msg + ‘\’);”>’ + text + ‘</a>’;
 2 这里介绍一个字符串格式化函数:
 3 String.format = function(str) {
 4     var args = arguments, re = new RegExp(”%([1-" + args.length + "])”, “g”);
 5     return String(str).replace(
 6             re,
 7             function($1, $2) {
 8                 return args[$2];
 9             }
10     );
11 };

调用方法很简单:

element.innerHTML = String.format(’<a href=”%1″ onclick=”alert(\’%2\’);”>%3</a>’, url, msg, text);

意思就是用第n个参数把%n替换掉。这样一来清晰多了吧。

 

3.2 为您的程序打造一个Config配置对象

编写Java或者C#程序的时候,我们一般会从XML读取程序的配置信息。在Javascript里面,用XML做配置信息不大划算,一方面要多请求一个XML文件或者把XML字符串转换为XML对象,另一方面XML对象的方法比较复杂冗长。轻量级的JSON是最好的选择。

程序中的常量应该放到Config配置对象中,比如Ajax请求的Url、某个操作的提示等,例如:

 1 var Config = {
 2     ajaxUrl : “test.jsp”,
 3 successTips : “请求完成”
 4 };
 5 如果Config的数量较多,可以根据配置类型多嵌套一层,比如:
 6 var Config = {
 7     url : {
 8         src1 : “test1.jsp”,
 9 src2 : “test2.jsp”,
10 .
11 .
12 },
13 tips : {
14     src1Suc : “请求1完成”,
15     src2Suc: “请求2完成”,
16 .
17 .
18 }
19 };
20 Config应放置于程序的最前面,方便查看和修改。

 

 

1、json字符串转成json对象:eval("(' + data + ')")

2、json对象转成Json字符串:eval('"{\'football_live_event\':\'" + sPlayerId +"\'}"')

3、查看对象中是否有指定的属性: var obj = {pro:"111"}   if("pro" in obj){}    // true

 

四、IE下操作iframe出现SCRIPT5: 拒绝访问错误提示

  在同一域下,因在父页面上设置了document.domain,而导致无法正常和Iframe(也是同域下)进行通信,IE下抛出的错误是:SCRIPT5: 拒绝访问,导致无法操作iframe中的内容。

解决方法:

1、不要设置父页面的domain(如果可以)

2、让双方均设置同样的domain

 

参考:http://ningxiaotao.iteye.com/blog/579321

 

 

五、iframe的contentWindow属性 指定的frame或者iframe所在的window对象

 1 function f(){ 
 2    var doc; 
 3 
 4    if (document.all){//IE 
 5        doc = document.frames["MyIFrame"].document; 
 6    }else{//Firefox    
 7        doc = document.getElementById("MyIFrame").contentDocument; 
 8    } 
 9 
10    doc.getElementById("s").style.color="blue"; 
11 } 

 

六、document.domain 跨域问题

1、domain只能在根域、子域之间来设置,如,www.test.com和live.test.com之间不同的域

1 var sUrl = lcoation.href;       // 比如是 a.test.com
2 if(sUrl.indexOf("b.test.com") != -1){    // 如果当前在b.test.com域中
3      document.domain = "a.test.com";    // 将域设置为a.test.com
4 };

用来得到当前网页的域名。
比如在地址栏里输入:

  javascript:alert(document.domain); //www.315ta.com

我们也可以给document.domain属性赋值,不过是有限制的,你只能赋成当前的域名或者基础域名。


比如:
javascript:alert(document.domain = "315ta.com"); //315ta.com
javascript:alert(document.domain = "www.315ta.com");//www.315ta.com

上面的赋值都是成功的,因为www.315ta.com是当前的域名,而315ta.com是基础域名。

但是下面的赋值就会出来"参数无效"的错误:
javascript:alert(document.domain = "cctv.net"); //参数无效
javascript:alert(document.domain = "ttt.315ta.com"); //参数无效

因为cctv.net与ttt.315tas.com不是当前的域名也不是当前域名的基础域名,所以会有错误出现。
这是为了防止有人恶意修改document.domain来实现跨域偷取数据。


利用document.domain实现跨域:
前提条件:这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域

Javascript出于对安全性的考虑,而禁止两个或者多个不同域的页面进行互相操作。
相同域的页面在相互操作的时候不会有任何问题。

比如在:aaa.com的一个网页(a.html)里面利用iframe引入了一个bbb.com里的一个网页(b.html)。
这时在a.html里面可以看到b.html里的内容,但是却不能利用javascript来操作它。因为这两个页面属于不同的域,在操作之前,js会检测两个页面的域是否相等,如果相等,就允许其操作,如果不相等,就会拒绝操作。
这里不可能把a.html与b.html利用JS改成同一个域的。因为它们的基础域名不相等。(强制用JS将它们改成相等的域的话会报跟上面一样的"参数无效错误。")

所以如果在a.html里引入aaa.com里的另一个网页,是不会有这个问题的,因为域相等。

有另一种情况,两个子域名:
aaa.xxx.com
bbb.xxx.com

aaa里的一个网页(a.html)引入了bbb 里的一个网页(b.html),
这时a.html里同样是不能操作b.html里面的内容的。
因为document.domain不一样,一个是aaa.xxx.com,另一个是bbb.xxx.com。

这时我们就可以通过Javascript,将两个页面的domain改成一样的,
需要在a.html里与b.html里都加入:

document.domain = "xxx.com";
这样这两个页面就可以互相操作了。也就是实现了同一基础域名之间的"跨域"。

 

七、去除数组内容

  使用splice可以对级数进行添加、删除、替换,

扩展Array方法,

1 Array.prototype.remove = function(val) {
2     var index = this.indexOf(val);
3     if (index > -1) {
4         this.splice(index, 1);
5     }
6 };
7 
8 var arr = [1,34,5,6,7,8];
9 arr.remove(5);        // 返回[1,34,6,7,8]

 

八、转成整形parseInt问题

在字符串转成数字时,如果以“0”开头的字符不定义以几进制来转换在ie7、8下有问题

1 var str1 = "125";
2 var str2 = "012";
3 console.log(parseInt(str1));        // 返回125
4 console.log(parseInt(str2));        // 返回10

 

九、数组对象和数组字符串排序

1、以 数组对象 值的大小来排序

 1 function sortNum(){
 2     var arr = [{"6":100}, {"5":22}, {"3":560}, {"1":17}, {"9":67}, {"8":1}, {"4":13}, {"2":46}, {"7":20}, {"10":500}]
 3     // var arr = [["6",100], ["5",22], ["3",560], ["1",17], ["9",67], ["8",1], ["4",13], ["2",46], ["7",20], ["10",500]];
 4 
 5     arr.sort(actionSort);       // 排序
 6     
 7     for(var i=0; i<arr.length; i++){
 8         for(var key in arr[i]){
 9             console.log("a", key, arr[i][key]);
10         }
11     } 
13 }
14 
15 function actionSort(v1, v2){
16     var sVal1 = 0;
17     var sVal2 = 0;
18     $.each(v1, function(i,val){ sVal1 = parseInt(val) });
19     $.each(v2, function(i,val){ sVal2 = parseInt(val) });
20     return sVal1 - sVal2;
21 }
22 
23 sortNum();

 

2、以数组字符串来进行排序

 1 function smallSort(v1, v2){
 2     // 13:4,02:1,11:3
 3     var nVal1 = 0;
 4     var nVal2 = 0;
 5 
 6     nVal1 = v1.split(":")[0];
 7     nVal2 = v2.split(":")[0];
 8 
 9     return nVal1 - nVal2;
10 }
11 
12 var str = "13:4,02:1,11:3";    // 以13、02、11进行排序
13 var sArr = str.split(",");
14 console.log(sArr);
15 sArr.sort(smallSort);
16 console.log(sArr);

 

3、对数组对象的一个属性值进行排序

1 <script>
2     var data = [{id:1, year:10}, {id:2, year:250}, {id:2, year:3850}, {id:2, year:30}, {id:2, year:2}];
3 
4     data.sort(function(d1, d2){
5         return d1.year > d2.year;
6     })
7 
8     console.log(data);
9 </script>

 

十、日期转换

  如果new Date获取什么1970年到现在的毫秒数需要.getTime()

  也可以简写成 var setDate = new Date();  console.log(+setDate);

 

  1、js毫秒时间转换成日期时间

  var oldTime = (new Date("2012/12/25 20:11:11")).getTime(); //得到毫秒数

  // 不是上面格式的时间需要转换

  // starttime ='2012-12-25 20:17:24';
  starttime = starttime.replace(new RegExp("-","gm"),"/");
  var starttimeHaoMiao = (new Date(starttime)).getTime(); //得到毫秒数

  

  2、毫秒数转化为时间

  var oldTime = (new Date("2012/12/25 20:11:11")).getTime(); //得到毫秒数
  var newTime = new Date(oldTime); //就得到普通的时间了

 

十一、js跳转锚点

  <div name="traderList"></div>

  window.location.hash = "traderList";

 

十二、js绘制圆

 1 <script>
 2 
 3     function getId(id){
 4         return document.getElementById(id);
 5     }
 6 
 7     var oCanvas = getId("canvas");
 8     var oContext = oCanvas.getContext("2d");
 9 
10     function runRect(x, y){
11         oContext.fillStyle = "#ccc";
12         oContext.fillRect(x, y, 2, 2);
13     }
14 
15     var x = 0; y = 0;
16     var timer = setInterval(function(){
17 
18         var  nX = Math.round(200 * Math.sin((x++) * Math.PI / 180))+200;        // 公式:半径 * Math.sin(步数) * Math.PI / 180
19         var  nY = Math.round(200 * Math.cos((y++) * Math.PI / 180))+200;
20 
21         console.log(nX, nY);
22         runRect(nX, nY);
23 
24     }, 10);
25 
26 
27     var oBtn = getId("but");
28     oBtn.onclick = function(){
29         clearInterval(timer);
30     }
31 
32 </script>

 

十三、控制子页面(Iframe)文档

一、父页面控制iframe子页面

  1、contentWindow属性来获取iframe的window对象,都兼容,但在chrome下需要有服务器环境才可以,chrome安全沙箱的原因

  2、contentDocument属性来获取iframe的document对象,不兼容ie6、7

 1 <iframe id="ifm" src="iframe.html"></iframe>
 2 <input type="button" id="but" value="改变iframe">
 3 
 4 <script>
 5     function getId(id){
 6         return document.getElementById(id);
 7     }
 8 
 9     var oIfm = getId("ifm");
10     var oBut = getId("but");
11 
12     function editIframe(){
13         // 如果我们要操作一个iframe的DOM元素使用contentWindow来引用
14         // 也可以作用于一个新窗口的操作
15         var ifrdoc = oIfm.contentWindow;
16         // var s = fixingHTB.innerHTML;     // 进入可编辑模式前存好
17         ifrdoc.document.designMode = 'On';  // 文档进入可编辑模式
18         ifrdoc.document.contentEditable = true;// 文档进入可编辑模式
19         // ifrdoc.open();
20         
21         // 改变Iframe背景颜色
22         ifrdoc.document.body.style.background = "red";
23 
24         // ifrdoc.close();
25     }
26 
27     oBut.onclick = function(){
28         editIframe()
29     }
30 </script>

 

二、iframe控制父页面

  1、window.parent来获取父页面的window对象  

 1 <div id="box">
 2     <iframe src="index.html" id="ifr"></iframe>
 3     <input type="button" id="btn" value="改变">
 4 </div>
 5 
 6 <script>
 7 var oIfr = document.getElementById("ifr");
 8 var oBtn = document.getElementById("btn");
 9 
10 oBtn.onclick = function(){
11     var docIfr = oIfr.contentWindow.document.getElementById("listNum");
12     docIfr.style.backgroundColor = "#ff0000";
13 }
14 </script>
15 
16 // iframe页面index.html
17 var oSelectBtn = document.getElementById("selectBtn");
18 selectBtn.onclick = function(){
19     var parentIfr = window.parent.document.getElementById("ifr");
20     parentIfr.style.width = "500px";
21     parentIfr.style.height = "300px";
22 }

 

   2、window.top为找到最顶层,例如:index页面嵌套一个iframe为demo1.html,demo1中又嵌套一个iframe为demo2,这样如果在demo2中使用window.parent,其实找到的demo1这个页面,如果使用window.top就可以直接找到index.html这个页面

 

三、iframe的onload事件

  当iframe加载完后也就是onload事件,但在IE要执行onload必须得使用事件绑定才可以兼容

 1 window.onload = function(){
 2     var oIfr = document.getElementById("ifr");
 3     oIfr.src = "index.html";
 4 
 5     addEvent(oIfr, "load", function(){
 6         alert("aa");
 7     })
 8 
 9     function addEvent(obj, type, fn){
10         if(obj.addEventListener){
11             obj.addEventListener(type, fn, true);
12         }
13         else{
14             obj.attachEvent("on"+type, fn);
15         }
16     }
17 }

 

十四、onMouseEnter、onMouseLeave与onMouseOver、onMouseOut的区别

  在IE下解决问题很简单,用onMouseEnter、onMouseLeave来代 替onMouseOver、onMouseOut就行了,他们的作用基本相同,前者不会发生冒泡。

区别:

不论鼠标指针离开被选元素还是任何子元素,都会触发 mouseout 事件。

只有在鼠标指针离开被选元素时,才会触发 mouseleave 事件。

 1 <style>
 2     .select { width: 300px; position: relative;}
 3     .sel_nav { width: 298px; height: 28px; border: 1px #ccc solid; text-align: center; line-height: 28px; cursor: pointer; }
 4     .sel_box { width: 298px; border: 1px #ccc solid; position: absolute; top: 29px; left: 0; display: none; }
 5     .sel_box li { width: 298px; height: 30px; line-height: 30px; border-bottom: 1px #ccc solid; background:#90BEDB; text-align: center;}
 6     .sel_box li.active { background: #414244;}
 7 </style>
 8 <div class="select" id="sel">
 9     <div class="sel_nav">
10         选择内容
11     </div>
12     <div class="sel_box">
13         <ul>
14             <li>1111</li>
15             <li>2222</li>
16             <li>3333</li>
17             <li>4444</li>
18             <li>5555</li>
19         </ul>
20     </div>
21 </div>
22 
23 <script src="jquery-1.7.1.min.js"></script>
24 <script>
25     function Select(id){
26         this.sel = $(id);
27         this.nav = $(".sel_nav", this.sel);
28         this.box = $(".sel_box", this.sel);
29 
30         this.init();
31     }
32 
33     Select.prototype.init = function(){
34         var o = this;
35         var isOpen = false;
36 
37         o.sel.mouseleave(function(){        // 如果使用mouseout比如鼠标移到文字“选择内容”时也会触发
38             o.box.slideUp(300);  isOpen = true;
39         })
40 
41         o.nav.click(function(){
42             if(isOpen){
43                 o.box.slideUp(300);
44                 isOpen = false;
45             }
46             else{
47                 o.box.slideDown(300);
48                 isOpen = true;
49             }
50         })
51 
52         $("li", o.sel).mouseover(function(){
53             $(this).addClass(o.active).siblings().removeClass(o.active);
54         })
55 
56         $("li", o.sel).click(function(){
57             var val = $(this).html();
58             o.nav.html(val);
59             o.box.slideUp(300);
60             isOpen = false;
61         })
62     }
63 
64     var sel = new Select("#sel")
65 </script>

 

十五、jQuery的音频插件jquery.jmp3,消耗CPU的问题

  原理,调用一次,在html标签插入一个<object>标签来创建一个flash的播放器,在读出声音

 

  项目中使用jquery音频插件来读取音,当循环多次时会极度消耗cpu解决方法:

  1、如果是循环的创建只输出一次

  2、每次创建前将元素内清空,这样最后播放的时候只播放一次,而不是循环的多次.

 

 

十六、JavaScript中双叹号“!!”作用

!!一般用来将后面的表达式强制转换为布尔类型的数据(boolean),也就是只能是true或者false;

var a;
var b=!!a;

a默认是undefined。!a是true,!!a则是false,所以b的值是false,而不再是undefined,也非其它值,主要是为后续判断提供便利。

 

十七、在一个元素上同时绑定click和dbclick事件的处理方法

双击(dblclick)的流程是:mousedown,mouseout,click,mousedown,mouseout,click,dblclick;

在双击事件(dblclick),触发的两次单击事件(click)中,第一次的单击事件(click)会被屏蔽掉,但第二次不会。也就是说双击事件(dblclick)会返回一次单击事件(click)结果和一次双击事件(dblclick) 结果。而不是一次双击事件(dblclick)结果和两次单击事件结果(click)。 

所以可以使用计时的方式去除掉一个多余的单击事件就行了

 1 <script>
 2 
 3     var TimeFn = null;
 4     $("#div1").click(function () {
 5         clearTimeout(TimeFn);
 6         TimeFn = setTimeout(function(){
 7             console.log("a");
 8         },200);
 9     });
10 
11     $('#div1').dblclick(function () {
12         clearTimeout(TimeFn);
13         console.log("b");
14     })
15 
16 </script>