91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

js6的未來(二)函數增強

發布時間:2020-08-04 09:59:52 來源:網絡 閱讀:772 作者:mashaoli 欄目:開發技術

js6的未來(二)函數增強

函數聲明中的解構

JavaScript 的新解構賦值得名于數組或對象可以 “解構” 并提取出組成部分的概念。在 第 1 部分 中,學習了如何在局部變量中使用解構。它在函數參數聲明中也很有用。

如果某個函數需要一個對象,可以在函數開始之前,利用解構功能提取出該對象的相關部分。可通過向函數的參數添加解構語法來實現此目的,

1. 函數聲明中的解構

let  person = {
      firstName: "Ted",
      lastName: "Neward",
      age: 45,
      favoriteLanguages: [
        "ECMAScript", "Java", "C#", "Scala", "F#"
      ]
    }
    function displayDetails({firstName, age}) {
      console.log(`${firstName} is ${age} years old`);
    }
    displayDetails(person);

displayDetails 函數僅關心傳入的對象的 firstName 和 age 字段。向對象語法添加一個解構賦值,可在函數調用中有效地提取出這些值。調用語法本身保持不變,重構遺留代碼來使用新語法變得更容易。
也可以在函數中使用解構數組語法,但它沒有您即將看到的其他一些功能那么令人印象深刻。

函數參數

ECMAScript 對函數參數執行了語法改動。為函數調用引入了默認參數值、剩余參數和展開運算符,
首先將介紹默認參數,這可能是 3 個概念中最容易理解的概念。

2. 默認參數

let sayHello = function(message = "Hello world!") {
  console.log(message);
}
sayHello();          // prints "Hello world!"
sayHello("Howdy!");  // prints "Howdy!"

基本上講:在調用位置指定了一個參數,那么該參數將接受傳遞的值;未指定值,則會分配默認值。
與 第 1 部分 中介紹的一些更新一樣,新的默認參數實質上就是語法糖。以前的 ECMAScript 版本中,

3. 舊式默認參數

var sayHello = function(message) {
  if (message === undefined) {
    message = "Hello world!";
  }
  console.log(message);
}
sayHello();          // prints "Hello world!"
sayHello("Howdy!");  // prints "Howdy!"

剩余參數(Rest parameters)

ECMAScript 庫中的一種更常見的做法是,定義函數或方法來接受一個或多個固定參數,后跟一組通過用戶定義方式細化或修改調用的可選參數。在過去,可以通過訪問靜默構建并傳遞給每個函數調用的內置 arguments 參數來實現此目的:

4. 舊式可選參數

function greet(firstName) {
  var args = Array.prototype.slice.call(arguments, greet.length);
  console.log("Hello",firstName);
  if (args !== undefined)
    args.forEach(function(arg) { console.log(arg); });
}
greet("Ted");
greet("Ted", "Hope", "you", "like", "this!");

5. 剩余參數

function greet(firstName, ...args) {
 console.log("Hello",firstName);
 args.forEach(function(arg) { console.log(arg); });
}
greet("Ted");
greet("Ted", "Hope", "you", "like", "this!");

注意,剩余參數(第一個清單中的 args)不需要測試存在與否;該語言可確保它將以長度為 0 的數組形式存在,即使沒有傳遞其他參數。

展開運算符

展開運算符(Spread operator)在某些方面與剩余參數的概念正好相反。剩余參數將會收集傳入某個給定調用的一些可選值,展開運算符獲取一個值數組并 “展開” 它們,基本上講,就是解構它們以用作被調用的函數的各個參數。
展開運算符的最簡單用例是將各個元素串聯到一個數組中:

6. 使用展開運算符進行串聯

let arr1 = [0, 1, 2];
let arr2 = [...arr1, 3, 4, 5];
console.log(arr2); // prints 0,1,2,3,4,5

沒有展開運算符語法,需要提取第一個數組中的每個元素并附加到第二個數組,然后才添加剩余元素。
也可以在函數調用中使用展開運算符;事實上,這是您最有可能使用它的地方:

7. 函數調用中的展開運算符

