2011年9月29日 星期四

JavaScript: The Good Parts

Bear在這裡買的
amazon的參考

JavaScript語言精粹
作者:(美)克羅克福德|譯者:趙澤欣//鄢學鶤
出版社:電子工業
ISBN:9787121084379
出版日期:2009/04/01
頁數:155
人民幣:RMB 35 元


Bear: 這本書的作者是頁對頁對翻譯,所以原文和中譯頁數相同。

P.6
在JavaScript中,那些字符對也可能出現在正則表達式字面上,所以以塊注釋對於被注釋的代碼塊來說是不安全的。

The /* */ form of block comments came from a language called PL/I. PL/I chose those strange pairs as the symbols for comments because they were unlikely to occur in that language’s programs, except perhaps in string literals. In JavaScript, those pairs can also occur in regular expression literals, so block comments are not safe for commenting out blocks of code. For example:
/*
var rm_a = /a*/.match(s);
*/

所以,我建議避免使用/* */注釋,而以 // 注釋代替它。

causes a syntax error. So, it is recommended that /* */ comments be avoided and // comments be used instead. In this book, // will be used exclusively.

P.7
JavaScript只有一個單一的數字類型,它在內部被表示為64位的浮點數,和Java的 double 不一樣。不像大多數其他的編程語言,它沒有分離出整數類型,所以1 和 1.0是相同的值。

JavaScript has a single number type. Internally, it is represented as 64-bit floating point, the same as Java’s double. Unlike most other programming languages, there is no separate integer type, so 1 and 1.0 are the same value.

P.16(原文P.16)
typeof運算符產生的值有'number', 'string', 'boolean', 'undefined', 'function', and 'object'。如果運算數是一個數組或null,那麼結果是'object',這是不對的。

The values produced by typeof are 'number', 'string', 'boolean', 'undefined', 'function', and 'object'. If the operand is an array or null, then the result is 'object', which is wrong. There will be more about typeof in Chapter 6 and Appendix A.

P.21
如果字符串表達式是一個常數,而且它是一個合法的JavaScript標識符而並非保留字,那麼也可以用 . 表示法代替。優先考慮使用 . 表示法,因為它更緊湊且可讀性更好。

Values can be retrieved from an object by wrapping a string expression in a [ ] suffix. If the string expression is a constant, and if it is a legal JavaScript name and not a reserved word, then the . notation can be used instead. The . notation is preferred because it is more compact and it reads better:

stooge["first-name"] // "Joe"
flight.departure.IATA // "SYD"

The undefined value is produced if an attempt is made to retrieve a nonexistent member:

stooge["middle-name"] // undefined
flight.status // undefined
stooge["FIRST-NAME"] // undefined

 || 運算符可以用來填充默認值。

The || operator can be used to fill in default values:

var middle = stooge["middle-name"] || "(none)";
var status = flight.status || "unknown";

嘗試檢索一個 undefined 值將會導致 TypeError 異常。這可以通過 && 運算符來避免錯誤。

Attempting to retrieve values from undefined will throw a TypeError exception. This can be guarded against with the && operator:

flight.equipment // undefined
flight.equipment.model // throw "TypeError"
flight.equipment && flight.equipment.model // undefined

P.22
如果屬性名已經存在於對象中,那麼這個屬性的值被替換。

A value in an object can be updated by assignment. If the property name already exists in the object, the property value is replaced:

stooge['first-name'] = 'Jerome';

如果對象之前並沒有擁有那個屬性名,那麼該屬性就被擴充到該對象中。

If the object does not already have that property name, the object is augmented:

stooge['middle-name'] = 'Lester';
stooge.nickname = 'Curly';
flight.equipment = {
model: 'Boeing 777'
};
flight.status = 'overdue';

P.23
It is easy to inspect an object to determine what properties it has by attempting to retrieve the properties and examining the values obtained. The typeof operator can be very helpful in determining the type of a property:

typeof flight.number // 'number'
typeof flight.status // 'string'
typeof flight.arrival // 'object'
typeof flight.manifest // 'undefined'

Some care must be taken because any property on the prototype chain can produce a value:

typeof flight.toString // 'function'
typeof flight.constructor // 'function'

