在設計函式與呼叫函式前,或許得認識到一些限制,這些限制有可能造成需要使用不同的設計方式或呼叫方式。就來談一下一些在JavaScript語言裡的一些限制吧!
JavaScript裡關於「整數」是有範圍限制在的,按照規範這個值的範圍是(±2**53)
內,也就是-9007199254740991~9007199254740992
。這個值你可以透過Number.MIN_SAFE_INTEGER
和Number.MAX_SAFE_INTEGER
取得。
在ES11後多加了一個基本類別BigInt
,儘管這個類型的使用方式和Number
並不相容^1。但是在過去寫過的7天搞懂js進階議題中曾經使用過。如果你有需要超過-9007199254740991~9007199254740992
範圍的整數,可以考慮使用BigInt
。
Array
會需要留意:屬性.length
的最大值爲2**32-1
也就是4294967295
。
這意味著以下一些操作是會出問題的
var arr = Array(4294967296); // 超出最大範圍
{
let arr = Array(4294967295);
arr.push(0); // 超出最大範圍
}
此外,-1
的索引值並不是像Python會得到最後一個元素^2。實際上經過以下操作:
var arr = [1,2,3,4];
arr[-1] = 5;
console.log(arr);
最後arr
的結果應該會像是:
[-1: 5, 1, 2, 3, 4]
另外.length
也不會算上-1
的索引值。
console.log(arr.length); // 4
實際上存取和賦值和Object
類似,可以使用任何字串存取:
console.log(arr["0"]); // 1
console.log(arr["-1"]); // 5
注意的是0.1 + 0.2
和0.3
是不同的:
arr[0.1+0.2] = 0.3;
console.log(arr[0.3]); // undefined
儘管Array
的屬性.length
的最大值爲4294967295
,但是Array遠可以儲存超過這個數量的物件。
經過嘗試,
Array
取值同樣接受類型BigInt
,這意味者你可以透過BigInt
存取或賦值超過整數安全範圍的值。但是屬性.length
仍不會超過最大值。
arguments
一般函式會有一個隱藏變數arguments
保留了所有輸入參數(但是箭頭函式不會有)。
function fn() {
console.log(arguments);
}
fn(1, 2, 3, 4); // Arguments(4)[1, 2, 3, 4]
這個變數雖然有Array
的特性,但是卻不是Array
function fn() {
console.log(arguments instanceof Array);
}
fn(1, 2, 3, 4); // false
相對來說參數蒐集(剩餘參數)就是Array
類型
function fn(...args) {
console.log(args instanceof Array);
}
fn(1, 2, 3, 4); // true
其實經過以上訊息,就可以猜想到 函式參數的有最大數量的限制 。這個數值從語言層面來看可能是9007199254740992
或是4294967295
。但其實通常遠小於這個數值,我在Edge瀏覽器特定情況下最大可以傳遞125681
個參數:
var arr = Array(125681);
function fn(...args){
console.log(args[0]);
}
fn(...arr)
fn(1, ...arr); // 超出限制
實際上這並不是一個常數值。
function fn2(...args){
fn(...args);
}
fn2(...arr); // 超出限制
還記得提到過的Call Stack嗎?
Call Stack通常是有最大容量限制的。
這意味著以下函式最終會出現問題:
function recursive(){
return recursive();
}
recursive(); // 最終會發生錯誤
function fn(...args){
console.log(args[0]);
}
function fn2(...args){
fn(...args);
}
fn2(1, 2, 3);
實際上參數傳遞也可能會佔用Call Stack的空間。以上面程式碼片段來說,這個過程更像是:
fn2(1, 2, 3)
| Call Stack |
| ---------- |
| |
| |
| |
| |
| Num : 3 |
| Num : 2 |
| Num : 1 |
| Func: fn2 |
+------------+
fn(1, 2, 3)
| Call Stack |
| ---------- |
| Num : 3 |
| Num : 2 |
| Num : 1 |
| Func: fn |
| Num : 3 |
| Num : 2 |
| Num : 1 |
| Func: fn2 |
+------------+
| Call Stack |
| ---------- |
| |
| |
| |
| undefined |
| Num : 3 |
| Num : 2 |
| Num : 1 |
| Func: fn2 |
+------------+
| Call Stack |
| ---------- |
| |
| |
| |
| |
| |
| |
| |
| undefined |
+------------+
當你在設計或使用函式時,需要考慮應該使用 剩餘參數 還是接受一個 陣列物件 。視情況還有可能使用Array.prototype.reduce()
來處理一些問題。這些語言以及實現層面的限制存在,也就需要了解Function.prototype.call()
和Function.prototype.apply()
。
本文同時發表於我的隨筆