## 3.2 值
### 3.2.1 數組
在JavaScript 中,數組可以容納任何類型的值,可以是字符串、數字、對象(object),甚至是其他數組(多維數組就是通過這種方式來實現的):
~~~
var a = [ 1, "2", [3] ];
a.length; // 3
a[0] === 1; // true
a[2][0] === 3; // true
~~~
對數組聲明后即可向其中加入值,不需要預先設定大小:
~~~
var a = [ ];
a.length; // 0
a[0] = 1;
a[1] = "2";
a[2] = [ 3 ];
a.length; // 3
~~~
(使用delete 運算符可以將單元從數組中刪除,但是請注意,單元刪除后,數組的length 屬性并不會發生變化。)
在創建“稀疏”數組(sparse array,即含有空白或空缺單元的數組)時要特別注意:
~~~
var a = [ ];
a[0] = 1;
// 此處沒有設置a[1]單元
a[2] = [ 3 ];
a[1]; // undefined
a.length; // 3
~~~
上面的代碼可以正常運行,但其中的“空白單元”(empty slot)可能會導致出人意料的結果。a[1] 的值為undefined,但這與將其顯式賦值為undefined(a[1] = undefined)還是有所區別。
數組通過數字進行索引,但它們也是對象,所以也可以包含字符串鍵值和屬性(但這些并不計算在數組長度內):
~~~
var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; // 1
a["foobar"]; // 2
a.foobar; // 2
~~~
如果字符串鍵值能夠被強制類型轉換為十進制數字的話,它就會被當作數字索引來處理。
~~~
var a = [ ];
a["13"] = 42;
a.length; // 14
~~~
**建議盡量使用對象來存放鍵值/ 屬性值,用數組來存放數字索引值。**
#### 類數組
有時需要將類數組(一組通過數字索引的值)轉換為真正的數組,這一般通過數組工具函數(如`indexOf(..)、concat(..)、forEach(..)` 等)來實現。
一些DOM 查詢操作會返回DOM 元素列表,它們并非真正意義上的數組,但十分類似。另一個例子是通過arguments 對象(類數組)將函數的參數當作列表來訪問(從ES6 開始已廢止)。
工具函數slice(..) 經常被用于這類轉換:
~~~
function foo() {
var arr = Array.prototype.slice.call( arguments );
arr.push( "bam" );
console.log( arr );
}
foo( "bar", "baz" ); // ["bar","baz","bam"]
~~~
如上所示,`slice() `返回參數列表(上例中是一個類數組)的一個數組復本。
用ES6 中的內置工具函數Array.from(..) 也能實現同樣的功能:
~~~
...
var arr = Array.from( arguments );
...
~~~
### 3.2.2 字符串
JavaScript 中的字符串和字符數組并不是一回事,只是看上去相似而已。
~~~
var a = "foo";
var b = ["f","o","o"];
~~~
字符串和數組的確很相似,它們都是類數組,都有length 屬性以及`indexOf(..)`(從ES5開始數組支持此方法)和`concat(..)` 方法:
~~~
a.length; // 3
b.length; // 3
a.indexOf( "o" ); // 1
b.indexOf( "o" ); // 1
var c = a.concat( "bar" ); // "foobar"
var d = b.concat( ["b","a","r"] ); // ["f","o","o","b","a","r"]
a === c; // false
b === d; // false
a; // "foo"
b; // ["f","o","o"]
~~~
但這并不意味著它們都是“字符的數組”,比如:
~~~
a[1] = "O";
b[1] = "O";
a; // "foo"
b; // ["f","O","o"]
~~~
JavaScript 中字符串是不可變的,而數組是可變的。,正確的方法應該是:`a.charAt(1)`
字符串不可變是指字符串的成員函數不會改變其原始值,而是創建并返回一個新的字符串。而數組的成員函數都是在其原始值上進行操作。
~~~
c = a.toUpperCase();
a === c; // false
a; // "foo"
c; // "FOO"
b.push( "!" );
b; // ["f","O","o","!"]
~~~
許多數組函數用來處理字符串很方便。雖然字符串沒有這些函數,但可以通過“借用”數組的非變更方法來處理字符串:
~~~
a.join; // undefined
a.map; // undefined
var c = Array.prototype.join.call( a, "-" );
var d = Array.prototype.map.call( a, function(v){
return v.toUpperCase() + ".";
} ).join( "" );
c; // "f-o-o"
d; // "F.O.O."
~~~
另一個不同點在于字符串反轉。數組有一個字符串沒有的可變更成員函數`reverse()`:
~~~
a.reverse; // undefined
b.reverse(); // ["!","o","O","f"]
b; // ["f","O","o","!"]
~~~
可惜我們無法“借用”數組的可變更成員函數,因為字符串是不可變的:
~~~
Array.prototype.reverse.call( a );
// 返回值仍然是字符串"foo"的一個封裝對象
~~~
一個變通(破解)的辦法是先將字符串轉換為數組,待處理完后再將結果轉換回字符串:
~~~
var c = a
// 將a的值轉換為字符數組
.split( "" )
// 將數組中的字符進行倒轉
.reverse()
// 將數組中的字符拼接回字符串
.join( "" );
c; // "oof"
~~~
這種方法對簡單的字符串完全適用,對于包含復雜字符(Unicode,如星號、多字節字符等)的字符串并不適用。
### 3.2.3 數字
JavaScript 只有一種數值類型:**number(數字)**,包括“整數”和帶小數的十進制數。此處“整數”之所以加引號是因為和其他語言不同,JavaScript 沒有真正意義上的整數。JavaScript 中的“整數”就是沒有小數的十進制數。所以42.0 即等同于“整數”42。
JavaScript 中的數字類型是基于IEEE 754 標準來實現的,該標準通常也被稱為“浮點數”。JavaScript 使用的是“雙精度”格式(即64 位二進制)。
**1. 數字的語法**
JavaScript 中的數字常量一般用十進制表示。例如:
~~~
var a = 42;
var b = 42.3;
~~~
數字前面的0 可以省略:
~~~
var a = 0.42;
var b = .42;
~~~
小數點后小數部分最后面的0 也可以省略:
~~~
var a = 42.0;
var b = 42.; //不建議這樣寫
~~~
默認情況下大部分數字都以十進制顯示,小數部分最后面的0 被省略,如:
~~~
var a = 42.300;
var b = 42.0;
a; // 42.3
b; // 42
~~~
特別大和特別小的數字默認用指數格式顯示,與`toExponential() `函數的輸出結果相同。
例如:
~~~
var a = 5E10;
a; //50000000000
a.toExponential(); // "5e+10"
var b = a * a;
b; // 2.5e+21
var c = 1 / a;
c; // 2e-11
~~~
由于數字值可以使用Number 對象進行封裝,因此數字值可以調用`Number.prototype `中的方法。例如,`tofixed(..) `方法可指定小數部分的顯示位數:
~~~
var a = 42.59;
a.toFixed( 0 ); // "43"
a.toFixed( 1 ); // "42.6"
a.toFixed( 2 ); // "42.59"
a.toFixed( 3 ); // "42.590"
a.toFixed( 4 ); // "42.5900"
~~~
上例中的輸出結果實際上是給定數字的字符串形式。
`toPrecision(..) `方法用來指定有效數位的顯示位數:
~~~
var a = 42.59;
a.toPrecision( 1 ); // "4e+1"
a.toPrecision( 2 ); // "43"
a.toPrecision( 3 ); // "42.6"
a.toPrecision( 4 ); // "42.59"
a.toPrecision( 5 ); // "42.590"
a.toPrecision( 6 ); // "42.5900"
~~~
上面的方法不僅適用于數字變量,也適用于數字常量。不過對于`. 運算符`需要給予特別注意,因為它是一個有效的數字字符,會被優先識別為數字常量的一部分,然后才是對象屬性訪問運算符。
~~~
// 無效語法:
42.toFixed( 3 ); // SyntaxError
// 下面的語法都有效:
(42).toFixed( 3 ); // "42.000"
0.42.toFixed( 3 ); // "0.420"
42..toFixed( 3 ); // "42.000"
~~~
下面的語法也是有效的(請注意其中的空格):
~~~
42 .toFixed(3); // "42.000" ,不建議使用
~~~
還可以用指數形式來表示較大的數字,如:
~~~
var onethousand = 1E3; // 即 1 * 10^3
var onemilliononehundredthousand = 1.1E6; // 即 1.1 * 10^6
~~~
數字常量還可以用其他格式來表示,如二進制、八進制和十六進制。當前的JavaScript 版本都支持這些格式:
~~~
0xf3; // 243的十六進制
0Xf3; // 同上
0363; // 243的八進制
~~~
從ES6 開始,嚴格模式(strict mode)不再支持0363 八進制格式(新格式如下)。0363 格式在非嚴格模式(non-strict mode)中仍然受支持,但是考慮到將來的兼容性,最好不要再使用。
ES6 支持以下新格式:
~~~
0o363; // 243的八進制
0O363; // 同上
0b11110011; // 243的二進制
0B11110011; // 同上
~~~
考慮到代碼的易讀性,不推薦使用0O363 格式,因為`0` 和大寫字母`O` 在一起容易混淆。建議盡量使用小寫的0x、0b 和0o。
**2. 較小的值**
二進制浮點數最大的問題是會出現如下情況:
~~~
0.1 + 0.2 === 0.3; // false
~~~
簡單來說,二進制浮點數中的0.1 和0.2 并不是十分精確,它們相加的結果并非剛好等于0.3,而是一個比較接近的數字0.30000000000000004,所以條件判斷結果為false。
那么應該怎樣來判斷0.1 + 0.2 和0.3 是否相等呢?
最常見的方法是設置一個誤差范圍值,通常稱為“機器精度”(machine epsilon), 對JavaScript 的數字來說,這個值通常是2^-52 (2.220446049250313e-16)。
從ES6 開始,該值定義在`Number.EPSILON` 中,我們可以直接拿來用,也可以為ES6 之前的版本寫polyfill:
~~~
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
~~~
可以使用Number.EPSILON 來比較兩個數字是否相等(在指定的誤差范圍內):
~~~
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCloseEnoughToEqual( a, b ); // true
numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
~~~
能夠呈現的最大浮點數大約是1.798e+308(這是一個相當大的數字),它定義在`Number.MAX_VALUE` 中。最小浮點數定義在`Number.MIN_VALUE` 中,大約是5e-324,它不是負數,但無限接近于0 !
**3. 整數的安全范圍**
數字的呈現方式決定了“整數”的安全值范圍遠遠小于`Number.MAX_VALUE`。能夠被“安全”呈現的最大整數是2^53 - 1,即9007199254740991,在ES6 中被定義為`Number.MAX_SAFE_INTEGER`。最小整數是-9007199254740991,在ES6 中被定義為`Number.MIN_SAFE_INTEGER`。
有時JavaScript 程序需要處理一些比較大的數字, 如數據庫中的64 位ID 等。由于JavaScript 的數字類型無法精確呈現64 位數值,所以必須將它們保存(轉換)為字符串。好在大數值操作并不常見(它們的比較操作可以通過字符串來實現)。如果確實需要對大數值進行數學運算,目前還是需要借助相關的工具庫。
**4. 整數檢測**
要檢測一個值是否是整數,可以使用ES6 中的Number.isInteger(..) 方法:
~~~
Number.isInteger( 42 ); // true
Number.isInteger( 42.000 ); // true
Number.isInteger( 42.3 ); // false
~~~
也可以為ES6 之前的版本polyfill Number.isInteger(..) 方法:
~~~
if (!Number.isInteger) {
Number.isInteger = function(num) {
return typeof num == "number" && num % 1 == 0;
};
}
~~~
要檢測一個值是否是安全的整數,可以使用ES6 中的`Number.isSafeInteger(..) `方法:
~~~
Number.isSafeInteger( Number.MAX_SAFE_INTEGER ); // true
Number.isSafeInteger( Math.pow( 2, 53 ) ); // false
Number.isSafeInteger( Math.pow( 2, 53 ) - 1 ); // true
~~~
可以為ES6 之前的版本polyfill Number.isSafeInteger(..) 方法:
~~~
if (!Number.isSafeInteger) {
Number.isSafeInteger = function(num) {
return Number.isInteger( num ) &&
Math.abs( num ) <= Number.MAX_SAFE_INTEGER;
};
}
~~~
**5. 32位有符號整數**
雖然整數最大能夠達到53 位,但是有些數字操作(如數位操作)只適用于32 位數字,所以這些操作中數字的安全范圍就要小很多,變成從Math.pow(-2,31)(-2147483648,約-21 億)到Math.pow(2,31) - 1(2147483647,約21 億)。
`a | 0 `可以將變量a 中的數值轉換為32 位有符號整數,因為`數位運算符| `只適用于32 位整數(它只關心32 位以內的值,其他的數位將被忽略)。因此與0 進行操作即可截取a 中的32 位數位。
某些特殊的值并不是32 位安全范圍的,如`NaN `和`Infinity`,此時會對它們執行虛擬操作(abstract operation)`ToInt32`,以便轉換為符合數位運算符要求的`+0 `值。
### 3.2.4 特殊的值
**1. 不是值的值**
undefined 類型只有一個值,即undefined。null 類型也只有一個值,即null。它們的名稱既是類型也是值。
undefined 和null 常被用來表示“空的”值或“不是值”的值。二者之間有一些細微的差別。例如:
? null 指空值(empty value)
? undefined 指沒有值(missing value)
或者:
? undefined 指從未賦值
? null 指曾賦過值,但是目前沒有值
`null` 是一個特殊關鍵字,不是標識符,不能將其當作變量來使用和賦值。然而`undefined `卻是一個標識符,可以被當作變量來使用和賦值。
**2. undefined**
在非嚴格模式下,可以為全局標識符undefined 賦值:
~~~
function foo() {
undefined = 2; // 非常糟糕的做法!
}
foo();
function foo() {
"use strict";
undefined = 2; // TypeError!
}
foo();
~~~
在非嚴格和嚴格兩種模式下,我們可以聲明一個名為undefined 的局部變量。
~~~
function foo() {
"use strict";
var undefined = 2;
console.log( undefined ); // 2
}
foo();
~~~
**永遠不要重新定義undefined!**
#### void運算符
undefined 是一個內置標識符(除非被重新定義),它的值為undefined,通過`void 運算符`即可得到該值。
表達式`void ___ `沒有返回值,因此返回結果是undefined。void 并不改變表達式的結果,只是讓表達式不返回值:
~~~
var a = 42;
console.log( void a, a ); // undefined 42
~~~
按慣例我們用`void 0` 來獲得`undefined`。`void 0、void 1 ` 和 `undefined `之間并沒有實質上的區別。
void 運算符在其他地方也能派上用場,比如不讓表達式返回任何結果(即使其有副作用)。
例如:
~~~
function doSomething() {
// 注: APP.ready 由程序自己定義
if (!APP.ready) {
// 稍后再試
return void setTimeout( doSomething,100 );
}
var result;
// 其他
return result;
}
// 現在可以了嗎?
if (doSomething()) {
// 立即執行下一個任務
}
~~~
這里`setTimeout(..) `函數返回一個數值(計時器間隔的唯一標識符,用來取消計時),但是為了確保if 語句不產生誤報(false positive),要void 掉它。
分開操作效果一樣,只是沒有使用void 運算符:
~~~
if (!APP.ready) {
// 稍后再試
setTimeout( doSomething,100 );
return;
}
~~~
總之,如果要將代碼中的值(如表達式的返回值)設為undefined,就可以使用void。這種做法并不多見,但在某些情況下卻很有用。
**3. 特殊的數字**
* (1)不是數字的數字
如果數學運算的操作數不是數字類型(或者無法解析為常規的十進制或十六進制數字),就無法返回一個有效的數字,這種情況下返回值為`NaN`。NaN 意指“不是一個數字”(not a number),將它理解為“無效數值”“失敗數值”或者“壞數值”可能更準確些。
例如:
~~~
var a = 2 / "foo"; // NaN
typeof a === "number"; // true
~~~
換句話說,“不是數字的數字”仍然是數字類型。
NaN 是一個“警戒值”(sentinel value,有特殊用途的常規值),用于指出數字類型中的錯誤情況,即“執行數學運算沒有成功,這是失敗后返回的結果”。
NaN 是一個特殊值,它和自身不相等,是唯一一個非自反(自反,reflexive,即x === x 不成立)的值,而`NaN != NaN 為true`。
因此,我們無法對NaN進行比較(結果永遠為false):
~~~
var a = 2 / "foo";
a == NaN; // false
a === NaN; // false
~~~
但可以使用內建的全局工具函數`isNaN(..) `來判斷一個值是否是NaN。
~~~
var a = 2 / "foo";
isNaN( a ); // true
~~~
isNaN(..) 有一個嚴重的缺陷,它的檢查方式過于死板,就是“檢查參數是否不是NaN,也不是數字”。但是這樣做的結果并不太準確:
~~~
var a = 2 / "foo";
var b = "foo";
a; // NaN
b; "foo"
window.isNaN( a ); // true
window.isNaN( b ); // true!
~~~
從ES6 開始我們可以使用工具函數Number.isNaN(..)。ES6 之前的瀏覽器的polyfill 如下:
~~~
if (!Number.isNaN) {
Number.isNaN = function(n) {
return (
typeof n === "number" &&
window.isNaN( n )
);
};
}
var a = 2 / "foo";
var b = "foo";
Number.isNaN( a ); // true
Number.isNaN( b ); // false!
~~~
實際上還有一個更簡單的方法,即利用NaN 不等于自身這個特點。NaN 是JavaScript 中**唯一**一個不等于自身的值。
于是我們可以這樣:
~~~
if (!Number.isNaN) {
Number.isNaN = function(n) {
return n !== n;
};
}
~~~
很多JavaScript 程序都可能存在NaN 方面的問題,所以應該盡量使用`Number.isNaN(..)`這樣可靠的方法,無論是系統內置還是polyfill。
* (2)無窮數
熟悉傳統編譯型語言(如C)的開發人員可能都遇到過編譯錯誤(compiler error)或者運行時錯誤(runtime exception),例如“除以0”:
~~~
var a = 1 / 0;
~~~
然而在JavaScript 中上例的結果為`Infinity`(即`Number.POSITIVE_INfiNITY`)。同樣:
~~~
var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity
~~~
如果除法運算中的一個操作數為負數, 則結果為-Infinity( 即`Number.NEGATIVE_INfiNITY`)。
JavaScript 使用有限數字表示法(finite numeric representation),所以和純粹的數學運算不同,JavaScript 的運算結果有可能溢出,此時結果為Infinity 或者-Infinity。
例如:
~~~
var a = Number.MAX_VALUE; // 1.7976931348623157e+308
a + a; // Infinity
a + Math.pow( 2, 970 ); // Infinity
a + Math.pow( 2, 969 ); // 1.7976931348623157e+308
~~~
計算結果一旦溢出為無窮數(infinity)就無法再得到有窮數。換句話說,就是可以從有窮走向無窮,但無法從無窮回到有窮。
從數學運算和JavaScript 語言的角度來說,`Infinity/Infinity` 是一個未定義操作,結果為`NaN`。有窮正數除以Infinity 結果是0。
* (3)零值
JavaScript 有一個常規的0(也叫作+0)和一個-0。
-0 除了可以用作常量以外,也可以是某些數學運算的返回值。例如:
~~~
var a = 0 / -3; // -0
var b = 0 * -3; // -0
~~~
加法和減法運算不會得到負零(negative zero)。
負零在開發調試控制臺中通常顯示為-0,但在一些老版本的瀏覽器中仍然會顯示為0。根據規范,對負零進行字符串化會返回"0":
~~~
var a = 0 / -3;
a; // -0
// 但是規范定義的返回結果是這樣!
a.toString(); // "0"
a + ""; // "0"
String( a ); // "0"
// JSON也如此,很奇怪
JSON.stringify( a ); // "0"
~~~
有意思的是,如果反過來將其從字符串轉換為數字,得到的結果是準確的:
~~~
+"-0"; // -0
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0
~~~
負零轉換為字符串的結果令人費解,它的比較操作也是如此:
~~~
var a = 0;
var b = 0 / -3;
a == b; // true
-0 == 0; // true
a === b; // true
-0 === 0; // true
0 > -0; // false
a > b; // false
~~~
要區分-0 和0,不能僅僅依賴開發調試窗口的顯示結果,還需要做一些特殊處理:
~~~
function isNegZero(n) {
n = Number( n );
return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 ); // true
isNegZero( 0 / -3 ); // true
isNegZero( 0 ); // false
~~~
**4. 特殊等式**
ES6 中新加入了一個工具方法Object.is(..) 來判斷兩個值是否絕對相等:
~~~
var a = 2 / "foo";
var b = -3 * 0;
Object.is( a, NaN ); // true
Object.is( b, -0 ); // true
Object.is( b, 0 ); // false
~~~
對于ES6 之前的版本,Object.is(..) 有一個簡單的polyfill:
~~~
if (!Object.is) {
Object.is = function(v1, v2) {
// 判斷是否是-0
if (v1 === 0 && v2 === 0) {
return 1 / v1 === 1 / v2;
}
// 判斷是否是NaN
if (v1 !== v1) {
return v2 !== v2;
}
// 其他情況
return v1 === v2;
};
}
~~~
能使用`==` 和`===`時就盡量不要使用Object.is(..),因為前者效率更高、更為通用。`Object.is(..) `主要用來處理那些特殊的相等比較。
### 3.2.5 值和引用
JavaScript 中**沒有指針**,引用的工作機制也不盡相同。在JavaScript 中變量不可能成為指向另一個變量的引用。
JavaScript 引用指向的是值。如果一個值有10 個引用,這些引用指向的都是同一個值,它們**相互之間沒有引用/ 指向關系**。
JavaScript 對值和引用的賦值/ 傳遞在語法上沒有區別,完全根據值的類型來決定。
~~~
var a = 2;
var b = a; // b是a的值的一個副本
b++;
a; // 2
b; // 3
var c = [1,2,3];
var d = c; // d是[1,2,3]的一個引用
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
~~~
**簡單值**(即標量基本類型值,scalar primitive)總是通過值復制的方式來賦值/ 傳遞,包括null、undefined、字符串、數字、布爾和ES6 中的symbol。
**復合值**(compound value)——對象(包括數組和封裝對象)和函數,則總是通過引用復制的方式來賦值/ 傳遞。
由于引用指向的是值本身而非變量,所以一個引用無法更改另一個引用的指向。
~~~
var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// 然后
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
~~~
b=[4,5,6] 并不影響a 指向值[1,2,3],除非b 不是指向數組的引用,而是指向a 的指針,但在JavaScript 中不存在這種情況!
函數參數就經常讓人產生這樣的困惑:
~~~
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// 然后
x = [4,5,6];
x.push( 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // 是[1,2,3,4],不是[4,5,6,7]
~~~
向函數傳遞a 的時候,實際是將引用a 的一個副本賦值給x,而a 仍然指向[1,2,3]。在函數中我們可以通過引用x 來更改數組的值(push(4) 之后變為[1,2,3,4])。但x =[4,5,6] 并不影響a 的指向,所以a 仍然指向[1,2,3,4]。
如果要將a 的值變為[4,5,6,7],必須更改x 指向的數組,而不是為x 賦值一個新的數組。
~~~
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// 然后
x.length = 0; // 清空數組
x.push( 4, 5, 6, 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // 是[4,5,6,7],不是[1,2,3,4]
~~~
如果通過值復制的方式來傳遞復合值(如數組),就需要為其創建一個副本,這樣傳遞的就不再是原始值。例如:
~~~
foo( a.slice() );
~~~
`slice(..) `不帶參數會返回當前數組的一個淺復本(shallow copy)。由于傳遞給函數的是指向該復本的引用,所以foo(..) 中的操作不會影響a 指向的數組。
相反,如果要將標量基本類型值傳遞到函數內并進行更改,就需要將該值封裝到一個復合值(對象、數組等)中,然后通過引用復制的方式傳遞。
~~~
function foo(wrapper) {
wrapper.a = 42;
}
var obj = {
a: 2
};
foo( obj );
obj.a; // 42
~~~
這里obj 是一個封裝了標量基本類型值a 的封裝對象。obj 引用的一個副本作為參數wrapper 被傳遞到foo(..) 中。這樣就可以通過wrapper 來訪問該對象并更改它的屬性。函數執行結束后obj.a 將變成42。
- 前言
- 第一章 JavaScript簡介
- 第三章 基本概念
- 3.1-3.3 語法、關鍵字和變量
- 3.4 數據類型
- 3.5-3.6 操作符、流控制語句(暫略)
- 3.7函數
- 第四章 變量的值、作用域與內存問題
- 第五章 引用類型
- 5.1 Object類型
- 5.2 Array類型
- 5.3 Date類型
- 5.4 基本包裝類型
- 5.5 單體內置對象
- 第六章 面向對象的程序設計
- 6.1 理解對象
- 6.2 創建對象
- 6.3 繼承
- 第七章 函數
- 7.1 函數概述
- 7.2 閉包
- 7.3 私有變量
- 第八章 BOM
- 8.1 window對象
- 8.2 location對象
- 8.3 navigator、screen與history對象
- 第九章 DOM
- 9.1 節點層次
- 9.2 DOM操作技術
- 9.3 DOM擴展
- 9.4 DOM2和DOM3
- 第十章 事件
- 10.1 事件流
- 10.2 事件處理程序
- 10.3 事件對象
- 10.4 事件類型
- 第十一章 JSON
- 11.1-11.2 語法與序列化選項
- 第十二章 正則表達式
- 12.1 創建正則表達式
- 12.2-12.3 模式匹配與RegExp對象
- 第十三章 Ajax
- 13.1 XMLHttpRequest對象
- 你不知道的JavaScript
- 一、作用域與閉包
- 1.1 作用域
- 1.2 詞法作用域
- 1.3 函數作用域與塊作用域
- 1.4 提升
- 1.5 作用域閉包
- 二、this與對象原型
- 2.1 關于this
- 2.2 全面解析this
- 2.3 對象
- 2.4 混合對象“類”
- 2.5 原型
- 2.6 行為委托
- 三、類型與語法
- 3.1 類型
- 3.2 值
- 3.3 原生函數