什么是ECMAScript
ECMAScript 是一種由 ECMA國際(前身為歐洲計(jì)算機(jī)制造商協(xié)會)通過 ECMA-262 標(biāo)準(zhǔn)化的腳本程序設(shè)計(jì)語言。
Ecma國際(Ecma International)是一家國際性會員制度的信息和電信標(biāo)準(zhǔn)組織。1994年之前,名為歐洲計(jì)算機(jī)制造商協(xié)會(European Computer Manufacturers Association)。因?yàn)橛?jì)算機(jī)的國際化,組織的標(biāo)準(zhǔn)牽涉到很多其他國家,因此組織決定改名表明其國際性?,F(xiàn)名稱已不屬于首字母縮略字。
與國家政府標(biāo)準(zhǔn)機(jī)構(gòu)不同,Ecma國際是企業(yè)會員制的組織。組織的標(biāo)準(zhǔn)化過程比較商業(yè)化,自稱這種營運(yùn)方式減少官僚追求效果。
其實(shí) Ecma國際負(fù)責(zé)了很多標(biāo)準(zhǔn)的制定,比如有如下這些規(guī)范。大家可以看到這里面有我們今天的主角,ECMAScript 規(guī)范、 C#語言規(guī)范、 C++/CLI語言規(guī)范等。
ECMAScript 和 JavaScript 的關(guān)系
1996 年 11 月,JavaScript 的創(chuàng)造者 Netscape 公司,決定將 JavaScript 提交給標(biāo)準(zhǔn)化組織 ECMA,希望這種語言能夠成為國際標(biāo)準(zhǔn)。次年,ECMA 發(fā)布 262 號標(biāo)準(zhǔn)文件(ECMA-262)的第一版,規(guī)定了瀏覽器腳本語言的標(biāo)準(zhǔn),并將這種語言稱為 ECMAScript,這個版本就是 1.0 版。
該標(biāo)準(zhǔn)從一開始就是針對 JavaScript 語言制定的,但是之所以不叫 JavaScript,有兩個原因。一是商標(biāo),Java 是 Sun 公司的商標(biāo),根據(jù)授權(quán)協(xié)議,只有 Netscape 公司可以合法地使用 JavaScript 這個名字,且 JavaScript 本身也已經(jīng)被 Netscape 公司注冊為商標(biāo)。二是想體現(xiàn)這門語言的制定者是 ECMA,不是 Netscape,這樣有利于保證這門語言的開放性和中立性。
因此,ECMAScript 和 JavaScript 的關(guān)系是,前者是后者的規(guī)格,后者是前者的一種實(shí)現(xiàn)。
ES6 與 ECMAScript 2015 的關(guān)系
ECMAScript 2015(簡稱 ES2015)這個詞,也是經(jīng)??梢钥吹降?。它與 ES6 是什么關(guān)系呢?
2011 年,ECMAScript 5.1 版發(fā)布后,就開始制定 6.0 版了。因此,ES6 這個詞的原意,就是指 JavaScript 語言的下一個版本。
但是,因?yàn)檫@個版本引入的語法功能太多,而且制定過程當(dāng)中,還有很多組織和個人不斷提交新功能。事情很快就變得清楚了,不可能在一個版本里面包括所有將要引入的功能。常規(guī)的做法是先發(fā)布 6.0 版,過一段時間再發(fā) 6.1 版,然后是 6.2 版、6.3 版等等。
標(biāo)準(zhǔn)委員會最終決定,標(biāo)準(zhǔn)在每年的 6 月份正式發(fā)布一次,作為當(dāng)年的正式版本。接下來的時間,就在這個版本的基礎(chǔ)上做改動,直到下一年的 6 月份,草案就自然變成了新一年的版本。這樣一來,就不需要以前的版本號了,只要用年份標(biāo)記就可以了。
因此,ES6 既是一個歷史名詞,也是一個泛指,含義是 5.1 版以后的 JavaScript 的下一代標(biāo)準(zhǔn),涵蓋了 ES2015、ES2016、ES2017 等等,而 ES2015 則是正式名稱,特指該年發(fā)布的正式版本的語言標(biāo)準(zhǔn)。
ECMAScript 的歷史
1996年11月,Netscape公司將Js提交給國際化標(biāo)準(zhǔn)組織ECMA,當(dāng)初該語言能夠成為國際化標(biāo)準(zhǔn)。
1997年,ECMAScript 1.0版本推出。(在這年,ECMA發(fā)布262號標(biāo)準(zhǔn)文件(ECMA-262)的第一版,規(guī)定瀏覽器腳本語言的標(biāo)準(zhǔn),并將這種語言稱為ECMAScript,也就是ES1.0版本。)
1998年6月,ES 2.0 版發(fā)布。
1999年12月,ES 3.0 版發(fā)布,并成為JS的通行標(biāo)準(zhǔn),得到廣泛支持。
2007年10月,ES 4.0 版草案發(fā)布。
2008年7月,由于各方分歧太大,ECMA決定終止ES 4.0的開發(fā)。轉(zhuǎn)而將其中涉及現(xiàn)有功能改善的一小部分發(fā)布為ES 3.1 。但是回后不久將其改名為ES 5.0版;
2009年12月,ES 5.0 版正式發(fā)布。
2011年6月,ES 5.1 版發(fā)布,并成為ISO國際標(biāo)準(zhǔn)(ISO/IEC 16262:2011)。
2013年3月,ES 6 草案終結(jié),并且不再添加新的功能。
2013年12月,ES 6 草案發(fā)布。
2015年6月,ES 6 正式版本發(fā)布。
從此后面每年6月都會發(fā)布一個正式版本,所以目前最新的版本是2021年6月發(fā)布的ES12。
ECMAScript各版本新增特性
ES6新增特性
1.類
ES6 引入了 class(類),讓 JavaScript 的面向?qū)ο缶幊套兊酶雍唵魏鸵子诶斫狻?/p>
class Student {
constructor() {
console.log("I'm a student.");
}
study() {
console.log('study!');
}
static read() {
console.log("Reading Now.");
}
}
console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."
2.模塊化
ES5 支持原生的模塊化,在ES6中模塊作為重要的組成部分被添加進(jìn)來。模塊的功能主要由 export 和 import 組成。每一個模塊都有自己單獨(dú)的作用域,模塊之間的相互調(diào)用關(guān)系是通過 export 來規(guī)定模塊對外暴露的接口,通過 import 來引用其它模塊提供的接口。同時還為模塊創(chuàng)造了命名空間,防止函數(shù)的命名沖突。
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
3.箭頭函數(shù)
=>不只是關(guān)鍵字 function 的簡寫,它還帶來了其它好處。箭頭函數(shù)與包圍它的代碼共享同一個 this,能幫你很好的解決this的指向問題。比如 var self = this;或 var that =this這種引用外圍this的模式。但借助 =>,就不需要這種模式了。
() => 1
v => v+1
(a,b) => a+b
() => {
alert("foo");
}
e => {
if (e == 0){
return 0;
}
return 1000/e;
}
4.模板字符串
ES6 支持 模板字符串,使得字符串的拼接更加的簡潔、直觀。
//不使用模板字符串
var name = 'Your name is ' + first + ' ' + last + '.'
//使用模板字符串
var name = `Your name is ${first} ${last}.`
在 ES6 中通過 ${}就可以完成字符串的拼接,只需要將變量放在大括號之中。
5.解構(gòu)賦值
解構(gòu)賦值語法是 JavaScript 的一種表達(dá)式,可以方便的從數(shù)組或者對象中快速提取值賦給定義的變量。
// 對象
const student = {
name: 'Sam',
age: 22,
sex: '男'
}
// 數(shù)組
// const student = ['Sam', 22, '男'];
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);
// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);
6.延展操作符
延展操作符...可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時, 將數(shù)組表達(dá)式或者 string 在語法層面展開;還可以在構(gòu)造對象時, 將對象表達(dá)式按 key-value 的方式展開。
//在函數(shù)調(diào)用時使用延展操作符
function sum(x, y, z) {
return x + y + z
}
const numbers = [1, 2, 3]
console.log(sum(...numbers))
//數(shù)組
const stuendts = ['Jine', 'Tom']
const persons = ['Tony', ...stuendts, 'Aaron', 'Anna']
conslog.log(persions)
7.Promise
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案 callback 更加的優(yōu)雅。它最早由社區(qū)提出和實(shí)現(xiàn)的,ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了 Promise 對象。
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出錯了', error);
});
8.let 與 const
在之前 JS 是沒有塊級作用域的,const與 let 填補(bǔ)了這方便的空白,const與 let 都是塊級作用域。
function f() {
{
let x;
{
// 正確
const x = "sneaky";
// 錯誤,常量const
x = "foo";
}
// 錯誤,已經(jīng)聲明過的變量
let x = "inner";
}
}
ES7新增特性
1.Array.prototype.includes()
includes() 函數(shù)用來判斷一個數(shù)組是否包含一個指定的值,如果包含則返回 true,否則返回 false。
[1, 2, 3].includes(-1) // false
[1, 2, 3].includes(1) // true
[1, 2, 3].includes(3, 4) // false
[1, 2, 3].includes(3, 3) // false
[1, 2, NaN].includes(NaN) // true
['foo', 'bar', 'quux'].includes('foo') // true
['foo', 'bar', 'quux'].includes('norf') // false
2.指數(shù)操作符
在 ES7 中引入了指數(shù)運(yùn)算符 **, **具有與 Math.pow(..)等效的計(jì)算結(jié)果。使用指數(shù)運(yùn)算符 **,就像 +、- 等操作符一樣。
//之前的版本
Math.pow(5, 2)
// ES7
5 ** 2
// 5 ** 2 === 5 * 5
ES8新增特性
1.async/await
異步函數(shù)返回一個AsyncFunction對象并通過事件循環(huán)異步操作。
const resolveAfter3Seconds = function() {
console.log('starting 3 second promsise')
return new Promise(resolve => {
setTimeout(function() {
resolve(3)
console.log('done in 3 seconds')
}, 3000)
})
}
const resolveAfter1Second = function() {
console.log('starting 1 second promise')
return new Promise(resolve => {
setTimeout(function() {
resolve(1)
console.log('done, in 1 second')
}, 1000)
})
}
const sequentialStart = async function() {
console.log('***SEQUENTIAL START***')
const one = await resolveAfter1Second()
const three = await resolveAfter3Seconds()
console.log(one)
console.log(three)
}
sequentialStart();
- Object.values()
Object.values()是一個與 Object.keys()類似的新函數(shù),但返回的是 Object 自身屬性的所有值,不包括繼承的值。
const obj = { a: 1, b: 2, c: 3 }
//不使用 Object.values()
const vals = Object.keys(obj).map((key) => obj[key])
console.log(vals)
//使用 Object.values()
const values = Object.values(obj1)
console.log(values)
從上述代碼中可以看出 Object.values()為我們省去了遍歷 key,并根據(jù)這些 key 獲取 value 的步驟。
3.Object.entries()
Object.entries()函數(shù)返回一個給定對象自身可枚舉屬性的鍵值對的數(shù)組。
//不使用 Object.entries()
Object.keys(obj).forEach((key) => {
console.log('key:' + key + ' value:' + obj[key])
})
//key:b value:2
//使用 Object.entries()
for (let [key, value] of Object.entries(obj1)) {
console.log(`key: ${key} value:${value}`)
}
//key:b value:2
4.String padding
在 ES8 中 String 新增了兩個實(shí)例函數(shù) String.prototype.padStart和 String.prototype.padEnd,允許將空字符串或其他字符串添加到原始字符串的開頭或結(jié)尾。
console.log('0.0'.padStart(4, '10'))
console.log('0.0'.padStart(20))
console.log('0.0'.padEnd(4, '0'))
console.log('0.0'.padEnd(10, '0'))
5、Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors()函數(shù)用來獲取一個對象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對象。
let myObj = {
property1: 'foo',
property2: 'bar',
property3: 42,
property4: () => console.log('prop4')
}
Object.getOwnPropertyDescriptors(myObj)
/*
{ property1: {…}, property2: {…}, property3: {…}, property4: {…} }
property1: {value: "foo", writable: true, enumerable: true, configurable: true}
property2: {value: "bar", writable: true, enumerable: true, configurable: true}
property3: {value: 42, writable: true, enumerable: true, configurable: true}
property4: {value: ?, writable: true, enumerable: true, configurable: true}
__proto__: Object
*/
ES9新增特性
1.async iterators
ES9 引入異步迭代器(asynchronous iterators), await可以和 for...of循環(huán)一起使用,以串行的方式運(yùn)行異步操作。
//如果在 async/await中使用循環(huán)中去調(diào)用異步函數(shù),則不會正常執(zhí)行
async function demo(arr) {
for (let i of arr) {
await handleDo(i);
}
}
//ES9
async function demo(arr) {
for await (let i of arr) {
handleDo(i);
}
}
2.Promise.finally()
一個 Promise 調(diào)用鏈要么成功到達(dá)最后一個 .then(),要么失敗觸發(fā) .catch()。在某些情況下,你想要在無論 Promise 運(yùn)行成功還是失敗,運(yùn)行相同的代碼,例如清除,刪除對話,關(guān)閉數(shù)據(jù)庫連接等。
.finally()允許你指定最終的邏輯。
function doSomething() {
doSomething1()
.then(doSomething2)
.then(doSomething3)
.catch((err) => {
console.log(err)
})
.finally(() => {})
}
3.Rest/Spread屬性
Rest:對象解構(gòu)賦值的其余屬性。
Spread:對象解構(gòu)賦值的傳播屬性。
//Rest
let { fname, lname, ...rest } = { fname: "Hemanth", lname: "HM", location: "Earth", type: "Human" };
fname; //"Hemanth"
lname; //"HM"
rest; // {location: "Earth", type: "Human"}
//Spread
let info = {fname, lname, ...rest};
info; // { fname: "Hemanth", lname: "HM", location: "Earth", type: "Human" }
ES10新增特性
1.Array的 flat()方法和 flatMap()方法
flat() 方法會按照一個可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個新數(shù)組返回。
flatMap() 方法首先使用映射函數(shù)映射每個元素,然后將結(jié)果壓縮成一個新數(shù)組。它與 map 和 深度值1的 flat 幾乎相同,但 flatMap通常在合并成一種方法的效率稍微高一些。
let arr = ['a', 'b', ['c', 'd']];
let flattened = arr.flat();
console.log(flattened); // => ["a", "b", "c", "d"]
arr = ['a', , , 'b', ['c', 'd']];
flattened = arr.flat();
console.log(flattened); // => ["a", "b", "c", "d"]
arr = [10, [20, [30]]];
console.log(arr.flat()); // => [10, 20, [30]]
console.log(arr.flat(1)); // => [10, 20, [30]]
console.log(arr.flat(2)); // => [10, 20, 30]
console.log(arr.flat(Infinity)); // => [10, 20, 30]
2.String的 trimStart()方法和 trimEnd()方法
分別去除字符串首尾空白字符
const str = " string ";
console.log(str.trimStart()); // => "string "
console.log(str.trimEnd()); // => " string"
- Object.fromEntries()
Object.entries()方法的作用是返回一個給定對象自身可枚舉屬性的鍵值對數(shù)組,其排列與使用 for...in 循環(huán)遍歷該對象時返回的順序一致(區(qū)別在于 for-in 循環(huán)也枚舉原型鏈中的屬性)。
而 Object.fromEntries() 則是 Object.entries() 的反轉(zhuǎn),Object.fromEntries() 函數(shù)傳入一個鍵值對的列表,并返回一個帶有這些鍵值對的新對象。
const myArray = [['one', 1], ['two', 2], ['three', 3]];
const obj = Object.fromEntries(myArray);
console.log(obj); // => {one: 1, two: 2, three: 3}
ES11新增特性
1.Promise.allSettled
Promise.all最大問題就是如果其中某個任務(wù)出現(xiàn)異常(reject),所有任務(wù)都會掛掉,Promise 直接進(jìn)入 reject 狀態(tài)。
Promise.allSettled在并發(fā)任務(wù)中,無論一個任務(wù)正?;蛘弋惓?,都會返回對應(yīng)的的狀態(tài)(fulfilled 或者 rejected)與結(jié)果(業(yè)務(wù) value 或者 拒因 reason),在 then 里面通過 filter 來過濾出想要的業(yè)務(wù)邏輯結(jié)果,這就能最大限度的保障業(yè)務(wù)當(dāng)前狀態(tài)的可訪問性。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
- String.prototype.matchAll
matchAll() 方法返回一個包含所有匹配正則表達(dá)式及分組捕獲結(jié)果的迭代器。 在 matchAll出現(xiàn)之前,通過在循環(huán)中調(diào)用 regexp.exec來獲取所有匹配項(xiàng)信息(regexp需使用 /g 標(biāo)志)。如果使用 matchAll,就可以不必使用 while 循環(huán)加 exec 方式(且正則表達(dá)式需使用/g標(biāo)志)。使用 matchAll會得到一個迭代器的返回值,配合 for...of, array spread, or Array.from() 可以更方便實(shí)現(xiàn)功能。
const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
const array = [...str.matchAll(regexp)];
console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]
console.log(array[1]);
// expected output: Array ["test2", "e", "st2", "2"]
ES12新增特性
- Promise.any
Promise.any() 接收一個Promise可迭代對象,只要其中的一個 promise 成功,就返回那個已經(jīng)成功的 promise 。如果可迭代對象中沒有一個 promise 成功(即所有的 promises 都失敗/拒絕),就返回一個失敗的 promise。
const promise1 = new Promise((resolve, reject) => reject('我是失敗的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失敗的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then(values=>{
console.log(values);
})
.catch(e=>{
console.log(e);
});
2.邏輯運(yùn)算符和賦值表達(dá)式
邏輯運(yùn)算符和賦值表達(dá)式,新特性結(jié)合了邏輯運(yùn)算符(&&=,||=,??=)。
a ||= b
//等價(jià)于
a = a || (a = b)
a &&= b
//等價(jià)于
a = a && (a = b)
a ??= b
//等價(jià)于
a = a ?? (a = b)
- replaceAll
返回一個全新的字符串,所有符合匹配規(guī)則的字符都將被替換掉。
const str = 'hello world';
str.replaceAll('l', ''); // "heo word"
4.數(shù)字分隔符
數(shù)字分隔符,可以在數(shù)字之間創(chuàng)建可視化分隔符,通過_下劃線來分割數(shù)字,使數(shù)字更具可讀性。
const money = 1_000_000_000;
//等價(jià)于
const money = 1000000000;
1_000_000_000 === 1000000000; // true
相關(guān)文檔查找
1、在MDN的javascript里可以根據(jù)關(guān)鍵詞查詢到特性的介紹和示例
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript
2、官方文檔
https://262.ecma-international.org/#sec-intro
3、阮一峰es6入門(實(shí)際不止es6新增的特性,每年都在持續(xù)更新中)