function printPerson(first, last, age) {
 console.log(first, last age);
}
let args = ["Ted", "Neward", 45];
printPerson(...args);

注意,不同于剩余參數,展開運算符是在調用點上使用,而不是在函數定義中使用。

函數語法和語義

本節將介紹最重要的更新。只需記住,JavaScript 程序中的原始語法仍然可行。

箭頭函數

箭頭函數語法,這是一種用于創建函數字面量的速記符號。從 ECMAScript 6 開始,可以使用所謂的粗箭頭(與細箭頭相對)創建函數字面量,就像這樣:

8. 創建函數字面量的箭頭語法

let names = ["Ted","Jenni","Athen"];
names.forEach((n) => console.log(n));

箭頭函數也很容易理解:箭頭前的括號將參數捕獲到函數主體,箭頭本身表示函數主題的開頭。如果主體僅包含一條語句或表達式,則不需要使用花括號。如果主體包含多條語句或表達式,那么可以通過在箭頭后輸入花括號來表示它們:

9. 表示多條語句或表達式

let names = ["Ted","Jenni","Athen"];
names.forEach((n) => {
    console.log(n)
});

如果只有一個參數,您可以選擇完全省略括號,

10. 單個參數

names.forEach(n => console.log(n));

注意,箭頭函數的主體是只有一個值的單個表達式,則無需顯式返回,而是應該將單一表達式隱式返回給箭頭函數的調用方。但是,如果主體不只一條語句或表達式,則必須使用花括號,而且所有返回的值都必須通過常用的 “return” 語法發回給調用方。

箭頭函數不能直接取代函數關鍵字。一般而言,您應該繼續使用 function 定義方法(即與一個對象實例關聯的函數)。為對象無關的場景保留箭頭函數,比如 Array.forEach 或 Array.map 調用的主體。因為箭頭函數對待 this 的方式與普通函數不同,所以在方法定義中使用它們可能導致意料之外的結果。

‘this’ 的新定義

this 參數引用的對象上會調用一個方法,如下所示:

11. ‘this’ 引用一個對象實例

let bob = {
  firstName: "Bob",
  lastName: "Robertson",
  displayMe: function() {
    for (let m in this) {
      console.log(m,"=",this[m]);
    }
  }
};
bob.displayMe();

上面的參數顯然引用了實例 bob,而且忠實地打印出 firstName、lastName 和 displayMe 方法(因為它也是該對象的成員)的名稱和值。

當從一個存在于全局范圍的函數引用 this 時,情況會變得有點怪異:

12. ‘this’ 引用一個全局范圍對象

let displayThis = function() {
  for (let m in this) {
    console.log(m);
  }
};
displayThis()

ECMAScript 將全局范圍定義為一個對象,所以當在全局范圍內的函數使用時,this 引用全局范圍對象,在上面的情況中,它忠實地打印出全局范圍的每個成員,包括頂級全局變量、函數和對象(比如上面的示例中的 “console”)。

出于這個原因,我們也可以在兩種不同的上下文中重用該函數,知道它每次將或多或少執行一些我們期望的操作:

13. 重用全局范圍函數

let displayThis = function() {
  for (let m in this) {
    console.log(m);
  }
};
displayThis(); // this == global object
let bob = {
  firstName: "Bob",
  lastName: "Robertson",
  displayMe: displayThis
};
bob.displayMe(); // this == bob

可能此語法有點奇怪,但只要您理解了規則,就不是問題。直到您嘗試使用 ECMAScript 構造函數作為對象類型時,情況才會真正偏離主題:

14. 一個過度使用的 ‘this’

function Person() {
  // The Person() constructor defines "this" as an instance
  // of itself
  this.age = 0;
 
  setInterval(function growUp() {
    // In non-strict mode, the growUp() function defines "this"
    // as the global object; thus, "this.age" refers to a global
    // "age" value, not the one defined on the instance of Person
    this.age++;
  }, 1000);
}
var p = new Person();
// Every second, p.age is supposed to go up by one.
// But because the "this" in the "growUp" function literal
// refers to the global object, and not "p", p.age will
// never change from 0.

