<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 23.可調用值 > 原文: [http://exploringjs.com/impatient-js/ch_callables.html](http://exploringjs.com/impatient-js/ch_callables.html) > > 貢獻者:[阿純](https://github.com/rechun) ### 23.1。各種功能 JavaScript 有兩類功能: * 普通函數 可以扮演幾個角色: * 實際功能(在其他語言中,你只需使用術語“功能”;在 JavaScript 中,我們需要區分角色“真實功能”和可以扮演該角色的實體“普通功能”) * 方法 * 構造函數 * 專用功能 只能扮演其中一個角色。例如: * 箭頭功能 只能是實際功能。 * 方法 只能是一種方法。 * 類 只能是構造函數。 接下來的部分將解釋所有這些內容的含義。 ### 23.2。普通功能 以下代碼顯示了三種方法(大致)相同的事情:創建一個普通的函數。 ```js // Function declaration (a statement) function ordinary1(a, b, c) { // ··· } // Anonymous function expression const ordinary2 = function (a, b, c) { // ··· }; // Named function expression const ordinary3 = function myName(a, b, c) { // `myName` is only accessible in here }; ``` 正如我們在[中看到的關于變量](ch_variables-assignment.html#hoisting)的章節,函數聲明被提升,而變量聲明(例如通過`const`)則沒有。我們將在本章后面探討其后果。 函數聲明和函數表達式的語法非常相似。上下文決定哪個是哪個。有關這種語法歧義的更多信息,請參閱[關于語法](ch_syntax.html#ambiguous-syntax)的章節。 #### 23.2.1。函數聲明的一部分 讓我們通過一個例子來檢查函數聲明的各個部分: ```js function add(x, y) { return x + y; } ``` * `add`是函數聲明的 _ 名稱 _。 * `add(x, y)`是函數聲明的 _ 頭 _。 * `x`和`y`是 _ 參數 _。 * 花括號(`{`和`}`)以及它們之間的所有東西都是函數聲明的 _ 體 _。 * `return`運算符顯式返回函數的值。 #### 23.2.2。普通功能的名稱 函數表達式的名稱只能在函數內部訪問,函數可以使用它來引用自身(例如,用于自遞歸): ```js const func = function funcExpr() { return funcExpr }; assert.equal(func(), func); // The name `funcExpr` only exists inside the function: assert.throws(() => funcExpr, ReferenceError); ``` 相反,函數聲明的名稱可在當前范圍內訪問: ```js function funcDecl() { return funcDecl } // The name `funcDecl` exists inside the current scope assert.equal(funcDecl(), funcDecl); ``` #### 23.2.3。普通功能扮演的角色 請考慮上一節中的以下函數聲明: ```js function add(x, y) { return x + y; } ``` 該函數聲明創建一個名為`add`的普通函數。作為一個普通的功能,`add()`可以扮演三個角色: * 實函數:通過函數調用調用。這是大多數編程語言認為簡單的 _ 函數 _。 ```js assert.equal(add(2, 1), 3); ``` * 方法:存儲在屬性中,通過方法調用調用。 ```js const obj = { addAsMethod: add }; assert.equal(obj.addAsMethod(2, 4), 6); ``` * 構造函數/類:通過`new`調用。 ```js const inst = new add(); assert.equal(inst instanceof add, true); ``` (順便說一句,類的名稱通常以大寫字母開頭。) ### 23.3。專業功能 專業功能是普通功能的專用版本。他們每個人只扮演一個角色: * 箭頭函數 只能是一個真正的函數: ```js const arrow = () => { return 123 }; assert.equal(arrow(), 123); ``` * 方法 只能是一種方法: ```js const obj = { method() { return 'abc' } }; assert.equal(obj.method(), 'abc'); ``` * 類 只能是構造函數: ```js class MyClass { /* ··· */ } const inst = new MyClass(); ``` 除了更好的語法之外,每種專用功能還支持新功能,使其在工作中比普通功能更好。 * 本章稍后將解釋箭頭功能[。](ch_callables.html#arrow-functions) * 方法在[關于單個對象](ch_single-objects.html#methods)的章節中進行了解釋。 * 在[關于原型鏈和類](ch_proto-chains-classes.html#classes)的章節中解釋了類。 TBL。 [18](#tbl:capabilities-of-functions) 列出了普通和專用功能的功能。 Table 18: Capabilities of four kinds of functions. | | 普通功能 | 箭頭功能 | 方法 | 類 | | --- | --- | --- | --- | --- | | 函數調用 | `?` | `?` | `?` | `?` | | 方法調用 | `?` | 詞匯`this` | `?` | `?` | | 構造函數調用 | `?` | `?` | `?` | `?` | #### 23.3.1。專業功能仍然是功能 值得注意的是,箭頭函數,方法和類仍然歸類為函數: ```js > (() => {}) instanceof Function true > ({ method() {} }.method) instanceof Function true > (class SomeClass {}) instanceof Function true ``` #### 23.3.2。建議:更喜歡專門的功能 通常,您應該優先使用專用函數而不是普通函數,尤其是類和方法。但是,箭頭功能和普通功能之間的選擇不那么明確: * 箭頭函數沒有`this`作為隱式參數。如果你使用真正的函數,這幾乎總是你想要的,因為它避免了一個重要的`this`相關的陷阱(詳見[關于單個對象](ch_single-objects.html#avoiding-pitfalls-of-this)的章節)。 * 但是,我喜歡語法上的函數聲明(它產生一個普通的函數)。如果你不在其中使用`this`,它大部分相當于`const`加箭頭功能: ```js function funcDecl(x, y) { return x * y; } const arrowFunc = (x, y) => { return x * y; }; ``` #### 23.3.3。箭頭功能 Arrow 函數被添加到 JavaScript 中有兩個原因: 1. 為創建函數提供更簡潔的方法。 2. 更容易使用實際函數:不能在普通函數中引用周圍范圍的`this`(詳見[很快](ch_callables.html#arrow-functins-lexical-this))。 ##### 23.3.3.1。箭頭函數的語法 讓我們回顧一下匿名函數表達式的語法: ```js const f = function (x, y, z) { return 123 }; ``` (粗略)等效箭頭函數如下所示。箭頭函數是表達式。 ```js const f = (x, y, z) => { return 123 }; ``` 這里,箭頭函數的主體是一個塊。但它也可以是一種表達方式。以下箭頭功能與前一個功能完全相同。 ```js const f = (x, y, z) => 123; ``` 如果箭頭函數只有一個參數且該參數是標識符(不是解構模式),那么您可以省略參數周圍的括號: ```js const id = x => x; ``` 將箭頭函數作為參數傳遞給其他函數或方法時,這很方便: ```js > [1,2,3].map(x => x+1) [ 2, 3, 4 ] ``` 最后一個例子展示了箭頭功能的第一個好處 - 簡潔。相反,這是相同的方法調用,但帶有函數表達式: ```js [1,2,3].map(function (x) { return x+1 }); ``` ##### 23.3.3.2。箭頭功能:詞匯`this` 普通函數既可以是方法,也可以是實際函數。唉,這兩個角色是沖突的: * 由于每個普通函數都可以是一個方法,因此它有自己的`this`。 * 自己的`this`使得無法從普通函數內部訪問周圍范圍的`this`。這對于真正的功能來說是不方便的。 以下代碼演示了一種常見的解決方法: ```js const prefixer = { prefix: '==> ', prefixStringArray(stringArray) { const that = this; // (A) return stringArray.map( function (x) { return that.prefix + x; // (B) }); }, }; assert.deepEqual( prefixer.prefixStringArray(['a', 'b']), ['==> a', '==> b']); ``` 在 B 行中,我們想要訪問`.prefixStringArray()`的`this`。但我們不能,因為周圍的普通函數有自己的`this` _ 陰影 _(阻止訪問)方法的`this`。因此,我們將方法的`this`保存在額外變量`that`(A 行)中,并在 B 行中使用該變量。 箭頭函數沒有`this`作為隱式參數,它從周圍環境中獲取其值。也就是說,`this`的行為與任何其他變量一樣。 ```js const prefixer = { prefix: '==> ', prefixStringArray(stringArray) { return stringArray.map( x => this.prefix + x); }, }; ``` 總結一下: * 在普通函數中,`this`是隱式(_ 動態 _)參數([中有關單個對象](ch_single-objects.html#methods)的章節中的詳細信息)。 * 箭頭函數從其周圍的范圍獲得`this`(_ 詞法 _)。 ##### 23.3.3.3。語法陷阱:從箭頭函數返回一個對象字面值 如果希望箭頭函數的表達式主體是對象字面值,則必須將字面值放在括號中: ```js const func1 = () => ({a: 1}); assert.deepEqual(func1(), { a: 1 }); ``` 如果不這樣,JavaScript 認為,箭頭函數有一個塊體(不返回任何內容): ```js const func2 = () => {a: 1}; assert.deepEqual(func2(), undefined); ``` `{a: 1}`被解釋為[標簽`a:`](ch_control-flow.html#labels) 和表達式語句`1`的塊。 這個陷阱是由[語法模糊](ch_syntax.html#ambiguous-syntax)引起的:對象字面值和代碼塊具有相同的語法,我們必須幫助 JavaScript 區分它們。 ### 23.4。吊裝功能 函數聲明是 _ 懸掛 _(內部移動到頂部): ```js assert.equal(foo(), 123); // OK function foo() { return 123; } ``` 提升允許您在聲明之前調用`foo()`。 變量聲明不會被掛起:在以下示例中,您只能在聲明后使用`bar()`。 ```js assert.throws( () => bar(), // before declaration ReferenceError); const bar = () => { return 123; }; assert.equal(bar(), 123); // after declaration ``` 類聲明不會懸掛,也可以: ```js assert.throws( () => new MyClass(), ReferenceError); class MyClass {} assert.equal(new MyClass() instanceof MyClass, true); ``` #### 23.4.1。在沒有吊裝的情況下提前召喚 注意函數`f()`仍然可以在聲明之前調用非提升函數`g()` - 如果在聲明`g()`之后調用`f()`: ```js const f = () => g(); const g = () => 123; // We call f() after g() was declared: assert.equal(f(), 123); ``` 通常在執行模塊的完整主體之后調用模塊的功能。因此,您很少需要擔心模塊中的函數順序。 #### 23.4.2。吊裝的陷阱 如果您在聲明之前依賴于提升來調用函數,那么您需要注意它不能訪問非提升數據。 ```js hoistedFunc(); const MY_STR = 'abc'; function hoistedFunc() { assert.throws( () => MY_STR, ReferenceError); } ``` 和以前一樣,如果你在最后調用函數`hoistedFunc()`,問題就會消失。 ### 23.5。從函數返回值 使用`return`運算符從函數返回值: ```js function func() { return 123; } assert.equal(func(), 123); ``` 另一個例子: ```js function boolToYesNo(bool) { if (bool) { return 'Yes'; } else { return 'No'; } } assert.equal(boolToYesNo(true), 'Yes'); assert.equal(boolToYesNo(false), 'No'); ``` 如果在函數末尾沒有顯式返回任何內容,JavaScript 會為您返回`undefined`: ```js function noReturn() { // No explicit return } assert.equal(noReturn(), undefined); ``` ### 23.6。參數處理 #### 23.6.1。術語:參數(parameters)與參數(arguments) 術語 參數 (parameters) 和術語 參數 (arguments)基本上意思相同。如果您愿意,可以進行以下區分: * 參數 (parameters) 是函數定義的一部分。它們也被稱為 形式參數 (formal parameters )和 形式參數 (formal arguments.)。 * 參數 (arguments) 是函數調用的一部分。它們也被稱為 實際參數 actual parameters 和 實際參數 actual arguments.。 #### 23.6.2。術語:回調 _ 回調 _ 或 _ 回調函數 _ 是作為參數傳遞給另一個函數或方法的函數。此術語經常在 JavaScript 社區中廣泛使用。 以下是回調的示例: ```js const myArray = ['a', 'b']; const callback = (x) => console.log(x); myArray.forEach(callback); // Output: // 'a' // 'b' ``` #### 23.6.3。參數太多或不夠 如果函數調用提供的參數數量不同于函數定義所預期的數量,則 JavaScript 不會抱怨: * 額外的參數被忽略。 * 缺少的參數設置為`undefined`。 例如: ```js function foo(x, y) { return [x, y]; } // Too many arguments: assert.deepEqual(foo('a', 'b', 'c'), ['a', 'b']); // The expected number of arguments: assert.deepEqual(foo('a', 'b'), ['a', 'b']); // Not enough arguments: assert.deepEqual(foo('a'), ['a', undefined]); ``` #### 23.6.4。參數默認值 參數默認值指定在未提供參數時要使用的值。例如: ```js function f(x, y=0) { return [x, y]; } assert.deepEqual(f(1), [1, 0]); assert.deepEqual(f(), [undefined, 0]); ``` `undefined`也會觸發默認值: ```js assert.deepEqual( f(undefined, undefined), [undefined, 0]); ``` #### 23.6.5。休息參數 通過在標識符前面添加三個點(`...`)來聲明 rest 參數。在函數或方法調用期間,它接收包含所有剩余參數的數組。如果最后沒有額外的參數,那么它是一個空數組。例如: ```js function f(x, ...y) { return [x, y]; } assert.deepEqual( f('a', 'b', 'c'), ['a', ['b', 'c']]); assert.deepEqual( f(), [undefined, []]); ``` ##### 23.6.5.1。通過 rest 參數強制執行一定數量的參數 您可以使用 rest 參數來強制執行一定數量的參數。例如,以下功能。 ```js function bar(a, b) { // ··· } ``` 這就是我們強制調用者總是提供兩個參數的方法: ```js function bar(...args) { if (args.length !== 2) { throw new Error('Please provide exactly 2 arguments!'); } const [a, b] = args; // ··· } ``` #### 23.6.6。命名參數 當有人調用函數時,調用者提供的參數將分配給被調用者接收的參數。執行映射的兩種常用方法是: 1. 位置參數:如果參數具有相同的位置,則將參數分配給參數。僅具有位置參數的函數調用如下所示。 ```js selectEntries(3, 20, 2) ``` 2. 命名參數:如果參數具有相同的名稱,則將參數分配給參數。 JavaScript 沒有命名參數,但您可以模擬它們。例如,這是一個只有(模擬)命名參數的函數調用: ```js selectEntries({start: 3, end: 20, step: 2}) ``` 命名參數有幾個好處: * 它們導致更加自我解釋的代碼,因為每個參數都有一個描述性標簽。只需比較`selectEntries()`的兩個版本:使用第二個版本,可以更容易地看到發生了什么。 * 參數順序無關緊要(只要名稱正確)。 * 處理多個可選參數更方便:調用者可以輕松提供所有可選參數的任何子集,而不必知道它們省略的那些(使用位置參數,您必須使用`undefined`填寫前面的可選參數])。 #### 23.6.7。模擬命名參數 JavaScript 沒有真正的命名參數。模擬它們的官方方法是通過對象字面值: ```js function selectEntries({start=0, end=-1, step=1}) { return {start, end, step}; } ``` 此函數使用 _ 解構 _ 來訪問其單個參數的屬性。它使用的模式是以下模式的縮寫: ```js {start: start=0, end: end=-1, step: step=1} ``` 這種解構模式適用于空對象字面值: ```js > selectEntries({}) { start: 0, end: -1, step: 1 } ``` 但是如果你在沒有任何參數的情況下調用函數它就不起作用: ```js > selectEntries() TypeError: Cannot destructure property `start` of 'undefined' or 'nu ``` 您可以通過為整個模式提供默認值來解決此問題。此默認值與更簡單的參數定義的默認值相同:如果缺少該參數,則使用默認值。 ```js function selectEntries({start=0, end=-1, step=1} = {}) { return {start, end, step}; } assert.deepEqual( selectEntries(), { start: 0, end: -1, step: 1 }); ``` #### 23.6.8。將(`...`)傳播到函數調用中 spread 參數的前綴(`...`)與 rest 參數的前綴相同。在調用函數或方法時使用前者。它的操作數必須是可迭代的對象。迭代的值轉換為位置參數。例如: ```js function func(x, y) { console.log(x); console.log(y); } const someIterable = ['a', 'b']; func(...someIterable); // Output: // 'a' // 'b' ``` 因此,擴展參數和 rest 參數用于相反的目的: * 定義函數或方法時使用 Rest 參數。他們在數組中收集參數。 * 調用函數或方法時使用 Spread 參數。他們將可迭代對象轉換為參數。 ##### 23.6.8.1。示例:傳播到`Math.max()` `Math.max()`返回其零個或多個參數中最大的一個。唉,它不能用于數組,但傳播給了我們一條出路: ```js > Math.max(-1, 5, 11, 3) 11 > Math.max(...[-1, 5, 11, 3]) 11 > Math.max(-1, ...[-5, 11], 3) 11 ``` ##### 23.6.8.2。示例:傳播到`Array.prototype.push()` 類似地,Array 方法`.push()`破壞性地將其零個或多個參數添加到其 Array 的末尾。 JavaScript 沒有破壞性地將數組附加到另??一個數組的方法,但我們再次通過傳播保存: ```js const arr1 = ['a', 'b']; const arr2 = ['c', 'd']; arr1.push(...arr2); assert.deepEqual(arr1, ['a', 'b', 'c', 'd']); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:參數處理** * 位置參數:`exercises/callables/positional_parameters_test.js` * 命名參數:`exercises/callables/named_parameters_test.js` ![](https://img.kancloud.cn/ff/a8/ffa8e16628cad59b09c786b836722faa.svg) **測驗** 參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
                  <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>

                              哎呀哎呀视频在线观看