# 核心
## 為什么不要使用 `eval`
`eval` 函數會在當前作用域中執行一段 JavaScript 代碼字符串。
```
var foo = 1;
function test() {
var foo = 2;
eval('foo = 3');
return foo;
}
test(); // 3
foo; // 1
```
但是 `eval` 只在被**直接**調用并且調用函數就是 `eval` 本身時,才在當前作用域中執行。
```
var foo = 1;
function test() {
var foo = 2;
var bar = eval;
bar('foo = 3');
return foo;
}
test(); // 2
foo; // 3
```
**[譯者注](http://cnblogs.com/sanshi/):**上面的代碼等價于在全局作用域中調用 `eval`,和下面兩種寫法效果一樣:
```
// 寫法一:直接調用全局作用域下的 foo 變量
var foo = 1;
function test() {
var foo = 2;
window.foo = 3;
return foo;
}
test(); // 2
foo; // 3
// 寫法二:使用 call 函數修改 eval 執行的上下文為全局作用域
var foo = 1;
function test() {
var foo = 2;
eval.call(window, 'foo = 3');
return foo;
}
test(); // 2
foo; // 3
```
在**任何情況下**我們都應該避免使用 `eval` 函數。99.9% 使用 `eval` 的場景都有**不使用** `eval` 的解決方案。
### 偽裝的 `eval`
[定時函數](#other.timeouts) `setTimeout` 和 `setInterval` 都可以接受字符串作為它們的第一個參數。 這個字符串**總是**在全局作用域中執行,因此 `eval` 在這種情況下沒有被直接調用。
### 安全問題
`eval` 也存在安全問題,因為它會執行**任意**傳給它的代碼, 在代碼字符串未知或者是來自一個不信任的源時,絕對不要使用 `eval` 函數。
### 結論
絕對不要使用 `eval`,任何使用它的代碼都會在它的工作方式,性能和安全性方面受到質疑。 如果一些情況必須使用到 `eval` 才能正常工作,首先它的設計會受到質疑,這**不應該**是首選的解決方案, 一個更好的不使用 `eval` 的解決方案應該得到充分考慮并優先采用。
## `undefined` 和 `null`
JavaScript 有兩個表示‘空’的值,其中比較有用的是 `undefined`。
### `undefined` 的值
`undefined` 是一個值為 `undefined` 的類型。
這個語言也定義了一個全局變量,它的值是 `undefined`,這個變量也被稱為 `undefined`。 但是這個變量**不是**一個常量,也不是一個關鍵字。這意味著它的_值_可以輕易被覆蓋。
**ES5 提示:** 在 ECMAScript 5 的嚴格模式下,`undefined` **不再是** _可寫_的了。 但是它的名稱仍然可以被隱藏,比如定義一個函數名為 `undefined`。
下面的情況會返回 `undefined` 值:
* 訪問未修改的全局變量 `undefined`。
* 由于沒有定義 `return` 表達式的函數隱式返回。
* `return` 表達式沒有顯式的返回任何內容。
* 訪問不存在的屬性。
* 函數參數沒有被顯式的傳遞值。
* 任何被設置為 `undefined` 值的變量。
### 處理 `undefined` 值的改變
由于全局變量 `undefined` 只是保存了 `undefined` 類型實際_值_的副本, 因此對它賦新值**不會**改變類型 `undefined` 的值。
然而,為了方便其它變量和 `undefined` 做比較,我們需要事先獲取類型 `undefined` 的值。
為了避免可能對 `undefined` 值的改變,一個常用的技巧是使用一個傳遞到[匿名包裝器](#function.scopes)的額外參數。 在調用時,這個參數不會獲取任何值。
```
var undefined = 123;
(function(something, foo, undefined) {
// 局部作用域里的 undefined 變量重新獲得了 `undefined` 值
})('Hello World', 42);
```
另外一種達到相同目的方法是在函數內使用變量聲明。
```
var undefined = 123;
(function(something, foo) {
var undefined;
...
})('Hello World', 42);
```
這里唯一的區別是,在壓縮后并且函數內沒有其它需要使用 `var` 聲明變量的情況下,這個版本的代碼會多出 4 個字節的代碼。
**[譯者注](http://cnblogs.com/sanshi/):**這里有點繞口,其實很簡單。如果此函數內沒有其它需要聲明的變量,那么 `var` 總共 4 個字符(包含一個空白字符) 就是專門為 `undefined` 變量準備的,相比上個例子多出了 4 個字節。
### `null` 的用處
JavaScript 中的 `undefined` 的使用場景類似于其它語言中的 _null_,實際上 JavaScript 中的 `null` 是另外一種數據類型。
它在 JavaScript 內部有一些使用場景(比如聲明原型鏈的終結 `Foo.prototype = null`),但是大多數情況下都可以使用 `undefined` 來代替。
## 自動分號插入
盡管 JavaScript 有 C 的代碼風格,但是它**不**強制要求在代碼中使用分號,實際上可以省略它們。
JavaScript 不是一個沒有分號的語言,恰恰相反上它需要分號來就解析源代碼。 因此 JavaScript 解析器在遇到由于缺少分號導致的解析錯誤時,會**自動**在源代碼中插入分號。
```
var foo = function() {
} // 解析錯誤,分號丟失
test()
```
自動插入分號,解析器重新解析。
```
var foo = function() {
}; // 沒有錯誤,解析繼續
test()
```
自動的分號插入被認為是 JavaScript 語言**最大**的設計缺陷之一,因為它_能_改變代碼的行為。
### 工作原理
下面的代碼沒有分號,因此解析器需要自己判斷需要在哪些地方插入分號。
```
(function(window, undefined) {
function test(options) {
log('testing!')
(options.list || []).forEach(function(i) {
})
options.value.test(
'long string to pass here',
'and another long string to pass'
)
return
{
foo: function() {}
}
}
window.test = test
})(window)
(function(window) {
window.someLibrary = {}
})(window)
```
下面是解析器"猜測"的結果。
```
(function(window, undefined) {
function test(options) {
// 沒有插入分號,兩行被合并為一行
log('testing!')(options.list || []).forEach(function(i) {
}); // <- 插入分號
options.value.test(
'long string to pass here',
'and another long string to pass'
); // <- 插入分號
return; // <- 插入分號, 改變了 return 表達式的行為
{ // 作為一個代碼段處理
foo: function() {}
}; // <- 插入分號
}
window.test = test; // <- 插入分號
// 兩行又被合并了
})(window)(function(window) {
window.someLibrary = {}; // <- 插入分號
})(window); //<- 插入分號
```
**注意:** JavaScript 不能正確的處理 `return` 表達式緊跟換行符的情況, 雖然這不能算是自動分號插入的錯誤,但這確實是一種不希望的副作用。
解析器顯著改變了上面代碼的行為,在另外一些情況下也會做出**錯誤的處理**。
### 前置括號
在前置括號的情況下,解析器**不會**自動插入分號。
```
log('testing!')
(options.list || []).forEach(function(i) {})
```
上面代碼被解析器轉換為一行。
```
log('testing!')(options.list || []).forEach(function(i) {})
```
`log` 函數的執行結果**極大**可能**不是**函數;這種情況下就會出現 `TypeError` 的錯誤,詳細錯誤信息可能是 `undefined is not a function`。
### 結論
建議**絕對**不要省略分號,同時也提倡將花括號和相應的表達式放在一行, 對于只有一行代碼的 `if` 或者 `else` 表達式,也不應該省略花括號。 這些良好的編程習慣不僅可以提到代碼的一致性,而且可以防止解析器改變代碼行為的錯誤處理。