詞法 ‘this’ 綁定

為了解決與 this 相關的定義問題,箭頭函數擁有所謂的詞法 this 綁定。這意味著箭頭函數在定義函數時使用 this 值,而不是在執行它時。
采用規則:完全理解新 this 規則可能需要一段時間。新箭頭函數規則并不總是這么直觀。作為開發人員,可以計劃對 “內聯” 函數使用箭頭函數,對方法使用傳統函數。如果這么做,各個方面都應按預期工作。
或許理解這一區別的最簡單方法是借助一個舊的 Node.js 對象 EventEmitter。回想一下,EventEmitter(獲取自 events 模塊)是一個簡單的發布-訂閱式消息系統:您可以在某個特定事件名稱上的發射器上注冊回調,當該事件被 “發出” 時,則按注冊的順序觸發回調。
如果向 EventEmitter 注冊一個遺留函數,捕獲的 this 將是在運行時確定的參數。但是如果您向 EventEmitter 注冊一個箭頭函數,this 將在定義箭頭函數時綁定:

15. ‘this’ 被箭頭函數綁定

let EventEmitter = require('events');
let ee = new EventEmitter();
ee.on('event', function() {
 console.log("function event fired", this);
});
ee.on('event', () => {
 console.log("arrow event fired", this);
});
var bob = {
 firstName: "Bob",
 lastName: "Robertson"
};
bob.handleEventLegacy = function() {
 console.log("function event fired", this);
};
bob.handleEventArrow = () => {
 console.log("arrow event fired", this);
};
ee.on('event', bob.handleEventLegacy);
ee.on('event', bob.handleEventArrow);
ee.emit('event');

在觸發函數事件時,this 被綁定到 EventEmitter 本身,而箭頭事件未綁定到任何目標(它們分別打印一個空對象)。

生成器函數

生成器函數旨在生成一個值流供其他方使用。許多函數語言都使用了生成器,它們在其中可能名為流 或序列。現在 ECMAScript 中也引入了它們。
要了解生成器的實際工作原理,需要稍作解釋。首先,想象一組簡單的名稱:

16. 一個簡單的集合

var names = ["Ted", "Charlotte", "Michael", "Matthew"];

17. 一個返回每個名稱的函數

var getName = (function() {
  var current = 0;
  return function() {
    if (current > names.length)
      return undefined;
    else {
      var temp = names[current];
      current++;
      return temp;
    }
  };
})();
console.log(getName()); // prints Ted
console.log(getName()); // prints Charlotte
console.log(getName()); // prints Michael
console.log(getName()); // prints Matthew
console.log(getName()); // prints undefined

起初,上面的函數返回函數的方式可能看起來很陌生。這是必要的,因為 getName 函數需要在多個函數調用中跟蹤它的狀態。在類似 C 的語言中,可以將狀態存儲在getName 函數內的靜態變量中,但像類似的 Java 和 C# 一樣,ECMAScript 不支持在函數中使用靜態變量。在這種情況下,我們將使用閉包,以便函數字面量在返回后繼續綁定到 “當前” 變量,使用該變量存儲自己的狀態。
要理解的重要一點是,此函數不會一次獲取一個有限的值序列(采用返回數組的形式),它一次獲取一個元素,直到沒有剩余的元素。
但是如果要返回的元素永遠用不完,該怎么辦?

函數編程中的無限流

18. 一個有限的流

var getName = (function() {
  var current = 0;
  return function() {
    switch (current++) {
      case 0: return "Ted";
      case 1: return "Charlotte";
      case 2: return "Michael";
      case 3: return "Matthew";
      default: return undefined;
    }
  };
})();<< span="">

從技術上講,您看到的仍是一個迭代器,但它的實現看起來與來自樣本的迭代器截然不同;這里沒有集合,只有一組硬編碼的值。
從本質上講,它是一個沒有關聯集合的迭代器,這突出了一個重要的事實:我們的函數生成的值的來源 現在深度封裝在離調用方很遠的地方。這進而引入了一個更有趣的想法:調用方可能不知道最初沒有集合,不知道生成的值永無止境。這就是一些語言所稱的無限流。
斐波納契數列(全球每種函數語言的 “Hello World” 等效程序)就是這樣一個無限流:

19. 斐波納契數的無限流

var fibo = (function() {
  var prev1 = undefined;
  var prev2 = undefined;
  return function() {
    if (prev1 == undefined && prev2 == undefined) {
      prev1 = 0;
      return 0;
    }
    if (prev1 == 0 && prev2 == undefined) {
      prev1 = 1;
      prev2 = 0;
      return 1;
    }
    else {
      var ret = prev1 + prev2;
      prev2 = prev1;
      prev1 = ret;
      return ret;
    }
  };
})();

無限流 是一個從不會用完要返回的值的流。在這種情況下,斐波拉契數列沒有邏輯終點。
JavaScript 中的反應式編程非常復雜。如果您打算了解更多的信息,可以訪問 JavaScript 反應式編程 GitHub 頁面。
盡管起初看起來很奇怪,但無限流的概念是其他一些基于 ECMAScript 的有趣技術(比如反應式編程)的核心。想想如果我們將用戶事件(比如移動鼠標、單擊按鈕和按鍵)視為無限流,函數從流中獲取每個事件并進行處理,結果會怎樣?
構建無限流所需的代碼量非常大,所以 ECMAScript 6 定義了一種新語法(和一個新關鍵字)來讓代碼更加簡潔。在這里可以看到,我重寫了清單 17 中的示例:

20. 一個使用生成器的有限值流

function* getName() {
  yield "Ted";
  yield "Charlotte";
  yield "Michael";
  yield "Matthew";
}
let names = getName();
console.log(names.next().value);
console.log(names.next().value);
console.log(names.next().value);
console.log(names.next().value);
console.log(names.next().value);

同樣地,該函數將按順序打印出每個名稱。當它用完所有名稱時,它會不停地打印 “undefined”。在語法上,yield 關鍵字看起來類似于 return,但事實上,它表示 “返回但記住我在此函數中的位置,以便下次調用它時,從離開的位置開始執行。”這顯然比傳統的 return 更復雜。
生成器的使用與第一個示例稍微不同:我們捕獲了 getName 函數的返回值,然后像迭代器一樣使用該對象。這是 ECMAScript 6 中的一個特意的設計決定。從技術上講,生成器函數返回一個 Generator 對象,該對象用于從生成器函數獲取各個值。新語法旨在盡可能地模擬迭代器。
談到迭代器,還有最后一個需要知道的語法更改。

for-of 關鍵字

經典的 for 循環在 ECMAScript 6 具有了新形式,這是由于添加了一個輔助關鍵字:of。在許多方面,新語法與 for-in 沒多大區別,但它支持生成器函數。
返回到清單 19 中的斐波納契數列,這是向函數添加 for-of 關鍵字時發生的情況:

21. 斐波納契數列中的 ‘for of’

function* fibo() { // a generator function
  yield 0;
  yield 1;
  let [prev, curr] = [0, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}
 
for (let n of fibonacci()) {
  console.log(n);
   
  // By the way, this is an infinite stream, so this loop
  // will never terminate unless you break out of it
}

for-of 和 for-in 之間存在著細微區別,但在大多數情況下,您可以使用 for-of 直接取代舊語法。它添加了隱式使用生成器的能力 — 就像我們在無限流示例中使用 getName() 執行的操作一樣。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

js js6 j
AI

灌云县| 攀枝花市| 福海县| 天等县| 南召县| 高雄市| 神池县| 沁阳市| 绍兴县| 金湖县| 平泉县| 堆龙德庆县| 平和县| 正镶白旗| 成安县| 拉孜县| 黄石市| 集贤县| 新晃| 汝南县| 清水县| 崇义县| 桐梓县| 外汇| 和林格尔县| 石阡县| 长子县| 陆河县| 新蔡县| 湘乡市| 南召县| 项城市| 安义县| 平舆县| 义乌市| 高州市| 宜城市| 和林格尔县| 林州市| 望城县| 和龙市|