前端JavaScript基础训练系列一百三十五:全局 DOM 变量

声明一个全局变量(使用 var 或者不使用)的结果并不仅仅是创建一个
全局变量,而且还会在 global 对象(在浏览器中为 window)中创建一个同名属性。 还有一个不太为人所知的事实是:由于浏览器演进的历史遗留问题,在创建带有 id 属性
的 DOM 元素时也会创建同名的全局变量。例如:


以及:

if (typeof foo == "undefined") {
foo = 42; // 永远也不会运行
}
console.log( foo ); // HTML元素

你可能认为只有JavaScript代码才能创建全局变量,并且习惯使用typeof或… in window 来检测全局变量。但是如上例所示,HTML 页面中的内容也会产生全局变量,并且稍不注 意就很容易让全局变量检查错误百出。
这也是尽量不要使用全局变量的一个原因。如果确实要用,也要确保变量名的唯一性,从 而避免与其他地方的变量产生冲突,包括 HTML 和其他第三方代码。

原生原型

一个广为人知的 JavaScript 的最佳实践是:不要扩展原生原型。
如果向 Array.prototype 中加入新的方法和属性,假设它们确实有用,设计和命名都很得 当,那它最后很有可能会被加入到 JavaScript 规范当中。这样一来你所做的扩展就会与之 冲突。
我自己就曾遇到过这样一个例子。
当时我正在为一些网站开发一个嵌入式构件,该构件基于 jQuery(基本上所有的框架都 会犯这样的错误)。基本上它在所有的网站上都可以运行,但是在某个网站上却彻底无法 运行。
经过差不多一个星期的分析调试之后,我发现这个网站有一段遗留代码,如下:

// Netscape 4没有Array.push Array.prototype.push = function(item) {
         this[this.length-1] = item;
     };

除了注释以外(谁还会关心 Netscape 4 呢?),上述代码似乎没有问题,是吧?
问题在于 Array.prototype.push 随后被加入到了规范中,并且和这段代码不兼容。标准的
push(…) 可以一次加入多个值。而这段代码中的 push 方法则只会处理第一个值。 几乎所有 JavaScript 框架的代码都使用 push(…) 来处理多个值。我的问题则是 CSS 选择器
引擎(CSS selector)。可想而知其他很多地方也会有这样的问题。 最初编写这个方法的开发人员将其命名为 push 没有问题,但是并未预见到需要处理多个值
的情况。这相当于挖了一个坑,而大约 10 年之后,我无意间掉了进去。
从中我们可以吸取几个教训。
首先,不要扩展原生方法,除非你确信代码在运行环境中不会有冲突。如果对此你并非 100% 确定,那么进行扩展是非常危险的。这需要你自己仔细权衡利弊。
其次,在扩展原生方法时需要加入判断条件(因为你可能无意中覆盖了原来的方法)。对 于前面的例子,下面的处理方式要更好一些:

if (!Array.prototype.push) {
// Netscape 4没有Array.push Array.prototype.push = function(item) {
             this[this.length-1] = item;
         };
}

其中,if 语句用来确保当 JavaScript 运行环境中没有 push() 方法时才将扩展加入。这应该
可以解决我的问题。但它并非万全之策,并且存在着一定的隐患。
如果网站代码中的 push(…) 原本就不打算处理多个值的情况,那么标准的 push(…) 出台 后会导致代码运行出错。
如果在 if 判断前引入了其他第三方的 push(…) 方法,并且该方法的功能不同,也会导致 代码运行出错。
这里突出了一个不太为 JavaScript 开发人员注意的问题:在各种第三方代码混合运行的环 境中,是否应该只使用现有的原生方法?
答案是否定的,但是实际上不太行得通。通常你无法重新定义所有会用到的原生方法,同 时确保它们的安全。即使可以,这种做法也是一种浪费。
那么是否应该既检测原生方法是否存在,又要测试它能否执行我们想要的功能?如果测试 没通过,是不是意味着代码要停止执行?

// 不要信任 Array.prototype.push (function(){
if (Array.prototype.push) {
             var a = [];
             a.push(1,2);
             if (a[0] === 1 && a[1] === 2) {
// 测试通过,可以放心使用!
return; }
}
         throw Error(
             "Array#push() is missing/broken!"
); })();

理论上说这个方法不错,但实际上不可能为每个原生函数都做这样的测试。
那应该怎么办呢?我们是否应该逐一做测试?还是假设一切没问题,等出现问题时再 处理?
这里没有标准答案。实际上,只要我们自己不去扩展原生原型,就不会遇到这类问题。
如果你和第三方代码都遵循以上原则,那么你的程序是安全的。否则就要更加谨慎小心地 对待程序,以防任何可能出现的类似问题。
针对各种运行环境做单元和回归测试能够早点发现问题,却不能够完全杜绝问题。

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值