There are two approaches to dealing with these undesired properties. The first is to have your program look for and reject function values. Generally, when you are reflecting, you are interested in data, and so you should be aware that some values could be functions.

hasOwnProperty方法不會檢查原型鍵。

The other approach is to use the hasOwnProperty method, which returns true if the object has a particular property. The hasOwnProperty method does not look at the prototype chain:

flight.hasOwnProperty('number') // true
flight.hasOwnProperty('constructor') // false

P.24
如果你想要確保屬性以特定順序出現,最好的辦法就是完全避免使用 for in 語句,而是創建一個數組,在其中以正確的順序包含屬性名。

There is no guarantee on the order of the names, so be prepared for the names to appear in any order. If you want to assure that the properties appear in a particular order, it is best to avoid the for in statement entirely and instead make an array containing the names of the properties in the correct order:

var i;
var properties = [
'first-name',
'middle-name',
'last-name',
'profession'
];
for (i = 0; i < properties.length; i += 1) {
document.writeln(properties[i] + ': ' +
another_stooge[properties[i]]);
}

通過使用 for 而不是 for in ,可以得到我們想要的屬性,而不用擔心可能發掘出原型鍵中的屬性,並且我們按正確的順序取得了它們的值。

By using for instead of for in, we were able to get the properties we wanted without worrying about what might be dredged up from the prototype chain, and we got them in the correct order.

P.24
它將會移除對象中確定包含的屬性。它不會觸及原型鍵中的任何對象。

The delete operator can be used to remove a property from an object. It will remove a property from the object if it has one. It will not touch any of the objects in the prototype linkage.

P.25
最小化使用全局變量的一個方法是在你的應用中只創建唯一一個全局變量。

One way to minimize the use of global variables is to create a single global variable for your application:

P.27
第一個部份是保留字function。

A function literal has four parts. The first part is the reserved word function.

第一個部份是函數名,它可以被省略。

The optional second part is the function’s name.

P.27
通過函數字面量創建的函數對象包含一個連到外部上下文的連接。這被稱為閉包(closure)。

A function literal can appear anywhere that an expression can appear. Functions can be defined inside of other functions. An inner function of course has access to its parameters and variables. An inner function also enjoys access to the parameters and variables of the functions it is nested within. The function object created by a function literal contains a link to that outer context. This is called closure. This is the source of enormous expressive power.

P.28
當實際參數(arguments)的個數與形式參數(parameters)的個數不匹配時不會導致運行時錯誤。如果實際參數值過多了,超出的參數值將被忽略,如果實際參數值過少,缺失的值將被替換為undefined。對參數值不會進行類型檢查:任何類型的值都可以被傳遞給參數。

The invocation operator is a pair of parentheses that follow any expression that produces a function value. The parentheses can contain zero or more expressions, separated by commas. Each expression produces one argument value. Each of the argument values will be assigned to the function’s parameter names. There is no runtime error when the number of arguments and the number of parameters do not match. If there are too many argument values, the extra argument values will be ignored. If there are too few argument values, the undefined value will be substituted for the missing values. There is no type checking on the argument values: any type of value can be passed to any parameter.

P.29
new前綴也會改變return語句的行為。

The new prefix also changes the behavior of the return statement. We will see more about that next.

P.31
當函數被調用時,會得到一個"免費"奉送的參數,那就是arguments數組。通過它函數可以訪問所有它被調用時傳遞給它的參數列表,包括那些沒有被分配給函數聲明時定義的形式參數的多餘參數。

A bonus parameter that is available to functions when they are invoked is the arguments array. It gives the function access to all of the arguments that were supplied with the invocation, including excess arguments that were not assigned to parameters. This makes it possible to write functions that take an unspecified number of parameters:

// Make a function that adds a lot of stuff.
// Note that defining the variable sum inside of
// the function does not interfere with the sum
// defined outside of the function. The function
// only sees the inner one.
var sum = function ( ) {
var i, sum = 0;
for (i = 0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
};
document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108

This is not a particularly useful pattern. In Chapter 6, we will see how we can add a similar method to an array.

arguments擁有一個length屬性,但它缺少所有的數組方法。

Because of a design error, arguments is not really an array. It is an array-like object. arguments has a length property, but it lacks all of the array methods. We will see a consequence of that design error at the end of this chapter.

P.
一個函數總是會返回一個值。如果沒有指定返回值,則返回undefined。

A function always returns a value. If the return value is not specified, then undefined is returned.

如果函數以在前面加上new前綴方式來調用,且返回值不是一個對象,則返回this(該新對象)。

If the function was invoked with the new prefix and the return value is not an object, then this (the new object) is returned instead.

P.33
一個保險的做法就是只在確定沒有該方法時才添加它。

The prototypes of the basic types are public structures, so care must be taken when mixing libraries. One defensive technique is to add a method only if the method is known to be missing:

// Add a method conditionally.
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
};

