本文总结了 JavaScript 开发中常见的几个关键陷阱。首先指出函数不区分大小写且不支持重载,同名函数会被覆盖。其次深入解析闭包在循环中的经典问题,即变量引用时机导致的错误,并提供通过辅助函数捕获当前值的解决方案。最后对比了数组迭代与 for-in 遍历的区别,强调在处理类数组对象时使用 hasOwnProperty 过滤继承属性的重要性,以避免意外错误。
函数命名与重载限制
在 JavaScript 中,所有创建的函数名都是严格区分大小写的。
单引号('字符串')和双引号("字符串")在 JavaScript 中创建字符串时没有功能上的区别。然而,作为通用开发规范,大多数 Web 开发者倾向于使用单引号,这是因为 XHTML 规范要求所有属性值必须使用双引号包裹。
JavaScript 不支持函数重载。在脚本执行过程中,引擎不会根据参数数量或类型来区分函数定义,而是直接采用作用域链中最后定义的那个函数。这意味着,如果定义了多个同名函数,前一个会被后一个覆盖,始终只保留最后一个实例。
闭包与变量作用域陷阱
闭包是与作用域紧密相关的概念,指内部函数即使在外部函数执行完毕并终止后,仍能访问其外部函数的属性。当引用变量或方法时,JavaScript 会沿着由对象执行路径构成的作用域链进行解析,查找变量最近定义的值,一旦找到即使用该值。以下代码展示了相关场景:
示例代码如下:
for (var i=1; i <=3; i++){
var anchor = document.GetElementById('anchor' + i);
anchor.attachEvent('onclick', function() {
alert('my id is anchor' + i);
});
}
} 假设页面中存在三个 A 元素,ID 分别为 anchor1 到 anchor3,程序为这三个 A 元素注册 onclick 事件。预期单击每个 A 元素时显示 "my id is anchorX",但实际运行结果却是单击任意元素都显示 "my id is anchor4"。原因在于变量 i 的值是在单击事件触发时才从作用域链中获取的。当点击发生时,initAnchors() 函数已经执行完毕,此时循环变量 i 的值已变为 4。解决此问题的方法如下所示:
修正后的代码如下:
function registerAnchorListner(anchor,i){
anchor.attachEvent('onclick', function() {
alert('my id is anchor' + i);
}
}
function initAnchors(event){
for (var i=1; i <=3; i++){
var anchor = document.GetElementById('anchor' + i);
registerAnchorListner(anchor,i);
}
}
var anchor = document.GetElementById('anchor' + i);
anchor.attachEvent('onclick', function() {
alert('my id is anchor' + i);
}); 迭代遍历与属性过滤
在编写脚本时,迭代操作非常常见。例如:
示例代码如下:
var list = [1,2,3,4];
for(var i = 0;i < list.length; i++){
alert(list);
} 另一种可选的迭代方式是使用 for-in 循环遍历 list 中的每个属性:
示例代码如下:
for(var i in list){
alert(list);
} 此时,得到的结果与使用前一种迭代方法相同,因为 list 是一个 Array 对象。
然而,当使用 for(var i in item) 方法操作类似数组但不是数组的对象时,必须格外小心。例如:
示例代码如下:
var all=document.getElementsByTagName('*');
for(var i in all){
//对照all元素进行某些操作
} 在此迭代过程中,i 的值可能分别等于 length、item 和 namedItem,这极易导致代码出现意外错误。在某些情况下,可以使用对象的 hasOwnProperty() 方法来避免此类问题。如果对象的属性或方法是非继承的,hasOwnProperty() 方法返回 true。这意味着该检查仅涉及在特定对象自身中直接创建的属性(如分配给数组的元素),而不涉及从其他对象继承的属性和方法。因此,如果在 for 循环中加入这种检查,循环将跳过 length 等属性,因为 length 并非数组 all 的直接属性,而是从派生数组 all 的 NameNodeMap 对象中继承的属性。修正后的代码示例如下:
修正后的代码如下:
var all=document.getElementsByTagName('*');
for(var i in all){
if(!all.hasOwnProperty(i)) continue;
//对照all元素进行某些操作
}