日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

什么?ES6 中还有 Tail Calls!

發布時間:2024/4/13 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 什么?ES6 中还有 Tail Calls! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

先吐槽一件事,最近把原先的 TOP 域名更換到 CN 域名,并且用 Gatsby 重建個人站點,之前是用采用 HTTPS 部署的方式繞過阿里云的域名備案系統。更換 CN 域名后,這招不管用了,?? 域名必須要備案了,等待幕布郵寄中……

有人要問了,都 9102 年,ES10 都出來了,怎么還在講 ES6,非也!本文針對 ES6 幾個不為人知、和重要的特性做講解,精彩的在后面!

基礎篇

Let + Const

ES6 除了固有的函數作用域,還引入了塊級作用域({})

function f() {{let x; // ①{// 包含在當前塊中,與 ① 中的 x 分屬不同作用域const x = "sneaky";// 錯誤,const 定義的變量不可以重新賦值,如果 const 定義了一個對象,那么對象的屬性是可以修改的x = "foo";}// let 定義的變量可以重新賦值x = "bar";// 錯誤,x 在 ① 塊中已被定義let x = "inner";} } 復制代碼

默認、剩余、展開參數(Default + Rest + Spread)

function f(x, y=12) {// y 等于 12 如果不傳遞 (或者傳遞 undefined)return x + y; } f(3); // 15 復制代碼function f(x, ...y) {// y 是一個數組return x * y.length; } f(3, "hello", true); // 6 復制代碼function f(x, y, z) {return x + y + z; } // 將數組的每一項作為參數傳遞 f(...[1,2,3]); // 6 復制代碼

解構(Destructuring)

var [a, ,b] = [1,2,3]; a === 1; // true b === 3; // truevar { op: a, lhs: { op: b }, rhs: c } = getASTNode()// var {op: op, lhs: lhs, rhs: rhs} = getASTNode() var {op, lhs, rhs} = getASTNode() // 參數解構 function g({name: x}) {console.log(x); } g({name: 5})var [a] = []; a === undefined; // truevar [a = 1] = []; a === 1; // true// 解構 + 默認參數 function r({x, y, w = 10, h = 10}) {return x + y + w + h; } r({x:1, y:2}) === 23 // true 復制代碼

箭頭函數(Arrows and Lexical This)

// 除了支持返回語句,還可以將表達式作為返回主體 const foo = () => ({ name: 'es6' }); const bar = (num) => (num++, num ** 2);foo(); // 返回一個對象 { name: 'es6' } bar(3); // 執行多個表達式,并返回最后一個表達式的值 16 復制代碼

JS 中 this 的指向問題一直都是面試高頻考點,不少人在實戰中也掉入坑中,總結起來就是一句話:“ this 永遠指向調用它的那個對象”,而箭頭函數則改寫了這一規則,就是

箭頭函數共享當前代碼上下文的 this

什么意思呢?可以理解為

  • 箭頭函數不會創建自己的 this,它只會從自己的作用域鏈的上一層繼承 this,如果上一層還是箭頭函數,則繼續向上查找,直至全局作用域,在瀏覽器環境下即 window。
  • 函數具有作用域鏈,對象則不具有

因此,在下面的代碼中,傳遞給 setInterval 的函數內的 this 與 sayHello 函數中的 this 一致:

const bob = {name: 'Bob',sayHello() {setTimeout(() => {console.log(`hello, I am ${this.name}`);}, 1000);} }; const hello = bob.sayHello;bob.sayHello(); // hello, I am Bob // 作為對象的方法調用,sayHello的this指向bob hello(); // hello, I am undefined // 作為普通函數調用,相當于window.hello(),this指向全局對象 hello.call({name:'Mike'}); // hello, I am Mike // call,apply調用,第一個參數為this指向的對象 復制代碼language = 'Python'; const obj = {language: 'TS',speak() {language = 'GO';return function() {return () => {console.log(`I speak ${this.language}`);};};} };obj.speak()()(); // 做個小測試,會打印什么呢? 復制代碼

箭頭函數還有以下特點

  • 由于箭頭函數沒有自己的 this 指針,通過 call 或 apply 調用,第一個參數會被忽略
  • 不綁定 Arguments 對象,其引用上一層作用域鏈的 Arguments 對象
  • 不能用作構造器,和 new 一起用會拋出錯誤。
  • 沒有 prototype 屬性。

現在你應該明白為何 React 中的函數寫法都為箭頭函數,就是為了綁定 this

Symbols

ES6 引入了一種新的原始數據類型 Symbol,表示獨一無二的值,它的功能類似于一種標識唯一性的 ID

// 每個 Symbol 實例都是唯一的。因此,當你比較兩個 Symbol 實例的時候,將總會返回 false const s1 = Symbol('macOS'); const s2 = Symbol('macOS');// Symbol.for 機制有點類似于單例模式 const s3 = Symbol.for('windows'); // 注冊一個全局 Symbol const s4 = Symbol.for('windows'); // 已存在相同名稱的 Symbol,返回全局 Symbols1 === s2; // false s3 === s4; // true復制代碼let key = Symbol('key');function MyClass(privateData) {// 注意,Symbol值作為對象屬性名時,不能用點運算符this[key] = privateData; }MyClass.prototype = {doStuff() {console.log(this[key]);} };// Symbol的一些特性必須要瀏覽器的原生實現,不可被 transpiled 或 polyfilled typeof key // symbollet c = new MyClass('hello'); c.key; // undefined c[key]; // hello復制代碼

應用場景

  • 更好的設計我們的數據對象,讓“對內操作”和“對外選擇性輸出”變得更加優雅。

在實際應用中,我們經常會需要使用 Object.keys() 或者 for...in 來枚舉對象的屬性名,那在這方面,Symbol 類型的 key 表現的會有什么不同之處呢?來看以下示例代碼:

let obj = {[Symbol('name')]: '一斤代碼',age: 18,title: 'Engineer' }Object.keys(obj) // ['age', 'title']for (let p in obj) {console.log(p) // 分別會輸出:'age' 和 'title' }Object.getOwnPropertyNames(obj) // ['age', 'title'] 復制代碼

也正因為這樣一個特性,當使用 JSON.stringify() 將對象轉換成 JSON 字符串的時候,Symbol 屬性也會被排除在輸出內容之外:

JSON.stringify(obj) // {"age":18,"title":"Engineer"} 復制代碼

由上代碼可知,Symbol 類型的 key 是不能通過 Object.keys() 或者 for...in 來枚舉的,所以,利用該特性,我們可以把一些不需要對外操作和訪問的屬性使用 Symbol 來定義。

  • 消除魔術字符串
const TYPE_AUDIO = 'AUDIO' const TYPE_VIDEO = 'VIDEO' const TYPE_IMAGE = 'IMAGE'function handleFileResource(resource) {switch(resource.type) {case TYPE_AUDIO:playAudio(resource)breakcase TYPE_VIDEO:playVideo(resource)breakcase TYPE_IMAGE:previewImage(resource)breakdefault:throw new Error('Unknown type of resource')} } 復制代碼

上面的代碼中那樣,我們需要為常量賦一個唯一的值(比如這里的 'AUDIO'),'AUDIO' 就是一個魔術字符串,它本身沒意義,只是為了保證常量唯一的關系。常量一多,就變得十分臃腫且難以理解

現在有了 Symbol,我們大可不必這么麻煩了:

// 保證了三個常量的值是唯一的 const TYPE_AUDIO = Symbol() const TYPE_VIDEO = Symbol() const TYPE_IMAGE = Symbol() 復制代碼

增強的對象字面量(Enhanced Object Literals)

const obj = {// 允許設置原型__proto__: theProtoObj,// 允許覆蓋屬性['__proto__']: somethingElse,// 屬性簡寫,等于 ‘handler: handler’handler,// 計算 (動態) 屬性名['prop_' + (() => 42)()]: 42 }; obj.prop_42 // 42 obj.__proto__ // somethingElse 復制代碼

__proto__ 需要原生支持,它在之前的 ECMAScript 版本中被移除,但大多數瀏覽器都實現了這一特性,包括 Node 環境

Map + Set + WeakMap + WeakSet

Set

Set 是 ES6 中新增的數據結構,它允許創建唯一值的集合。集合中的值可以是簡單的基本類型(如字符串或數值),但更復雜的對象類型(如對象或數組)也可以,亦或是一個新的 Set

let animals = new Set();animals.add('?'); animals.add('?'); animals.add('?'); animals.add('?'); console.log(animals.size); // 4 animals.add('?'); console.log(animals.size); // 4console.log(animals.has('?')); // true animals.delete('?'); console.log(animals.has('?')); // falseanimals.forEach(animal => {console.log(`Hey ${animal}!`); });// Hey ?! // Hey ?! // Hey ?!animals.clear(); console.log(animals.size); // 0 復制代碼

我們還可以傳入一個數組來初始化集合

let myAnimals = new Set(['?', '?', '?', '?']);myAnimals.add(['?', '?']); myAnimals.add({ name: 'Rud', type: '?' }); console.log(myAnimals.size); // 4// Set 內置了遍歷器,可以調用 forEach, for…of myAnimals.forEach(animal => {console.log(animal); });// ? // ? // ["?", "?"] // Object { name: "Rud", type: "?" } 復制代碼

Map

與普通對象(Object)不同,Map 的鍵名(Key)可以是任何類型,不再局限于字符串(String),包括但不限于 objects 或 functions

let things = new Map();const myFunc = () => '?';things.set('?', 'Car'); things.set('?', 'House'); things.set('??', 'Airplane'); things.set(myFunc, '? Key is a function!');things.size; // 4things.has('?'); // truethings.has(myFunc) // true things.has(() => '?'); // false things.get(myFunc); // '? Key is a function!'things.delete('??'); things.has('??'); // falsethings.clear(); things.size; // 0// 鏈式設置鍵值對 things.set('?', 'Wrench').set('?', 'Guitar').set('?', 'Joystick');const myMap = new Map();// 甚至鍵名可以是另一個 Map things.set(myMap, 'Oh gosh!'); things.size; // 4 things.get(myMap); // 'Oh gosh!'復制代碼

可以通過傳入包含兩個元素的數組來初始化 Map

const funArray = [['?', 'Champagne'],['?', 'Lollipop'],['?', 'Confetti'], ];let funMap = new Map(funArray); funMap.get('?'); // Champagne 復制代碼

WeakMap

WeakMap 對象是一組鍵/值對的集合,其中的鍵是弱引用的。其鍵必須是對象,而值可以是任意的。它最重要的特性是 WeakMap 保持了對鍵名所引用的對象的弱引用

我們可以通過 Node 來證明一下這個問題:

// 允許手動執行垃圾回收機制 node --expose-gcglobal.gc(); // 返回 Nodejs 的內存占用情況,單位是 bytes process.memoryUsage(); // heapUsed: 4640360 ≈ 4.4Mlet map = new Map(); let key = new Array(5 * 1024 * 1024); // new Array 當為 Obj map.set(key, 1); global.gc(); process.memoryUsage(); // heapUsed: 46751472 注意這里大約是 44.6M// 所以當你設置 key = null 時,只是去掉了 key 對 Obj 的強引用 // 并沒有去除 arr 對 Obj 的強引用,所以 Obj 還是不會被回收掉 key = null; global.gc(); process.memoryUsage(); // heapUsed: 46754648 ≈ 44.6M// 這句話其實是無用的,因為 key 已經是 null 了 map.delete(key); global.gc(); process.memoryUsage(); // heapUsed: 46755856 ≈ 44.6M 復制代碼node --expose-gcglobal.gc(); process.memoryUsage(); // heapUsed: 4638992 ≈ 4.4Mconst wm = new WeakMap(); let key = new Array(5 * 1024 * 1024); wm.set(key, 1); global.gc(); process.memoryUsage(); // heapUsed: 46776176 ≈ 44.6M// 當我們設置 key = null 的時候,就只有 wm 對所引用對象的弱引用 // 下次垃圾回收機制執行的時候,該引用對象就會被回收掉。 key = null; global.gc(); process.memoryUsage(); // heapUsed: 4800792 ≈ 4.6M 復制代碼

應用場景

傳統使用 jQuery 的時候,我們會通過 $.data() 方法在 DOM 對象上儲存相關信息(就比如在刪除按鈕元素上儲存帖子的 ID 信息),jQuery 內部會使用一個對象管理 DOM 和對應的數據,當你將 DOM 元素刪除,DOM 對象置為空的時候,相關聯的數據并不會被刪除,你必須手動執行 $.removeData() 方法才能刪除掉相關聯的數據,WeakMap 就可以簡化這一操作:

let wm = new WeakMap(), element = document.querySelector(".element"); wm.set(element, "data");let value = wm.get(elemet); console.log(value); // dataelement.parentNode.removeChild(element); element = null; 復制代碼

WeakSet

特性與 WeakMap 相似

遍歷器(Iterators + For..Of)

遍歷器(Iterator)它是一種接口,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數據結構的所有成員)。 Iterator 的作用有三個:

  • 為各種數據結構,提供一個統一的、簡便的訪問接口;
  • 使得數據結構的成員能夠按某種次序排列;
  • ES6 創造了一種新的遍歷命令 for...of 循環,Iterator 接口主要供 for...of 消費。

ES6 規定,默認的 Iterator 接口部署在數據結構的 Symbol.iterator 屬性,或者說,一個數據結構只要具有 Symbol.iterator 屬性,就可以認為是“可遍歷的”(iterable)

let fibonacci = {[Symbol.iterator]() {let pre = 0, cur = 1;return {next() {[pre, cur] = [cur, pre + cur]; // 數組解構return { done: false, value: cur }}}} }for (var n of fibonacci) {// 當n超過1000時停止if (n > 1000)break;console.log(n); } 復制代碼

上面代碼中,對象 fibonacci 是可遍歷的(iterable),因為具有 Symbol.iterator 屬性。執行這個屬性,會返回一個遍歷器對象。該對象的根本特征就是具有 next 方法。每次調用 next 方法,都會返回一個代表當前成員的信息對象,具有 value 和 done 兩個屬性

原生具備 Iterator 接口的數據結構如下

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函數的 arguments 對象
  • NodeList 對象

for...in 和 for..of 的差別

  • for...in 遍歷鍵名(Key)并轉化為字符串,for...of 遍歷鍵值(Value)
  • for...in 語句以任意順序遍歷一個對象自有的、繼承的、可枚舉的、非 Symbol 的屬性
  • for...in 更適合遍歷對象,for...of 更適合遍歷數組。
for (let i in [1, 2, 3]) {console.log(typeof i); // string 數組下標被轉化字符串console.log(i); // '1', '2', '3' }var triangle = { a: 1, b: 2, c: 3 };function ColoredTriangle() {this.color = 'red'; }ColoredTriangle.prototype = triangle;var obj = new ColoredTriangle();for (var prop in obj) {if (obj.hasOwnProperty(prop)) { // 如果去了 hasOwnProperty() 這個約束條件會怎么樣?console.log(`obj.${prop} = ${obj[prop]}`); // obj.color = red} } 復制代碼

新增 API(Math + Number + String + Object APIs)

我們先來看看新增的 Number.EPSILON,不少人都是懵逼的狀態,WTF?
先來看看的 JS 世界中的一道送命題

0.1 + 0.2 // 結果0.30000000000000004 而不是0.3 復制代碼

事出必有因,這是因為 JS 的數值采用了 IEEE 754 標準,而且 JS 是弱類型語言,所以數字都是以64位雙精度浮點數據類型儲存。也就是說,JS 語言底層根本沒有整數,所有數字都是小數!當我們以為在用整數進行計算時,都會被轉換為小數

而浮點數都是以多位二進制的方式進行存儲的

十進制的0.1用二進制表示為:0.0 0011 0011 0011 0011…,循環部分是0011
十進制0.2用二進制表示為:0.0011 0011 0011 0011…,循環部分是0011

由于存儲空間有限,最后計算機會舍棄后面的數值,所以我們最后就只能得到一個近似值

JS中采用的 IEEE 754 的雙精度標準也是一樣的道理在存儲空間有限的情況下,當出現這種無法整除的小數的時候就會取一個近似值,在 JS 中如果這個近似值足夠近似,那么 JS 就會認為他就是那個值。

console.log(0.1000000000000001) // 0.1000000000000001 (中間14個0,不會被近似處理,輸出本身) console.log(0.10000000000000001) // 0.1 (中間15個0,js會認為兩個值足夠近似,所以輸出0.1) 復制代碼

那么這個近似的界限如何判斷呢?

ES6的 Number.EPSILON就是一個界限,它表示 1 與大于 1 的最小浮點數之間的差。

對于 64 位浮點數來說,大于 1 的最小浮點數相當于二進制的1.00..001,小數點后面有連續 51 個零。這個值減去 1 之后,就等于 2 的 -52 次方

Number.EPSILON === Math.pow(2, -52) // true Number.EPSILON // 2.220446049250313e-16 Number.EPSILON.toFixed(20) // "0.00000000000000022204" 復制代碼

Number.EPSILON 實際上是 JavaScript 能夠表示的最小精度。誤差如果小于這個值,就可以認為已經沒有意義了,即不存在誤差了。

0.1 + 0.2 - 0.3 // 5.551115123125783e-175.551115123125783e-17.toFixed(20) // '0.00000000000000005551' 復制代碼0.00000000000000005551 < 0.00000000000000022204 // true 復制代碼

顯然,0.30000000000000004 不存在誤差,不會被近似處理

我們可以通過以下手段來達到我們想要的效果

function withinErrorMargin (left, right) {return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2); }0.1 + 0.2 === 0.3 // false withinErrorMargin(0.1 + 0.2, 0.3) // true 復制代碼

其他一些新增的 API

Number.isInteger(Infinity) // false Number.isNaN("NaN") // falseMath.sign(-5) // 判斷一個數到底是正數、負數、還是零 -1 Math.hypot(3, 4) // 返回所有參數的平方和的平方根 5 Math.imul(-2, -2) // 返回兩個數以 32 位帶符號整數形式相乘的結果 4"abcde".includes("cd") // true "abc".repeat(3) // "abcabcabc"Array.from(document.querySelectorAll("*")) // 返回一個真正的數組 Array.of(1, 2, 3) // [1,2,3] [0, 0, 0].fill(7, 1) // [0,7,7] [1,2,3].findIndex(x => x == 2) // 1 ["a", "b", "c"].entries() // [0, "a"], [1,"b"], [2,"c"] ["a", "b", "c"].keys() // 0, 1, 2 ["a", "b", "c"].values() // "a", "b", "c"Object.assign(Point, { origin: new Point(0,0) }) // 合并對象 復制代碼

二進制和八進制字面量(Binary and Octal Literals)

0b111 === 7 // true 二進制 0o111 === 73 // true 八進制 0x111 === 273 // true 十六進制 復制代碼

進階篇

尾遞歸(Tail Calls)

假設現在要實現一個階乘函數,即 5!= 120,我們很容易想到遞歸實現

function factorial(n) {if (n === 1) return 1;return n * factorial(n - 1); } 復制代碼

但遞歸非常耗費內存,因為需要同時保存成千上百個調用記錄,很容易發生"棧溢出"錯誤(stack overflow)。但對于尾遞歸來說,由于只存在一個調用記錄,所以永遠不會發生"棧溢出"錯誤。

何為調用記錄,在示例代碼中,由于最后一步返回了一個表達式,內存會保留 n 這個變量的信息和 factorial(n - 1) 調用下一次函數的位置,形成一層層的調用棧

尾遞歸的實現,往往需要改寫遞歸函數,確保最后一步只調用自身,返回函數本身。做到這一點的方法,就是把所有用到的內部變量改寫成函數的參數。尾遞歸優化如下

function factorial(n, acc = 1) { "use strict";if (n <= 1) return acc;return factorial(n - 1, n * acc); }factorial(100000) 復制代碼

由此可見,"尾調用優化"對遞歸操作意義重大,所以一些函數式編程語言將其寫入了語言規格。ES6 也是如此,第一次明確規定,所有 ECMAScript 的實現,都必須部署"尾調用優化"。這就是說,在 ES6 中,只要使用尾遞歸,就不會發生棧溢出,相對節省內存。

ES6的尾調用優化只在嚴格模式下開啟,正常模式是無效的。

反射(Reflect)

Reflect 對象與 Proxy 對象一樣,也是 ES6 為了操作對象而提供的新 API。Reflect 對象的設計目的有這樣幾個。

  • 將Object對象的一些明顯屬于語言內部的方法(比如 Object.defineProperty),放到 Reflect 對象上
  • 修改某些 Object 方法的返回結果,讓其變得更合理。比如,Object.defineProperty(obj, name, desc) 在無法定義屬性時,會拋出一個錯誤,而 Reflect.defineProperty(obj, name, desc) 則會返回 false。
var O = {a: 1}; Reflect.defineProperty(O, 'b', {value: 2}); O[Symbol('c')] = 3;Reflect.ownKeys(O); // ['a', 'b', Symbol(c)] Reflect.getOwnPropertyDescriptor(O, 'b'); // { value: 2, writable: false, enumerable: false, configurable: false } function C(a, b){this.c = a + b; } var instance = Reflect.construct(C, [20, 22]); instance.c; // 42 復制代碼

獲取屬性名的方法有很多,以上面的代碼為例子,它們的區別如下

方法結果解釋
Object.getOwnPropertyNames(O)[ 'a', 'b' ]獲取除 Symbol 外的所有屬性
Object.getOwnPropertySymbols(O)[ Symbol(c) ]只獲取 Symbol 屬性
OReflect.ownKeys(O)[ 'a', 'b', Symbol(c) ]獲取所有屬性
for...ina獲取除 Symbol 外的可枚舉屬性

代理(Proxy)

Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。

// 代理一個對象 var target = {}; var handler = {get: function (receiver, name) {return `Hello, ${name}!`;} };var p = new Proxy(target, handler); p.world; // "Hello, world!" 復制代碼// 代理一個函數 var target = function () { return "I am the target"; }; var handler = {apply: function (receiver, ...args) {return "I am the proxy";} };var p = new Proxy(target, handler); p(); // "I am the proxy" 復制代碼// 代理會將所有應用到它的操作轉發到這個對象上 let target = {}; let p = new Proxy(target, {});p.a = 37; target.a; // 37 操作轉發到目標 復制代碼// 如何實現 a == 1 && a == 2 && a == 3,利用Proxy的get劫持 const a = new Proxy({},{val: 1,get() {return () => this.val++;}} ); a == 1 && a == 2 && a == 3; // true 復制代碼

由于 ES5 的限制,Proxy 不能被 transpiled or polyfilled,自己親自入的坑,由于在項目中使用了 Mobx5.x,其內部是用 Proxy 寫的,結果 IE11 不支持 ES6,只得回退版本 Mobx 到 4.x

生成器(Generators)

Generator 函數是 ES6 提供的一種異步編程解決方案。 Generator 函數有多種理解角度。語法上,首先可以把它理解成,Generator 函數是一個狀態機,封裝了多個內部狀態。

形式上,Generator 函數是一個普通函數,但是有兩個特征。一是,function 關鍵字與函數名之間有一個星號;二是,函數體內部使用 yield 表達式,定義不同的內部狀態

function* helloWorldGenerator() {yield 'hello'; // yield使Generator函數暫停了執行,并將結果返回給調用者yield 'world'; // 當下一次調用時,從它中斷的地方恢復執行return 'ending'; }var hw = helloWorldGenerator(); a = hw.next(); // { value: 'hello', done: false } b = hw.next(); // { value: 'world', done: false } c = hw.next(); // { value: 'ending', done: true } 復制代碼

可以利用這種暫停執行的特性,來實現惰性求值

向Generator傳遞數據

function* sayFullName() {const firstName = yield;const secondName = yield;console.log(firstName + ' ' + secondName); } let fullName = sayFullName(); fullName.next(); // 第一次調用,代碼暫停在 const firstName = yield,因為沒有通過 yield 發送任何值,因此 next 將返回 undefined fullName.next('Handsome'); // 第二次調用,傳入了值 Handsome,yield 被 Handsome 替代,因此 firstName 的值變為 Handsome,代碼執行恢復 // 直到再次遇到 const secondName = yield 暫停執行 fullName.next('Jack'); // 第三次調用,傳入了值 Jack,yield 被 Jack 替代,因此 secondName 的值變為 Jack,代碼執行恢復 // 打印 Handsome Jack 復制代碼

使用Generator處理異步調用

let generator; let getDataOne = () => {setTimeout(() => {generator.next('dummy data one');}, 1000); }; let getDataTwo = () => {setTimeout(() => {generator.next('dummy data one');}, 1000); };function* main() {let dataOne = yield getDataOne();let dataTwo = yield getDataTwo();console.log(dataOne, dataTwo); } generator = main(); generator.next(); // 執行 getDataOne(),然后 yield 暫停 // 直至一秒后 generator.next('dummy data one') 恢復代碼執行,并賦值 dataOne console.log('i am previous print'); // i am previous print // dummy data one dummy data one 復制代碼

Promises

Promises 是一個異步編程的解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。

所謂 Promise,簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。

function timeout(duration = 0) {return new Promise((resolve, reject) => {setTimeout(resolve, duration);}) }var p = timeout(1000).then(() => {return timeout(2000); }).then(() => {throw new Error("hmm"); }).catch(err => {return Promise.all([timeout(100), timeout(200)]); }) 復制代碼

這里強調幾點

  • 不要剝奪函數 return 的能力,很多人寫 Promise,照樣有大量嵌套,掉進 Promise 地獄,要記得及時 return,避免嵌套
  • 當需要多個請求全部結束時,才更新數據,可以用 Promise.all(fetch1,fetch2)
  • 當需要從多個請求中,接受最先返回數據的那個請求,可以用 Promise.race(fetch1,fetch2)

結尾

ES6 是 ECMAScript 一個非常重要的版本,我們必須深入理解,不僅能提高我們書寫代碼的能力,還能增強業務能力

附上一張我之前精心整理的思維導圖

本文參考資料

  • Learn ES2015
  • ECMAScript 6 入門
  • ES6 系列之 WeakMap

轉載于:https://juejin.im/post/5cf0ebc26fb9a07ee27afc60

總結

以上是生活随笔為你收集整理的什么?ES6 中还有 Tail Calls!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。