另一個要注意的就是for in 語句用在原型上時表現很糟糕。我們在第3章已經看到了幾個減輕這個問題的影響辦法:我們可以使用hasOwnProperty方法去篩選出繼承而來的屬性,或者我們可以查找特定的類型。

Another concern is that the for in statement interacts badly with prototypes. We saw a couple of ways to mitigate that in Chapter 3: we can use the hasOwnProperty method to screen out inherited properties, and we can look for specific types.

P.36
很多現代語言都推薦盡可能遲地聲明變量。而用在JavaScript上的話卻會成為糟糕的建議,因為它缺少塊級作用域。所以,最好的做法是在函數體的頂部聲明函數中可能用到的所有變量。

In many modern languages, it is recommended that variables be declared as late as possible, at the first point of use. That turns out to be bad advice for JavaScript because it lacks block scope. So instead, it is best to declare all of the variables used in a function at the top of the function body.

P.98
把{放在一行的結尾而不是下一行的開頭,因為它會避免在JavaScript的return語句中的一個可怕的設計錯誤。

I always use the K&R style, putting the { at the end of a line instead of the front, because it avoids a horrible design blunder in JavaScript’s return statement.

P.102
這本來是為方便初學者而有意讓變量在使用前無須聲明。不幸的是,忘記聲明變量成了一個非常普遍的錯誤。JavaScript的策略是讓那些忘記預先聲明的變量成為全局變量,這導致查找 BUG 非常困難。

This was intended as a convenience to beginners by making it unnecessary to declare variables before using them. Unfortunately, forgetting to declare a variable is a very common mistake. JavaScript’s policy of making forgotten variables global creates bugs that can be very difficult to find.

P.102
代碼塊中聲明的變量在包含此代碼塊的函數的任何位置都是可見的。

JavaScript’s syntax comes from C. In all other C-like languages, a block (a set of statements wrapped in curly braces) creates a scope. Variables declared in a block are not visible outside of the block. JavaScript uses the block syntax, but does not provide block scope: a variable declared in a block is visible everywhere in the function containing the block. This can be surprising to programmers with experience in other languages.

更好的方式是在每個函數的開頭部份聲明所有變量。

In most languages, it is generally best to declare variables at the site of first use. That turns out to be a bad practice in JavaScript because it does not have block scope. It is better to declare all variables at the top of each function.

P.102
JavaScript has a mechanism that tries to correct faulty programs by automatically inserting semicolons. Do not depend on this. It can mask more serious errors. It sometimes inserts semicolons in places where they are not welcome. Consider the consequences of semicolon insertion on the return statement. If a return statement returns a value, that value expression must begin on the same line as the return:

return
{
status: true
};

這看起來是要返回一個包含 status 成員元素的對象。不幸的是,自動插入分號讓它變成了返回 undefined 。自動插入分號導致程序被誤解卻沒有任何警告提醒。如果把{放在上一行的尾部而不是下一行的頭部就可以避免該問題。

This appears to return an object containing a status member. Unfortunately, semicolon insertion turns it into a statement that returns undefined. There is no warning that semicolon insertion caused the misinterpretation of the program. The problem can be avoided if the { is placed at the end of the previous line and not at the beginning of the next line:

return {
status: true
};

P.103
當保留字被用作對象字面量的鍵值時,它們必須被引號括起來。它們不能被用在點表示法中,所以有時必須使用括號表示法。

They cannot be used to name variables or parameters. When reserved words are used as keys in object literals, they must be quoted. They cannot be used with the dot notation, so it is sometimes necessary to use the bracket notation instead:

var method; // ok
var class; // illegal
object = {box: value}; // ok
object = {case: value}; // illegal
object = {'case': value}; // ok
object.box = value; // ok
object.case = value; // illegal
object['case'] = value; // ok

Bear: 簡單說,不要把保留字拿來用就不會有這樣的困擾了,也就可以全面使用 . 的表示法。

P.103
Unicode把一對字符視為一個單一的字符。而JavaScript認一對字符是兩個不同的字符。

JavaScript’s characters are 16 bits. That is enough to cover the original 65,536 (which is now known as the Basic Multilingual Plane). Each of the remaining million characters can be represented as a pair of characters. Unicode considers the pair to be a single character. JavaScript thinks the pair is two distinct characters.

P.103
The typeof operator returns a string that identifies the type of its operand. So:

typeof 98.6

produces 'number'. Unfortunately:

typeof null

返回'object' 而不是 'null'。真糟糕。更好的檢測null的方式其實很簡單:

returns 'object' instead of 'null'. Oops. A better test for null is simply:

my_value === null

P.104
typeof不能辦別出 null 與對象。

A bigger problem is testing a value for objectness. typeof cannot distinguish between null and objects, but you can because null is falsy and all objects are truthy:

if (my_value && typeof my_value === 'object') {
// my_value is an object or an array!
}

Also see the later sections “NaN” and “Phony Arrays.”

在對正則表達式的類別識別上,各種JavaScript的實現不太一致。

Implementations disagree on the type of regular expression objects. Some implementations report that:

typeof /a/

一些實現會報告為'object'而其它的說它是 'function'。

is 'object', and others say that it is 'function'. It might have been more useful to report 'regexp', but the standard does not allow that.

P.104
如果該字符串第一個字符是0,那麼該字符串將被基於八進制而不是十進制來求值。在八進制中,8和9不是數字,所以parseInt("08") 和 parseInt("09") 產生 0 作為結果。這個錯誤導致了程序解析日期和時間時出現問題。幸運的是,parseInt 可以接受一個基數作為參數,如此一來,parseInt("08",10) 結果為8。我建議你總是提供這個基數參數。

If the first character of the string is 0, then the string is evaluated in base 8 instead of base 10. In base 8, 8 and 9 are not digits, so parseInt("08") and parseInt("09") produce 0 as their result. This error causes problems in programs that parse dates and times. Fortunately, parseInt can take a radix parameter, so that parseInt("08",10) produces 8. I recommend that you always provide the radix parameter.

P.105
舉例來說,美元可以通過乘以100而全部轉為美分,然後就可以準確地將美分相加,它們的和可以除以100來轉換回美元。當人們計算貨幣時當然會期望得到精確的結果。

For example, dollar values can be converted to whole cents values by multiplying them by 100. The cents then can be accurately added. The sum can be divided by 100 to convert back into dollars. People have a reasonable expectation when they count money that the results will be exact.

P.105
The value NaN is a special quantity defined by IEEE 754. It stands for not a number, even though:

typeof NaN === 'number' // true

The value can be produced by attempting to convert a string to a number when the string is not in the form of a number. For example:

+ '0' // 0
+ 'oops' // NaN

If NaN is an operand in an arithmetic operation, then NaN will be the result. So, if you have a chain of formulas that produce NaN as a result, at least one of the inputs was NaN, or NaN was generated somewhere.

typeof不能辦別數字和NaN,並且事實證明NaN不等同於它自已。

You can test for NaN. As we have seen, typeof does not distinguish between numbers and NaN, and it turns out that NaN is not equal to itself. So, surprisingly:

NaN === NaN // false
NaN !== NaN // true

JavaScript provides an isNaN function that can distinguish between numbers and NaN:

isNaN(NaN) // true
isNaN(0) // false
isNaN('oops') // true
isNaN('0') // false

判斷一個值是否可用做數字的最佳方法是使用isFinite函數,因為它會篩除掉NaN和Infinity。

The isFinite function is the best way of determining whether a value can be used as a number because it rejects NaN and Infinity. Unfortunately, isFinite will attempt to convert its operand to a number, so it is not a good test if a value is not actually a number. You may want to define your own isNumber function:

var isNumber = function isNumber(value) { return typeof value === 'number' &&
isFinite(value);
}

P.106
typeof運算符不能辦別數組和對象。要判斷一個值是否為數組,你還需要檢查它的constructor屬性。

The typeof operator does not distinguish between arrays and objects. To determine that a value is an array, you also need to consult its constructor property:

if (my_value && typeof my_value === 'object' &&
my_value.constructor === Array) {
// my_value is an array!
}

上面的檢測對於在不同幀或窗口創建的數組將會給出false。

That test will give a false negative if an array was created in a different frame or window. This test is more reliable when the value might have been created in another frame:

if (my_value && typeof my_value === 'object' &&
typeof my_value.length === 'number' &&
!(my_value.propertyIsEnumerable('length')) {
// my_value is truly an array!
}

The arguments array is not an array; it is an object with a length member. That test will identify the arguments array as an array, which is sometimes what youwant, even though arguments does not have any of the array methods. In any case, the test can still fail if the propertyIsEnumerable method is overridden.

P.106
簡體書釋者譯注:

如果期望能辦別arguments和數組,可以再增加一個判斷條件: typeof my value.slice === 'function'。

P.109
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true

請始終使用 === 和 !== 。

The lack of transitivity is alarming. My advice is to never use the evil twins. Instead,
always use === and !==. All of the comparisons just shown produce false with the
=== operator.

P.111
Function 的構造器是 eval 的另一種形式,所以它同樣也應該被避免使用。

The Function constructor is another form of eval, and should similarly be avoided.

P.112
在我自已的實踐中,我觀察到,當我使用++和--時,代碼往往變得過於緊密、復雜和隱晦。因此,作為一條原則,我不再使用它們。

In my own practice, I observed that when I used ++ and --, my code tended to be too tight, too tricky, too cryptic. So, as a matter of discipline, I don’t use them any more. I think that as a result, my coding style has become cleaner.

P.113
JavaScript很少被用來執行位元操作。

In Java, the bitwise operators work with integers. JavaScript doesn’t have integers. It only has double precision floating-point numbers. So, the bitwise operators convert their number operands into integers, do their business, and then convert them back. In most languages, these operators are very close to the hardware and very fast. In JavaScript, they are very far from the hardware and very slow. JavaScript is rarely used for doing bit manipulation.

P.113
JavaScript has a function statement as well as a function expression. This is confusing because they can look exactly the same. A function statement is shorthand for a var statement with a function value.
The statement:

function foo( ) {}

means about the same thing as:

var foo = function foo( ) {};

在整本書中,我一直使用的是第二種形式,因為它能明確表示foo是一個包含一個函數值的變量。

Throughout this book, I have been using the second form because it makes it clear that foo is a variable containing a function value. To use the language well, it is important to understand that functions are values.

function statements are subject to hoisting. This means that regardless of where a function is placed, it is moved to the top of the scope in which it is defined. This relaxes the requirement that functions should be declared before used, which I think leads to sloppiness. It also prohibits the use of function statements in if statements. It turns out that most browsers allow function statements in if statements, but they vary in how that should be interpreted. That creates portability problems.

一個語句不能以一個函數表達式開頭,因為官方的語法假定以單詞function開頭的語句是一個function語句。解決方法就是把函數表達括在一個圓括號之中。

The first thing in a statement cannot be a function expression because the official grammar assumes that a statement that starts with the word function is a function statement. The workaround is to wrap the function expression in parentheses:

(function ( ) {

var hidden_variable;
// This function can have some impact on
// the environment, but introduces no new
// global variables.
})( );

P.114
JavaScript has a set of typed wrappers. For example:

new Boolean(false)

不要使用new Boolean 、 new Number 、 new String。

produces an object that has a valueOf method that returns the wrapped value. This turns out to be completely unnecessary and occasionally confusing. Don’t use new Boolean or new Number or new String.

此外也請避免使用new Object 和 new Array。可使用{} 和 [] 來代替。

Also avoid new Object and new Array. Use {} and [] instead.


P.114
一個更好的應對策略就是根本不去使用 new 。

An even better coping strategy is to not use new at all.

P.114
而在JavaScript裡,void是一個運算符,它接受一個運算數並返回undefined。這沒有沒什麼用,而且會令人非常困惑。應避免使用它。

In many languages, void is a type that has no values. In JavaScript, void is an operator that takes an operand and returns undefined. This is not useful, and it is very confusing. Avoid void.