Can not step twice into the same river

标签 js 下的文章

01月 26

几个超实用的JavaScript技巧及最佳实践

1.第一次给变量赋值时,别忘记var关键字
如果初次赋值给未声明的变量,该变量会被自动创建为全局变量,在JS开发中,应该避免使用全局变量,这是大家容易忽略的错误。

2.使用===而非==
并且永远不要使用=或!=。

[10] === 10    // is false  
[10]  == 10    // is true  
'10' == 10     // is true  
'10' === 10    // is false  
 []   == 0     // is true  
 [] ===  0     // is false  
 '' == false   // is true but true == "a" is false  
 '' ===   false // is false  

3.使用分号来作为行终止字符
在行终止的地方使用分号是一个很好的习惯,即使开发人员忘记加分号,编译器也不会有任何提示,因为在大多数情况下,JavaScript解析器会自动加上。

  function Person(firstName, lastName){  
        this.firstName =  firstName;  
        this.lastName = lastName;          
    }    
      
    var Saad = new Person("Saad", "Mousliki"); 

5.小心使用typeof、instanceof和constructor

var arr = ["a", "b", "c"];  
typeof arr;   // return "object"   
arr  instanceof Array // true  
arr.constructor();  //[]
 

6.创建一个自调用(Self-calling)函数
通常被称为自调用匿名函数或即刻调用函数表达式(LLFE)。当函数被创建的时候就会自动执行,如下:

 (function(){  
        // some private code that will be executed automatically  
    })();    
    (function(a,b){  
        var result = a+b;  
        return result;  
    })(10,20)  

7.给数组创建一个随机项

var items = [12, 548 , 'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' , 2145 , 119];  
var  randomItem = items[Math.floor(Math.random() * items.length)];  

8.在特定范围里获得一个随机数
下面这段代码非常通用,当你需要生成一个测试的数据时,比如在最高工资和最低工资之间获取一个随机数的话。

var x = Math.floor(Math.random() * (max - min + 1)) + min;

9.在数字0和最大数之间生成一组随机数

 var numbersArray = [] , max = 100;  
for( var i=1; numbersArray.push(i++) < max;);  // numbers = [0,1,2,3 ... 100]  

10.生成一组随机的字母数字字符

 function generateRandomAlphaNum(len) {  
        var rdmstring = "";  
        for( ; rdmString.length < len; rdmString  += Math.random().toString(36).substr(2));  
        return  rdmString.substr(0, len);  
      
    }  

11.打乱数字数组

   var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];  
    numbers = numbers.sort(function(){ return Math.random() - 0.5});  
    /* the array numbers will be equal for example to [120, 5, 228, -215, 400, 458, -85411, 122205]  */  

12.字符串trim函数
trim函数可以删除字符串两端的空白字符,可以用在Java、C#、PHP等多门语言里。

String.prototype.trim = function(){return this.replace(/^\s+|\s+$/g, "");};

13.数组追加

var array1 = [12 , "foo" , {name "Joe"} , -2458];  
var array2 = ["Doe" , 555 , 100];  
Array.prototype.push.apply(array1, array2);  
/* array1 will be equal to  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */  

14.将参数对象转换为数组

var argArray = Array.prototype.slice.call(arguments); 

15.验证一个指定参数是否为数字

function isNumber(n){  
    return !isNaN(parseFloat(n)) && isFinite(n);  
}  
 

16.验证一个给定的参数为数组

function isArray(obj){  
        return Object.prototype.toString.call(obj) === '[object Array]' ;  
    }

注意,如果toString()方法被重写了,你将不会得到预期结果。
或者你可以这样写:

  Array.isArray(obj); // its a new Array method  

同样,如果你使用多个frames,你可以使用instancesof,如果内容太多,结果同样会出错。

var myFrame = document.createElement('iframe');  
    document.body.appendChild(myFrame);  
      
    var myArray = window.frames[window.frames.length-1].Array;  
    var arr = new myArray(a,b,10); // [a,b,10]    
      
    // instanceof will not work correctly, myArray loses his constructor   
    // constructor is not shared between frames  
    arr instanceof Array; // false  

17.从数字数组中获得最大值和最小值

