ECMAScript 6.0( 以下簡稱ES6) 是JavaScript語言的下一代標(biāo)準(zhǔn)及老。
ECMAScript和JavaScript的關(guān)系是, 前者是后者的規(guī)格谤祖, 后者是前者的一種實現(xiàn)( 另外的ECMAScript方言還有Jscript和ActionScript) 妇多。 日常場合菜皂, 這兩個詞是可以互換的。
在前端工程化的現(xiàn)在镇草,學(xué)習(xí)es6還是有必要的眶痰。
本文為個人根據(jù)阮老師的es6標(biāo)準(zhǔn)入門學(xué)習(xí)筆記。
ES6
let和const命令
let
- let用來聲明變量梯啤。 它的用法類似于var竖伯, 但是所聲明的變量, 只在let命令所在的代碼塊內(nèi)有效
- 在循環(huán)中因宇,如果變量i是var聲明的七婴, 在全局范圍內(nèi)都有效。 所以每一次循環(huán)察滑,新的i值都會覆蓋舊值打厘,如果變量i是let聲明的, 當(dāng)前的i只在本輪循環(huán)有效贺辰, 所以每一次循環(huán)的i其實都是一個新的變量户盯。
- let不像var那樣會發(fā)生“變量提升”現(xiàn)象。 所以饲化, 變量一定要在聲明后使用莽鸭, 否則報錯
- let不允許在相同作用域內(nèi), 重復(fù)聲明同一個變量
//所聲明的變量吃靠, 只在let命令所在的代碼塊內(nèi)有效
{
let a = 10;
var b = 1;
console.log('a=' + a + '\nb=' + b);
}
console.log('let代碼塊外b=' + b);
// console.log('let代碼塊外b=' + a);
arr = [1, 2, 3, 4, 5, 6, 4];
for (let i = 0; i < arr.length; i++) {
console.log(i);
}
/**
* 變量i是var聲明的硫眨, 在全局范圍內(nèi)都有效。 所以每一次循環(huán)巢块,
* 新的i值都會覆蓋舊值礁阁, 導(dǎo)致最后輸出的是最后一輪的i的值
*/
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = i;
}
console.log(i);
/**
* 變量i是let聲明的, 當(dāng)前的i只在本輪循環(huán)有效夕冲, 所以每一次循環(huán)的i其實都是一個新的變量氮兵, 所以最后輸出的是6
* @type {Array}
*/
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = i;
}
console.log(a[6]);
/**
* 只要塊級作用域內(nèi)存在let命令, 它所聲明的變量就“綁定”( binding) 這個區(qū)域歹鱼, 不再受外部的影響
*存在全局變量tmp泣栈, 但是塊級作用域內(nèi)let又聲明了一個局部變量tmp, 導(dǎo)致后者綁定這個塊級作用域, 所以在let聲明變量前南片, 對tmp賦
值會報錯掺涛。
如果區(qū)塊中存在let和const命令, 這個區(qū)塊對這些命令聲明的變量疼进, 從一開始就形成了封閉作用域薪缆。 凡是在聲明之前就使用這些變
量, 就會報錯伞广。
總之拣帽, 在代碼塊內(nèi), 使用let命令聲明變量之前嚼锄, 該變量都是不可用的
暫時性死區(qū)”也意味著typeof不再是一個百分之百安全的操作
* @type {number}
*/
var tmp = 123;
if (true) {
//tmp = 'abc'; // ReferenceError
let tmp;
}
/**
*調(diào)用bar函數(shù)之所以報錯
*參數(shù)x默認(rèn)值等于另一個參數(shù)y减拭, 而此時y還沒有聲明
* @param x
* @param y
* @returns {[null,null]}
*/
function bar(x = y, y = 2) {
return [x, y];
}
//bar(); //報錯
function bar(x = 2, y = x) {
return [x, y];
}
bar();
/**
* let不允許在相同作用域內(nèi), 重復(fù)聲明同一個變量,都會報錯
*/
// function () {
// let a = 10;
// var a = 1;
// }
//
// function () {
// let a = 10;
// let a = 1;
// }
結(jié)果為:
const
- const聲明一個只讀的常量区丑。 一旦聲明拧粪, 常量的值就不能改變。
- const聲明的變量不得改變值沧侥, 這意味著可霎, const一旦聲明變量, 就必須立即初始化宴杀, 不能留到以后賦值癣朗。
- onst的作用域與let命令相同: 只在聲明所在的塊級作用域內(nèi)有效。
- const命令聲明的常量也是不提升婴氮, 同樣存在暫時性死區(qū)斯棒, 只能在聲明的位置后面使用
- const聲明的常量, 也與let一樣不可重復(fù)聲明
const PI=3.1415;
console.log(PI);
/**
* 量a是一個數(shù)組主经, 這個數(shù)組本身是可寫的荣暮, 但是如果將另一個數(shù)組賦值給a, 就會報錯
* @type {Array}
*/
const a=[];
a.push('hello');
//a = ['Dave'];
全局對象的屬性
- var命令和function命令聲明的全局變量罩驻, 依舊是全局對象的屬性
- let命令穗酥、 const命令、 class命令聲明的全局變量惠遏, 不屬于全局對象的屬性
var a = 1;
// 如果在Node的REPL環(huán)境砾跃, 可以寫成global.a
// 或者采用通用方法, 寫成this.a
this.a // 1
let b = 1;
//window.b // undefined
變量的解構(gòu)賦值
數(shù)組的解構(gòu)賦值
- ES6允許按照一定模式节吮, 從數(shù)組和對象中提取值抽高, 對變量進(jìn)行賦值, 這被稱為解構(gòu)( Destructuring)
/**
* 同時給abc賦值可以用一下方式
*/
var [a, b, c] = [1, 2, 3];
console.log('a='+a+' b='+b+' c='+c);
/**
* 這種寫法屬于“模式匹配”透绩, 只要等號兩邊的模式相同翘骂, 左邊的變量就會被賦予對應(yīng)的值
*/
let [foo, [[bar], baz]] = [1, [[2], 3]];
console.log('foo='+foo+' bar='+bar+' baz='+baz);
let [ , , third] = ["foo", "bar", "baz"];
console.log('third='+third);
let [x, , y] = [1, 2, 3];
console.log('x='+x+' y='+y);
let [head, ...tail] = [1, 2, 3, 4];
console.log('head='+head+' tail='+tail);
/**
*另一種情況是不完全解構(gòu)壁熄, 即等號左邊的模式, 只匹配一部分的等號右邊的數(shù)組
*/
let [x2, y2] = [1, 2, 3];
console.log('x2='+x2+' y2='+y2);
let [a2, [b2], c2] = [1, [2, 3], 4];
console.log('a2='+a2+' b='+b2+' c2='+c2);
結(jié)果為:
注:
- 只要某種數(shù)據(jù)結(jié)構(gòu)具有Iterator接口碳竟, 都可以采用數(shù)組形式的解構(gòu)賦值
- 解構(gòu)賦值允許指定默認(rèn)值,ES6內(nèi)部使用嚴(yán)格相等運算符( ===) 草丧, 判斷一個位置是否有值。 所以莹桅, 如果一個數(shù)組成員不嚴(yán)格等于undefined昌执, 默認(rèn)值是不會生效的
對象的解構(gòu)賦值
解構(gòu)不僅可以用于數(shù)組, 還可以用于對象
- 對象的解構(gòu)與數(shù)組有一個重要的不同诈泼。 數(shù)組的元素是按次序排列的懂拾, 變量的取值由它的位置決定; 而對象的屬性沒有次序铐达, 變量必須與屬性同名委粉, 才
能取到正確的值。 - 對象的解構(gòu)也可以指定默認(rèn)值娶桦。默認(rèn)值生效的條件是, 對象的屬性值嚴(yán)格等于undefined汁汗。
var {foo, bar} = {foo: "aaa", bar: "bbb"};
console.log('foo=' + foo + ' bar=' + bar);
var {foo2: foo2, bar2: bar2} = {foo2: "aaa", bar2: "bbb"};
console.log('foo2=' + foo2 + ' bar2=' + bar2);
/**
* 真正被賦值的是變量baz衷畦, 而不是模式foo。
*/
var {foo: baz} = {foo: "aaa", bar: "bbb"};
console.log(baz);
/**
* 和數(shù)組一樣知牌, 解構(gòu)也可以用于嵌套結(jié)構(gòu)的對象
* 這時p是模式祈争, 不是變量, 因此不會被賦值
* @type {{p: [string,null]}}
*/
var obj = {
p: [
'Hello',
{y: 'World'}
]
};
var {p: [x, {y}]} = obj;
console.log('x=' + x + ' y=' + y);
/**
* line和column是變量角寸, loc和start都是模式菩混, 不會被賦值
* @type {{loc: {start: {line: number, column: number}}}}
*/
var node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
var {loc: {start: {line, column}}} = node;
console.log('line=' + line + ' column=' + column);
/**
* 嵌套賦值
* let命令下面一行的圓括號是必須的, 否則會報錯扁藕。 因為解析器會將起首的大括號沮峡, 理解成一個代碼塊, 而不是賦值語句亿柑。
* @type {{}}
*/
let obj2 = {};
let arr = [];
({foo: obj2.prop, bar: arr[0]} = {foo: 123, bar: true});
console.log('obj2.prop='+obj2.prop+' arr='+arr);
/**
* 對象的解構(gòu)賦值邢疙, 可以很方便地將現(xiàn)有對象的方法, 賦值到某個變量
* 將Math對象的對數(shù)望薄、 正弦冀痕、 余弦三個方法斋配, 賦值到對應(yīng)的變量上
*/
let { log, sin, cos } = Math;
結(jié)果為:
字符串的解構(gòu)賦值
字符串也可以解構(gòu)賦值。 這是因為此時, 字符串被轉(zhuǎn)換成了一個類似數(shù)組的對象
注:類似數(shù)組的對象都有一個length屬性劝篷, 因此還可以對這個屬性解構(gòu)賦值
let [a, b, c, d, e] = 'hello';
let {length : len} = 'hello';
console.log(a+b+c+d+e+' length='+len);
結(jié)果:
hello length=5
函數(shù)參數(shù)的解構(gòu)賦值
函數(shù)的參數(shù)也可以使用解構(gòu)賦值。
函數(shù)add的參數(shù)表面上是一個數(shù)組全陨, 但在傳入?yún)?shù)的那一刻痴晦, 數(shù)組參數(shù)就被解構(gòu)成變量x和y
function add([x, y]) {
return x + y;
}
console.log(add([1, 2]));
/**
* 使用默認(rèn)值
* @param x
* @param y
* @returns {[null,null]}
*/
function move({x = 0, y = 0} = {}) {
return [x, y];
}
圓括號問題
只要有可能導(dǎo)致解構(gòu)的歧義儒陨, 就不得使用圓括號
不能使用圓括號的情況
- 變量聲明語句中, 不能帶有圓括號
- 函數(shù)參數(shù)中板熊, 模式不能帶有圓括號框全。
- 賦值語句中, 不能將整個模式干签, 或嵌套模式中的一層津辩, 放在圓括號之中。
// 全部報錯
var [(a)] = [1];
var {x: (c)} = {};
var ({x: c}) = {};
var {(x: c)} = {};
var {(x): c} = {};
var { o: ({ p: p }) } = { o: { p: 2 } };
// 報錯
function f([(z)]) { return z; }
// 全部報錯
({ p: a }) = { p: 42 };
([a]) = [5];
可以使用圓括號的情況
賦值語句的非模式部分容劳, 可以使用圓括號喘沿。
[(b)] = [3]; // 正確
({ p: (d) } = {}); // 正確
[(parseInt.prop)] = [3]; // 正確
變量的解構(gòu)賦值用途
交換變量的值
[x, y] = [y, x];
從函數(shù)返回多個值
// 返回一個數(shù)組
function example() {
return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一個對象
function example() {
return {
foo: 1,
bar: 2
};
}
var {foo, bar} = example();
提取JSON數(shù)據(jù)
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let {id, status, data: number} = jsonData;
遍歷Map結(jié)構(gòu)
任何部署了Iterator接口的對象, 都可以用for...of循環(huán)遍歷竭贩。 Map結(jié)構(gòu)原生支持Iterator接口蚜印, 配合變量的解構(gòu)賦值, 獲取鍵名和鍵值就非常方便留量。
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
如果只想獲取鍵名窄赋, 或者只想獲取鍵值, 可以寫成下面這樣
// 獲取鍵名
for (let [key] of map) {
// ...
}
// 獲取鍵值
for (let [,value] of map) {
// ...
}
字符串
字符串的遍歷器接口
字符串可以被for...of循環(huán)遍歷
for (let codePoint of 'hello') {
console.log(codePoint)
}
常用的新方法
includes(), startsWith(), endsWith()
- includes(): 返回布爾值楼熄, 表示是否找到了參數(shù)字符串
- startsWith(): 返回布爾值忆绰, 表示參數(shù)字符串是否在源字符串的頭部
- endsWith(): 返回布爾值, 表示參數(shù)字符串是否在源字符串的尾部可岂。
var s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
- 三個方法都支持第二個參數(shù)错敢, 表示開始搜索的位置
var s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
repeat()
repeat 方法返回一個新字符串, 表示將原字符串重復(fù)n 次缕粹。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
模板字符串
模板字符串( template string) 是增強(qiáng)版的字符串稚茅, 用反引號( `) 標(biāo)識。 它可以當(dāng)作普通字符串使用平斩, 也可以用來定義多行字符串亚享, 或者在字符串中
嵌入變量
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入變量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
//可以放入任意的JavaScript表達(dá)式, 可以進(jìn)行運算
var x = 1;
var y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
//調(diào)用函數(shù)
function fn() {
return "Hello World";
}
`foo ${fn()} bar`
注:
- 如果在模板字符串中需要使用反引號双戳, 則前面要用反斜杠轉(zhuǎn)義
- 如果使用模板字符串表示多行字符串虹蒋, 所有的空格和縮進(jìn)都會被保留在輸出之中
- 模板字符串中嵌入變量, 需要將變量名寫在${}之中
- 大括號內(nèi)部可以放入任意的JavaScript表達(dá)式飒货, 可以進(jìn)行運算魄衅, 以及引用對象屬性
- 模板字符串之中還能調(diào)用函數(shù)
數(shù)值擴(kuò)展
Math對象的擴(kuò)展
Math.trunc()
Math.trunc方法用于去除一個數(shù)的小數(shù)部分, 返回整數(shù)部分
Math.sign()
Math.sign方法用來判斷一個數(shù)到底是正數(shù)塘辅、 負(fù)數(shù)晃虫、 還是零
Math.cbrt()
Math.cbrt方法用于計算一個數(shù)的立方根
數(shù)組的擴(kuò)展
Array.from()
Array.from方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組: 類似數(shù)組的對象( array-like object) 和可遍歷( iterable) 的對象( 包括ES6新增的數(shù)據(jù)結(jié)構(gòu)Set和
Map) 。
//常見的類似數(shù)組的對象是DOM操作返回的NodeList集合
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
console.log(p);
});
//只要是部署了Iterator接口的數(shù)據(jù)結(jié)構(gòu)扣墩, Array.from都能將其轉(zhuǎn)為數(shù)組
Array.from('hello')
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']
數(shù)組實例的find()和findIndex()
數(shù)組實例的find方法哲银, 用于找出第一個符合條件的數(shù)組成員扛吞。 它的參數(shù)是一個回調(diào)函數(shù), 所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù)荆责, 直到找出第一個返回值
為true的成員滥比, 然后返回該成員。 如果沒有符合條件的成員做院, 則返回undefined盲泛。
數(shù)組實例的fill()
fill方法使用給定值, 填充一個數(shù)組
- fill方法還可以接受第二個和第三個參數(shù)键耕, 用于指定填充的起始位置和結(jié)束位置
數(shù)組實例的entries()寺滚, keys()和values()
可以用for...of循環(huán)進(jìn)行遍歷, 唯一的區(qū)別是keys()是對鍵名的遍歷屈雄、 values()是對鍵值的遍歷村视, entries()是對鍵值對的遍歷。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
//如果不使用for...of循環(huán)酒奶, 可以手動調(diào)用遍歷器對象的next方法蚁孔, 進(jìn)行遍歷
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']
數(shù)組實例的includes()
表示某個數(shù)組是否包含給定的值, 與字符串的includes方法類似
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true
該方法的第二個參數(shù)表示搜索的起始位置惋嚎, 默認(rèn)為0勒虾。 如果第二個參數(shù)為負(fù)數(shù), 則表示倒數(shù)的位置瘸彤, 如果這時它大于數(shù)組長度( 比如第二個參數(shù)為-4,
但數(shù)組長度為3) 笛钝, 則會重置為從0開始
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
函數(shù)的擴(kuò)展
函數(shù)參數(shù)的默認(rèn)值
ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值质况, 即直接寫在參數(shù)定義的后面
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
rest參數(shù)
ES6引入rest參數(shù)( 形式為“...變量名”) , 用于獲取函數(shù)的多余參數(shù)玻靡, 這樣就不需要使用arguments對象了结榄。 rest參數(shù)搭配的變量是一個數(shù)組, 該變量將多余的參數(shù)放入數(shù)組中
//利用rest參數(shù)囤捻, 可以向該函數(shù)傳入任意數(shù)目的參數(shù)
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
//rest參數(shù)中的變量代表一個數(shù)組臼朗, 所以數(shù)組特有的方法都可以用于這個變量
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
注:rest參數(shù)之后不能再有其他參數(shù)( 即只能是最后一個參數(shù))
擴(kuò)展運算符
擴(kuò)展運算符( spread) 是三個點( ...) 。 它好比rest參數(shù)的逆運算蝎土, 將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列视哑。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
//合并數(shù)組
[1, 2, ...more]
[...arr1, ...arr2, ...arr3]
//轉(zhuǎn)為真正的數(shù)組。
var nodeList = document.querySelectorAll('div');
var array = [...nodeList];
//map轉(zhuǎn)數(shù)組
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
箭頭函數(shù)
使用“箭頭”( =>) 定義函數(shù)
var f = v => v;
//等同于
// var f = function (v) {
// return v;
// };
//如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù)誊涯, 就使用一個圓括號代表參數(shù)部分
var f2 = () => 5;
// 等同于
// var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
// var sum = function(num1, num2) {
// return num1 + num2;
// };
//如果箭頭函數(shù)的代碼塊部分多于一條語句挡毅, 就要使用大括號將它們括起來, 并且使用return語句返回
var sum = (num1, num2) => {
return num1 + num2;
}
//由于大括號被解釋為代碼塊暴构, 所以如果箭頭函數(shù)直接返回一個對象跪呈, 必須在對象外面加上括號
var getTempItem = id => ({id: id, name: "Temp"});
console.log(getTempItem(5));
const full = ({ first, last }) => first + ' ' + last;
// 等同于
// function full(person) {
// return person.first + ' ' + person.last;
// }
console.log([1,2,3].map(x => x * x));
注:
- 函數(shù)體內(nèi)的this對象段磨, 就是定義時所在的對象, 而不是使用時所在的對象耗绿。
- 不可以當(dāng)作構(gòu)造函數(shù)苹支, 也就是說, 不可以使用new命令误阻, 否則會拋出一個錯誤
函數(shù)綁定
箭頭函數(shù)可以綁定this對象债蜜, 大大減少了顯式綁定this對象的寫法( call、 apply堕绩、 bind) 策幼。
函數(shù)綁定運算符是并排的兩個雙冒號( ::) , 雙冒號左邊是一個對象奴紧, 右邊是一個函數(shù)特姐。 該運算符會自動將左邊的對象, 作為上下文環(huán)境( 即this對象) 黍氮, 綁定到右邊的函數(shù)上面唐含。
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
//如果雙冒號左邊為空, 右邊是一個對象的方法沫浆, 則等于將該方法綁定在該對象上面捷枯。
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
let log = ::console.log;
// 等同于
var log = console.log.bind(console);
尾調(diào)用優(yōu)化
尾調(diào)用
某個函數(shù)的最后一步是調(diào)用另一個函數(shù)
function f(x){
return g(x);
}
//函數(shù)f的最后一步是調(diào)用函數(shù)g, 這就叫尾調(diào)用
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
//函數(shù)m和n都屬于尾調(diào)用专执, 因為它們都是函數(shù)f的最后一步操作
Set和Map數(shù)據(jù)結(jié)構(gòu)
Set
提供了新的數(shù)據(jù)結(jié)構(gòu)Set淮捆。 它類似于數(shù)組, 但是成員的值都是唯一的本股, 沒有重復(fù)的值
//Set本身是一個構(gòu)造函數(shù)攀痊, 用來生成Set數(shù)據(jù)結(jié)構(gòu)
let s = new Set();
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));
for (let i of s) {
console.log(i);
}
//Set函數(shù)接受數(shù)組作為參數(shù)
//Set函數(shù)可以接受一個數(shù)組( 或類似數(shù)組的對象) 作為參數(shù), 用來初始化
var set = new Set([1, 2, 3, 4, 4]);
// 例二
var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(items.size) // 5
//接受類似數(shù)組的對象作為參數(shù)
// 例三
function divs() {
return [...document.querySelectorAll('div')];
}
var set2 = new Set(divs());
console.log(set2.size); // 56
// 類似于
divs().forEach(div => set.add(div));
console.log(set2.size); // 56
Set實例的屬性和方法
屬性
- Set.prototype.constructor: 構(gòu)造函數(shù)拄显, 默認(rèn)就是Set函數(shù)
- Set.prototype.size: 返回Set實例的成員總數(shù)苟径。
方法
- 操作方法:
- add(value): 添加某個值, 返回Set結(jié)構(gòu)本身
- delete(value): 刪除某個值躬审, 返回一個布爾值棘街, 表示刪除是否成功
- has(value): 返回一個布爾值, 表示該值是否為Set的成員
- clear(): 清除所有成員承边, 沒有返回值
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
- 遍歷方法
- keys(): 返回鍵名的遍歷器
- values(): 返回鍵值的遍歷器
- entries(): 返回鍵值對的遍歷器
- forEach(): 使用回調(diào)函數(shù)遍歷每個成員
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(value * 2) )
// 2
// 4
// 6
注:由于Set結(jié)構(gòu)沒有鍵名遭殉, 只有鍵值( 或者說鍵名和鍵值是同一個值) , 所以key方法和value方法的行為完全一致博助。
- 應(yīng)用
//擴(kuò)展運算符( ...) 內(nèi)部使用for...of循環(huán)恩沽, 所以也可以用于Set結(jié)構(gòu)
let set = new Set(['red', 'green', 'blue']);
let arr = [...set];
// ['red', 'green', 'blue']
//去除數(shù)組的重復(fù)成員。
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
// [3, 5, 2]
//數(shù)組的map和filter方法也可以用于Set
let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
// 返回Set結(jié)構(gòu): {2, 4, 6}
let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
// 返回Set結(jié)構(gòu): {2, 4}
//并集( Union) 翔始、 交集( Intersect) 和差集( Difference)
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
Map
Map結(jié)構(gòu)提供了“值—值”的對應(yīng)罗心, 是一種更完善的Hash結(jié)構(gòu)實現(xiàn)里伯。 如果你需要“鍵值對”的
數(shù)據(jù)結(jié)構(gòu),請使用Map
var m = new Map();
var o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
// Map也可以接受一個數(shù)組作為參數(shù)渤闷。 該數(shù)組的成員是一個個表示鍵值對的數(shù)組
var map = new Map([
['name', '張三'],
['title', 'Author']
]);
console.log(map); //Map { 'name' => '張三', 'title' => 'Author' }
map.size // 2
map.has('name') // true
map.get('name') // "張三"
map.has('title') // true
map.get('title') // "Author"
//如果對同一個鍵多次賦值疾瓮, 后面的值將覆蓋前面的值
let map = new Map();
map
.set(1, 'aaa')
.set(1, 'bbb');
map.get(1) // "bbb"
屬性和操作方法
size屬性
size屬性返回Map結(jié)構(gòu)的成員總數(shù)。
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
set(key, value)
set方法設(shè)置key所對應(yīng)的鍵值飒箭, 然后返回整個Map結(jié)構(gòu)狼电。 如果key已經(jīng)有值, 則鍵值會被更新弦蹂, 否則就新生成該鍵
var m = new Map();
m.set("edition", 6) // 鍵是字符串
m.set(262, "standard") // 鍵是數(shù)值
m.set(undefined, "nah") // 鍵是undefined
get(key)
get方法讀取key對應(yīng)的鍵值肩碟, 如果找不到key, 返回undefined
var m = new Map();
var hello = function() {console.log("hello");}
m.set(hello, "Hello ES6!") // 鍵是函數(shù)
m.get(hello) // Hello ES6!
has(key)
has方法返回一個布爾值凸椿, 表示某個鍵是否在Map數(shù)據(jù)結(jié)構(gòu)中
var m = new Map();
m.set("edition", 6);
m.set(262, "standard");
m.set(undefined, "nah");
m.has("edition") // true
m.has("years") // false
m.has(262) // true
m.has(undefined) // true
delete(key)
delete方法刪除某個鍵削祈, 返回true。 如果刪除失敗脑漫, 返回false髓抑。
var m = new Map();
m.set(undefined, "nah");
m.has(undefined) // true
m.delete(undefined)
m.has(undefined) // false
clear()
clear方法清除所有成員, 沒有返回值
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0
遍歷
- keys(): 返回鍵名的遍歷器优幸。
- values(): 返回鍵值的遍歷器
- entries(): 返回所有成員的遍歷器吨拍。
- forEach(): 遍歷Map的所有成員
let map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
結(jié)合數(shù)組的map方法、 filter方法网杆, 可以實現(xiàn)Map的遍歷和過濾( Map本身沒有map和filter方法)
let map0 = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
let map1 = new Map(
[...map0].filter(([k, v]) => k < 3)
);
// 產(chǎn)生Map結(jié)構(gòu) {1 => 'a', 2 => 'b'}
let map2 = new Map(
[...map0].map(([k, v]) => [k * 2, '_' + v])
);
// 產(chǎn)生Map結(jié)構(gòu) {2 => '_a', 4 => '_b', 6 => '_c'}
map與其他數(shù)據(jù)結(jié)構(gòu)的互相轉(zhuǎn)換:
- Map轉(zhuǎn)為數(shù)組
Map轉(zhuǎn)為數(shù)組最方便的方法羹饰, 就是使用擴(kuò)展運算符( ...) 。
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
- 數(shù)組轉(zhuǎn)為Map
將數(shù)組轉(zhuǎn)入Map構(gòu)造函數(shù)碳却, 就可以轉(zhuǎn)為Map
new Map([[true, 7], [{foo: 3}, ['abc']]])
// Map {true => 7, Object {foo: 3} => ['abc']}
- Map轉(zhuǎn)為JSON
Map轉(zhuǎn)為JSON要區(qū)分兩種情況:
- Map的鍵名都是字符串严里, 這時可以選擇轉(zhuǎn)為對象JSON
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
}
let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
- Map的鍵名有非字符串, 這時可以選擇轉(zhuǎn)為數(shù)組JSON
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
- JSON轉(zhuǎn)為Map
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"yes":true,"no":false}')
// Map {'yes' => true, 'no' => false}
Generator 函數(shù)
Generator函數(shù)是ES6提供的一種異步編程解決方案追城, 語法行為與傳統(tǒng)函數(shù)完全不同。
執(zhí)行Generator函數(shù)會返回一個遍歷器對象燥撞, 也就是說座柱, Generator函數(shù)除了狀態(tài)機(jī),還是一個遍歷器對象生成函數(shù)物舒。 返回的遍歷器對象色洞, 可以依次遍歷Generator函數(shù)內(nèi)部的每一個狀態(tài)。
形式上冠胯, Generator函數(shù)是一個普通函數(shù)火诸, 但是有兩個特征。
- function關(guān)鍵字與函數(shù)名之間有一個星號荠察;
- 函數(shù)體內(nèi)部使用yield語句置蜀, 定義不同的內(nèi)部狀態(tài)( yield語句在英語里的意思就是“產(chǎn)出”) 奈搜。
Generator函數(shù)的調(diào)用方法與普通函數(shù)一樣, 也是在函數(shù)名后面加上一對圓括號盯荤。 不同的是馋吗, 調(diào)用Generator函數(shù)后, 該函數(shù)并不執(zhí)行秋秤, 返回的也不是函數(shù)運行結(jié)果宏粤, 而是一個指向內(nèi)部狀態(tài)的指針對象, 也就是上一章介紹的遍歷器對象( Iterator Object)
必須調(diào)用遍歷器對象的next方法灼卢, 使得指針移向下一個狀態(tài)绍哎。 也就是說, 每次調(diào)用next方法鞋真, 內(nèi)部指針就從函數(shù)頭部或上一次停下來的地方開始執(zhí)行崇堰, 直到遇到下一個yield語句( 或return語句) 為止。 換言之灿巧, Generator函數(shù)是分段執(zhí)行的赶袄,yield語句是暫停執(zhí)行的標(biāo)記, 而next方法可
以恢復(fù)執(zhí)行抠藕。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
yield語句
遍歷器對象的next方法的運行邏輯如下
- 遇到y(tǒng)ield語句饿肺, 就暫停執(zhí)行后面的操作, 并將緊跟在yield后面的那個表達(dá)式的值盾似, 作為返回的對象的value屬性值
- 下一次調(diào)用next方法時敬辣, 再繼續(xù)往下執(zhí)行, 直到遇到下一個yield語句零院。
- 如果沒有再遇到新的yield語句溉跃, 就一直運行到函數(shù)結(jié)束, 直到return語句為止告抄, 并將return語句后面的表達(dá)式的值撰茎, 作為返回的對象的value屬性值。
- 如果該函數(shù)沒有return語句打洼, 則返回的對象的value屬性值為undefined
注: yield語句不能用在普通函數(shù)中龄糊, 否則會報錯
Promise對象
所謂Promise, 簡單說就是一個容器募疮, 里面保存著某個未來才會結(jié)束的事件( 通常是一個異步操作) 的結(jié)果炫惩。 從語法上說, Promise是一個對象阿浓, 從它可以獲取異步操作的消息
Promise對象有以下兩個特點:
- 對象的狀態(tài)不受外界影響他嚷。 Promise對象代表一個異步操作, 有三種狀態(tài): Pending( 進(jìn)行中) 、 Resolved( 已完成筋蓖, 又稱Fulfilled)和Rejected( 已失斝对拧) 。 只有異步操作的結(jié)果扭勉, 可以決定當(dāng)前是哪一種狀態(tài)鹊奖, 任何其他操作都無法改變這個狀態(tài)。 這也是Promise這個名字的由來涂炎, 它的英語意思就是“承諾”忠聚, 表示其他手段無法改變
- 一旦狀態(tài)改變, 就不會再變唱捣, 任何時候都可以得到這個結(jié)果两蟀。 Promise對象的狀態(tài)改變, 只有兩種可能: 從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected震缭。 只要這兩種情況發(fā)生赂毯, 狀態(tài)就凝固了, 不會再變了拣宰, 會一直保持這個結(jié)果党涕。 就算改變已經(jīng)發(fā)生了, 你再對Promise對象添加回調(diào)函數(shù)巡社, 也會立即得到這個結(jié)果膛堤。 這與事件( Event) 完全不同, 事件的特點是晌该, 如果你錯過了它肥荔, 再去監(jiān)聽, 是得不到結(jié)果的朝群。
//創(chuàng)造了一個Promise實例燕耿。
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
//可以用then方法分別指定Resolved狀態(tài)和Reject狀態(tài)的回調(diào)函數(shù)
promise.then(function(value) {
// success
}, function(error) {
// failure
});
//用Promise對象實現(xiàn)的Ajax操作的例子
var getJSON = function (url) {
var promise = new Promise(function (resolve, reject) {
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.readyState !== 4) {
return;
}
if(this.status === 200)
{
resolve(this.response);
}
else
{
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("/posts.json").then(function (json) {
console.log('Contents: ' + json);
}, function (error) {
console.error('出錯了', error);
});
async函數(shù)
async函數(shù)就是Generator函數(shù)的語法糖。
async函數(shù)就是將Generator函數(shù)的星號( *) 替換成async姜胖, 將yield替換成await誉帅, 僅此而已
//async函數(shù)返回一個Promise對象, 可以使用then方法添加回調(diào)函數(shù)右莱。 當(dāng)函數(shù)執(zhí)行的時候蚜锨, 一旦遇到await就會先返回, 等到觸發(fā)的異步操作完成隧出, 再接著執(zhí)行函數(shù)體內(nèi)后面的語句。
async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result);
});
//指定多少毫秒后輸出一個值
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value)
}
asyncPrint('hello world', 50);
//Async函數(shù)有多種使用形式阀捅。
// 函數(shù)聲明
async function foo() {}
// 函數(shù)表達(dá)式
const foo = async function () {};
// 對象的方法
let obj = { async foo() {} };
// 箭頭函數(shù)
const foo = async () => {};
Class
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `x=${this.x} y=${this.y}`;
}
}
let point = new Point(1, 2);
console.log(point.toString()); //x=1 y=2
注:定義“類”的方法的時候胀瞪, 前面不需要加上function這個關(guān)鍵字, 直接把函數(shù)定義放進(jìn)去了就可以了。 另外凄诞, 方法之間不需要逗號分隔圆雁, 加了會報錯。
constructor方法
constructor方法是類的默認(rèn)方法帆谍, 通過new命令生成對象實例時伪朽, 自動調(diào)用該方法。 一個類必須有constructor方法汛蝙, 如果沒有顯式定義烈涮, 一個空的constructor方法會被默認(rèn)添加
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
Class的繼承
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 調(diào)用父類的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 調(diào)用父類的toString()
}
}
注:
- 子類必須在constructor方法中調(diào)用super方法, 否則新建實例時會報錯窖剑。 這是因為子類沒有自己的this對象坚洽, 而是繼承父類的this對象, 然后對其進(jìn)行加工
- 在子類的構(gòu)造函數(shù)中西土, 只有調(diào)用super之后讶舰, 才可以使用this關(guān)鍵字, 否則會報錯
Module
模塊功能主要由兩個命令構(gòu)成: export和import需了。 export命令用于規(guī)定模塊的對外接口跳昼, import命令用于輸入其他模塊提供的功能。
一個模塊就是一個獨立的文件肋乍。 該文件內(nèi)部的所有變量鹅颊, 外部無法獲取。 如果你希望外部能夠讀取模塊內(nèi)部的某個變量住拭, 就必須使用export關(guān)鍵字輸出該變量
export
//用export命令對外部輸出了三個變量
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//另外一種寫法
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
//export命令除了輸出變量挪略, 還可以輸出函數(shù)或類( class) 。
export function multiply(x, y) {
return x * y;
};
//export輸出的變量就是本來的名字滔岳, 但是可以使用as關(guān)鍵字重命名杠娱。
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
//export命令規(guī)定的是對外的接口, 必須與模塊內(nèi)部的變量建立一一對應(yīng)關(guān)系
// 寫法一
export var m = 1;
// 寫法二
var m = 1;
export {m};
// 寫法三
var n = 1;
export {n as m};
import
使用export命令定義了模塊的對外接口以后谱煤, 其他JS文件就可以通過import命令加載這個模塊( 文件)
import命令接受一個對象( 用大括號表示) 摊求, 里面指定要從其他模塊導(dǎo)入的變量名。 大括號里面的變量名刘离, 必須與被導(dǎo)入模塊( profile.js) 對外接口的名稱相同室叉。
//從profile中導(dǎo)入firstName, lastName, year
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
//import命令要使用as關(guān)鍵字, 將輸入的變量重命名
import { lastName as surname } from './profile';
模塊的整體加載
除了指定加載某個輸出值硫惕, 還可以使用整體加載茧痕, 即用星號( *) 指定一個對象, 所有輸出值都加載在這個對象上面
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
//整體加載
import * as circle from './circle';
console.log('圓面積: ' + circle.area(4));
console.log('圓周長: ' + circle.circumference(14));
export default命令
為模塊指定默認(rèn)輸出
// export-default.js
//默認(rèn)輸出是一個函數(shù)恼除。
export default function () {
console.log('foo');
}
// import-default.js
//import命令可以為該匿名函數(shù)指定任意名字踪旷。
import customName from './export-default';
customName(); // 'foo'
注:一個模塊只能有一個默認(rèn)輸出曼氛, 因此export deault命令只能使用一次。 所以令野, import命令后面才不用加大括號舀患, 因為只可能對應(yīng)一個方法。
跨模塊常量
const聲明的常量只在當(dāng)前代碼塊有效气破。 如果想設(shè)置跨模塊的常量( 即跨多個文件) 聊浅, 可以采用下面的寫法
// constants.js 模塊
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模塊
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模塊
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3