<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 第15章 函數 函數是可以調用的值。定義函數的一種方法稱為函數聲明。例如,以下代碼定義了具有單個參數`x`的函數`id`: ```js function id(x) { return x; } ``` `id`函數中,`return`語句返回一個值,您可以通過它的名稱來調用函數,后面是括號中的參數 ```js > id('hello') 'hello' ``` 如果不從函數返回任何東西,返回`undefined`(隱式地): ```js > function f(){} > f() undefined ``` 這部分只顯示了一種定義函數和調用函數的一種方式。其他的將在后面描述。 ## 函數在JavaScript中的三個角色 一旦您定義了一個函數,它就可以扮演多個角色: ### 非方法函數(“正常函數”) 您可以直接調用函數。然后它作為一個正常的函數。這是一個示例調用: ```js id('hello') ``` 按照慣例,正常函數的名稱以小寫字母開始。 ### 構造函數 您可以通過new操作員調用函數。然后它成為一個構造函數,一個對象的工廠。這是一個示例調用: ```js new Date() ``` 按照慣例,構造函數的名稱以大寫字母開頭。 ### 方法 您可以將函數存儲在對象的屬性中,將其轉換為可以通過該對象調用的方法。 這是一個調用示例: ``` obj.method() ``` 按照慣例,方法名稱以小寫字母開頭。 本章介紹非方法功能; [第17章](###)解釋了構造函數和方法。 ## 術語:“Parameter”對比“Argument” > 簡略描述為:parameter=形參(formal parameter), argument=實參(actual parameter)。 術語 Parameter 和 Argument 都經常互換使用,因為上下文通常會讓你明白意圖的含義是什么。以下是區分它們的經驗法則。 * *Parameter* 用于定義一個函數。它們也被稱為formal parameters (形式參數)和 formal arguments (形參)。在以下示例中,param1并且param2是形參: ```js function foo(param1, param2) { ... } ``` * *Arguments* 用于調用函數。它們也稱為 actual parameters (實際參數)和 actual arguments (實參)。在以下示例中,3并且7是實參: ```js foo(3, 7); ``` ## 定義函數 本節介紹創建函數的三種方法: 1. 通過函數表達式 2. 通過函數聲明 3. 通過構造函數 `Function()` 所有函數都是對象,都是`Function`的實例: ```js function id(x) { return x; } console.log(id instanceof Function); // true ``` 因此,函數從`Function.prototype`中獲取它們的方法。 ### 函數表達式 函數表達式產生一個值 - 一個函數對象。例如: ~~~ var add = function (x, y) { return x + y }; console.log(add(2, 3)); // 5 ~~~ 該代碼將函數表達式的結果分配給變量add,并通過該變量調用它。函數表達式產生的值可以分配給一個變量(如最后一個例子所示),作為參數傳遞給另一個函數等等。因為正常的函數表達式沒有名稱,它們也被稱為匿名函數表達式。 #### 命名函數表達式 命名函數表達式(Named Function Expression,即“有名字函數表達式”,與“匿名函數”相對。——譯者注) 你可以給一個函數表達式賦值一個名字。命名函數表達式允許**函數表達式引用自身,這對自我遞歸有用**: ```js var fac = function me(n) { if (n > 0) { return n * me(n-1); } else { return 1; } }; console.log(fac(3)); // 6 ``` <table> <th style="text-align:center">注意</th> <tr> <td> 命名函數表達式的名稱只能在函數表達式中訪問: ```js var repeat = function me(n, str) { return n > 0 ? str + me(n-1, str) : ''; }; console.log(repeat(3, 'Yeah')); // YeahYeahYeah console.log(me); // ReferenceError: me is not defined ``` </td> </tr> </table> ### 函數聲明 以下是一個函數聲明: ```js function add(x, y) { return x + y; } ``` 前面看起來像一個函數表達式,但它是一個語句(請參見[表達式與語句](###第7章))。它大致相當于以下代碼: ```js var add = function (x, y) { return x + y; }; ``` 換句話說,函數聲明聲明一個新變量,創建一個**函數對象(即:new Function)**,并將其分配給該變量。 ### 函數構造器 構造函數 `Function()`對存儲在字符串中的JavaScript代碼進行解析運行,例如,下面的代碼與前面的示例是一樣的: ```js var add = new Function('x', 'y', 'return x + y'); ``` 但是,這種定義函數的方法是運行緩慢的,并且將代碼保存在字符串中(工具無法訪問)。因此,如果我們盡可能還是使用函數表達式或函數聲明。[解析運行代碼](##第23章)一章更詳細地解釋了`Function()`,它和`eval()`類似。 ## 提升(Hoisting) 提升意味著“**移動到作用域的開始位置**”。 函數聲明被完全地提升,只有部分變量聲明被提升。 功能聲明完全懸掛。這允許您在聲明之前調用函數: ```js foo(); function foo() { //該函數被提升 ... } ``` 上述代碼的工作原理是JavaScript引擎將`foo`函數聲明移動到范圍的開頭。上面的代碼,就會像下面這樣被執行: ```js function foo() { ... } foo(); ``` `var`的聲明部分會被提升,賦值部分不會。因此,使用var聲明和與前一個示例類似的函數表達式會導致錯誤: ```js foo(); // TypeError: undefined is not a function var foo = function () { ... }; ``` 只有變量聲明被提起。JS引擎會這樣執行上面的代碼: ```js var foo; foo(); // TypeError: undefined is not a function foo = function () { ... }; ``` ## 函數的名稱 大多數JavaScript引擎都支持函數對象的非標準屬性`name`。函數聲明有: ```js > function f1() {} > f1.name 'f1' ``` 匿名函數表達式的名稱是空字符串: ```js > var f2 = function(){}; > f2.name “” ``` 但命名函數表達式有一個名稱:: ```js > var f3 = function myName() {}; > f3.name 'myName' ``` 函數的名稱對于調試非常有用。有些人出于這個原因會給出函數表達式的名字。 ## 哪個更好:函數聲明或函數表達式? 如果您更喜歡像下面這樣的函數聲明? ```js function id(x) { return x; } ``` 還是一個`var`聲明加上一個函數表達式達到同等效果? ```js var id = function (x) { return x; }; ``` 它們基本相同,但函數聲明相對于函數表達式有兩個優點: 1. 它們被提升(參見[提升](##)),因此在它們出現在源代碼之前,您可以調用它們 2. 它們有一個名稱(參見[函數的名稱](###))。然而,JavaScript引擎在推斷匿名函數表達式的名稱方面做得越來越好。 ## 更多控制函數的調用:call()、apply()和bind() `call()`,`apply()`和`bind()`是所有函數具有的方法(記住函數是對象,因此具有方法)。它們可以在調用方法時為`this`提供一個值,因此在面向對象的上下文中主要是有趣的(請參閱[調用函數:call()、apply()和bind()](###第17章#oop_call_apply_bind))。 本節解釋了非對象方法的兩個用例。 ### func.apply(thisValue,argArray) 此方法調用函數時使用 `argArray`的元素作為參數; 也就是說,以下兩個表達式是等效的: ```js func(arg1, arg2, arg3) func.apply(null, [arg1, arg2, arg3]) ``` 執行func時`thisValue`的值被傳遞給函數內的`this`。在非面向對象中不需要設置它,因此在它被設置為`null`。 當一個函數以類似數組的方式(但不是數組)接受多個參數時,可以使用`apply()` 多虧`apply()`,我們可以使用`Math.max()`(參見[其他函數](###第21章#Math_max))來確定數組的最大元素: ```js > Math.max(17, 33, 2) 33 > Math.max.apply(null, [17, 33, 2]) 33 ``` ### func.bind(thisValue, arg1, ..., argN) 它執行時是偏函數用法 - 創建了一個新的函數,該函數將調用func,并將其`this`設置為`thisValue`并且下面的參數是:`arg1`,到`argN`,是新函數的實際參數。`hisValue`在以下非面向對象中不需要設置,所以被它設置為`null`。 在這里,我們使用`bind()`來創建一個新的函數`plus1()`,它就像`add()`,但是它只需要參數`y`,因為`x`始終是1: ```js function add(x, y) { return x + y; } var plus1 = add.bind(null, 1); console.log(plus1(5)); // 6 ``` 換句話說,我們創建了一個相當于以下代碼的新函數: ```js function plus1(y) { return add(1, y); } ``` ## 處理缺失或額外參數 JavaScript有強制一個函數的參數數量:你可以用任意數量的實際參數來調用它,而與定義的形式參數無關。因此,實際參數和形式參數的數量可以有兩種不同: **實際參數比形式參數多** 多出的參數被忽略,但可以通過特殊的類數組變量`arguments`(稍后將討論)來獲取。 **實際參數比形式參數少** 缺少的形式參數的值為`undefined`。 ### 所有參數可以按索引訪問:特殊的變量參數 特殊變量`arguments`存在函數內部(包括方法)。它是一個類似數組的對象,它保存當前函數調用的所有實際參數。以下代碼使用它: ```js function logArgs() { for (var i=0; i<arguments.length; i++) { console.log(i+'. '+arguments[i]); } } ``` 這是交互結果輸出:: ``` > logArgs('hello', 'world') 0. hello 1. world ``` `arguments` 具有以下特點: * 它是類數組的,但不是數組。一方面,它有一個`length`屬性,并且可以通過索引讀取和寫入各個參數。 另一方面,arguments不是一個數組,它只是類似的。它沒有數組方法(slice(),forEach(),等等)。幸運的是,您可以借用數組方法或將`arguments`轉換為數組,如[Array-Like Objects和Generic Methods](##第17章#array-like_objects)所述。 * 它是一個對象,因此所有對象方法和操作符都是可用的。例如,您可以使用`in`運算符(迭代和檢測屬性)來檢查`arguments`是否具有給定的索引: ```js > function f() { return 1 in arguments } > f('a') false > f('a', 'b') true ``` 您可以以類似的方式使用`hasOwnProperty()`(迭代和屬性檢測): ```js > function g() { return arguments.hasOwnProperty(1) } > g('a', 'b') true ``` #### 棄用的參數的特性 嚴格模式會降低一些`arguments`的不尋常的特性: * `arguments.callee` 指向了當前函數。它主要用于匿名函數中的自遞歸,并且在嚴格模式下不允許。作為解決方法,使用命名函數表達式(請參閱[命名函數表達式](###)),它可以通過其名稱引用自身。 * 在非嚴格模式下,如果你改變了一個參數,`arguments`也會保持最新: ```js function sloppyFunc(param) { param = 'changed'; return arguments[0]; } console.log(sloppyFunc('value')); // 被改變的 ``` 在嚴格的模式下,這種保持更新的特性會失效: ```js function strictFunc(param) { 'use strict'; param = 'changed'; return arguments[0]; } console.log(strictFunc('value')); // value ``` * 嚴格模式禁止給`arguments`賦值(例如,通過`arguments++`)。仍然允許分配給其他元素和屬性。 ### 強制性參數,強制執行最低的的參數數量 有三種方法來判斷一個參數是否丟失。 首先,你可以檢查它是否為`undefined`: ```js function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error('Missing parameter: mandatory'); } } ``` 其次,您可以將該參數解釋為一個布爾值。那么`undefined`就被認為是`false`的。但是,還有一個警告:其他幾個值也被認為是假的(參見[Truthy和Falsy值](###)),所以這個檢查不能區分。 打個比方:`0`和一個缺失的參數,都為`false`: ```js if (!mandatory) { throw new Error('Missing parameter: mandatory'); } ``` 第三,你也可以檢查的`arguments`的長度,以強制執行最低的參數數量: ```js if (arguments.length < 1) { throw new Error('You need to provide at least 1 argument'); } ``` 最后一個方法與其他方法不同: * 前兩種方法不區分`foo()`和`foo(undefined)`。在這兩種情況下,拋出異常。 * 第三種方法`foo()`會拋出一個異常,并為`foo(undefined)`設置可選的選項。 ### 可選參數 如果一個參數是可選的,這意味著如果一個參數缺失,則給它一個默認值。 與強制參數類似,有四種選擇。 首先,檢查`undefined`: ```js function bar(arg1, arg2, optional) { if (optional === undefined) { optional = 'default value'; } } ``` 第二,將`optional`解釋為布爾型:: ```js if (!optional) { optional = 'default value'; } ``` 第三,您可以使用`||`操作符(參見[Logical Or(||)](###第10章#logical_or)),如果它為`true`,則返回左邊的操作數。否則,它返回右邊的操作數: ```js // Or operator: use left operand if it isn't falsy optional = optional || 'default value'; ``` 第四,您可以通過`arguments.length`的方式檢查函數的參數數量: ```js if (arguments.length < 3) { optional = 'default value'; } ``` 最后一個方法與其他方法不同: * 前三種方法不區分`bar(1, 2)`和`bar(1, 2, undefined)`。在這兩種情況下`optional`都是`'default value'`。 * 第四種方法設置`bar(1, 2)`下的`optional`為'default value',`bar(1, 2, undefined)`下的`optional`值為`undefined`。 另一種可能是將可選參數作為命名參數,作為對象字面量的屬性(請參閱[命名參數](http://speakingjs.com/es5/ch15.html#named_parameters))。 ### 模擬引用傳遞參數 在JavaScript中,不能通過引用傳遞參數; 也就是說,如果將一個變量傳遞給一個函數,它的值將被復制并傳遞給函數(通過值傳遞)。因此,這個函數不能更改這個變量。如果需要這樣做,則必須將變量的值封裝起來(例如:一個數組)。 此示例演示了增加變量的函數: ```js function incRef(numberRef) { numberRef[0]++; } var n = [7]; incRef(n); console.log(n[0]); // 8 ``` ### 缺陷:意想不到的可選參數 > 方法簽名由**方法名稱**和一個**參數列表**(方法的參數的順序和類型)組成。 方法的簽名可以唯一的確定這個函數。 如果你把一個函數`c`作為一個參數傳遞給另一個函數`f`,那么你必須知道兩個簽名: * 函數`f`需要知道其參數的簽名,`f`可以提供幾個參數,讓`c`來決定使用幾個參數(如果有的話)。 * 函數`c`的實際簽名,例如,`c`可能支持可選參數。 如果兩者有差異,你就會得到意想不到的結果:c原本可能有你不知道的可選參數并且這將解釋函數`f`提供的附加參數不正確。 例如,數組方法`map()`的參數是只有具有單個參數的普通函數(參見[轉換方法](###d第18章)): ```js > [ 1, 2, 3 ].map(function (x) { return x * x }) [ 1, 4, 9 ] ``` 可以作為參數傳遞的一個函數是`parseInt()`(參見[通過parseInt()得到的整數](###第11章)): ```js > parseInt('1024') 1024 ``` 你可能(錯誤地)認為`map()`只提供一個參數,并且`parseInt()`只接受一個參數。那么你會驚訝于以下結果: ```js > [ '1', '2', '3' ].map(parseInt) [ 1, NaN, NaN ] ``` `map()`期望一個具有以下簽名的函數: ```js function (element, index, array) ``` 但是`parseInt()`有以下簽名: ```js parseInt(string, radix?) ``` 因此,`map()`不僅填充了`string`(通過`element`),而且還填充了`radix`(通過`index`)。這意味著前面的數組的值如下: ```js > parseInt('1', 0) 1 > parseInt('2', 1) NaN > parseInt('3', 2) NaN ``` 總之,要注意那些你不確定其簽名的函數和方法。如果您使用它們,那么要明確知道接收哪些參數以及傳遞哪些參數是很有意義的。下面通過回調實現的: ```js > ['1', '2', '3'].map(function (x) { return parseInt(x, 10) }) [ 1, 2, 3 ] ``` ## 命名參數 在編程語言中調用函數(或方法)時,必須將實際參數(由調用者指定)映射到形式參數(函數定義)。有兩種常見的方法: 位置參數通過位置映射。第一個實際參數映射到第一個形式參數,第二個實際參數映射到第二個形式參數,依此類推。 命名參數使用名稱(標簽)來執行映射。名稱與函數定義中的形式參數相關聯,并在函數調用中標記實際參數。只要它們被正確地標記,命名參數出現的順序并不重要,。 命名參數有兩個主要優點: 它們為函數調用中的參數提供描述,它們也可以用于可選參數。我首先解釋好處,然后向您展示如何通過對象字面量模擬JavaScript中的命名參數。 ### 命名參數的描述 一旦一個函數有多個參數,你可能會對每個參數的用途感到困惑。例如,假設您有一個函數`selectEntries()`,它從數據庫返回條目。給定以下函數調用:: ```js selectEntries(3, 20, 2); ``` 這三個數字是什么意思?Python支持命名參數,使我們很容易弄清楚發生了什么: ```python selectEntries(start=3, end=20, step=2) # Python syntax ``` ### 可選的命名參數 可選的位置參數只有**在最后被省略時**才有效。在其他任何地方,您必須插入占位符,例如`null`,以便其余的參數具有正確的位置。對于可選的命名參數,這不是問題。你可以很容易地忽略其中任何一個。這里有些例子: ```python # Python syntax selectEntries(step=2) selectEntries(end=20, start=3) selectEntries() ``` ### 在JavaScript中模擬命名參數 JavaScript不支持像`Python`和許多其他語言那樣的命名參數。但是有一個相當優雅的模擬:通過一個對象字面量來命名參數,作為一個單獨的實際參數傳遞。當您使用該技術時,`selectEntries()`的調用看起來是這樣的: ```js selectEntries({ start: 3, end: 20, step: 2 }); //這是使用來對象字面量來模擬JS不支持的命名參數 ``` 該函數接收具有`start`, `end`, 和 `step`屬性的對象。你可以省略其中的任何一個: ```js selectEntries({ step: 2 }); selectEntries({ end: 20, start: 3 }); selectEntries(); ``` 您可以像下面一樣實現`selectEntries()`: ```js function selectEntries(options) { options = options || {}; var start = options.start || 0; var end = options.end || getDbLength(); var step = options.step || 1; ... } ``` 您還可以將位置參數與命名參數相結合。對后者來說,這是慣例: ```js someFunc(posArg1, posArg2, { namedArg1: 7, namedArg2: true }); ``` | **注意** | | :----------: | | 在JavaScript中,這里顯示的命名參數的模式有時被稱為*選項*或*選項對象*(例如:通過jQuery文檔)。 |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看