var  numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];   
var maxInNumbers = Math.max.apply(Math, numbers);   
var minInNumbers = Math.min.apply(Math, numbers);  

18.清空数组

var myArray = [12 , 222 , 1000 ];    
myArray.length = 0; // myArray will be equal to [].  

19.不要用delete从数组中删除项目
开发者可以使用split来替代delete去删除数组中的项目。好的方式是使用delete去替换数组中undefined的数组项目,而不是使用delete去删除数组中项目。

 var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ];   
items.length; // return 11   
delete items[3]; // return true   
items.length; // return 11   
/* items will be equal to [12, 548, "a", undefined × 1, 5478, "foo", 8852, undefined × 1, "Doe", 2154,       119]   */

应该如下使用

var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ];   
items.length; // return 11   
items.splice(3,1) ;   
items.length; // return 10   
/* items will be equal to [12, 548, "a", 5478, "foo", 8852, undefined × 1, "Doe", 2154,       119]   */ 

delete方法应该用来删除一个对象属性。

20.使用length属性截短数组
如上文提到的清空数组,开发者还可以使用length属性截短数组。

var myArray = [12 , 222 , 1000 , 124 , 98 , 10 ];    
myArray.length = 4; // myArray will be equal to [12 , 222 , 1000 , 124].

如果你所定义的数组长度值过高,那么数组的长度将会改变,并且会填充一些未定义的值到数组里,数组的length属性不是只读的。

myArray.length = 10; // the new array length is 10   
myArray[myArray.length - 1] ; // undefined  

21. 使用逻辑AND/OR来处理条件语句

var foo = 10;  
foo == 10 && doSomething(); // is the same thing as if (foo == 10) doSomething(); 
foo == 5 || doSomething(); // is the same thing as if (foo != 5) doSomething();

逻辑AND也可以用来设置含糊参数缺省的值

Function doSomething(arg1){ 
    Arg1 = arg1 || 10; // arg1 will have 10 as a default value if it’s not already set
}

22. 使用map()函数方法来循环数组里的项目

var squares = [1,2,3,4].map(function (val) {  
    return val * val;  
}); 
// squares will be equal to [1, 4, 9, 16] 

23. 按小数点后N位来四舍五入

var num =2.443242342;
num = num.toFixed(4);  // num will be equal to 2.4432

24. 浮点问题

0.1 + 0.2 === 0.3 // is false 
9007199254740992 + 1 // is equal to 9007199254740992  
9007199254740992 + 2 // is equal to 9007199254740994

为什么? 0.1 + 0.2 等于 0.30000000000000004 。你应该知道所有的javascript数字在64位2进制内部都是使用浮点表示

这个来自于IEEE 754标准。更多信息介绍,请参考:相关博客

你可以使用上面介绍的toFixed()和toPrecision()来解决这个问题

25. 使用for-in循环来检查对象的指定属性

下面的代码片段非常实用,可以避免从对象的prototype来循环遍历对象的属性:

for (var name in object) {  
    if (object.hasOwnProperty(name)) { 
        // do something with name                    
    }  
}

26. 逗号操作符

var a = 0; 
var b = ( a++, 99 ); 
console.log(a);  // a will be equal to 1 
console.log(b);  // b is equal to 99

27. 缓存需要计算或者DOM查询的变量

使用jQuery的选择器,我们一定要记住缓存DOM元素,这样会提高执行效率:

var navright = document.querySelector('#right'); 
var navleft = document.querySelector('#left'); 
var navup = document.querySelector('#up'); 
var navdown = document.querySelector('#down');

28. 在传入isFinite()之前验证参数

isFinite(0/0) ; // false 
isFinite("foo"); // false 
isFinite("10"); // true 
isFinite(10);   // true 
isFinite(undifined);  // false 
isFinite();   // false 
isFinite(null);  // true  !!! 

29. 避免数组中index为负值

var numbersArray = [1,2,3,4,5]; 
var from = numbersArray.indexOf("foo") ;  // from is equal to -1 
numbersArray.splice(from,2);    // will return [5]

这里需要注意indexof的参数 不能为负值,但是splice可以

30. 序列化和反序列化(用来处理JSON)

