iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
Modern Web

這些那些你可能不知道我不知道的Web技術細節系列 第 15

為什麼你需要知道Function的三種用法

  • 分享至 

  • xImage
  •  

前言

在設計函式與呼叫函式前,或許得認識到一些限制,這些限制有可能造成需要使用不同的設計方式或呼叫方式。就來談一下一些在JavaScript語言裡的一些限制吧!

安全整數範圍

JavaScript裡關於「整數」是有範圍限制在的,按照規範這個值的範圍是(±2**53)內,也就是-9007199254740991~9007199254740992。這個值你可以透過Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGER取得。

BigInt

在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.20.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()

參考資料

本文同時發表於我的隨筆


上一篇
你可能不知道Function的三種用法
下一篇
你可能不知道的Function.prototype.bind()
系列文
這些那些你可能不知道我不知道的Web技術細節33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言