[译]JavaScript 开发者最常犯的 10 个错误
ShannonChenCHN opened this issue · 1 comments
如今,JavaScript几乎正处于所有现代 Web 应用的最中心。尤其是在最近的过去几年里,出现了大量的强大的 JavaScript 库和用于单页应用(SPA)开发、图形和动画的框架,甚至还有用于服务端的 JavaScript 平台的框架。JavaScript 俨然已经在 Web 开发领域无处不在,并因此成为了一项越来越重要的技能。
第一眼看上去,JavaScript 似乎很简单。实际上,对任何有经验的开发者来说,即便他们从未接触过 JavaScript,在网页中使用一些基本的 JavaScript 功能也是一件相当简单的任务。然而,随着逐渐深入,这门语言会让人比在刚开始接触它时,明显地感觉到更微妙,更强大,更复杂。确实,JavaScript 的一些微妙之处会经常引发一些导致程序无法运行的问题——我们在这里会讨论其中的 10 个问题——要想成为一名优秀的 JavaScript 开发者,能够意识到并且避免这些问题是非常重要的。
常见错误一:this
的错误使用
我曾经听过这样一个段子:
I’m not really here, because what’s here, besides there, without the ‘t’?
这个段子从多方面描绘出了开发者门经常在面对 JavaScript 中的 this
时所产生的种种困惑。我想说的是, this
真的就是指当前的这个个体(this)吗,或者是其他的什么东西?或者是 undefined
?
随着这些年 JavaScript 编程技术和设计模式变得越来越复杂,在 callbacks 和 closure 中的自引用(self-referencing scopes)也开始大量出现,这也往往就是 “this/that confusion” 的源头所在。
看看下面这个示例代码:
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(function() {
this.clearBoard(); // what is "this"?
}, 0);
};
执行上面的代码时将会得到如下的错误:
Uncaught TypeError: undefined is not a function
Why?
关键在于上下文。你得到上面的错误是因为,当你调用 setTimeout()
时,你实际上调用的是 window.setTimeout()
。因此,传给 setTimeout()
的匿名函数就被定义在 window
对象的上下文中,而 window
是没有 clearBoard()
这个方法的。
一个传统的、适用于旧标准的浏览器的解决方法,就是把你的 this
引用保存到一个变量中,这个变量可以在后面的 closure 中被使用。
示例:
Game.prototype.restart = function () {
this.clearLocalStorage();
var self = this; // save reference to 'this', while it's still this!
this.timer = setTimeout(function(){
self.clearBoard(); // oh OK, I do know who 'self' is!
}, 0);
};
或者,在新标准的浏览器中,你可以使用 bind()
方法将真正的 this
传到函数中去。
示例:
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(this.reset.bind(this), 0); // bind to 'this'
};
Game.prototype.reset = function(){
this.clearBoard(); // ahhh, back in the context of the right 'this'!
};
常见错误二:错误理解 JavaScript 中的块级作用范围
在我们 JavaScript Hiring Guide 中曾经讨论过,JavaScript 开发者的一个常见误解是(因此这也是一个常见的 bug 始作俑者),以为 JavaScript 会为每一个代码块创建一个新的作用域。尽管在其他语言中确实是这样,但是在 JavaScript 中并不是如此。
让我们来看看下面的例子:
for (var i = 0; i < 10; i++) {
/* ... */
}
console.log(i); // what will this output?
如果你认为调用 console.log()
会输出 undefined
或者抛出一个错误,你就大错特错了。信不信由你,正确的结果是,会输出 10。
Why?
在其他大部分语言中,上面的代码将会导致一个错误,因变量 i 只应该“存活”在 for 循环的代码块中。然而,在 JavaScript 中,事实并不是那样,变量 i 在 for 循环执行完后还会继续存活,并在 for 循环执行完后保留着最后一次的值。(这种特征被称为 variable hoisting)
尽管支持块级作用域(block-level scopes)并没有什么意义,但 JavaScript 标准中还是新增了 let
关键字来支持块级作用域。let
关键字在 JavaScript 1.7 中已经可以用了,并且预计在 ECMAScript 6 中将正式成为 JavaScript 关键字。
你是 JavaScript 新手?读读 scopes, prototypes, and more。