var person = {name :'Saad', age : 26, department : {ID : 15, name : "R&D"} }; 
var stringFromPerson = JSON.stringify(person); 
/* stringFromPerson is equal to "{"name":"Saad","age":26,"department":{"ID":15,"name":"R&D"}}"   */ 
var personFromString = JSON.parse(stringFromPerson);  
/* personFromString is equal to person object  */

31. 避免使用eval或者Function构建器

使用eval或者function构建器是一件非常消耗资源的操作,因为每次调用script引擎都必须将源代码转换为可执行的代码

var func1 = new Function(functionCode); //避免使用!!
var func2 = eval(functionCode);//避免使用!!

32. 避免使用with()

使用with()可以用来插入一个变量到全局。然而,如果另外一个变量拥有同样的名字,将会导致非常混乱并且会覆盖数值

33. 避免在数组中使用for-in循环

不推荐使用:

var sum = 0;  
for (var i in arrayNumbers) {  
    sum += arrayNumbers[i];  
}

如下代码将会更好:

var sum = 0;  
for (var i = 0, len = arrayNumbers.length; i < len; i++) {  
    sum += arrayNumbers[i];  
}

作为额外的好处,i和len的实例化都执行一次,因为都是循环中的第一个语句,但是比下面执行速度更快:

for (var i = 0; i < arrayNumbers.length; i++)

为什么? arrayNumbers的长度在每次循环都计算一次

34. 传递函数,而非字符串到setTimeout()和setInterval()中

如果你传递一个字符串到setTimeout和setInterval中,处理方式和eval将会类似,速度会很慢,不要使用如下:

setInterval('doSomethingPeriodically()', 1000);  
setTimeOut('doSomethingAfterFiveSeconds()', 5000);
推荐使用如下

setInterval(doSomethingPeriodically, 1000);  
setTimeOut(doSomethingAfterFiveSeconds, 5000);

35. 使用switch/case语句而非一系列的if/else

如果多余两个条件,使用switch/case将会更快,而且语法更优雅(代码组织的更好)。对于多余10个条件的避免使用。

36. 使用switch/case语句处理数值区域

使用如下小技巧处理数值区域:

function getCategory(age) {  
    var category = "";  
    switch (true) {  
        case isNaN(age):  
            category = "not an age";  
            break;  
        case (age >= 50):  
            category = "Old";  
            break;  
        case (age <= 20):  
            category = "Baby";  
            break;  
        default:  
            category = "Young";  
            break;  
    };  
    return category;  
}  
getCategory(5);  // will return "Baby"

37. 创建一个prototype是指定对象的对象

使用如下代码可以生成一个prototype是指定对象的对象:

function clone(object) {  
    function OneShotConstructor(){}; 
    OneShotConstructor.prototype= object;  
    return new OneShotConstructor(); 
} 
clone(Array).prototype ;  // []

**39. 一个HTMLescaper方法**

function escapeHTML(text) {  
    var replacements= {"<": "&lt;", ">": "&gt;","&": "&amp;", "\"": "&quot;"};                      
    return text.replace(/[<>&"]/g, function(character) {  
        return replacements[character];  
    }); 
}

编译:当然,前台处理并不安全,后台处理更彻底

40. 在循环中避免使用try-catch-finally

不要使用如下代码:

var object = ['foo', 'bar'], i;  
for (i = 0, len = object.length; i <len; i++) {  
    try {  
        // do something that throws an exception 
    }  
    catch (e) {   
        // handle exception  
    } 
}

使用这段代码:

var object = ['foo', 'bar'], i;  
try { 
    for (i = 0, len = object.length; i <len; i++) {  
        // do something that throws an exception 
    } 
} 
catch (e) {   
    // handle exception  
} 

40. 设置XMLHttpRequests的timeout

如果一个XHR花费了太多时间,你可以在XHR调用中使用setTimeout来退出连接:

var xhr = new XMLHttpRequest (); 
xhr.onreadystatechange = function () {  
    if (this.readyState == 4) {  
        clearTimeout(timeout);  
        // do something with response data 
    }  
}  
var timeout = setTimeout( function () {  
    xhr.abort(); // call error callback  
}, 60*1000 /* timeout after a minute */ ); 
xhr.open('GET', url, true);  
 
