ES8
在es8中主要有6個特性:
主要的有:
Shared memory and atomics (共享內存和原子)
Async Functions(異步函數)
其他的特性:
Object.values/Object.entries (配合Object.keys使用)
String padding (字符串填充)
Object.getOwnPropertyDescriptors()
Trailing commas in function parameter lists and calls(在函數參數列表和調用中減少逗號的使用)
首先我們來介紹一下主要的兩個特性:
Shared memory and atomics (共享內存和原子)在我們先需要主要需要了解SharedArrayBuffer 和 Atomics
SharedArrayBuffer在了解SharedArrayBuffer 之前我們需要了解下需要share的這個ArrayBuffer:
ArrayBuffer對象代表儲存二進制數據的一段內存,它不能直接讀寫,只能通過視圖(TypedArray視圖和DataView視圖)來讀寫,視圖的作用是以指定格式解讀二進制數據。這個接口的原始設計目的,與 WebGL 項目有關。所謂WebGL,就是指瀏覽器與顯卡之間的通信接口,為了滿足 JavaScript 與顯卡之間大量的、實時的數據交換,它們之間的數據通信必須是二進制的,而不能是傳統的文本格式。文本格式傳遞一個32位整數,兩端的 JavaScript 腳本與顯卡都要進行格式轉化,將非常耗時。這時要是存在一種機制,可以像 C 語言那樣,直接操作字節,將4個字節的32位整數,以二進制形式原封不動地送入顯卡,腳本的性能就會大幅提升。
那么我們為什么需要SharedArrayBuffer這個function呢?
任何能夠從主線程負載減少工作的方法都對代碼運行效率有幫助,某些情況下,ArrayBuffers 可以減少大量應該由主線程做的工作(免去了格式轉換的耗時和壓力直接操作字符),但是也有些時候減少主線程負載是遠遠不夠的,有時你需要增援,你需要分割你的任務,那么我們這個時候就需要傳說中的多線程。
在 JavaScript 里,你可以借助 web worker 做這種事,但是web worker是不能共享內存的,那么這意味著如果你想分配你的任務給別的線程,你需要完整把任務復制過去,這可以通過 postMessage 實現,postMessage 把你傳給它的任何對象都序列化,發送到其它 web worker,然后那邊接收后反序列化并放進內存。可見這個過程也是相當慢的。我們下面來看下這個過程的code [from MDN]:
main.js
var first = document.querySelector("#number1"); var second = document.querySelector("#number2"); var result = document.querySelector(".result"); if (window.Worker) { // Check if Browser supports the Worker api. // Requires script name as input var myWorker = new Worker("worker.js"); // onkeyup could be used instead of onchange if you wanted to update the answer every time // an entered value is changed, and you don"t want to have to unfocus the field to update its .value first.onchange = function() { myWorker.postMessage([first.value,second.value]); // Sending message as an array to the worker console.log("Message posted to worker"); }; second.onchange = function() { myWorker.postMessage([first.value,second.value]); console.log("Message posted to worker"); }; myWorker.onmessage = function(e) { result.textContent = e.data; console.log("Message received from worker"); }; }
worker.js
onmessage = function(e) { console.log("Message received from main script"); var workerResult = "Result: " + (e.data[0] * e.data[1]); console.log("Posting message back to main script"); postMessage(workerResult); }
從上面的code可以看出這樣傳來傳去是非常慢的。
那么我們想多個 web worker 就可以同時讀寫同一塊內存,這樣我們就不用傳來傳去的了,這就是SharedArrayBuffers 為我們提供的。
我們來看下使用使用shareArrayBuffers是怎樣實現這個乘法的
main.js
"use strict"; var first = document.querySelector("#number1"); var second = document.querySelector("#number2"); var result = document.querySelector(".result"); if (window.Worker) { // Check if Browser supports the Worker api. // Requires script name as input var myWorker = new Worker("worker.js"); // onkeyup could be used instead of onchange if you wanted to update the answer every time // an entered value is changed, and you don"t want to have to unfocus the field to update its .value var sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT); first.onchange = function() { addNumber(); } second.onchange = function() { addNumber(); }; const addNumber = () => { myWorker.postMessage({ aTopic: [first.value, second.value], aBuf: sharedBuffer // The array buffer that we passed to the transferrable section 3 lines below }); console.log("Message posted to worker"); result.textContent = new Int32Array(sharedBuffer)[0]; console.log("Message received from worker"); } }
worker.js
onmessage = function(e) { console.log("Message received from main script"); var workerResult = e.data.aTopic[0] * e.data.aTopic[1]; new Int32Array(e.data.aBuf)[0] = workerResult; }
我們這個時候也不用擔心postMessage 伴有時延的通信。但是多個worker同時訪問一塊內存這個時候會出現競爭的問題的。
從 CPU 層面看,增加一個變量值需要三條指令,這是因為計算機同時有長期存儲器(內存)和短期存儲器(寄存器所有的線程共享同一個長期存儲器(內存),但是短期存儲器(寄存器)并不是共享的。每個線程需要把值先從內存搬到寄存器,之后就可以在寄存器上進行計算了,再然后會把計算后的值寫回內存)。
這個因為競爭的關系會產生錯誤的結果,那么我們應該怎樣讓SharedArrayBuffers 發揮他應有的價值呢?
這個時候伴隨它誕生的還有:
原子操作做的一件事就是在多線程中讓計算機按照人所想的單操作方式工作。
這就是為什么被叫做原子操作,因為它可以讓一個包含多條指令(指令可以暫停和恢復)的操作執行起來像是一下子就完了,就好像一條指令,類似一個不可分割的原子。
var sab = new SharedArrayBuffer(1024); var ta = new Uint8Array(sab); //The static Atomics.add() method adds a given value at a given //position in the array and returns the old value at that position. Atomics.add(ta, 0, 1); // returns 0, the old value Atomics.load(ta, 0); // 1async 函數
首先我們來看下在async 函數誕生之前我們是怎么處理異步的,我先簡單列舉幾個編程模型:
回調函數:
// more code function loading(callback) { // wait 3s setTimeout(function () { callback(); }, 3000); } function show() { // show the data. } loading(show); // more code
在這種情況下容易出現大家所熟知的回調黑洞:
A(function () { B(function () { C(function() { D(function() { // ... }) }) }) })
Promise模式
所謂 Promise,就是一個對象,用來傳遞異步操作的消息。它代表了某個未來才會知道結果的事件(通常是一個異步操作),并且這個事件提供統一的 API,可供進一步處理。
let promise = new Promise(function(resolve, reject) { console.log("Promise"); resolve(); }); promise.then(function() { console.log("Resolved."); }); console.log("Hi!"); // Promise // Hi! // Resolved
這種實現方式和AngularJs中的Promise用法相近。
下面簡單提下ES6中的Generator 函數的使用:
function* gen(x) { var y = yield x + 2; return y; } var g = gen(1); g.next() // { value: 3, done: false } g.next() // { value: undefined, done: true }
gen函數返回不是函數運行結果,而是一個指向內部狀態的指針對象,也就是遍歷器對象(Iterator Object)。
其實在ES8之前我們還有許多其他的異步實現方式,比如:
在angualrJs中我們實現的subscribe, unsubscribe, publish這種消息訂閱/發布模式其實也是實現異步的一種方式。
下面我們來說說今天的主角:async 函數
關鍵詞:await async
那么我們先看下await, await后面可以是Promise 對象和原始類型的值(數值、字符串和布爾值,但這時等同于同步操作),當函數執行的時候,一旦遇到await就會先返回,等到異步操作完成,再接著執行函數體內后面的語句。
async函數返回一個 Promise 對象,可以使用then方法添加回調函數。
我們看下下面的代碼來理解上面的話:
function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function add(x) { var a = await resolveAfter2Seconds(20); var b = await resolveAfter2Seconds(30); return x + a + b; } add(10).then(v => { console.log(v); // prints 60 after 4 seconds. });Object.values/Object.entries
ES5 引入了Object.keys方法,返回一個數組,成員是參數對象自身的所有可遍歷(enumerable)屬性的鍵名。
var obj = { foo: "bar", baz: 42 }; Object.keys(obj) // ["foo", "baz"]
es8中新添加的Object.values/Object.entries作為遍歷對象的一種補充手段。
Object.values方法返回一個數組,成員是參數對象自身的所有可遍歷(enumerable)屬性的鍵值。
var obj = { 100: "a", "a": "b", 7: "c" }; Object.values(obj) // ["c", "a", "b"]
Object.entries方法返回一個數組,成員是參數對象自身的所有可遍歷(enumerable)屬性的鍵值對數組。
var obj = { "foo": "bar", "baz": 42 , "1": 43}; Object.entries(obj) // [["1", 43], ["foo", "bar"], ["baz", 42] ]
以上的遍歷對象的屬性,都遵守同樣的屬性遍歷的次序規則。
首先遍歷所有屬性名為數值的屬性,按照數字排序。
其次遍歷所有屬性名為字符串的屬性,按照生成時間排序。
最后遍歷所有屬性名為 Symbol 值的屬性,按照生成時間排序。
(Object.keys,Object.values,Object.entries都會過濾屬性名為 Symbol(一種新的原始數據類型, 表示獨一無二的值) 值的屬性。)
字符填充函數,在es8中引入的兩個方法: String.padStart 和String.padEnd, 這兩個放發的主要是為了在一定程度上填充字符串的長度, 語法如下:
str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])
這個方法主要是為了實現字符串補全長度的功能。如果某個字符串不夠指定長度,會在頭部或尾部補全。padStart()用于頭部補全,padEnd()用于尾部補全。
"x".padStart(5, "ab") // "ababx" "x".padStart(4, "ab") // "abax" "x".padEnd(5, "ab") // "xabab" "x".padEnd(4, "ab") // "xaba" //如果原字符串的長度,等于或大于指定的最小長度,則返回原字符串。 "xxx".padStart(2, "ab") // "xxx" "xxx".padEnd(2, "ab") // "xxx" //如果省略第二個參數,默認使用空格補全長度。 "x".padStart(4) // " x" "x".padEnd(4) // "x " //用途: 為數值補全指定位數。 "1".padStart(10, "0") // "0000000001" "12".padStart(10, "0") // "0000000012" "123456".padStart(10, "0") // "0000123456" //另一個用途是提示字符串格式。 "12".padStart(10, "YYYY-MM-DD") // "YYYY-MM-12" "09-12".padStart(10, "YYYY-MM-DD") // "YYYY-09-12"getOwnPropertyDescriptors函數
getOwnPropertyDescriptors函數: 返回指定對象所有自身屬性(非繼承屬性)的描述對象。
ES5 有一個Object.getOwnPropertyDescriptor方法,返回某個對象屬性的描述對象(descriptor)。
var obj = { p: "a" }; Object.getOwnPropertyDescriptor(obj, "p") // Object { value: "a", // writable: true, // enumerable: true, // configurable: true // }
ES2017 引入了Object.getOwnPropertyDescriptors方法,返回指定對象所有自身屬性(非繼承屬性)的描述對象。
const obj = { foo: 123, get bar() { return "abc" } }; Object.getOwnPropertyDescriptors(obj) // { foo: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: bar], // set: undefined, // enumerable: true, // configurable: true } }
value
包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。該特性的默認值為undefined。直接在對象上定義的屬性,該特性被設置為指定的值
writable
表示能否修改屬性的值。直接在對象上定義的屬性,該特性默認為true
get
獲取該屬性的訪問器函數(getter)。如果沒有訪問器, 該值為undefined。(僅針對包含訪問器或設置器的屬性描述有效)
set
獲取該屬性的設置器函數(setter)。 如果沒有設置器, 該值為undefined。(僅針對包含訪問器或設置器的屬性描述有效)
configurable
表示能否通過delete刪除屬性從而重新定義屬性。,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。直接在對象上定義的屬性,該特性默認為true;
enumerable
表示能否通過for-in循環返回屬性。直接在對象上定義的屬性,該特性默認為true
應用:淺拷貝
const shallowClone = (obj) => Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj) );
在裝飾器上應用
Trailing commas in function parameter lists and calls(結尾逗號)此處結尾逗號指的是在函數參數列表中最后一個參數之后的逗號以及函數調用時最后一個參數之后的逗號。ES8 允許在函數定義或者函數調用時,最后一個參數之后存在一個結尾逗號而不報 SyntaxError 的錯誤。示例代碼如下:
函數聲明時
function es8(var1, var2, var3,) { // ... }
函數調用時
es8(10, 20, 30,);
ES8的這項新特性受啟發于對象或者數組中最后一項內容之后的逗號,如 [10, 20, 30,] 和 { x: 1, } 。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87402.html
摘要:特性概述整理自,歸納于筆者的現代開發語法基礎與實踐技巧系列文章中也歡迎關注前端每周清單系列獲得一手資訊。本部分則介紹了新的構造器與包含靜態方法的命名空間對象。 ECMAScript 2017(ES8)特性概述 整理自 ES8 was Released and here are its Main New Features,歸納于筆者的現代 JavaScript 開發:語法基礎與實踐技巧系...
摘要:求冪運算符和簡單介紹求冪運算符求冪運算符求冪運算符打印打印打印打印打印判斷數組里面有沒有那個值在字符串前面填充打印打印打印打印打印在字符串后邊填充打印打印打印打印打印注釋其中第一個參數是目標長度,第二個參數是填充字符串,默認的值是空格。 es7求冪運算符和es8簡單介紹 es7求冪運算符: 求冪運算符 ** operator (求冪運算符) console.log(2**3); ...
摘要:距離上一篇走馬觀花已經快兩年時間了,上個月底正式發布,再寫一篇姊妹篇,介紹新特性。會議的每一項決議必須大部分人贊同,并且沒有人強烈反對才可以通過。已經準備就緒,該特性會出現在年度發布的規范之中。 距離上一篇《ES6 走馬觀花》已經快兩年時間了,上個月底 ES8 正式發布,再寫一篇姊妹篇,介紹 ES8 新特性。 什么是 ES8 ES8 是 ECMA-262 標準第 8 版的簡稱,從 ES...
摘要:我曾寫過一篇關于博客個最佳特性,這次我打算聊聊和特性。自從年雙十一正式上線,累計處理了億錯誤事件,得到了金山軟件百姓網等眾多知名用戶的認可。 譯者按: 轉眼ES6發布2年了,是時候了解一下ES7與ES8特性了! 原文: ES7 and ES8 Features 譯者: Fundebug 為了保證可讀性,本文采用意譯而非直譯,并且對源代碼進行了大量修改。另外,本文版權歸原作者所有...
摘要:字符串拓展在我們判斷字符串是否包含另一個字符串時,之前,我們只有方法,之后我們又多了三種方法返回布爾值,表示是否找到參數字符串。返回布爾值,表示參數字符串是否在原字符串的頭部。 本文是 重溫基礎 系列文章的第八篇。今日感受:人在異鄉,也不能忘記湯圓。 系列目錄: 【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎】1.語法和數據類型 【重溫基礎】2.流程控制和...
閱讀 825·2023-04-26 00:13
閱讀 2794·2021-11-23 10:08
閱讀 2432·2021-09-01 10:41
閱讀 2112·2021-08-27 16:25
閱讀 4177·2021-07-30 15:14
閱讀 2359·2019-08-30 15:54
閱讀 857·2019-08-29 16:22
閱讀 2736·2019-08-26 12:13