说明
刚读完javascript模式一书,以下是本人总结的知识点,作为读书笔记,同时也为广大爱好者提供参考。
了解模式——什么是模式
广义上模式是指“重现事件或者对象的主题…它是一个可以用来产生其他事物的模板或者模型”。
在软件开发过程中,模式是指一个通用问题的解决方案。一个模式不仅仅是一个可以用来赋值粘贴的代码解决方案,更多地市提供了一个更好的实践经验,有用的抽象化表示和解决一类问题的模板。
变量篇
尽量少用全局变量
大量的创建全局变量,总有可能发生命名冲突。javascript总是在不知不觉中就出人意料地创建了全局变量,原因在于javascript的两个特性。
1.javascript有个暗示全局变量的概念,即任何变量,如未经声明,就为全局对象所有。
让我们来看一个示例
function sum(x,y){
//反模式
result = x + y;
return result;
}
在这个例子中,result未经声明就使用了。代码虽然在一般情况下可以正常工作,但如果在调用该函数后,在全局命名空间使用了另外的result变量,就会出现问题。
2.另外一种创建隐式全局变量的反模式是带有var声明的链式赋值。在下面的代码片断中,a是局部变量,b是全局变量。
function foo(){
//反模式,不要使用
var a = b = 0;
//b为全局,内部var a = (b = 0);
return result;
}
如果对链式赋值的所有变量都进行了声明,就不会创建出不期望的全局变量。例如:
function foo(){
var a, b;
//...
a = b = 0;//均为局部变量
}
- 使用var 定义的全局变量不可以删除;
- 不使用var的全局变量可以删除;
说明:使用var 定义就是一个变量, 不使用var定义就是一个对象的属性。
var a = 1;
b = 2;//反模式
(function(){
c = 3;//反模式
})();
//企图删除
delete a; // false;
delete b; // true;
delete c; // true
//测试删除情况
typeof a; // number
typeof b; // undefined
typeof c; // undefined
在ES5 strict模式下,为未声明的变量赋值会抛出错误。
单一var模式
在函数顶部对所有变量通过一个 var 进行声明。好处如下:
- 提供一个单一的地址以找到函数所需的所有局部变量
- 防止出现变量在定义前就被使用的逻辑错误
- 帮助牢记要声明变量,以尽可能少地使用全局变量。
- 更少的编码
单一var模式如下:
function func(){
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
console.log(sum)
// 函数体
}
func()
使用逗号操作符可以在一条语句中执行多个操作。多用于声明多个变量,但还可以用于赋值,总会返回表达式的最后一项。
(1) 逗号表达式的运算过程为:从左往右逐个计算表达式。
(2) 逗号表达式作为一个整体,它的值为最后一个表达式的值。var num = (5,4,1,0); // num 为 0。
(3) 逗号运算符的优先级别在所有运算符中最低。
代码处理上分为两个阶段
- 这是个阶段创建变量、函数声明及形式参数。这是解析和进入上、下文阶段。
- 第二个阶段是代码运行时执行过程,创建函数表达式和不合格标识符(未定义变量)。
for循环
for循环主要用于两种情况,遍历 数组 和 类数组对象(HTML容器对象)的。
数组就是Array对象, var arr = [1, 2, 3, 4]; 这就是一个数组。
而HTML容器对象是DOM方法返回的对象,如:
document.getElementByName()
document.getElementsByClassName()
document.getElementsByTagName()
都是 dom方法返回的HTML容器对象。
还有很多其他HTML容器,他们在DOM标准以前就引入了,并一直使用至今。包括:
document.images //页面上所有的IMG元素
documents.forms //所有的Forms
document.links //所有的A标签元素
document.forms[0].elements //页面上第一个from内的所有字段
麻烦在于他们都在document下是活动的查询。每次访问任何容器的长度时,也就是在查询活动的DOM,非常耗时。
这就是为什么好的for循环模式是将已经遍历过的数组(或容器)的长度缓存起来。如:
for (var i = 0; i < myArray.length; i++) {
// 对myArray进行操作
}
在所有浏览器中,通过将HTML容器上需要遍历的次数缓存起来会大大提高速度。在safari3中会提高两倍,而IE7中会提高170倍。
for模式中的两个变量引出了一些操作,原因是:
- 使用最少的变量
- 逐步减至0,这样通常更快,因为同0比较比非同0数组比较更有效率。
修改后的模式:
//第一个
var i, myarray = [];
for (i = myArray.length; i--;) {
// 对myArray进行操作
}
//第二个用while
var myarray = [];
i = myarray.length;
while(i--){
// 对myArray进行操作
}
for-in
刚刚提到,for循环主要是用于遍历数组和类数组对象的(统统与数组有关),而for-in循环主要是用于遍历一个普通对象的属性的(与数组无关)。
当遍历对象属性来过滤遇到原型链的属性时,使用hasOwnProperty()方法是非常重要的,原形链式活动的,有时候我们并不需要枚举出新方法。
for(var key in object) {
if(object.hasOwnproperty(key)) {
console.log(key, ":", object[key]);
}
}
//另外一种模式
var key,
hasOWN = object.prototype.hasOwnproperty;
for(key in object) {
if(hasOwn.call(object,key)) {
console.log(key, ":", object[key]);
}
}
避免使用隐式类型转换
使用 === 或 !== 进行比较。增强代码可阅读性,避免猜测。
避免使用eval()
其实这个还是很好理解的,因为大多数时候,我们很少见到使用eval()方法的情况。
并且记住:eval()是一个魔鬼
为什么? 因为eval()可以将任意字符串当作一个JavaScript代码来执行。
new Functin() 与 eval()的不同:
第一点:
new Function()中的代码将在局部函数空间运行,因此代码中任何采用var定义的变量不会自动成为全局变量(即在函数内)。
eval()则会自动成为全局变量,但可通过立即调用函数对其进行封装。
console.log(typeof un);// "undefined"
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"
var jsstring = "var un = 1; console.log(un);";
eval(jsstring); // 打印出 "1"
jsstring = "var deux = 2; console.log(deux);";
new Function(jsstring)(); // 打印出 "2"
jsstring = "var trois = 3; console.log(trois);";
(function () {
eval(jsstring);
}()); // 打印出 "3"
console.log(typeof un); // "number"
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"
第二点:
eval()会影响到作用域链,而Function则像一个沙盒,无论在哪里执行Function,它都仅能看到全局作用域链。因此对局部变量的影响比较小。
(function () {
var local = 1;
eval("local = 3; console.log(local)"); // 打印出 3
console.log(local); // 打印出 3
}());
(function () {
var local = 1;
Function("console.log(typeof local);")(); // 打印出 undefined
}());
使用parseInt()进行数字转换
ECMAScript3中以0为前缀的字符串会被当作八进制数处理,这一点在ES5中已经有了改变。为了避免转换类型不一致而导致的意外结果,应当总是指定第二个参数:
var month = "06",
year = "09";
month = parseInt(month, 10);
year = parseInt(year, 10);
字符串转换为数字还有两种方法:
+"08" // 结果为8,隐式调用Number()
Number("08") // 结果为8
这两种方法要比parseInt()更快一些,因为顾名思义parseInt()是一种“解析”而不是简单的“转换”。但当你期望将“08 hello”这类字符串转换为数字,则必须使用parseInt(),其他方法都会返回NaN。