1.新的標(biāo)準(zhǔn)規(guī)范
ECMAScript2015 是 js 的一種的新的標(biāo)準(zhǔn)規(guī)范,就是對 js 的寫法上提出了新的語法要求和寫法格式。
2.ECMAScript 和 javaScript 關(guān)系
ECMAScript 和 JavaScript 的關(guān)系是,前者是后者的規(guī)格,后者是前者的一種實(shí)現(xiàn)。javascript 是 netscape 創(chuàng) 造的并交給了國際標(biāo)準(zhǔn)化組織 ECMA,之所以不叫做 JavaScript 由于商標(biāo)的問題,java 是 sun 公司的商標(biāo),根據(jù) 授權(quán)協(xié)議只有 Netscape 公司可以合法使用 JavaScript 這個名字,另外就是為了體現(xiàn) JavaScript 的標(biāo)準(zhǔn)的制定者 不是 ECMA 所以取名為 ECMAScript。


3.ES6 與 ECMAScript 2015 的關(guān)系
ES6 是 ECMA 為 JavaScript 制定的第 6 個版本的標(biāo)準(zhǔn),標(biāo)準(zhǔn)委員會最終決定,標(biāo)準(zhǔn)在每年的 6 月份正式發(fā)布一 次,作為當(dāng)年的正式版本。ECMAscript 2015 是在 2015 年 6 月份發(fā)布的 ES6 的第一個版本。依次類推 ECMAscript 2016 是 ES7, ECMAscript 2017 是 ES8, 后續(xù)版本都以年份為命名
塊級作用域
1.塊級作用域的種類
ECMAScript2015 為 js 提出的第三個作用域,凡是帶{}的都是一個塊級作用域。
if 語句的{},for 循環(huán)中的{},while 中的{},或者是我們單獨(dú)寫的{} try{}catch(error){}這些都提供了塊級作用域。
塊級作用域
為什么需要塊級作用域
-
內(nèi)層變量會覆蓋外層變量
var a = 'glh'; function fn() { console.log(a); //undefined if (false) { var a = 'hello'; } } fn();
這是因?yàn)?fn 函數(shù)體內(nèi)以及有 var 聲明的變量 a,只是還沒有賦值,默認(rèn)為 undefined。
-
用來計數(shù)的循環(huán)變量泄露為全局變量
for (var i = 0; i < 10; i++) {} // 10 console.log(i);
let const
1.let
基本用法
ECMAScript2015 新增了let命令,用來聲明變量。它的用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)有效。
let 主要聲明塊級作用域下的成員,這個成員變量的作用域范圍只能在當(dāng)前塊級作用域以及子作用域鏈中。
2.const
const 聲明變量的同時必須要賦值。
const 聲明之后,不允許去修改它的值,這里面的值說的是不允許修改它,是聲明之后不允許重新指向一個新
的內(nèi)存地址,可以去修改內(nèi)存地址中的屬性成員。
數(shù)組
1.數(shù)組解構(gòu)
ES6 允許按照一定模式,從數(shù)組和對象中提取值,對變量進(jìn)行賦值,這被稱為解構(gòu)。
-
完全解構(gòu) 將數(shù)組中的每一個值都對應(yīng)上相應(yīng)的變量。
var arr = ['a', 'b', 'c']; let [com, ind, work] = arr; console.log(work); // c -
不完全解構(gòu) 數(shù)組中的部分值對應(yīng)上了相應(yīng)的變量。
var arr = ['a', 'b', 'c']; let [, , work] = arr; console.log(work); // c注意:模式?jīng)]有匹配上的可以不填,但是必須要加逗號隔開。
-
解構(gòu)不成功
右邊的變量的個數(shù)超過了等號左邊中數(shù)組的元素
let [a, b, c] = [12]; console.log(b); //undefined如果解構(gòu)沒有成功,則變量的值是 undefined,如果是展開運(yùn)算的變量則是空數(shù)組。
let [a, b, ...c] = [12]; console.log(c); // []
2.數(shù)組的擴(kuò)展
-
展開運(yùn)算符說明
三個點(diǎn)(…)是一個展開運(yùn)算符,其功能為對三個點(diǎn)后面的變量進(jìn)行展開操作
三個點(diǎn)(…)展開運(yùn)算符:只能對具有 Iterator 接口的對象進(jìn)行展開操作
-
替代 apply()的使用技巧
我們之前在求一個數(shù)組中的最大值得時候采用得方式是 Math.max.apply(null,[12,34,56,43]) ==56
var max = Math.max.apply(null, [12, 34, 56, 43]); console.log(max); //56 var max2 = Math.max(...[12, 34, 56, 43]); console.log(max2); //56 -
Array 類的擴(kuò)展方法
- Array.from()
Array.from 方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數(shù)據(jù)結(jié)構(gòu) Set 和 Map)
var arraylike = { 0: 'a', 1: 'b', 2: 'c', length: 3, }; var arr = Array.from(arraylike); ['a', 'b', 'c'];Array.from 還可以接受第二個參數(shù),作用類似于數(shù)組的
map方法,用來對每個元素進(jìn)行處理,將處理后的值放入返回的數(shù)組var arr = Array.from([1, 2, 3], function (x) { return x * x; }); console.log(arr); //[1,4,9]-
Array.of 方法用于將一組值,轉(zhuǎn)換為數(shù)組,這個方法的主要目的,是彌補(bǔ)數(shù)組構(gòu)造函數(shù)
Array()的不足。因?yàn)閰?shù)個數(shù)的不同,會導(dǎo)致Array()的行為有差異。var arr = Array(3); //[emptyx3]這里面 3 表示數(shù)組中元素的長度。
var arr = Array(2, 3, 4); //[2,3,4]當(dāng)
Array()里的參數(shù)個數(shù)大于 1 的時候,表示的是數(shù)組元素。Array.of()方法不管里面參數(shù)的個數(shù)多少,都將其轉(zhuǎn)為數(shù)組的元素。var arr = Array.of(3); console.log(arr); //[3]
對象
1.對象解構(gòu)賦值
解構(gòu)不僅可以用于數(shù)組,還可以用于對象。對象的解構(gòu)與數(shù)組有一個重要的不同。數(shù)組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
let { name, work } = { name: 'glh', work: 'code' };
console.log(name, work); // glh code
2.對象的擴(kuò)展
-
對象的簡寫
-
當(dāng)變量名和屬性名同名式,省略同名的屬性值
const foo = 'bar'; const baz = { foo }; // 等同于 const baz = { foo: foo }; -
省略方法中的 function
const obj = { method() { return 'hello'; }, }; // 等同于 const obj = { method: function () { return 'hello'; }, }; -
屬性的賦值器(setter)和取值器(getter)
const obj = { name: 'glh', get com() { return this.name; }, set work(value) { this.name = this.name + value; }, }; console.log(obj.com); // glh obj.work = 'hello'; console.log(obj.name); //glhhello -
屬性名表達(dá)式
es5 中定義對象的屬性有兩種方法,一種是用標(biāo)識符(本質(zhì)為字符串)做屬性,一種是用表達(dá)式做屬性
方法一; obj.name = 'a'; // 方法二 obj['name'] = 'a'; var obj = { name: 'a', };如果使用大括號定義對象,那么在 es5 中只能使用標(biāo)識符定義屬性。
var obj = { name: 'a', };但是 ECMAScript2015 在使用大括號定義對象的時候,允許使用表達(dá)式定義屬性,把表達(dá)式放在方括號中。
let name = 'a'; const obj = { [name]: 'glh', }; console.log(obj); //{a: "glh"}
-
3.擴(kuò)展運(yùn)算符
-
用于對象的解構(gòu)
-
對象的解構(gòu)賦值用于從一個對象取值,相當(dāng)于將目標(biāo)對象自身的所有可遍歷的(enumerable)、但尚未被讀取的屬性,分配到指定的對象上面。所有的鍵和它們的值,都會拷貝到新對象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; console.log(z); //{a: 3, b: 4}上面代碼中,變量
z是解構(gòu)賦值所在的對象。它獲取等號右邊的所有尚未讀取的鍵(a和b),將它們連同值一起拷貝過來。注意: 1.解構(gòu)賦值必須是最后一個參數(shù)。2.解構(gòu)賦值的拷貝是淺拷貝。
-
-
用于擴(kuò)展運(yùn)算
-
對象的擴(kuò)展運(yùn)算符(
...)用于取出參數(shù)對象的所有可遍歷屬性,拷貝到當(dāng)前對象之中。let z = { name: 'a', work: 'b' }; let n = { ...z }; n; // { name: "a", work: "b" }
-
字符串
1.字符串模板
傳統(tǒng)的字符串里不能使用換行符,必須使用轉(zhuǎn)義符\n 替代,字符串模板里可以使用。模板字符串(template string)是增強(qiáng)版的字符串,用反引號(`)標(biāo)識。它可以當(dāng)作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
-
模板字符串中嵌入變量,需要將變量名寫在
${}之中。大括號內(nèi)部可以放入任意的 JavaScript 表達(dá)式,可以進(jìn)行運(yùn)算,以及引用對象屬性var name = 'glh'; var newName = `歡迎來到${name}`; console.log(newName);
2.標(biāo)簽?zāi)0?/h4>
-
模板字符串的功能,不僅僅是上面這些。它可以緊跟在一個函數(shù)名后面,該函數(shù)將被調(diào)用來處理這個模板字符串。這被稱為“標(biāo)簽?zāi)0濉惫δ堋?/p>
console.log`hello`;
等同于;
console.log(['hello']);
標(biāo)簽?zāi)0迤鋵?shí)不是模板,而是函數(shù)調(diào)用的一種特殊形式。“標(biāo)簽”指的就是函數(shù),緊跟在后面的模板字符串就是它的參數(shù)。
注意:如果模板字符里面有變量,就不是簡單的調(diào)用了,而是會將模板字符串先處理成多個參數(shù),再調(diào)用函數(shù)。
const name = 'Tony';
const age = 18;
// const string = `My name is ${name}, Age is ${age}`;
// console.log(string);
// 標(biāo)簽函數(shù)
// const str = console.log`My name is ${name}, Age is ${age}`;
// 標(biāo)簽函數(shù)作用是對字符串加工 返回一個新值。利用這個特性可以做語言切換(國際化),模板引擎插件
const Tag = (string, ...rest) => {
console.log(string);
console.log(rest);
return string.join();
};
const result = Tag`My name is ${name}, Age is ${age}`;
console.log(result);
3.擴(kuò)展的方法
-
字符串實(shí)例的方法
-
includes()
返回布爾值,表示是否找到了參數(shù)字符串
const name = 'glh';
const a = `Error: foo is not defined: ${name}`;
var b = a.includes('Err');
console.log(b); // true
-
startsWith()
返回布爾值,表示參數(shù)字符串是否在原字符串的頭部
const name = 'glh';
const a = `Error: foo is not defined: ${name}`;
const b = a.startsWith('Err');
console.log(b); / /true
-
endsWith()
返回布爾值,表示參數(shù)字符串是否在原字符串的尾部
const name = 'glh';
const a = `Error: foo is not defined: ${name}`;
const b = a.endsWith('glh');
console.log(b); // true
函數(shù)
參數(shù)默認(rèn)值
-
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面。
function fn(a, b = 'glh ') {
console.log(a + b);
}
fn('hello'); //hello glh
-
注意:
- 參數(shù)變量是默認(rèn)聲明的,所以不能用
let或const再次聲明.
- 使用參數(shù)默認(rèn)值時,函數(shù)不能有同名參數(shù)
-
參數(shù)默認(rèn)值的位置
-
通常情況下,定義了默認(rèn)值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因?yàn)檫@樣比較容易看出來,到底省略了哪些參數(shù)。如果非尾部的參數(shù)設(shè)置默認(rèn)值,實(shí)際上這個參數(shù)是沒法省略的。
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined]
f(, 1) // 報錯
rest 參數(shù)
-
ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
-
rest 參數(shù)和函數(shù)中的參數(shù)解構(gòu)有什么區(qū)別
- rest 參數(shù)是發(fā)生在函數(shù)的定義階段,函數(shù)的額參數(shù)解構(gòu)是發(fā)生在函數(shù)的調(diào)用階段
- 二者互為逆運(yùn)算
function add(...values) {
//這是rest參數(shù)
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
var arr = [1, 2, 3];
function fn(a, b, c) {
console.log(a + b + c);
}
fn(...arr); //6 這是參數(shù)的解構(gòu)
箭頭函數(shù)
- ES6 允許使用“箭頭”(
=>)定義函數(shù)。
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
模板字符串的功能,不僅僅是上面這些。它可以緊跟在一個函數(shù)名后面,該函數(shù)將被調(diào)用來處理這個模板字符串。這被稱為“標(biāo)簽?zāi)0濉惫δ堋?/p>
console.log`hello`;
等同于;
console.log(['hello']);
標(biāo)簽?zāi)0迤鋵?shí)不是模板,而是函數(shù)調(diào)用的一種特殊形式。“標(biāo)簽”指的就是函數(shù),緊跟在后面的模板字符串就是它的參數(shù)。
注意:如果模板字符里面有變量,就不是簡單的調(diào)用了,而是會將模板字符串先處理成多個參數(shù),再調(diào)用函數(shù)。
const name = 'Tony';
const age = 18;
// const string = `My name is ${name}, Age is ${age}`;
// console.log(string);
// 標(biāo)簽函數(shù)
// const str = console.log`My name is ${name}, Age is ${age}`;
// 標(biāo)簽函數(shù)作用是對字符串加工 返回一個新值。利用這個特性可以做語言切換(國際化),模板引擎插件
const Tag = (string, ...rest) => {
console.log(string);
console.log(rest);
return string.join();
};
const result = Tag`My name is ${name}, Age is ${age}`;
console.log(result);
字符串實(shí)例的方法
-
includes()
返回布爾值,表示是否找到了參數(shù)字符串
const name = 'glh'; const a = `Error: foo is not defined: ${name}`; var b = a.includes('Err'); console.log(b); // true -
startsWith()
返回布爾值,表示參數(shù)字符串是否在原字符串的頭部
const name = 'glh'; const a = `Error: foo is not defined: ${name}`; const b = a.startsWith('Err'); console.log(b); / /true -
endsWith()
返回布爾值,表示參數(shù)字符串是否在原字符串的尾部
const name = 'glh'; const a = `Error: foo is not defined: ${name}`; const b = a.endsWith('glh'); console.log(b); // true
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面。
function fn(a, b = 'glh ') {
console.log(a + b);
}
fn('hello'); //hello glh
注意:
- 參數(shù)變量是默認(rèn)聲明的,所以不能用
let或const再次聲明. - 使用參數(shù)默認(rèn)值時,函數(shù)不能有同名參數(shù)
參數(shù)默認(rèn)值的位置
-
通常情況下,定義了默認(rèn)值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因?yàn)檫@樣比較容易看出來,到底省略了哪些參數(shù)。如果非尾部的參數(shù)設(shè)置默認(rèn)值,實(shí)際上這個參數(shù)是沒法省略的。
function f(x = 1, y) { return [x, y]; } f() // [1, undefined] f(2) // [2, undefined] f(, 1) // 報錯
ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
rest 參數(shù)和函數(shù)中的參數(shù)解構(gòu)有什么區(qū)別
- rest 參數(shù)是發(fā)生在函數(shù)的定義階段,函數(shù)的額參數(shù)解構(gòu)是發(fā)生在函數(shù)的調(diào)用階段
- 二者互為逆運(yùn)算
function add(...values) {
//這是rest參數(shù)
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
var arr = [1, 2, 3];
function fn(a, b, c) {
console.log(a + b + c);
}
fn(...arr); //6 這是參數(shù)的解構(gòu)
=>)定義函數(shù)。var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分。
var f = () => 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ù)直接返回一個對象,必須在對象外面加上括號,否則會報錯。
let getItem = id => { id: id, name: "Temp" }; //報錯
let getItem = id => ({ id: id, name: "Temp" });//不報錯
- 箭頭函數(shù)有幾個使用注意點(diǎn)
- 函數(shù)體內(nèi)的
this對象,就是定義時所在的對象,而不是調(diào)用時所在的對象。 - 不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用
new命令,否則會拋出一個錯誤。 - 不可以使用
arguments對象,該對象在函數(shù)體內(nèi)不存在。如果要用,可以用 rest 參數(shù)代替。
- 函數(shù)體內(nèi)的
var name = "web"
var obj={
name: "a",
fn(){
var t = setTimeout(function() {
console.log(this.name)// web this是window(瀏覽器環(huán)境)
},1000)
}
}
obj.fn()
-----------------------------------
var name = "web"
var obj={
name: "a",
fn(){
var t = setTimeout(() => {
console.log(this.name)// a this是obj
},1000)
}
}
obj.fn()
Object
Object.assign()
-
Object.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性,復(fù)制到目標(biāo)對象(target)。const target = { a: 123, b: 123, }; const sourcel = { a: 456, c: 456, }; const result = Object.assign(target, sourcel); console.log(target); //{a: 456, b: 123, c: 456} console.log(target === result); //true如果目標(biāo)對象與源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。且
assign()的返回值就是第一個對象。如果有多個源對象有同名屬性,依然是后面的會覆蓋前面的屬性
const target = { a: 1, b: 1 }; const source1 = { b: 2, c: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target; // {a:1, b:2, c:3} -
利用
Object.assign()復(fù)制一個對象,且其中一個對象的修改不會影響到另一個對象const source = { a: 123, }; var obj = Object.assign({}, source); obj.a = 456; console.log(obj); //{a: 456} console.log(source); //{a: 123}
Object.is()
-
Object.is就是用來比較兩個值是否嚴(yán)格相等,與嚴(yán)格比較運(yùn)算符(===)的行為基本一致。ES5 比較兩個值是否相等,只有兩個運(yùn)算符:相等運(yùn)算符(
==)和嚴(yán)格相等運(yùn)算符(===)。它們都有缺點(diǎn),前者會自動轉(zhuǎn)換數(shù)據(jù)類型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一種運(yùn)算,在所有環(huán)境中,只要兩個值是一樣的,它們就應(yīng)該相等。console.log(Object.is(+0, -0)); //false console.log(+0 === -0); //true console.log(Object.is(NaN, NaN)); //true console.log(NaN === NaN); //false
Proxy
概述
Proxy 可以理解成一個快遞員,我們發(fā)快遞還是接受快遞,都需要這個快遞員充當(dāng)一個代理的作用。ES6 原生提供 Proxy 構(gòu)造函數(shù),用來生成 Proxy 實(shí)例,這個實(shí)例就是一個代理對象(快遞員)。
var proxy = new Proxy(target, handler);
目標(biāo)對象
這個代理對象有兩個參數(shù),一個是代理的目標(biāo)對象,第二個也是一個對象,它是配置對象,用來定制代理的攔截行為。
const person = {
name: 'glh',
age: 20,
};
const personProxy = new Proxy(person, {
get(target, property) {
console.log(target, property); //person{name:"glh",age:20}
return 100;
},
set() {},
});
配置對象
-
配置對象中一般有兩個方法
get和set,get是用來攔截對目標(biāo)對象屬性的訪問請求。get方法中有兩個參數(shù),第一個參數(shù)是目標(biāo)對象,第二個參數(shù)是訪問的那個屬性。const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property) { console.log(target, property); return 100; }, set() {}, }); console.log(personProxy.name); //100
注意,這個get方法的返回值就是我們獲取的這個屬性的返回值。
-
這個
get方法中有三個參數(shù),一個是代理的目標(biāo)對象,一個是代理的處理對象,第三個參數(shù)是 proxy 實(shí)例本身,且第三個參數(shù)是可選參數(shù)。const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property, o) { console.log(o); //proxy {name:"glh",age:20} return property in target ? target[property] : undefined; }, set() {}, }); console.log(personProxy.age); //20 -
這個
set方法用來攔截某個屬性的賦值操作,可以接受四個參數(shù),依次為目標(biāo)對象、屬性名、屬性值和 Proxy 實(shí)例本身,其中最后一個參數(shù)可選const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property, o) { return property in target ? target[property] : undefined; }, set(obj, pro, value, o) { console.log(obj, pro, value, o); }, }); console.log((personProxy.name = 'x')); //{name: "glh", age: 20} "name" "x" Proxy {name: "zce", age: 20}可以去設(shè)置一些屬性或修改
const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property, o) { return property in target ? target[property] : undefined; }, set(target, pro, value, o) { //可以做一些內(nèi)部校驗(yàn) target[pro] = value; }, }); console.log((personProxy.name = 'hello')); person; // {name:"glh",age:20}
Reflect
概述
Reflect對象與Proxy對象一樣,也是 ES6 為了操作對象而提供的新 API。Reflect對象的設(shè)計目的有這樣幾個。
將
Object對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty),放到Reflect對象上?,F(xiàn)階段,某些方法同時在Object和Reflect對象上部署,未來的新方法將只部署在Reflect對象上。也就是說,從Reflect對象上可以拿到語言內(nèi)部的方法-
修改某些
Object方法的返回結(jié)果,讓其變得更合理。比如,Object.defineProperty(obj, name, desc)在無法定義屬性時,會拋出一個錯誤,而Reflect.defineProperty(obj, name, desc)則會返回false// 老寫法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新寫法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure } -
讓
Object操作都變成函數(shù)行為。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函數(shù)行為。// 老寫法 'assign' in Object; // true // 新寫法 Reflect.has(Object, 'assign'); // true
靜態(tài)方法
-
Reflect.get
-
Reflect.get(target, name, receiver),Reflect.get方法查找并返回target對象的name屬性,如果沒有該屬性,則返回undefinedvar myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; Reflect.get(myObject, 'foo'); // 1 Reflect.get(myObject, 'bar'); // 2 Reflect.get(myObject, 'baz'); // 3 -
如果
name屬性部署了讀取函數(shù)(getter),則讀取函數(shù)的this綁定receiver。var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; var myReceiverObject = { foo: 4, bar: 4, }; Reflect.get(myObject, 'baz', myReceiverObject); // 8 -
如果第一個參數(shù)不是對象,
Reflect.get方法會報錯。Reflect.get(1, 'foo'); // 報錯 Reflect.get(false, 'foo'); // 報錯
-
-
Reflect.set
-
Reflect.set(target, name, value, receiver),Reflect.set方法設(shè)置target對象的name屬性等于valuevar myObject = { foo: 1, }; myObject.foo; // 1 Reflect.set(myObject, 'foo', 2); myObject.foo; // 2 -
如果
name屬性設(shè)置了賦值函數(shù),則賦值函數(shù)的this綁定receiver。var myObject = { foo: 4, set bar(value) { return (this.foo = value); }, }; var myReceiverObject = { foo: 0, }; Reflect.set(myObject, 'bar', 1, myReceiverObject); myObject.foo; // 4 myReceiverObject.foo; // 1
-
-
Reflect.has
-
Reflect.has(obj, name),Reflect.has方法對應(yīng)name in obj里面的in運(yùn)算符var myObject = { foo: 1, }; // 舊寫法 'foo' in myObject; // true // 新寫法 Reflect.has(myObject, 'foo'); // true如果
Reflect.has()方法的第一個參數(shù)不是對象,會報錯。
-
-
Reflect.deleteProperty
-
Reflect.deleteProperty(obj, name),
Reflect.deleteProperty方法等同于delete obj[name],用于刪除對象的屬性const myObj = { foo: 'bar' }; // 舊寫法 delete myObj.foo; // 新寫法 Reflect.deleteProperty(myObj, 'foo');該方法返回一個布爾值。如果刪除成功,或者被刪除的屬性不存在,返回
true;刪除失敗,被刪除的屬性依然存在,返回false。如果Reflect.deleteProperty()方法的第一個參數(shù)不是對象,會報錯.
-
Promise
概述
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對象。
所謂Promise,簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處理。
promise 特點(diǎn)
Promise對象有以下兩個特點(diǎn)。
(1)對象的狀態(tài)不受外界影響。Promise對象代表一個異步操作,有三種狀態(tài):pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失?。V挥挟惒讲僮鞯慕Y(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結(jié)果。Promise對象的狀態(tài)改變,只有兩種可能:從pending變?yōu)?code>fulfilled和從pending變?yōu)?code>rejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,這時就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù),也會立即得到這個結(jié)果。這與事件(Event)完全不同,事件的特點(diǎn)是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易。
promise 使用方法
ES6 規(guī)定,
Promise對象是一個構(gòu)造函數(shù),用來生成Promise實(shí)例。-
Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject。它們是兩個函數(shù),由 JavaScript 引擎提供,不用自己部署。var p = new Promise(function (resolve, reject) { if (true) { resolve(data); } else { reject(data); } });resolve函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved),在異步操作成功時調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;reject函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時調(diào)用,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。 -
Promise實(shí)例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)。p.then( function (value) { // success業(yè)務(wù)處理 }, function (error) { // failure } );then方法可以接受兩個回調(diào)函數(shù)作為參數(shù)。第一個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)?code>resolved時調(diào)用,第二個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)?code>rejected時調(diào)用。其中,第二個函數(shù)是可選的,不一定要提供。這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù)。-
看一個簡單的例子
function time(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } time(1000).then(value => { console.log(value); });
-
-
Promise 新建后就會立即執(zhí)行。
let promise = new Promise(function (resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function () { console.log('resolved.'); }); console.log('Hi!'); //Promise //Hi //resolved
class
概述
ES6 提供了更接近傳統(tǒng)語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過class關(guān)鍵字,可以定義類。基本上,ES6 的class可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已。上面的代碼用 ES6 的class改寫,就是下面這樣。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
基本介紹
-
constructor()-
constructor方法是類的默認(rèn)方法,通過new命令生成對象實(shí)例時,自動調(diào)用該方法。一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被默認(rèn)添加。
-
-
類的實(shí)例
-
生成類的實(shí)例的寫法,與 ES5 完全一樣,也是使用
new命令。前面說過,如果忘記加上new,像函數(shù)那樣調(diào)用Class,將會報錯。class Point { // ... } // 報錯 var point = Point(2, 3); // 正確 var point = new Point(2, 3); -
與 ES5 一樣,實(shí)例的屬性除非顯式定義在其本身(即定義在
this對象上),否則都是定義在原型上(即定義在class上)。class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.toString(); // (2, 3) point.hasOwnProperty('x'); // true point.hasOwnProperty('y'); // true point.hasOwnProperty('toString'); // false point.__proto__.hasOwnProperty('toString'); // true -
與 ES5 一樣,類的所有實(shí)例共享一個原型對象
var p1 = new Point(2, 3); var p2 = new Point(3, 2); p1.__proto__ === p2.__proto__; //true
-
-
getter 和 setter
-
與 ES5 一樣,在“類”的內(nèi)部可以使用
get和set關(guān)鍵字,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為。class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: ' + value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop; // 'getter'
-
-
屬性表達(dá)式
-
類的屬性名,可以采用表達(dá)式
let methodName = 'getArea'; class Square { constructor(length) { // ... } [methodName]() { // ... } }上面代碼中,
Square類的方法名getArea,是從表達(dá)式得到的
-
static
-
類相當(dāng)于實(shí)例的原型,所有在類中定義的方法,都會被實(shí)例繼承。如果在一個方法前,加上
static關(guān)鍵字,就表示該方法不會被實(shí)例繼承,而是直接通過類來調(diào)用,這就稱為“靜態(tài)方法”class Foo { static classMethod() { return 'hello'; } } Foo.classMethod(); // 'hello' var foo = new Foo(); foo.classMethod(); // TypeError: foo.classMethod is not a function解說:上面代碼中,
Foo類的classMethod方法前有static關(guān)鍵字,表明該方法是一個靜態(tài)方法,可以直接在Foo類上調(diào)用(Foo.classMethod()),而不是在Foo類的實(shí)例上調(diào)用。如果在實(shí)例上調(diào)用靜態(tài)方法,會拋出一個錯誤,表示不存在該方法。 -
注意,如果靜態(tài)方法包含
this關(guān)鍵字,這個this指的是類,而不是實(shí)例class Foo { static bar() { this.baz(); } static baz() { console.log('hello'); } baz() { console.log('world'); } } Foo.bar(); // hello解說:上面代碼中,靜態(tài)方法
bar調(diào)用了this.baz,這里的this指的是Foo類,而不是Foo的實(shí)例,等同于調(diào)用Foo.baz。另外,從這個例子還可以看出,靜態(tài)方法可以與非靜態(tài)方法重名。 -
父類的靜態(tài)方法,可以被子類繼承
class Foo { static classMethod() { return 'hello'; } } class Bar extends Foo {} Bar.classMethod(); // 'hello' -
靜態(tài)屬性
靜態(tài)屬性指的是 Class 本身的屬性,即
Class.propName,而不是定義在實(shí)例對象(this)上的屬性ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法,沒有靜態(tài)屬性。現(xiàn)在有一個提案提供了類的靜態(tài)屬性,寫法是在實(shí)例屬性的前面,加上
static關(guān)鍵字。class MyClass { static myStaticProp = 42; constructor() { console.log(MyClass.myStaticProp); // 42 } }
繼承
-
簡介
-
Class 可以通過
extends關(guān)鍵字實(shí)現(xiàn)繼承,這比 ES5 的通過修改原型鏈實(shí)現(xiàn)繼承,要清晰和方便很多。class Point {} class ColorPoint extends Point {}解說:上面代碼定義了一個
ColorPoint類,該類通過extends關(guān)鍵字,繼承了Point類的所有屬性和方法。但是由于沒有部署任何代碼,所以這兩個類完全一樣,等于復(fù)制了一個Point類 -
子類必須在
constructor方法中調(diào)用super方法,否則新建實(shí)例時會報錯。這是因?yàn)樽宇愖约旱?code>this對象,必須先通過父類的構(gòu)造函數(shù)完成塑造,得到與父類同樣的實(shí)例屬性和方法,然后再對其進(jìn)行加工,加上子類自己的實(shí)例屬性和方法。如果不調(diào)用super方法,子類就得不到this對象。class Point { /* ... */ } class ColorPoint extends Point { constructor() {} } let cp = new ColorPoint(); // ReferenceError -
在子類的構(gòu)造函數(shù)中,只有調(diào)用
super之后,才可以使用this關(guān)鍵字,否則會報錯。這是因?yàn)樽宇悓?shí)例的構(gòu)建,基于父類實(shí)例,只有super方法才能調(diào)用父類實(shí)例。class Point { constructor(x, y) { this.x = x; this.y = y; } } class ColorPoint extends Point { constructor(x, y, color) { this.color = color; // ReferenceError super(x, y); this.color = color; // 正確 } }
-
-
super
-
super這個關(guān)鍵字,既可以當(dāng)作函數(shù)使用,也可以當(dāng)作對象使用。在這兩種情況下,它的用法完全不同-
第一種情況,
super作為函數(shù)調(diào)用時,代表父類的構(gòu)造函數(shù)。ES6 要求,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù)。class A {} class B extends A { constructor() { super(); } }解說:上面代碼中,子類
B的構(gòu)造函數(shù)之中的super(),代表調(diào)用父類的構(gòu)造函數(shù)。這是必須的,否則 JavaScript 引擎會報錯。super雖然代表了父類A的構(gòu)造函數(shù),但是返回的是子類B的實(shí)例,即super內(nèi)部的this指的是B的實(shí)例,因此super()在這里相當(dāng)于A.prototype.constructor.call(this)` -
第二種情況,
super作為對象時,在普通方法中,指向父類的原型對象;在靜態(tài)方法中,指向父類class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 } } let b = new B(); -
由于
super指向父類的原型對象(prototype),所以定義在父類實(shí)例上的方法或?qū)傩?,是無法通過super調(diào)用的class A { constructor() { this.p = 2; } } class B extends A { get m() { return super.p; } } let b = new B(); b.m; // undefined
-
-
Set
基本用法
ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。
Set本身是一個構(gòu)造函數(shù),用來生成 Set 數(shù)據(jù)結(jié)構(gòu)。-
Set函數(shù)可以接受一個數(shù)組(或者具有 iterable 接口的其他數(shù)據(jù)結(jié)構(gòu))作為參數(shù),用來初始化。const set = new Set([1, 2, 3, 4, 4]); console.log(set); //[1,2,3,4]-
數(shù)組去重
[...new Set([1, 2, 3, 2, 4, 5])]; //[1,2,3,4,5] -
字符串去重
[...new Set('ababbc')].join(''); //"abc"
-
屬性和方法
-
Set 結(jié)構(gòu)的實(shí)例有以下屬性。
-
Set.prototype.constructor:構(gòu)造函數(shù),默認(rèn)就是Set函數(shù)。 -
Set.prototype.size:返回Set實(shí)例的成員總數(shù)。
-
-
Set 實(shí)例的方法分為兩大類
-
操作方法(用于操作數(shù)據(jù))
-
Set.prototype.add(value):添加某個值,返回 Set 結(jié)構(gòu)本身const items = new Set([]); items.add(1).add(2).add(3); console.dir(items); //[1,2,3] -
Set.prototype.delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功const items = new Set([12, 23, 34]); var b = items.delete(12); console.log(b); //true console.log(items); //Set(2) {23, 34} Set.prototype.has(value):返回一個布爾值,表示該值是否為Set的成員。-
Set.prototype.clear():清除所有成員,沒有返回值const items = new Set([12, 23, 34]); var b = items.clear(12); console.log(b); //undefined console.log(items); //Set(0) {}
-
-
遍歷方法(用于遍歷成員)
Set.prototype.keys():返回鍵名的遍歷器Set.prototype.values():返回鍵值的遍歷器-
Set.prototype.entries()
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"] -
Set.prototype.forEach()
let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)); // 1 : 1 // 4 : 4 // 9 : 9
-
Map
概述
JavaScript 的對象(Object),本質(zhì)上是鍵值對的集合(Hash 結(jié)構(gòu)),但是傳統(tǒng)上只能用字符串當(dāng)作鍵。這給它的使用帶來了很大的限制。為了解決這個問題,ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)。它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當(dāng)作鍵。也就是說,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對應(yīng),是一種更完善的 Hash 結(jié)構(gòu)實(shí)現(xiàn)。如果你需要“鍵值對”的數(shù)據(jù)結(jié)構(gòu),Map 比 Object 更合適。
基本用法
-
作為構(gòu)造函數(shù),Map 也可以接受一個數(shù)組作為參數(shù)。該數(shù)組的成員是一個個表示鍵值對的數(shù)組
const map = new Map([ ['name', '張三'], ['title', 'Author'], ]); map.size; // 2 map.has('name'); // true map.get('name'); // "張三" map.has('title'); // true map.get('title'); // "Author"
屬性和方法
-
size 屬性,
size屬性返回 Map 結(jié)構(gòu)的成員總數(shù)。const map = new Map(); map.set('foo', true); map.set('bar', false); map.size; // 2 -
Map.prototype.set(key, value)
set方法設(shè)置鍵名key對應(yīng)的鍵值為value,然后返回整個 Map 結(jié)構(gòu)。如果key已經(jīng)有值,則鍵值會被更新,否則就新生成該鍵, set()方法返回的是 set 對象可以采用鏈?zhǔn)綄懛?/p>const m = new Map(); m.set('edition', 6); // 鍵是字符串 m.set(262, 'standard'); // 鍵是數(shù)值 m.set(undefined, 'nah'); // 鍵是 undefined -
Map.prototype.get(key)
get方法讀取key對應(yīng)的鍵值,如果找不到key,返回undefined。const m = new Map(); const hello = function () { console.log('hello'); }; m.set(hello, 'Hello ES6!'); // 鍵是函數(shù) m.get(hello); // Hello ES6! -
Map.prototype.has(key)
has方法返回一個布爾值,表示某個鍵是否在當(dāng)前 Map 對象之中const 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
-
Map.prototype.delete(key)
delete方法刪除某個鍵,返回true。如果刪除失敗,返回false。const m = new Map(); m.set(undefined, 'nah'); m.has(undefined); // true m.delete(undefined); m.has(undefined); // false -
Map.prototype.clear()
clear方法清除所有成員,沒有返回值let map = new Map(); map.set('foo', true); map.set('bar', false); map.size; // 2 map.clear(); map.size; // 0
遍歷
Map.prototype.keys():返回鍵名的遍歷器。Map.prototype.values():返回鍵值的遍歷器Map.prototype.entries():返回所有成員的遍歷器-
Map.prototype.forEach():遍歷 Map 的所有成員const 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); } // "F" "no" // "T" "yes" map.forEach(function (value, key, map) { console.log('Key: %s, Value: %s', key, value); });forEach方法還可以接受第二個參數(shù),用來綁定this。
Symbol
概述
ES5 的對象屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個他人提供的對象,但又想為這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現(xiàn)有方法產(chǎn)生沖突。如果有一種機(jī)制,保證每個屬性的名字都是獨(dú)一無二的就好了,這樣就從根本上防止屬性名的沖突。這就是 ES6 引入Symbol的原因。ES6 引入了一種新的原始數(shù)據(jù)類型Symbol,表示獨(dú)一無二的值。它是 JavaScript 語言的第七種數(shù)據(jù)類型,前六種是:undefined、null、布爾值(Boolean)、字符串(String)、數(shù)值(Number)、對象(Object)。Symbol 值通過Symbol函數(shù)生成。這就是說,對象的屬性名現(xiàn)在可以有兩種類型,一種是原來就有的字符串,另一種就是新增的 Symbol 類型。凡是屬性名屬于 Symbol 類型,就都是獨(dú)一無二的,可以保證不會與其他屬性名產(chǎn)生沖突。
var obj = {
say: 'a',
};
var say = Symbol(); //say 是symbol類型
obj[say] = 'web';
console.log(obj); //{say: "a", Symbol(): "web"}
語法
Symbol函數(shù)前不能使用new命令,否則會報錯Symbol函數(shù)可以接受一個字符串作為參數(shù),表示對 Symbol 實(shí)例的描述,主要是為了在控制臺顯示,或者轉(zhuǎn)為字符串時,比較容易區(qū)分-
每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為標(biāo)識符,用于對象的屬性名,就能保證不會出現(xiàn)同名的屬性
var a = Symbol(); var b = Symbol(); console.log(a === b); //false var a = Symbol('a'); var b = Symbol('b'); console.log(a === b); //false注意:
Symbol函數(shù)的參數(shù)只是表示對當(dāng)前 Symbol 值的描述,因此相同參數(shù)的Symbol函數(shù)的返回值是不相等的var a = Symbol('a'); var b = Symbol('a'); console.log(a === b); //false -
Symbol 值不能與其他類型的值進(jìn)行運(yùn)算,會報錯
let sym = Symbol('My symbol'); 'your symbol is ' + sym // TypeError: can't convert symbol to string `your symbol is ${sym}`; // TypeError: can't convert symbol to string -
Symbol 值作為對象屬性名時,不能用點(diǎn)運(yùn)算符
const mySymbol = Symbol(); const a = {}; a.mySymbol = 'Hello!'; console.log(a[mySymbol]); //undefined console.log(a['mySymbol']); //hello
-
Symbol 作為屬性名,遍歷對象的時候,該屬性不會出現(xiàn)在
for...in、for...of循環(huán)中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有屬性,有一個Object.getOwnPropertySymbols()方法,可以獲取指定對象的所有 Symbol 屬性名。該方法返回一個數(shù)組,成員是當(dāng)前對象的所有用作屬性名的 Symbol 值。const obj = {}; let a = Symbol('a'); let b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; const objectSymbols = Object.getOwnPropertySymbols(obj); console.log(objectSymbols); //[Symbol(a), Symbol(b)] -
有時,我們希望重新使用同一個 Symbol 值,
Symbol.for()方法可以做到這一點(diǎn)。它接受一個字符串作為參數(shù),然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值。如果有,就返回這個 Symbol 值,否則就新建一個以該字符串為名稱的 Symbol 值,并將其注冊到全局。let s1 = Symbol.for('foo'); let s2 = Symbol.for('foo'); s1 === s2; // trueSymbol.for()與Symbol()這兩種寫法,都會生成新的 Symbol。它們的區(qū)別是,前者會被登記在全局環(huán)境中供搜索,后者不會。Symbol.for()不會每次調(diào)用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key是否已經(jīng)存在,如果不存在才會新建一個值。比如,如果你調(diào)用Symbol.for("cat")30 次,每次都會返回同一個 Symbol 值,但是調(diào)用Symbol("cat")30 次,會返回 30 個不同的 Symbol 值Symbol.for('bar') === Symbol.for('bar'); // true Symbol('bar') === Symbol('bar');由于
Symbol()寫法沒有登記機(jī)制,所以每次調(diào)用都會返回一個不同的值。Symbol.for()為 Symbol 值登記的名字,是全局環(huán)境的,不管有沒有在全局環(huán)境運(yùn)行function foo() { return Symbol.for('bar'); } const x = foo(); const y = Symbol.for('bar'); console.log(x === y); // true
可迭代接口
Iterater 的概念
-
簡單介紹
JavaScript 原有的表示“集合”的數(shù)據(jù)結(jié)構(gòu),主要是數(shù)組(
Array)和對象(Object),ES6 又添加了Map和Set。這樣就有了四種數(shù)據(jù)集合,用戶還可以組合使用它們,定義自己的數(shù)據(jù)結(jié)構(gòu),比如數(shù)組的成員是Map,Map的成員是對象。這樣就需要一種統(tǒng)一的接口機(jī)制,來處理所有不同的數(shù)據(jù)結(jié)構(gòu)。遍歷器(Iterator)就是這樣一種機(jī)制。它是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機(jī)制。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)。
Iterator 的作用有三個:一是為各種數(shù)據(jù)結(jié)構(gòu),提供一個統(tǒng)一的、簡便的訪問接口;二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列;三是 ES6 創(chuàng)造了一種新的遍歷命令
for...of循環(huán),Iterator 接口主要供for...of消費(fèi)。 -
Iterator 的遍歷過程
創(chuàng)建一個指針對象,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說,遍歷器對象本質(zhì)上,就是一個指針對象。
第一次調(diào)用指針對象的
next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員第二次調(diào)用指針對象的
next方法,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個成員-
不斷調(diào)用指針對象的
next方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置每一次調(diào)用
next方法,都會返回數(shù)據(jù)結(jié)構(gòu)的當(dāng)前成員的信息。具體來說,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當(dāng)前成員的值,done屬性是一個布爾值,表示遍歷是否結(jié)束。簡單的Iterator遍歷器的實(shí)現(xiàn); var it = easyIterator(['a', 'b']); it.next(); // { value: "a", done: false } it.next(); // { value: "b", done: false } it.next(); // { value: undefined, done: true } function easyIterator(array) { var nextIndex = 0; return { next: function () { return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { value: undefined, done: true }; }, }; }
Iterater 接口
-
字符串 數(shù)組 set map arguments 都有 iterater 接口,nodelist 集合,都可以用 for of 遍歷
var st = 'glh'; for (i of st) { console.log(i); // g l h } var arr = [1, 2]; for (v of arr) { console.log(v); //1 2 } function fn(a, b, c) { for (i of arguments) { console.log(i); //1 2 3 } } fn(1, 2, 3);
Modules
概述
- JavaScript 一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來。其他語言都有這項(xiàng)功能,比如 Ruby 的
require、Python 的import,甚至就連 CSS 都有@import,但是 JavaScript 任何這方面的支持都沒有,這對開發(fā)大型的、復(fù)雜的項(xiàng)目形成了巨大障礙。ES6 在語言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡單,完全可以取代 CommonJS 和 AMD 規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。
語法
-
export
export命令用于規(guī)定模塊的對外接口-
一個模塊就是一個獨(dú)立的文件。該文件內(nèi)部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內(nèi)部的某個變量,就必須使用
export關(guān)鍵字輸出該變量。下面是一個 JS 文件,里面使用export命令輸出變量//demo.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 };
-
import
import命令用于輸入其他模塊提供的功能-
import命令接受一對大括號,里面指定要從其他模塊導(dǎo)入的變量名。大括號里面的變量名,必須與被導(dǎo)入模塊(profile.js)對外接口的名稱相同// main.js import { firstName, lastName, year } from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
-
export default
為了給用戶提供方便,就要用到
export default命令,為模塊指定默認(rèn)輸出本質(zhì)上,
export default就是輸出一個叫做default的變量或方法,然后系統(tǒng)允許你為它取任意名字在
import命令后面,不再使用大括號-
export default命令用于指定模塊的默認(rèn)輸出。顯然,一個模塊只能有一個默認(rèn)輸出,因此export default命令只能使用一次// export-default.js export default function () { console.log('foo'); }// import-default.js import customName from './export-default'; customName(); // 'foo'
瀏覽器端加載實(shí)現(xiàn)
-
瀏覽器加載 ES6 模塊,也使用標(biāo)簽,但是要加入
type="module"屬性// 01.js export var a = 123;//demo.html <script type="module">import {a} from "./01.js"; console.log(a)//123</script>
-
腳本異步加載
<script src="path/to/myModule.js" defer></script> <script src="path/to/myModule.js" async></script>解說:上面代碼中,標(biāo)簽打開
defer或async屬性,腳本就會異步加載。渲染引擎遇到這一行命令,就會開始下載外部腳本,但不會等它下載和執(zhí)行,而是直接執(zhí)行后面的命令。defer 與 async 的區(qū)別是:defer 要等到整個頁面在內(nèi)存中正常渲染結(jié)束(DOM 結(jié)構(gòu)完全生成,以及其他腳本執(zhí)行完成),才會執(zhí)行;async 一旦下載完,渲染引擎就會中斷渲染,執(zhí)行這個腳本以后,再繼續(xù)渲染。一句話,defer 是“渲染完再執(zhí)行”,async 是“下載完就執(zhí)行”。另外,如果有多個 defer 腳本,會按照它們在頁面出現(xiàn)的順序加載,而多個 async 腳本是不能保證加載順序的。