xhr.send();

额外的好处,你可以完全避免同步AJAX调用

41. 处理WebSocket timeout

一般来说,当一个websocket连接建立后,服务器可以在30秒无响应的情况下time out你的连接。防火墙也可以做到。

为了处理timeout问题,你可以定时发送一个空的消息到服务器。为了实现,你可以添加两个方法到你的代码中:

一个保证连接的存在,另外一个取消连接。使用这个技巧,你可以处理timeout问题:

var timerID = 0; 
function keepAlive() { 
    var timeout = 15000;  
    if (webSocket.readyState == webSocket.OPEN) {  
        webSocket.send('');  
    }  
    timerId = setTimeout(keepAlive, timeout);  
}  
function cancelKeepAlive() {  
    if (timerId) {  
        cancelTimeout(timerId);  
    }  
}

keepAlive函数可以添加到webSocket的onOpen函数的最后。cancelKeepAlive添加到webSocket的onClose函数最后。

42. 记住,操作符比函数调用更快

不推荐使用:

var min = Math.min(a,b); 
A.push(v);

推荐使用:

var min = a < b ? a:b; 
A[A.length] = v;

43. 不要忘记使用代码美化工具。
在代码产品化前使用JSLint和代码压缩工具(例如,JSMin)来处理

03月 13

ExtJs使用总结

1.Ext.get
var el = Ext.get('myElementId');//获取元素,等同于document.getElementById('myElementId');//会缓存
2. Ext.fly
var el = Ext.fly('myElementId')//不需要缓存。
注:享元模式(Flyweight Design Pattern)是一种节省内存的模式,该模式的大概原理是建立单个全体对象然后不断反复使用它。
3.Ext.getDom
var elDom = Ext.getDom('elId'); // 依据id来查dom节点
var elDom1 = Ext.getDom(elDom); // 依据dom节点来查dom节点

二、CSS元素
4.addClass
Ext.fly('elId').addClass('myCls'); // 加入元素的'myCls'的样式
5.radioClass
Ext.fly('elId').radioClass('myCls');//添加一个或多个className到这个元素,并移除其所有侧边(siblings)节点上的同名样式。
6.removeClass
Ext.fly('elId').removeClass('myCls'); // 移除元素的样式
7.toggleClass
Ext.fly('elId').toggleClass('myCls'); // 加入样式
Ext.fly('elId').toggleClass('myCls'); // 移除样式
Ext.fly('elId').toggleClass('myCls'); // 再加入样式
8.hasClass
if (Ext.fly('elId').hasClass('myCls')) {//判断是否已加上这个样式
// 是有样式的……
}
10.replaceClass
Ext.fly('elId').replaceClass('myClsA', 'myClsB');//替换样式
11.getStyle
var color = Ext.fly('elId').getStyle('color');//返回该元素的统一化当前样式和计算样式。
var zIndx = Ext.fly('elId').getStyle('z-index');//返回该元素的统一化当前样式和计算样式。
12.setStyle
Ext.fly('elId').setStyle({
display : 'block',
overflow : 'hidden',
cursor : 'pointer'
});//设置元素的样式,也可以用一个对象参数包含多个样式。
13.getColor
Ext.fly('elId').getColor('color');//为指定的CSS属性返回CSS颜色
14.setOpacity

Ext.fly('elId').setOpacity(.45, true);//设置元素的透明度。
15.clearOpacity
Ext.fly('elId').clearOpacity();//清除这个元素的透明度设置

