ShannonChenCHN / eureka

✍️ I read, I write, I think, I do, I learn, I code.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[译]JavaScript 开发者最常犯的 10 个错误

ShannonChenCHN opened this issue · 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

常见错误三:内存泄漏

常见错误四:不能清楚理解相等性

常见错误五:低效的 DOM 操作

常见错误六:在 for 循环中错误使用函数定义

常见错误七:不会使用原型模型继承

常见错误八:错误地引用实例方法

常见错误九:将一个 string 作为 setTimeout 或者 setInterval 函数的第一个参数

常见错误十:不会使用“严格模式” (strict mode)