三、Dom游历
16.Ext.fly('elId').select('li:nth-child(2n)').addClass('red');
17.is 测试当前元素是否与传入的选择符相符一致。
复制代码代码如下:
var el = Ext.get('elId'); if (el.is('p.myCls')) { // 条件成立 }

18.findParent
定位于此节点,以此节点为起点,向外围搜索外层的父节点,搜索条件必须符合并匹配传入的简易选择符。
Ext.fly('elId').findParent('div'); // 返回dom节点
Ext.fly('elId').findParent('div', 4); // 查找4个节点
Ext.fly('elId').findParent('div', null, true); // 返回Ext.Element
19.findParentNode
定位于此节点的“父节点”,以此节点的“父节点”为起点,向外围搜索外层的“父父”节点,搜索条件必须符合并匹配传入的简易选择符。
Ext.fly('elId').findParentNode('div');
20.up
沿着DOM,向外围搜索外层的“父父”节点,搜索条件必须符合并匹配传入的简易选择符。
Ext.fly('elId').up('div');
Ext.fly('elId').up('div', 5); // 限5层的内查找
21.select
传入一个CSS选择符的参数,然后依据该CSS选择符从当前元素下面,形成期待匹配子节点的集合,也就是“选择”的操作,最后以一个Ext.CompositeElement类型的组合元素的形式返回。如果以Ext.select()调用表示从document可是搜索。
// 返回结果的CompositeElement
Ext.fly('elId').select('div:nth-child(2)');
// 返回数组
Ext.fly('elId').select('div:nth-child(2)',
true);
// 整个document都会搜索
Ext.select('div:nth-child(2)');
22.query
进行一次query的查询,返回DOM 节点组成的数组。可选地第二参数设置为查询的起点,如不指定则为 document。
// 返回dom节点组成的数组
Ext.query('div:nth-child(2)');
23.child
基于送入的选择符,不限定深度进行搜索,符合的话选取单个子节点。
Ext.fly('elId').child('p.highlight'); // 返回的类型是Ext.Element
Ext.fly('elId').child('p.highlight', true); // 返回dom节点
24.down
基于该选择符,"直接"选取单个子节点。
Ext.fly('elId').down('span'); // 返回的类型是Ext.Element
Ext.fly('elId').down('span', true); // 返回dom节点

25.parent
返回当前节点的那个父节点,可选地可送入一个期待的选择符。
// 返回父节点,类型是Ext.Element
Ext.fly('elId').parent();
// 返回父节点,类型是html dom
Ext.fly('elId').parent("", true);
// 返回父级节点,但一定要是div的,找到就返回,类型是Ext.Element
Ext.fly('elId').parent("div");
26.next
获取下一个侧边节点,跳过文本节点。可选地可送入一个期待的选择符。
// 返回下一个侧边节点,类型是Ext.Element
Ext.fly('elId').next();
// 返回下一个侧边节点,类型是html dom
Ext.fly('elId').next("", true);
// 返回下一个侧边节点,但一定要是div的,找到就返回,类型是Ext.Element
Ext.fly('elId').next("div");
27.prev
获取上一个侧边节点,跳过文本节点。可选地可送入一个期待的选择符。
// 返回上一个侧边节点,类型是Ext.Element
Ext.fly('elId').prev();
// 返回上一个侧边节点,类型是html dom
Ext.fly('elId').prev("", true);
// 返回上一个侧边节点,但一定要是div的,找到就返回,类型是Ext.Element
Ext.fly('elId').prev("div");
28.first
获取第一个侧边节点,跳过文本节点。可选地可送入一个期待的选择符。
// 返回第一个侧边节点,类型是Ext.Element
Ext.fly('elId').first();
// 返回第一个侧边节点,类型是html dom
Ext.fly('elId').first("", true);
// 返回第一个侧边节点,但一定要是div的,找到就返回,类型是Ext.Element
Ext.fly('elId').first("div");
29.last
获取最后一个侧边节点,跳过文本节点。可选地可送入一个期待的选择符。
// 返回最后一个侧边节点,类型是Ext.Element
Ext.fly('elId').last();
// 返回最后一个侧边节点,类型是html dom
Ext.fly('elId').last("", true);
// 返回最后一个侧边节点,但一定要是div的,找到就返回,类型是Ext.Element
Ext.fly('elId').last("div");

四、DOM操控(DHTML常见的一项任务就是DOM元素的增、删、改、查)
30.appendChild
把送入的元素归为这个元素的子元素。
var el = Ext.get('elId1');
// 用id指定
Ext.fly('elId').appendChild('elId2');
// Ext.Element添加
Ext.fly('elId').appendChild(el);
// 选择符组合地添加
Ext.fly('elId').appendChild(['elId2','elId3']);
// 直接添加dom节点
Ext.fly('elId').appendChild(el.dom);
// 添加CompositeElement,一组的div
Ext.fly('elId').appendChild(Ext.select('div'));
31.appendTo
把这个元素添加到送入的元素里面。
var el = Ext.get('elId1');
// 'elId'添加到'elId2'里面
Ext.fly('elId').appendTo('elId2');
Ext.fly('elId').appendTo(el); //
添加到Ext.Element el
32.insertBefore
传入一个元素的参数,将其放置在当前元素之前的位置。
var el = Ext.get('elId1');
// dom节点在前面插入
Ext.fly('elId').insertBefore('elId2');
//Ext.Element el在前面插入
Ext.fly('elId').insertBefore(el);
33.insertAfter
传入一个元素的参数,将其放置在当前元素之后的位置。
var el = Ext.get('elId1');
// dom节点在后面插入
Ext.fly('elId').insertAfter('elId2');
// Ext.Element el在后面插入
Ext.fly('elId').insertAfter(el);
34.insertFirst
可以是插入一个元素,也可以是创建一个元素(要创建的话请使用“DomHelper配置项对象”作为参数传入),总之,这个元素作为当前元素的第一个子元素出现。
var el = Ext.get('elId1');
// 插入的dom节点作为第一个元素
Ext.fly('elId').insertFirst('elId2');
// 插入的Ext.Element作为第一个元素
Ext.fly('elId').insertFirst(el);
// 用DomHelper配置项创建新节点,新节点会作为第一个子元素被插入。
Ext.fly('elId').insertFirst({
tag: 'p',
cls: 'myCls',
html: 'Hi I am the new first child'
});
35.replace
用于当前这个元素替换传入的元素。
var el = Ext.get('elId1');
// 'elId'去替换'elId2'
Ext.fly('elId').replace('elId2');
// 'elId'去替换'elId1'
Ext.fly('elId').replace(el);
36.replaceWith
用传入的元素替换这个元素。参数可以是新元素或是要创建的DomHelper配置项对象。
var el = Ext.get('elId1');
Ext.fly('elId').replaceWith('elId2'); // 'elId2'替换掉'elId'.
Ext.fly('elId').replaceWith(el); //
'elId1'替换掉'elId'
// 用DomHelper配置项创建新节点,并用该节点换掉‘elId'。
Ext.fly('elId').replaceWith({
tag: 'p',
cls: 'myCls',
html: 'Hi I have replaced elId'
});

五、DomHelper配置项
37.createChild
传入一个DomHelper配置项对象的参数,将其创建并加入到该元素。
var el = Ext.get('elId');
var dhConfig = {
tag: 'p',
cls: 'myCls',
html: 'Hi I have replaced elId'
};
// 创建新的节点,放到'elId'里面
el.createChild(dhConfig);
// 创建新的节点,居el第一个子元素之前
el.createChild(dhConfig, el.first());
38.wrap
创建一个新的元素,包裹在当前元素外面。
Ext.fly('elId').wrap(); // div包着elId
// 用新建的一个元素来包着elId
Ext.fly('elId').wrap({
tag: 'p',
cls: 'myCls',
html: 'Hi I have replaced elId'
});

六、Html片断
38.insertHtml
插入HTML片断到这个元素。至于要插入的html放在元素的哪里,你可指定beforeBegin, beforeEnd, afterBegin, afterEnd这几种。第二个参数是插入HTML片断,第三个参数是决定是否返回一个Ext.Element类型的DOM对象。
Ext.fly('elId').insertHtml(
'beforeBegin',
'

点击我

',
true
); // 返回Ext.Element
39.remove
从DOM里面移除当前元素,并从缓存中删除。.
Ext.fly('elId').remove(); //
elId在缓存和dom里面都没有
40.removeNode
移除document的DOM节点。如果是body节点的话会被忽略。
Ext.removeNode(node); // 从dom里面移除(HTMLElement)

七、Ajax
41.load
直接访问Updater的Ext.Updater.update()方法(相同的参数)。参数与Ext.Updater.update()方法的一致。
Ext.fly('elId').load({url: 'serverSide.php'})
42.getUpdater
获取这个元素的UpdateManager。
var updr = Ext.fly('elId').getUpdater();
updr.update({
url: 'http://myserver.com/index.php',
params: {
param1: "foo",
param2: "bar"
}
});